LCD设备驱动程序2

本文详细分析了LCD驱动的加载过程,重点介绍了s3c-fb设备的注册、LCD驱动的加载和卸载函数。在系统启动时,通过platform_add_devices将设备注册,并在后续的s3c_fb_probe函数中进行LCD控制器的初始化,包括内存分配、中断申请、GPIO设置等。同时,还探讨了fb_info结构体的填充函数s3cfb_init_fbinfo,该函数根据不同索引(index)设置不同的屏幕参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

三、LCD驱动分析

1,LCD平台设备的加载和卸载函数 
/linux/arch/arm/mach-s3c6410/mach-smdk6410.c

static void __init smdk6410_machine_init(void)
{

。。。。。。

platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));

//这个就是对device设备进行注册,在注册driver驱动时,会根据name找到相应的device进行匹配,然后会调用probe

}

系统把全部设备都放在smdk6410_devices[ ],寻找定义过去,再同一文件中

static struct platform_device *smdk6410_devices[] __initdata = {

。。。。。。

&s3c_device_fb,

&smdk6410_lcd_powerdev,//寻找结果并不是这个

}

根据命名,觉得如上2个device可能就是我们在找的LCD设备

struct platform_device s3c_device_fb = {
.name = "s3c-fb",//设备名字,匹配的依据
.id = -1,
.num_resources = ARRAY_SIZE(s3c_fb_resource),
.resource = s3c_fb_resource,
.dev.dma_mask = &s3c_device_fb.dev.coherent_dma_mask,
.dev.coherent_dma_mask = 0xffffffffUL,
};

static struct resource s3c_fb_resource[] = {
[0] = {
.start = S3C_PA_FB,
.end   = S3C_PA_FB + SZ_16K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_LCD_VSYNC,
.end   = IRQ_LCD_VSYNC,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = IRQ_LCD_FIFO,
.end   = IRQ_LCD_FIFO,
.flags = IORESOURCE_IRQ,
},
[3] = {
.start = IRQ_LCD_SYSTEM,
.end   = IRQ_LCD_SYSTEM,
.flags = IORESOURCE_IRQ,
},
};

结论smdk6410_lcd_powerdevf应该只是s3c_device_fb一个附属而已。

2,LCD驱动的加载和卸载函数

通过对全部文件搜索"s3c-fb",有2个近乎一样的驱动。以下再做分析排除

/drivers/video/s3c-fb.c  

static struct platform_driver s3c_fb_driver = {
.probe =
s3c_fb_probe,
.remove = __devexit_p(s3c_fb_remove),
.id_table =
s3c_fb_driver_ids,/* 用于probe函数调用 */
.driver = {
.name =
"s3c-fb",
.owner= THIS_MODULE,
.pm= &s3cfb_pm_ops,
},
};

static struct platform_device_id s3c_fb_driver_ids[] = {
{
.name= "s3c-fb",
.driver_data= (unsigned long)&s3c_fb_data_64xx,
}, {
.name= "s5pc100-fb",
.driver_data= (unsigned long)&s3c_fb_data_s5pc100,
}, {
.name= "s5pv210-fb",
.driver_data= (unsigned long)&s3c_fb_data_s5pv210,
}, {
.name= "s3c2443-fb",
.driver_data= (unsigned long)&s3c_fb_data_s3c2443,
},
{},
};

static int __devinit s3c_fb_probe(struct platform_device *pdev)
{
struct s3c_fb_driverdata *fbdrv;
struct device *dev = &pdev->dev;
struct s3c_fb_platdata *pd;
struct s3c_fb *sfb;
struct resource *res;
int win;
int ret = 0;

fbdrv = (struct s3c_fb_driverdata *)platform_get_device_id(pdev)->driver_data;

if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) {
dev_err(dev, "too many windows, cannot attach\n");
return -EINVAL;
}

pd = pdev->dev.platform_data;
if (!pd) {
dev_err(dev, "no platform data specified\n");
return -EINVAL;
}

sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL);
if (!sfb) {
dev_err(dev, "no memory for framebuffers\n");
return -ENOMEM;
}

dev_dbg(dev, "allocate new framebuffer %p\n", sfb);

sfb->dev = dev;
sfb->pdata = pd;
sfb->variant = fbdrv->variant;

sfb->bus_clk = clk_get(dev, "lcd");
if (IS_ERR(sfb->bus_clk)) {
dev_err(dev, "failed to get bus clock\n");
goto err_sfb;
}

clk_enable(sfb->bus_clk);

pm_runtime_enable(sfb->dev);

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "failed to find registers\n");
ret = -ENOENT;
goto err_clk;
}

sfb->regs_res = request_mem_region(res->start, resource_size(res),
  dev_name(dev));
if (!sfb->regs_res) {
dev_err(dev, "failed to claim register region\n");
ret = -ENOENT;
goto err_clk;
}

sfb->regs = ioremap(res->start, resource_size(res));
if (!sfb->regs) {
dev_err(dev, "failed to map registers\n");
ret = -ENXIO;
goto err_req_region;
}

res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(dev, "failed to acquire irq resource\n");
ret = -ENOENT;
goto err_ioremap;
}
sfb->irq_no = res->start;
ret = request_irq(sfb->irq_no, s3c_fb_irq,
 0, "s3c_fb", sfb);
if (ret) {
dev_err(dev, "irq request failed\n");
goto err_ioremap;
}

dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);

platform_set_drvdata(pdev, sfb);
pm_runtime_get_sync(sfb->dev);


/* setup gpio and output polarity controls */

pd->setup_gpio();

writel(pd->vidcon1, sfb->regs + VIDCON1);

/* zero all windows before we do anything */

for (win = 0; win < fbdrv->variant.nr_windows; win++)
s3c_fb_clear_win(sfb, win);


/* initialise colour key controls */
for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) {
void __iomem *regs = sfb->regs + sfb->variant.keycon;

regs += (win * 8);
writel(0xffffff, regs + WKEYCON0);
writel(0xffffff, regs + WKEYCON1);
}

/* we have the register setup, start allocating framebuffers */

for (win = 0; win < fbdrv->variant.nr_windows; win++) {
if (!pd->win[win])
continue;

if (!pd->win[win]->win_mode.pixclock)
s3c_fb_missing_pixclock(&pd->win[win]->win_mode);

ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win],
      &sfb->windows[win]);
if (ret < 0) {
dev_err(dev, "failed to create window %d\n", win);
for (; win >= 0; win--)
s3c_fb_release_win(sfb, sfb->windows[win]);
goto err_irq;
}
}

platform_set_drvdata(pdev, sfb);
pm_runtime_put_sync(sfb->dev);

return 0;


err_irq:
free_irq(sfb->irq_no, sfb);
err_ioremap:
iounmap(sfb->regs);
err_req_region:
release_resource(sfb->regs_res);
kfree(sfb->regs_res);
err_clk:
clk_disable(sfb->bus_clk);
clk_put(sfb->bus_clk);
err_sfb:
kfree(sfb);
return ret;
}

drivers/video/samsung/s3cfb.c

static struct platform_driver s3cfb_driver = {
.probe= s3cfb_probe,
.remove= s3cfb_remove,
.suspend= s3cfb_suspend,
.resume= s3cfb_resume,
        .driver= {
.name=
"s3c-fb",
.owner = THIS_MODULE,
},
};
static int __init s3cfb_probe(struct platform_device *pdev)
{
struct resource *res;
struct fb_info *fbinfo;
s3cfb_info_t *info;


char driver_name[] = "s3cfb";
int index = 0, ret, size;


/*分配s3cfb_info_t结构,主要存储FrameBuffer设备相关数据*/

fbinfo = framebuffer_alloc(sizeof(s3cfb_info_t), &pdev->dev)

if (!fbinfo)
return -ENOMEM;

platform_set_drvdata(pdev, fbinfo);
info = fbinfo->par;
info->dev = &pdev->dev;


/*获取LCD平台设备I/O端口资源*/

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get memory registers\n");
ret = -ENXIO;
goto dealloc_fb;
}
size = (res->end - res->start) + 1;
info->mem = request_mem_region(res->start, size, pdev->name);
if (info->mem == NULL) {
dev_err(&pdev->dev, "failed to get memory region\n");
ret = -ENOENT;
goto dealloc_fb;
}
info->io = ioremap(res->start, size);//映射到内存的虚拟地址
if (info->io == NULL) {
dev_err(&pdev->dev, "ioremap() of registers failed\n");
ret = -ENXIO;
goto release_mem;
}


s3cfb_pre_init();
s3cfb_set_backlight_power(1);
s3cfb_set_lcd_power(1);
s3cfb_set_backlight_level(S3CFB_DEFAULT_BACKLIGHT_LEVEL);

 /*从平台时钟队列获取LCD的时钟,各种控制信号的延时都与LCD的时钟有关*/
info->clk = clk_get(NULL, "lcd");
if (!info->clk || IS_ERR(info->clk)) {
printk(KERN_INFO "failed to get lcd clock source\n");
ret =  -ENOENT;
goto release_io;
}
clk_enable(info->clk);
printk("S3C_LCD clock got enabled :: %ld.%03ld Mhz\n", PRINT_MHZ(clk_get_rate(info->clk)));

s3cfb_fimd.vsync_info.count = 0;
init_waitqueue_head(&s3cfb_fimd.vsync_info.wait_queue);

/*获取LCD平台设备中断资源*/

res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get irq\n");
ret = -ENXIO;
goto release_clock;
}
ret = request_irq(res->start, s3cfb_irq, 0, "s3c-lcd", pdev);
if (ret != 0) {
printk("Failed to install irq (%d)\n", ret);
goto release_clock;
}
msleep(5);

/*下面一段代码,有个结构s3cfb_info_t s3cfb_info[S3CFB_NUM];

 (drivers/video/samsung/s3cfb.h),下文再做进一步分析*/

for (index = 0; index < S3CFB_NUM; index++)  //S3CFB_NUM见文知意,fb_info设备数

{
s3cfb_info[index].mem = info->mem;
s3cfb_info[index].io = info->io;
s3cfb_info[index].clk = info->clk;

/*终于找到填充fb_info结构体的关键处*/
s3cfb_init_fbinfo(&s3cfb_info[index], driver_name, index);

/* 分配DRAM内存给Frambuffer */

ret = s3cfb_map_video_memory(&s3cfb_info[index]);
if (ret) {
printk("Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto release_irq;
}
/*初始化LCD控制器的相关寄存器并做fb_info安装的最后检查*/
ret = s3cfb_init_registers(&s3cfb_info[index]);
ret = s3cfb_check_var(&s3cfb_info[index].fb.var, &s3cfb_info[index].fb);

if (index < 2){
if (fb_alloc_cmap(&s3cfb_info[index].fb.cmap, 256, 0) < 0)
goto dealloc_fb;
} else {
if (fb_alloc_cmap(&s3cfb_info[index].fb.cmap, 16, 0) < 0)
goto dealloc_fb;
}

ret = register_framebuffer(&s3cfb_info[index].fb);

if (ret < 0) {
printk(KERN_ERR "Failed to register framebuffer device: %d\n", ret);
goto free_video_memory;
}

printk(KERN_INFO "fb%d: %s frame buffer device\n",s3cfb_info[index].fb.node, s3cfb_info[index].fb.fix.id);
}

/* create device files */
ret = device_create_file(&(pdev->dev), &dev_attr_backlight_power);

if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");

ret = device_create_file(&(pdev->dev), &dev_attr_backlight_level);

if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");

ret = device_create_file(&(pdev->dev), &dev_attr_lcd_power);

if (ret < 0)
printk(KERN_WARNING "s3cfb: failed to add entries\n");

return 0;

free_video_memory:
s3cfb_unmap_video_memory(&s3cfb_info[index]);
release_irq:
free_irq(res->start, &info);
release_clock:
clk_disable(info->clk);
clk_put(info->clk);
release_io:
iounmap(info->io);
release_mem:
release_resource(info->mem);
kfree(info->mem);
dealloc_fb:
framebuffer_release(fbinfo);
return ret;
}

3,分析填充fb_info函数s3cfb_init_fbinfo(&s3cfb_info[index], driver_name, index);

typedef struct {
struct fb_infofb;
struct device *dev;


struct clk *clk;
struct resource*mem;
void __iomem *io;

unsigned int win_id;

unsigned int max_bpp;
unsigned int max_xres;
unsigned int max_yres;

/* raw memory addresses */
dma_addr_t map_dma_f1; /* physical */
u_char * map_cpu_f1; /* virtual */
unsigned int map_size_f1;

/* addresses of pieces placed in raw buffer */
u_char * screen_cpu_f1; /* virtual address of frame buffer */
dma_addr_t screen_dma_f1; /* physical address of frame buffer */

/* raw memory addresses */
dma_addr_t map_dma_f2; /* physical */
u_char * map_cpu_f2; /* virtual */
unsigned int map_size_f2;

/* addresses of pieces placed in raw buffer */
u_char * screen_cpu_f2; /* virtual address of frame buffer */
dma_addr_t screen_dma_f2; /* physical address of frame buffer */

unsigned int palette_ready;
unsigned int fb_change_ready;

/* keep these registers in case we need to re-write palette */
unsigned int palette_buffer[256];
unsigned int pseudo_pal[16];

unsigned int lcd_offset_x;
unsigned int lcd_offset_y;
unsigned int next_fb_info_change_req;
s3cfb_next_info_tnext_fb_info;
} s3cfb_info_t;

static void s3cfb_init_fbinfo(s3cfb_info_t *finfo, char *drv_name, int index)
{
int i = 0;

if (index == 0)
s3cfb_init_hw();

strcpy(finfo->fb.fix.id, drv_name);

finfo->win_id = index;
finfo->fb.fix.type = FB_TYPE_PACKED_PIXELS;
finfo->fb.fix.type_aux = 0;
finfo->fb.fix.xpanstep = 0;
finfo->fb.fix.ypanstep = 1;
finfo->fb.fix.ywrapstep = 0;
finfo->fb.fix.accel = FB_ACCEL_NONE;

finfo->fb.fbops = &s3cfb_ops;
finfo->fb.flags= FBINFO_FLAG_DEFAULT;

finfo->fb.pseudo_palette = &finfo->pseudo_pal;

finfo->fb.var.nonstd = 0;
finfo->fb.var.activate = FB_ACTIVATE_NOW;
finfo->fb.var.accel_flags = 0;
finfo->fb.var.vmode = FB_VMODE_NONINTERLACED;

finfo->fb.var.xoffset = s3cfb_fimd.xoffset;
finfo->fb.var.yoffset = s3cfb_fimd.yoffset;

//不懂为什么index取不同值会不一样??make config里面配置了2个Frambuffer,故index将会等于0,1
if (index == 0) {

//新结构体,下面再做分析。
finfo->fb.var.height = s3cfb_fimd.height; //屏幕高度
finfo->fb.var.width = s3cfb_fimd.width;//屏幕宽度

finfo->fb.var.xres = s3cfb_fimd.xres;//可见解析度,即分辨率
finfo->fb.var.yres = s3cfb_fimd.yres;

finfo->fb.var.xres_virtual = s3cfb_fimd.xres_virtual;//虚拟解析度
finfo->fb.var.yres_virtual = s3cfb_fimd.yres_virtual;
} else {
finfo->fb.var.height = s3cfb_fimd.osd_height;
finfo->fb.var.width = s3cfb_fimd.osd_width;

finfo->fb.var.xres = s3cfb_fimd.osd_xres;
finfo->fb.var.yres = s3cfb_fimd.osd_yres;

finfo->fb.var.xres_virtual = s3cfb_fimd.osd_xres_virtual;
finfo->fb.var.yres_virtual = s3cfb_fimd.osd_yres_virtual;
}

finfo->fb.var.bits_per_pixel = s3cfb_fimd.bpp;
        finfo->fb.var.pixclock = s3cfb_fimd.pixclock;
finfo->fb.var.hsync_len = s3cfb_fimd.hsync_len;
finfo->fb.var.left_margin = s3cfb_fimd.left_margin;
finfo->fb.var.right_margin = s3cfb_fimd.right_margin;
finfo->fb.var.vsync_len = s3cfb_fimd.vsync_len;
finfo->fb.var.upper_margin = s3cfb_fimd.upper_margin;
finfo->fb.var.lower_margin = s3cfb_fimd.lower_margin;
finfo->fb.var.sync = s3cfb_fimd.sync;
finfo->fb.var.grayscale = s3cfb_fimd.cmap_grayscale;

finfo->fb.fix.smem_len = finfo->fb.var.xres_virtual * finfo->fb.var.yres_virtual * s3cfb_fimd.bytes_per_pixel;
finfo->fb.fix.line_length = finfo->fb.var.width * s3cfb_fimd.bytes_per_pixel;

#if !defined(CONFIG_FB_S3C_EXT_VIRTUAL_SCREEN)

#if defined(CONFIG_FB_S3C_EXT_DOUBLE_BUFFERING)
if (index < 2)
finfo->fb.fix.smem_len *= 2;
#else
/*
* Some systems(ex. DirectFB) use FB0 memory as a video memory.
* You can modify the size of multiple.
*
* !WARN: smem_len*5 may break the probe for 1024*768
*/
if (index == 0)
finfo->fb.fix.smem_len *= 5;
#endif
#endif
for (i = 0; i < 256; i++)
finfo->palette_buffer[i] = S3CFB_PALETTE_BUFF_CLEAR;
}
补充一个概念:OSD是on-screen display的简称,即屏幕菜单式调节方式。一般是按Menu键后屏幕弹出的显示器各项调节项目信息的矩形菜单,可通过该菜单对显示器各项工作指标包括色彩、模式、几何形状等进行调整,从而达到最佳的使用状态。难道上面的index是为了做显示菜单?


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值