前言
前面我们了解了LCD的基本架构《》,接下来我们拿个具体的实例来分析分析。这样可以了解其大概是如何使用和工作的。
FrameBuffer驱动分析
内核版本:4.20
芯片平台:s3c2410
依然是使用之前的方式进行分析,大部分内容在注释。
(1)装载和卸载函数
static struct platform_driver s3c2410fb_driver = {
.probe = s3c2410fb_probe,
.remove = s3c2410fb_remove,
.suspend = s3c2410fb_suspend,
.resume = s3c2410fb_resume,
.driver = {
.name = "s3c2410-lcd",
},
};
int __init s3c2410fb_init(void)
{
int ret = platform_driver_register(&s3c2410fb_driver);
return ret;
}
static void __exit s3c2410fb_cleanup(void)
{
platform_driver_unregister(&s3c2410fb_driver);
}
module_init(s3c2410fb_init);
module_exit(s3c2410fb_cleanup);
上面比较简单,就是注册一个platform_driver, 控制器一般都是使用platform总线。
(2)probe()
static int s3c2410fb_probe(struct platform_device *pdev)
{
return s3c24xxfb_probe(pdev, DRV_S3C2410);
}
static int s3c24xxfb_probe(struct platform_device *pdev,
enum s3c_drv_type drv_type)
{
struct s3c2410fb_info *info;
struct s3c2410fb_display *display;
struct fb_info *fbinfo;
struct s3c2410fb_mach_info *mach_info;
struct resource *res;
int ret;
int irq;
int i;
int size;
u32 lcdcon1;
//获取板子配置信息
mach_info = dev_get_platdata(&pdev->dev);
//省略......
//display包含了屏幕的分辨率信息等
display = mach_info->displays + mach_info->default_display;
//获取中断号
irq = platform_get_irq(pdev, 0);
//分配一个fb_info
fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
//保存fbinfo到driver data
platform_set_drvdata(pdev, fbinfo);
info = fbinfo->par;
info->dev = &pdev->dev;
info->drv_type = drv_type;
//获取IO资源并申请
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
size = resource_size(res);
info->mem = request_mem_region(res->start, size, pdev->name);
//映射为虚拟地址
info->io = ioremap(res->start, size);
//获取中断基地址
info->irq_base = info->io + S3C2410_LCDINTBASE;
strcpy(fbinfo->fix.id, driver_name);
//操作寄存器,停止LCDC输出
lcdcon1 = readl(info->io + S3C2410_LCDCON1);
writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
//初始化固定参数
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
fbinfo->fix.type_aux = 0;
fbinfo->fix.xpanstep = 0;
fbinfo->fix.ypanstep = 0;
fbinfo->fix.ywrapstep = 0;
fbinfo->fix.accel = FB_ACCEL_NONE;
//初始化可变参数
fbinfo->var.nonstd = 0;
fbinfo->var.activate = FB_ACTIVATE_NOW;
fbinfo->var.accel_flags = 0;
fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
//设置操作函数
fbinfo->fbops = &s3c2410fb_ops;
fbinfo->flags = FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette = &info->pseudo_pal;
//清除画板
for (i = 0; i < 256; i++)
info->palette_buffer[i] = PALETTE_BUFF_CLEAR;
//申请中断
ret = request_irq(irq, s3c2410fb_irq, 0, pdev->name, info);
//获取时钟并使能
info->clk = clk_get(NULL, "lcd");
clk_prepare_enable(info->clk);
usleep_range(1000, 1100);
info->clk_rate = clk_get_rate(info->clk);
/* 计算显示所需分配的内存大小 */
for (i = 0; i num_displays; i++) {
unsigned long smem_len = mach_info->displays[i].xres;
smem_len *= mach_info->displays[i].yres;
smem_len *= mach_info->displays[i].bpp;
smem_len >>= 3;
if (fbinfo->fix.smem_len < smem_len)
fbinfo->fix.smem_len = smem_len;
}
/* 初始化显存,这里面设置了DMA */
ret = s3c2410fb_map_video_memory(fbinfo);
//根据屏幕信息初始化fbinfo中的可变参数
fbinfo->var.xres = display->xres;
fbinfo->var.yres = display->yres;
fbinfo->var.bits_per_pixel = display->bpp;
//初始化LCDC相关寄存器
s3c2410fb_init_registers(fbinfo);
//注册framebuffer
ret = register_framebuffer(fbinfo);
return 0;
}
上面省略了一些错误判断。
上面总结下来就两个部分:
1. 根据屏幕信息填充fb_info, 然后调用register_framebuffer进行注册
2. LCDC相对应的寄存器的配置(硬件平台相关的)
(3)各种操作屏幕的函数
static struct fb_ops s3c2410fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = s3c2410fb_check_var, //检查可变参数合法性
.fb_set_par = s3c2410fb_set_par, //设置可变参数
.fb_blank = s3c2410fb_blank, //设置开关屏
.fb_setcolreg = s3c2410fb_setcolreg, //设置颜色寄存器
//下面三个都是使用内核自带的函数
.fb_fillrect = cfb_fillrect, //绘制矩形
.fb_copyarea = cfb_copyarea, //区域拷贝
.fb_imageblit = cfb_imageblit,//绘制位图
};
简单看几个函数,其实就是对寄存器的操作。
static int s3c2410fb_set_par(struct fb_info *info)
{
//设置参数信息
struct fb_var_screeninfo *var = &info->var;
switch (var->bits_per_pixel) {
case 32:
case 16:
case 12:
info->fix.visual = FB_VISUAL_TRUECOLOR;
break;
case 1:
info->fix.visual = FB_VISUAL_MONO01;
break;
default:
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
break;
}
info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
//设置寄存器,该函数中都是寄存器的操作
s3c2410fb_activate_var(info);
return 0;
}
static int s3c2410fb_blank(int blank_mode, struct fb_info *info)
{
struct s3c2410fb_info *fbi = info->par;
void __iomem *tpal_reg = fbi->io;
dprintk("blank(mode=%d, info=%p)n", blank_mode, info);
tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;
//根据开关设置寄存器
if (blank_mode == FB_BLANK_POWERDOWN)
s3c2410fb_lcd_enable(fbi, 0);
else
s3c2410fb_lcd_enable(fbi, 1);
if (blank_mode == FB_BLANK_UNBLANK)
writel(0x0, tpal_reg);
else {
dprintk("setting TPAL to output 0x000000n");
writel(S3C2410_TPAL_EN, tpal_reg);
}
return 0;
}
这些操作函数和裸机程序基本差不多,就是一些对寄存器的操作。
总结
上面其实没有多少内容,我们并不需要过多关注细节。比如寄存器配置的含义之类的,因为每个平台都不一样,即使你把这款芯片的所有细节理解了,换个平台,依然要重新来。所以我们应该理解的是框架。
随着技术的发展,特别是GPU的出现,单纯使用Framebuffer来显示越来越少,它已经渐渐成为DRM的一部分了。特别是现在的Android设备, 对显示要求越来越高。后期会带来一些DRM相关的文章!
end
往期推荐
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,一年会员只需98元,全站资源免费下载 点击查看详情
站 长 微 信: lzxmw777
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。