Linux 一次简单的FrameBuffer驱动开发
设施 | 版本 |
---|---|
CPU | Allwinner F1C200s |
linux | 6.4.0-rc4 |
显示器 | 1.28 inch 16-grayscale OLED 128x128 驱动IC SSD1327 |
Orangepi 5 |
声明
本驱动仓库位于:https://github.com/AllwinnerSuniv/suniv-epd/tree/main/ssd1327
本驱动代码采样自 fbtft(linux/drivers/staging/fbtft) 项目
本案例用到的显示器可从如下链接购买:
模组:
https://item.taobao.com/item.htm?spm=a1z10.3-c-s.w4002-24706531953.9.7b7d6a4bKhv5j9&id=665635617961
面板:
https://item.taobao.com/item.htm?spm=a1z10.3-c-s.w4002-24706531953.11.7b7d6a4bKhv5j9&id=666350647617
思路
这块显示屏是16灰度格式的,这意味着一个像素点需要用4bit来表示,同理1byte表示2像素
我们在fb驱动中获取 rgb565 数据 转成16灰度 发给显示屏即可,转换函数如下:
#define RED(a) ((((a) & 0xf800) >> 11) << 3)
#define GREEN(a) ((((a) & 0x07e0) >> 5) << 2)
#define BLUE(a) (((a) & 0x001f) << 3)
static inline u8 rgb565_to_4bit_grayscale(u16 rgb565)
{
int r, g, b;
__maybe_unused int level;
u16 gray;
/* get each channel and expand them to 8 bit */
r = RED(rgb565);
g = GREEN(rgb565);
b = BLUE(rgb565);
/* convert rgb888 to grayscale */
gray = ((r * 77 + g * 151 + b * 28) >> 8); // 0 ~ 255
if (gray == 0)
return gray;
/*
* so 4-bit grayscale like:
* B3 B2 B1 B0
* 0 0 0 0
* which means have 16 kind of gray
*/
gray /= 16;
return gray;
}
驱动流程如下
- 根据显示器宽高、bpp申请 vmem
- 申请 fbops fbinfo 结构体
- 配置 fbops 中的函数
fbops->fb_read = fb_sys_read;
fbops->fb_write = ssd1327_fb_write;
fbops->fb_fillrect = ssd1327_fb_fillrect;
fbops->fb_copyarea = ssd1327_fb_copyarea;
fbops->fb_imageblit = ssd1327_fb_imageblit;
fbops->fb_setcolreg = ssd1327_fb_setcolreg;
fbops->fb_blank = ssd1327_fb_blank;
- 配置 fbinfo 结构体
snprintf(info->fix.id, sizeof(info->fix.id), "%s", dev->driver->name);
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_TRUECOLOR;
// info->fix.visual = FB_VISUAL_MONO10;
info->fix.xpanstep = 0;
info->fix.ypanstep = 0;
info->fix.ywrapstep = 0;
info->fix.line_length = width * bpp / BITS_PER_BYTE;
info->fix.accel = FB_ACCEL_NONE;
info->fix.smem_len = vmem_size;
info->var.rotate = rotate;
info->var.xres = width;
info->var.yres = height;
info->var.xres_virtual = info->var.xres;
info->var.yres_virtual = info->var.yres;
info->var.bits_per_pixel = bpp;
info->var.nonstd = 1;
info->var.grayscale = 1;
switch (info->var.bits_per_pixel) {
case 1:
case 2:
case 4:
case 8:
info->var.red.offset = info->var.green.offset = info->var.blue.offset = 0;
info->var.red.length = info->var.green.length = info->var.blue.length = 8;
break;
case 16:
info->var.red.offset = 11;
info->var.red.length = 5;
info->var.green.offset = 5;
info->var.green.length = 6;
info->var.blue.offset = 0;
info->var.blue.length = 5;
info->var.transp.offset = 0;
info->var.transp.length = 0;
break;
default:
dev_err(dev, "color depth %d not supported\n",
info->var.bits_per_pixel);
break;
}
info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
- 我们用到了fbdeferred_io 机制,配置如下
fbdefio->delay = HZ / display.fps;
fbdefio->deferred_io = ssd1327_deferred_io;
fb_deferred_io_init(info);
- 调色板的配置,对于rgb565
info->pseudo_palette = &par->pseudo_palette;
/* from pxafb.c */
static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
chan &= 0xffff;
chan >>= 16 - bf->length;
return chan << bf->offset;
}
static int ssd1327_fb_setcolreg(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
unsigned int transp, struct fb_info *info)
{
...
switch (info->fix.visual) {
case FB_VISUAL_TRUECOLOR:
if (regno < 16) {
val = chan_to_field(red, &info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue, &info->var.blue);
((u32 *)(info->pseudo_palette))[regno] = val;
ret = 0;
}
break;
default:
dev_err(info->dev, "unsupported color format!");
ret = -1;
break;
}
...
}
- 其他接口的初始化,例如锁等
- 初始化显示屏硬件,init_display 序列等
- 注册 fbinfo 结构体
- sysfs的管理
fbtft驱动的显示刷新流程
fbops->fb_write = ssd1327_fb_write;
fbops->fb_fillrect = ssd1327_fb_fillrect;
fbops->fb_copyarea = ssd1327_fb_copyarea;
fbops->fb_imageblit = ssd1327_fb_imageblit;
上述接口会根据传入的宽高调用mk_dirty标记脏数据,然后通过schdule deferred_io work调用 update_display 完成区域更新