一、LCD驱动探测(probe)和(remove)函数
分析一个总线驱动一般都从探测函数开始,现在看下探测函数,在LCD驱动第一部分末尾讲述了LCD驱动定义的结构体,本文在多处将使用这些定义的结构体。
-
static int __devinit xxxxfb_probe(struct platform_device *pdev)
-
{
-
int ret = 0;
-
struct clk *lcd_clk;
-
struct fb_info *fb;
-
struct resource *mem;
-
unsigned long rate = 0;
-
struct xxxxfb *xxxxfb;
-
struct xxxx_fb_platform_data *pdata = pdev->dev.platform_data;
-
if (!pdata) {
-
dev_err(&pdev->dev, "Missing platform data\n");
-
return -ENXIO;
-
}
-
printk(KERN_INFO "######xxxx_lcdfb_probe start######\n");
-
-
#ifdef CONFIG_FB_xxxx_SVGA
-
*(volatile unsigned int *)addr = 0x4; //addr为系统寄存器中设置LCD分频寄存器
-
#else
-
*(volatile unsigned int *)addr = 0x5;
-
#endif
-
-
lcd_clk = clk_get(NULL, "lcd");
-
if (IS_ERR(lcd_clk)) {
-
DBG("failed to find watchdog clock source\n");
-
ret = PTR_ERR(lcd_clk);
-
return -ENXIO;
-
}
-
rate = clk_get_rate(lcd_clk);
-
DBG("lcd clk rate is %ld\n", rate);
-
clk_enable(lcd_clk);
-
-
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
if (!mem) {
-
dev_err(&pdev->dev, "Failed to get register memory resource\n");
-
ret = -ENXIO;
-
goto err_put_lpclk;
-
}
-
mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
-
if (!mem) {
-
dev_err(&pdev->dev, "Failed to request register memory region\n");
-
ret = -EBUSY;
-
goto err_put_lpclk;
-
}
-
fb = framebuffer_alloc(sizeof(struct xxxxfb), &pdev->dev);
-
if (!fb) {
-
dev_err(&pdev->dev, "Failed to allocate framebuffer device\n");
-
ret = -ENOMEM;
-
goto err_release_mem_region;
-
}
-
fb->fbops = &xxxx_ops; //fb支持的操作函数集,在(二)中讲述
-
fb->flags = FBINFO_DEFAULT;
-
gsc3280fb = fb->par;
-
gsc3280fb->pdev = pdev;
-
gsc3280fb->pdata = pdata;
-
gsc3280fb->mem = mem;
-
gsc3280fb->ldclk = lcd_clk;
-
gsc3280fb->base = ioremap(mem->start, resource_size(mem)); //寄存器映射
-
DBG("xxxxfb->base = %p\n", xxxxfb->base);
-
if (!xxxxfb->base) {
-
dev_err(&pdev->dev, "Failed to ioremap register memory region\n");
-
ret = -EBUSY;
-
goto err_framebuffer_alloc;
-
}
-
//调色板io地址映射
-
xxxxfb->pseudo_palette = ioremap(XXXX_LCDC_CMAP_REG, XXXX_LCDC_CMAP_LEN);
-
if (!xxxxfb->pseudo_palette ) {
-
dev_err(&pdev->dev, "Failed to ioremap cmap register memory region\n");
-
ret = -EBUSY;
-
if (xxxxfb->base ) {
-
iounmap(gsc3280fb->base);
-
}
-
goto err_framebuffer_alloc;
-
}
-
platform_set_drvdata(pdev, xxxxfb);
-
mutex_init(&xxxxfb->lock);
-
fb_videomode_to_modelist(pdata->modes, pdata->num_modes, &fb->modelist);
-
fb_videomode_to_var(&fb->var, pdata->modes); //从pdata->modes赋值到fb->var,完全的赋值函数
-
fb->var.bits_per_pixel = pdata->bpp; //赋值bpp
-
xxxxfb_check_var(&fb->var, fb); //校验参数,在(二)中讲述
-
ret = xxxxfb_alloc_devmem(gsc3280fb); //申请DMA内存,接下来讲述
-
if (ret) {
-
dev_err(&pdev->dev, "Failed to allocate video memory\n");
-
goto err_iounmap;
-
}
-
fb->fix = xxxxfb_fix; //fix结构体,内容见后面
-
fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8;
-
fb->fix.mmio_start = mem->start;
-
fb->fix.mmio_len = resource_size(mem);
-
fb->fix.smem_start = gsc3280fb->vidmem_phys;
-
fb->fix.smem_len = fb->fix.line_length * fb->var.yres;
-
fb->screen_base = gsc3280fb->vidmem;
-
fb->pseudo_palette = gsc3280fb->pseudo_palette;
-
fb_alloc_cmap(&fb->cmap, 256, 0);
-
gsc3280fb->is_enabled = 1;
-
writel(gsc3280fb->vidmem_phys, gsc3280fb->base + XXXX_REG_LCD_VBAR); //写DMA基地址到LCD寄存器
-
fb->mode = NULL;
-
xxxxfb_set_par(fb); //设置参数,在(二)中讲述
-
ret = register_framebuffer(fb); //注册framebuffer
-
if (ret) {
-
dev_err(&pdev->dev, "Failed to register framebuffer: %d\n", ret);
-
goto err_free_devmem;
-
}
-
xxxxfb->fb = fb;
-
my_vga_mode = 1;
-
-
#ifdef CONFIG_FB_XXXX_VGA
-
//VGA的参数
-
DBG("init vga mode");
-
if (my_vga_mode == 0) {
-
writel(0x879f03ff, xxxxfb->base + XXXX_REG_LCD_HTIM);
-
writel(0x1c0502ff, xxxxfb->base + XXXX_REG_LCD_VTIM);
-
writel(0x05400326, xxxxfb->base + XXXX_REG_LCD_HVLEN);
-
}
-
else if (my_vga_mode == 1) {
-
writel(0xd78704ff, xxxxfb->base + XXXX_REG_LCD_HTIM);
-
writel(0x1d0203bf, xxxxfb->base + XXXX_REG_LCD_VTIM);
-
writel(0x06b003e2, xxxxfb->base + XXXX_REG_LCD_HVLEN);
-
}
-
else {
-
;
-
}
-
#else
-
DBG("init lcd mode");
-
writel(0x2700031f, gsc3280fb->base + XXXX_REG_LCD_HTIM);
-
writel(0x031301f3, gsc3280fb->base + XXXX_REG_LCD_VTIM);
-
writel(0x0420020d, gsc3280fb->base + XXXX_REG_LCD_HVLEN);
-
#endif
-
-
xxxxfb_enable(xxxx fb); //使能LCD,接下来讲述
-
printk(KERN_INFO "######xxxx_lcdfb_probe success######\n");
-
return 0;
-
-
err_free_devmem:
-
fb_dealloc_cmap(&fb->cmap);
-
xxxxfb_free_devmem(xxxxfb);
-
err_iounmap:
-
iounmap(xxxxfb->base);
-
iounmap(xxxxfb->pseudo_palette);
-
err_framebuffer_alloc:
-
framebuffer_release(fb);
-
err_release_mem_region:
-
release_mem_region(mem->start, resource_size(mem));
-
err_put_lpclk:
-
clk_put(lcd_clk);
-
printk(KERN_INFO "######xxxx_lcdfb_probe error######\n");
-
return ret;
-
}
fix结构体:
-
//宏定义见fb.h
-
static const struct fb_fix_screeninfo xxxxfb_fix __devinitdata = {
-
.id = "XXXXFB",
-
.type = FB_TYPE_PACKED_PIXELS,
-
.visual = FB_VISUAL_TRUECOLOR,
-
.xpanstep = 0,
-
.ypanstep = 0,
-
.ywrapstep = 0,
-
.accel = FB_ACCEL_NONE,
-
};
现在看下申请DMA内存函数xxxxfb_alloc_devmem(xxxxfb):
-
static int xxxxfb_alloc_devmem(struct xxxxfb *xxxxfb)
-
{
-
int i = 0, max_videosize = 0;
-
struct fb_videomode *mode = xxxxfb->pdata->modes;
-
for (i = 0; i < xxxxfb->pdata->num_modes; ++mode, ++i) {
-
if (max_videosize < mode->xres * mode->yres)
-
max_videosize = mode->xres * mode->yres;
-
}
-
max_videosize *= xxxxfb_get_controller_bpp(xxxxfb) >> 3;
-
xxxxfb->framedesc = dma_alloc_coherent(&xxxxfb->pdev->dev,
-
sizeof(*xxxxfb->framedesc),
-
&xxxxfb->framedesc_phys, GFP_KERNEL);
-
if (!xxxxfb->framedesc)
-
return -ENOMEM;
-
xxxxfb->vidmem_size = PAGE_ALIGN(max_videosize); //DMA内存大小
-
xxxxfb->vidmem = dma_alloc_coherent(&xxxxfb->pdev->dev,
-
xxxx0fb->vidmem_size,
-
&xxxxfb->vidmem_phys, GFP_KERNEL); //申请DMA内存
-
if (!xxxxfb->vidmem)
-
goto err_free_framedesc;
-
xxxxfb->framedesc->next = xxxxfb->framedesc_phys;
-
xxxxfb->framedesc->addr = xxxxfb->vidmem_phys;
-
xxxxfb->framedesc->id = 0xdeafbead;
-
xxxxfb->framedesc->cmd = 0;
-
xxxxfb->framedesc->cmd |= max_videosize / 4;
-
return 0;
-
err_free_framedesc:
-
dma_free_coherent(&xxxx0fb->pdev->dev, sizeof(*xxxxfb->framedesc),
-
xxxxfb->framedesc, xxxxfb->framedesc_phys);
-
return -ENOMEM;
-
}
使能LCD函数xxxxfb_enable:
-
static void gsc3280fb_enable(struct xxxxfb *xxxxfb)
-
{
-
writel(0x101c, xxxxfb->base + XXXX_REG_LCD_CTRL); //设置控制寄存器
-
writel(0x1, xxxxfb->base + XXXX_REG_LCD_ENABLE); //使能
-
clk_enable(xxxxfb->ldclk); //使能时钟
-
}
二、fb支持的函数集xxxx_ops
xxxx_ops具体内容如下:
-
/*Framebuffer底层硬件操作各接口函数*/
-
static struct fb_ops xxxxfb_ops = {
-
.owner = THIS_MODULE,
-
.fb_check_var = xxxxfb_check_var,
-
.fb_set_par = xxxxfb_set_par, /*设置fb_info中的参数,主要是LCD的显示模式*/
-
.fb_blank = xxxxfb_blank, /*显示空白(即:LCD开关控制)*/
-
.fb_cursor = xxxxfb_cursor,
-
.fb_setcolreg = xxxxfb_setcolreg, /*设置颜色表*/
-
/*以下三个函数是可选的,主要是提供fb_console的支持,在内核中已经实现,这里直接调用即可*/
-
.fb_fillrect = sys_fillrect, /*定义在drivers/video/cfbfillrect.c中*/
-
.fb_copyarea = sys_copyarea, /*定义在drivers/video/cfbcopyarea.c中*/
-
.fb_imageblit = sys_imageblit, /*定义在drivers/video/cfbimgblt.c中*/
-
};
除了内核中实现的最后三个函数外,现在我们一一讲述各个接口函数。
1、xxxxfb_check_var
-
static int xxxxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb)
-
{
-
struct xxxxfb *xxxxfb = fb->par;
-
struct fb_videomode *mode;
-
-
if (var->bits_per_pixel != xxxxfb_get_controller_bpp(xxxxfb) &&
-
var->bits_per_pixel != xxxxfb->pdata->bpp) //获取bpp,接下来讲述
-
return -EINVAL;
-
mode = xxxxfb_get_mode(xxxxfb, var); //获取模式,内容接下来讲述
-
if (mode == NULL)
-
return -EINVAL;
-
/* 设置时钟像素,行、帧切换值,水平同步、垂直同步长度值 */
-
fb_videomode_to_var(var, mode); //从mode赋值到var
-
/*根据色位模式(BPP)来设置可变参数中R、G、B的颜色位域。*/
-
switch (xxxxfb->pdata->bpp) {
-
case 8:
-
break;
-
case 15:
-
var->red.offset = 10;
-
var->red.length = 5;
-
var->green.offset = 6;
-
var->green.length = 5;
-
var->blue.offset = 0;
-
var->blue.length = 5;
-
break;
-
case 16:
-
var->red.offset = 11;
-
var->red.length = 5;
-
var->green.offset = 5;
-
var->green.length = 6;
-
var->blue.offset = 0;
-
var->blue.length = 5;
-
break;
-
default:
-
break;
-
}
-
return 0;
-
}
xxxxfb_get_controller_bpp
:
-
static int xxxxfb_get_controller_bpp(struct xxxxfb *xxxxfb)
-
{
-
switch (xxxxfb->pdata->bpp) {
-
case 8:
-
return 8;
-
case 15:
-
case 16:
-
return 16;
-
default:
-
return gsc3280fb->pdata->bpp;
-
}
-
}
xxxxfb_get_mode:
-
static struct fb_videomode *xxxxfb_get_mode(struct xxxxfb *xxxxfb,
-
struct fb_var_screeninfo *var)
-
{
-
size_t i;
-
struct fb_videomode *mode = xxxxfb->pdata->modes;
-
-
for (i = 0; i < xxxxfb->pdata->num_modes && mode != NULL; ++i, ++mode) { //逐一比较
-
if ((mode->xres == var->xres) && (mode->yres == var->yres)) { //比较判断
-
return mode;
-
}
-
}
-
return NULL;
-
}
2、设置参数函数xxxxfb_set_par
-
static int xxxxfb_set_par(struct fb_info *info)
-
{
-
struct xxxxfb *xxxxfb = info->par;
-
struct gsc3280_fb_platform_data *pdata = xxxxfb->pdata;
-
struct fb_var_screeninfo *var = &info->var;
-
struct fb_videomode *mode;
-
uint32_t ctrl;
-
uint32_t vtim, htim, hvlen, vt, ht;
-
//unsigned long rate;
-
mode = xxxxfb_get_mode(xxxxfb, var); //取得模式,上面讲述
-
if (mode == NULL)
-
return -EINVAL;
-
if (mode == info->mode)
-
return 0;
-
info->mode = mode;
-
//根据寄存器的定义组装各个值
-
htim = (mode->hsync_len << 24) | (mode->left_margin << 16) | ((mode->xres-1) & 0xffff ) ;
-
vtim = (mode->vsync_len << 24) | (mode->upper_margin << 16) | ((mode->yres -1) & 0xffff ) ;
-
ht = mode->hsync_len + mode->left_margin + mode->right_margin + mode->xres -1;
-
vt = mode->vsync_len + mode->upper_margin + mode->lower_margin + mode->yres- 1;
-
hvlen = ht << 16 | (vt & 0xffff);
-
ctrl = 0;
-
switch (pdata->bpp) {
-
case 16:
-
ctrl |= GSC3280_LCD_CTRL_BPP_16;
-
break;
-
case 8:
-
ctrl |= GSC3280_LCD_CTRL_BPP_8;
-
break;
-
default:
-
break;
-
}
-
ctrl |= pdata->lcd_type & 0xff;
-
if (!(mode->sync & FB_SYNC_HOR_HIGH_ACT))
-
ctrl |= XXXX_LCD_CTRL_HSYNC_ACTIVE_LOW;
-
if (!(mode->sync & FB_SYNC_VERT_HIGH_ACT))
-
ctrl |= XXXX_LCD_CTRL_VSYNC_ACTIVE_LOW;
-
if (pdata->lcd_type == XXXX_LCD_TYPE_16BIT_TFT)
-
ctrl |= 0x1;
-
writel(htim, xxxxfb->base + XXXX_REG_LCD_HTIM);
-
writel(vtim, xxxxfb->base + XXXX_REG_LCD_VTIM);
-
writel(hvlen, xxxxfb->base + XXXX_REG_LCD_HVLEN);
-
//writel(ctrl, xxxxfb->base + XXXX_REG_LCD_CTRL);
-
if (!xxxxfb->is_enabled)
-
;
-
//clk_disable(xxxxfb->ldclk);
-
mutex_unlock(&xxxxfb->lock);
-
//clk_set_rate(xxxxfb->lpclk, rate);
-
//clk_set_rate(xxxxfb->ldclk, rate * 3);
-
return 0;
-
}
3、清空函数xxxxfb_blank
-
static int xxxxfb_blank(int blank_mode, struct fb_info *info)
-
{
-
struct gsc3280fb *xxxxfb = info->par;
-
switch (blank_mode) {
-
case FB_BLANK_UNBLANK:
-
mutex_lock(&xxxxfb->lock);
-
if (xxxxfb->is_enabled) {
-
mutex_unlock(&xxxxfb->lock);
-
return 0; //已经使能了,不用再使能
-
}
-
xxxxfb_enable(gsc3280fb);
-
xxxxfb->is_enabled = 1;
-
mutex_unlock(&xxxxfb->lock);
-
break;
-
default:
-
mutex_lock(&xxxxfb->lock);
-
if (!xxxxfb->is_enabled) {
-
mutex_unlock(&xxxxfb->lock);
-
return 0; //已经停止了,不用再停止了
-
}
-
xxxxfb_disable(xxxxfb); //停用LCD,接下来讲述
-
xxxxfb->is_enabled = 0;
-
mutex_unlock(&xxxxfb->lock);
-
break;
-
}
-
return 0;
-
}
停用LCD函数xxxxfb_disable:
-
static void xxxxfb_disable(struct xxxxfb *xxxxfb)
-
{
-
uint32_t ctrl;
-
ctrl = readl(xxxxfb->base + XXXX_REG_LCD_ENABLE);
-
ctrl &= ~XXXX_LCD_CTRL_DISABLE; //停用
-
writel(ctrl, xxxxfb->base + XXXX_REG_LCD_ENABLE);
-
do {
-
ctrl = readl(xxxxfb->base + XXXX_REG_LCD_ENABLE);
-
} while ((ctrl & XXXX_LCD_STATE_DISABLED)); //等待停止成功
-
clk_disable(xxxxfb->ldclk); //停用时钟
-
}
A = dma_alloc_writecombine(B,C,D,GFP_KERNEL);
含义:
A: 内存的虚拟起始地址,在内核要用此地址来操作所分配的内存
B: struct device指针,可以平台初始化里指定,主要是dma_mask之类,可参考framebuffer
C: 实际分配大小,传入dma_map_size即可
D: 返回的内存物理地址,dma就可以用。
所以,A和D是一一对应的,只不过,A是虚拟地址,而D是物理地址。对任意一个操作都将改变缓冲区内容。
我对此函数的理解是,调用此函数将会分配一段内存,D将返回这段内存的实际物理地址供DMA来使用,A将是D对应的
虚拟地址供操作系统调用,对A和D的的任意一个进行操作,都会改变这段内存缓冲区的内容。
阅读(1782) | 评论(0) | 转发(0) |