关于NUC972 LCD驱动的分析
2019-07-26 13:37:20
1279
一.关于硬件设备节点的描述
内核平台代码路径linux-3.10.x\arch\arm\mach-nuc970下dev.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平台的人工智能与工控物联网产品,以及瑞芯微(Rockchip)、海思、NXP、新唐等平台的机器视觉类AIoT模组开发,为您的产品从创意到落地、批量市场化助力。
公司可提供从硬件设计(原理开发及PCB Layout),Linux驱动开发,PCB制板,SMT及接插件焊接,产品测试,产品老化全流程外包服务,收费合理,品质可靠。
定制开发找矽控,品质可靠省费用
垂询电话:0510-83488567-1 业务邮箱:wxdianzi#foxmail.com (#更换为@)