Hello,
This is driving me crazy. I wrote this custom fb driver for an
organic LED display for an embedded ARM system.
The display is connected trough an I2C bus, therefore the display
buffer is not memory mapped.
Anyway, I want to support mmap() and my driver allocates shadow
buffer with __get_free_pages() which gets periodically copied
to the display by a thread. This is unlike most fb drivers which
just point smem_start to the phisical address of their framebuffer.
>From user space, opening /dev/fb0 and writing to it works just
fine. mmap()'ing the file and writing to it does not have any
effect.
Writing the phisical address in smem_start and letting the
fbgen code do the rest didn't seem to work, so I reimplemented
the fb_mmap hook.
I don't feel confident with the Linux VM, so I tried several
strategies to allocate the shadow buffer, including vmalloc()
and kmalloc().
The virtual framebuffer (vfb) also uses vmalloc() but crashes
calling processes because it confuses physical and virtual
addresses, or so it seems.
Maybe it's just my kernel or my platform... does anybody use
a similar technique? Can anybody point me to known-good code
that approximates my needs?
If you want to review the code below, look for the allocation in
oledfb_init() and usage in oledfb_mmap(). This code runs on
2.4.19-rmk7 because I can't upgrade to a newer kernel on this
target.
/*
* linux/drivers/video/oledfb.c -- STV8102 OLED frame buffer device
*
* Copyright 2006 Develer S.r.l. (http://www.develer.com/)
* Author: Bernardo Innocenti <[EMAIL PROTECTED]>
* Author: Stefano Fedrigo <[EMAIL PROTECTED]>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* Define to either 0 or 1 */
#define OLED_DEBUG 1
/* Driver name used in several places */
#define OLED_NAME "oledfb"
/* Fully qualified driver name for users */
#define OLED_FRIENDLY_NAME "STV8102 OLED"
/* Dimensions in pixels */
#define OLED_WIDTH 128
#define OLED_HEIGHT 33
/* Dimensions in millimeters */
#define OLED_WIDTH_MM 55
#define OLED_HEIGHT_MM 14
/* Size of an horizontal line in bytes */
#define OLED_WIDTH_BYTES ((OLED_WIDTH + 7) / 8)
/* Framebuffer size in bytes */
#define OLED_MEMSIZE (OLED_WIDTH_BYTES * OLED_HEIGHT)
#define OLED_MEMORDER (get_order(PAGE_ALIGN(OLED_MEMSIZE)))
/* OLED refresh delay in milliseconds */
#define OLED_REFRESH_DELAY 300
#define OLED_REFRESH_JIFFIES ((OLED_REFRESH_DELAY * HZ)/1000)
/* Set to 1 to enable an X11-like backfilling pattern */
#define OLED_BACKFILL_PATTERN 0
/* I2C address of the OLED command/data registers */
#define I2C_DRIVERID_STV8102_CMD 0x3C
#define I2C_DRIVERID_STV8102_DATA 0x3D
/* Use a kernel thread to refresh the OLED periodically */
#define CONFIG_OLED_REFRESH_THREAD 1
/* BROKEN: i2c code sleeps in timer context */
#define CONFIG_OLED_REFRESH_TIMER 0
#define OLED_CMD_XSTART 0x00 /* address in lower 4 bits */
#define OLED_CMD_YSTART 0x40 /* address in lower 4 bits */
#define OLED_CMD_DISPON 0xAF
#define OLED_CMD_DISPOFF 0xAE
#define OLED_CMD_MOVE 0x80 /* effects in lower 4 bits */
#define OLED_CMD_HSPEED 0x90 /* speed in lower 3 bits */
#define OLED_CMD_VSPEED 0x98 /* speed in lower 3 bits */
#define OLED_CMD_HMIN 0xC0
#define OLED_CMD_HMAX 0xC2
#define OLED_CMD_VMIN 0xC6
#define OLED_CMD_VMAX 0xC8
/* Missing utility macros */
#define countof(x) (sizeof(x) / sizeof(x[0]))
#ifndef bool
# define bool int
# define false 0
# define true 1
#endif
#if OLED_DEBUG == 1
#define OLED_TRACE printk(KERN_DEBUG "%s:%s()\n", OLED_NAME,
__FUNCTION__)
#define OLED_TRACEMSG(msg,...) printk(KERN_DEBUG "%s:%s(): " msg "\n", \
OLED_NAME, __FUNCTION__, ## __VA_ARGS__)
#elif OLED_DEBUG == 0
#define OLED_TRACE do {} while (0)
#define OLED_TRACEMSG(msg,...)
#else
#error Define OLED_DEBUG to either 0 or 1
#endif
struct oledfb_info {
struct fb_info_gen gen;
/* Shadow buffer for the memory mapped framebuffer */
uint8_t *shadow;
/* Second copy of shadow buffer for optimized refesh */
uint8_t *shadow2;
/* Physical address of shadow buffer as required by fbmem */
unsigned long shadow_phys;
/* I2C client we talk to for OLED command register read/write */
struct i2c_client i2c_cmd;
/* I2C client we talk to for OLED data register write */
struct i2c_client i2c_data;
bool screensaver_running;
atomic_t open_cnt;
#if CONFIG_OLED_REFRESH_THREAD
int thread_pid;
/* Used to tell our refresh thread to quit asap */
/*bool*/ int quitting;
struct