关于NUC972 LCD驱动的分析

2019-07-26 13:37:20 1214

一.关于硬件设备节点的描述


内核平台代码路径linux-3.10.x\arch\arm\mach-nuc970dev.c

内核设备节点如下:

struct platform_device nuc970fb_device_lcd = {
.name             = "nuc970-lcd",
.id               = -1,
.num_resources    = ARRAY_SIZE(nuc970fb_lcd_resource),
.resource         = nuc970fb_lcd_resource,     
.dev              = {
.dma_mask               = &nuc970fb_device_lcd_dmamask,
.coherent_dma_mask      = -1,
.platform_data = &nuc970fb_fb_info,
}
};


nuc970fb_lcd_resource中定义了两个资源 lcd基地址和中断:

static struct resource nuc970fb_lcd_resource[] = {
[0] = {
.start = NUC970_PA_LCD,
.end   = NUC970_PA_LCD + NUC970_SZ_LCD - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_LCD,
.end   = IRQ_LCD,
.flags = IORESOURCE_IRQ,
}
};


自定义的lcd描述硬件设备结构体:

static struct nuc970fb_mach_info nuc970fb_fb_info = {
.displays = &nuc970fb_lcd_info[0],
.num_displays = ARRAY_SIZE(nuc970fb_lcd_info),
.default_display = 0,
    .gpio_blen          = NUC970_PG3,
    .gpio_lcs           = NUC970_PG2,
};

其中的nuc970fb_lcd_info添加了屏幕的详细信息:

static struct nuc970fb_display nuc970fb_lcd_info[] = {
#ifdef CONFIG_A025DL02_320X240
/* AUO A035QN02V0 320x240 TFT Panel , 18bits*/
[0] = {
.type = LCM_DCCS_VA_SRC_RGB565,
.width = 320,
.height = 240,
.xres = 320,
.yres = 240,
.bpp = 16,
.pixclock = 4000000,
.left_margin = 10,
.right_margin   = 54,
.hsync_len = 10,
.upper_margin = 2,
.lower_margin = 4,
.vsync_len = 1,
.dccs = 0x0e00041a,
.devctl = 0x060800c0,
.fbctrl = 0x00a000a0,
.scale = 0x04000400,
},
#endif

二,driver部分的代码:

static struct platform_driver nuc970fb_driver = {
.probe = nuc970fb_probe,
.remove = nuc970fb_remove,
.suspend = nuc970fb_suspend,
.resume = nuc970fb_resume,
.driver = {
.name = "nuc970-lcd",
.owner = THIS_MODULE,
},
};


其中probe函数的实现:

static int nuc970fb_probe(struct platform_device *pdev)
{
struct nuc970fb_info *fbi;
struct nuc970fb_display *display;
struct fb_info    *fbinfo;
struct nuc970fb_mach_info *mach_info;
struct resource *res;
int ret;
int irq;
int i;
int size;
struct pinctrl *p;
struct clk *clkmux, *clkuplldiv;
    
dev_dbg(&pdev->dev, "devinit\n");
mach_info = pdev->dev.platform_data;
if (mach_info == NULL) {
dev_err(&pdev->dev,
"no platform data for lcd, cannot attach\n");
return -EINVAL;
}
if (mach_info->default_display > mach_info->num_displays) {
dev_err(&pdev->dev,
"default display No. is %d but only %d displays \n",
mach_info->default_display, mach_info->num_displays);
return -EINVAL;
}
display = mach_info->displays + mach_info->default_display;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq for device\n");
return -ENOENT;
}
fbinfo = framebuffer_alloc(sizeof(struct nuc970fb_info), &pdev->dev);
if (!fbinfo)
return -ENOMEM;
platform_set_drvdata(pdev, fbinfo);
fbi = fbinfo->par;
fbi->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
size = resource_size(res);
fbi->mem = request_mem_region(res->start, size, pdev->name);
if (fbi->mem == NULL) {
dev_err(&pdev->dev, "failed to alloc memory region\n");
ret = -ENOENT;
goto free_fb;
}
fbi->io = ioremap(res->start, size);
if (fbi->io == NULL) {
dev_err(&pdev->dev, "ioremap() of lcd registers failed\n");
ret = -ENXIO;
goto release_mem_region;
}
fbi->irq_base = fbi->io + REG_LCM_INT_CS;
/* Stop the LCD */
writel(0, fbi->io + REG_LCM_DCCS);
/* fill the fbinfo*/
strcpy(fbinfo->fix.id, driver_name);
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 = &nuc970fb_ops;
fbinfo->flags = FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette = &fbi->pseudo_pal;
ret = request_irq(irq, nuc970fb_irqhandler, 0,
  pdev->name, fbinfo);
if (ret) {
dev_err(&pdev->dev, "cannot register irq handler %d -err %d\n",
irq, ret);
ret = -EBUSY;
goto release_regs;
}
clk_prepare(clk_get(NULL, "lcd_hclk"));
clk_enable(clk_get(NULL, "lcd_hclk"));
fbi->clk = clk_get(NULL, "lcd_eclk");
    
    if(display->pixclock > 12000000)
    { 
        // change clock source to upll
        clkmux = clk_get(NULL, "lcd_eclk_mux");
        if (IS_ERR(clkmux)) {
            printk(KERN_ERR "nuc970-lcd:failed to get lcd clock mux control\n");
            ret = PTR_ERR(clkmux);
            return ret;
        }
        clkuplldiv = clk_get(NULL, "lcd_uplldiv");
        if (IS_ERR(clkuplldiv)) {
            printk(KERN_ERR "nuc970-lcd:failed to get lcd clock divider control\n");
            ret = PTR_ERR(clkuplldiv);
            return ret;
        }
        
        // select lcd clock from upll
        clk_set_parent(clkmux, clkuplldiv);
    }
    
    clk_set_rate(fbi->clk, display->pixclock);
clk_prepare(fbi->clk); 
clk_enable(fbi->clk);
fbi->clk_rate = clk_get_rate(fbi->clk);
dev_dbg(&pdev->dev, "got and enabled clock\n");
/* calutate the video buffer size */
for (i = 0; i < mach_info->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;
}
/* Initialize Video Memory */
ret = nuc970fb_map_video_memory(fbinfo);
if (ret) {
printk(KERN_ERR "Failed to allocate video RAM: %x\n", ret);
goto release_clock;
}
dev_dbg(&pdev->dev, "got video memory\n");
fbinfo->var.xres = display->xres;
fbinfo->var.yres = display->yres;
fbinfo->var.bits_per_pixel = display->bpp;
nuc970fb_init_registers(fbinfo);
nuc970fb_check_var(&fbinfo->var, fbinfo);
ret = nuc970fb_cpufreq_register(fbi);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register cpufreq\n");
goto free_video_memory;
}
ret = register_framebuffer(fbinfo);
if (ret) {
printk(KERN_ERR "failed to register framebuffer device: %d\n",
ret);
goto free_cpufreq;
}
#if defined(CONFIG_FB_NUC970_16BIT_PIN)
p = devm_pinctrl_get_select(&pdev->dev, "lcd-16bit");
#elif defined(CONFIG_FB_NUC970_18BIT_PIN)
p = devm_pinctrl_get_select(&pdev->dev, "lcd-18bit"); 
#else
p = devm_pinctrl_get_select(&pdev->dev, "lcd-24bit");
#endif
    if(IS_ERR(p)) {
        dev_err(&pdev->dev, "unable to reserve pin\n");
        ret = PTR_ERR(p);
    } 
printk(KERN_INFO "fb%d: %s frame buffer device\n",
fbinfo->node, fbinfo->fix.id);
        
#ifdef CONFIG_ILI9431_MPU80_240x320
    init_ili9341(fbinfo);
#endif
return 0;
free_cpufreq:
nuc970fb_cpufreq_deregister(fbi);
free_video_memory:
nuc970fb_unmap_video_memory(fbinfo);
release_clock:
clk_disable(fbi->clk);
clk_put(fbi->clk);
free_irq(irq, fbi);
release_regs:
iounmap(fbi->io);
release_mem_region:
release_mem_region(res->start, size);
free_fb:
framebuffer_release(fbinfo);
return ret;
}




矽控物联

矽控电子核心团队拥有十余年的硬件正向研发,生产制程,测试手法,品质控制经验。尤其擅长嵌入式ARM平台的工控物联网产品,以及海思平台的IPC视频类模组开发,为您的产品从创意到落地、批量市场化助力。公司可提供从硬件设计(原理开发及PCB Layout),PCB制板,SMT及接插件焊接,产品测试,产品老化全流程外包服务,收费合理,品质可靠。

垂询电话:0510-83488567-1     业务邮箱:wxdianzi#foxmail.com (#更换为@)