atmel_lcdfb.c
来自「linux 内核源代码」· C语言 代码 · 共 806 行 · 第 1/2 页
C
806 行
* @red: The red value which can be up to 16 bits wide * @green: The green value which can be up to 16 bits wide * @blue: The blue value which can be up to 16 bits wide. * @transp: If supported the alpha value which can be up to 16 bits wide. * @info: frame buffer info structure * * Set a single color register. The values supplied have a 16 bit * magnitude which needs to be scaled in this function for the hardware. * Things to take into consideration are how many color registers, if * any, are supported with the current color visual. With truecolor mode * no color palettes are supported. Here a psuedo palette is created * which we store the value in pseudo_palette in struct fb_info. For * pseudocolor mode we have a limited color palette. To deal with this * we can program what color is displayed for a particular pixel value. * DirectColor is similar in that we can program each color field. If * we have a static colormap we don't need to implement this function. * * Returns negative errno on error, or zero on success. In an * ideal world, this would have been the case, but as it turns * out, the other drivers return 1 on failure, so that's what * we're going to do. */static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, unsigned int blue, unsigned int transp, struct fb_info *info){ struct atmel_lcdfb_info *sinfo = info->par; unsigned int val; u32 *pal; int ret = 1; if (info->var.grayscale) red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16; switch (info->fix.visual) { case FB_VISUAL_TRUECOLOR: if (regno < 16) { pal = info->pseudo_palette; val = chan_to_field(red, &info->var.red); val |= chan_to_field(green, &info->var.green); val |= chan_to_field(blue, &info->var.blue); pal[regno] = val; ret = 0; } break; case FB_VISUAL_PSEUDOCOLOR: if (regno < 256) { val = ((red >> 11) & 0x001f); val |= ((green >> 6) & 0x03e0); val |= ((blue >> 1) & 0x7c00); /* * TODO: intensity bit. Maybe something like * ~(red[10] ^ green[10] ^ blue[10]) & 1 */ lcdc_writel(sinfo, ATMEL_LCDC_LUT(regno), val); ret = 0; } break; case FB_VISUAL_MONO01: if (regno < 2) { val = (regno == 0) ? 0x00 : 0x1F; lcdc_writel(sinfo, ATMEL_LCDC_LUT(regno), val); ret = 0; } break; } return ret;}static int atmel_lcdfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info){ dev_dbg(info->device, "%s\n", __func__); atmel_lcdfb_update_dma(info, var); return 0;}static struct fb_ops atmel_lcdfb_ops = { .owner = THIS_MODULE, .fb_check_var = atmel_lcdfb_check_var, .fb_set_par = atmel_lcdfb_set_par, .fb_setcolreg = atmel_lcdfb_setcolreg, .fb_pan_display = atmel_lcdfb_pan_display, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit,};static irqreturn_t atmel_lcdfb_interrupt(int irq, void *dev_id){ struct fb_info *info = dev_id; struct atmel_lcdfb_info *sinfo = info->par; u32 status; status = lcdc_readl(sinfo, ATMEL_LCDC_ISR); lcdc_writel(sinfo, ATMEL_LCDC_IDR, status); return IRQ_HANDLED;}static int __init atmel_lcdfb_init_fbinfo(struct atmel_lcdfb_info *sinfo){ struct fb_info *info = sinfo->info; int ret = 0; memset_io(info->screen_base, 0, info->fix.smem_len); info->var.activate |= FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW; dev_info(info->device, "%luKiB frame buffer at %08lx (mapped at %p)\n", (unsigned long)info->fix.smem_len / 1024, (unsigned long)info->fix.smem_start, info->screen_base); /* Allocate colormap */ ret = fb_alloc_cmap(&info->cmap, 256, 0); if (ret < 0) dev_err(info->device, "Alloc color map failed\n"); return ret;}static void atmel_lcdfb_start_clock(struct atmel_lcdfb_info *sinfo){ if (sinfo->bus_clk) clk_enable(sinfo->bus_clk); clk_enable(sinfo->lcdc_clk);}static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo){ if (sinfo->bus_clk) clk_disable(sinfo->bus_clk); clk_disable(sinfo->lcdc_clk);}static int __init atmel_lcdfb_probe(struct platform_device *pdev){ struct device *dev = &pdev->dev; struct fb_info *info; struct atmel_lcdfb_info *sinfo; struct atmel_lcdfb_info *pdata_sinfo; struct resource *regs = NULL; struct resource *map = NULL; int ret; dev_dbg(dev, "%s BEGIN\n", __func__); ret = -ENOMEM; info = framebuffer_alloc(sizeof(struct atmel_lcdfb_info), dev); if (!info) { dev_err(dev, "cannot allocate memory\n"); goto out; } sinfo = info->par; if (dev->platform_data) { pdata_sinfo = (struct atmel_lcdfb_info *)dev->platform_data; sinfo->default_bpp = pdata_sinfo->default_bpp; sinfo->default_dmacon = pdata_sinfo->default_dmacon; sinfo->default_lcdcon2 = pdata_sinfo->default_lcdcon2; sinfo->default_monspecs = pdata_sinfo->default_monspecs; sinfo->atmel_lcdfb_power_control = pdata_sinfo->atmel_lcdfb_power_control; sinfo->guard_time = pdata_sinfo->guard_time; } else { dev_err(dev, "cannot get default configuration\n"); goto free_info; } sinfo->info = info; sinfo->pdev = pdev; strcpy(info->fix.id, sinfo->pdev->name); info->flags = ATMEL_LCDFB_FBINFO_DEFAULT; info->pseudo_palette = sinfo->pseudo_palette; info->fbops = &atmel_lcdfb_ops; memcpy(&info->monspecs, sinfo->default_monspecs, sizeof(info->monspecs)); info->fix = atmel_lcdfb_fix; /* Enable LCDC Clocks */ if (cpu_is_at91sam9261() || cpu_is_at32ap7000()) { sinfo->bus_clk = clk_get(dev, "hck1"); if (IS_ERR(sinfo->bus_clk)) { ret = PTR_ERR(sinfo->bus_clk); goto free_info; } } sinfo->lcdc_clk = clk_get(dev, "lcdc_clk"); if (IS_ERR(sinfo->lcdc_clk)) { ret = PTR_ERR(sinfo->lcdc_clk); goto put_bus_clk; } atmel_lcdfb_start_clock(sinfo); ret = fb_find_mode(&info->var, info, NULL, info->monspecs.modedb, info->monspecs.modedb_len, info->monspecs.modedb, sinfo->default_bpp); if (!ret) { dev_err(dev, "no suitable video mode found\n"); goto stop_clk; } regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) { dev_err(dev, "resources unusable\n"); ret = -ENXIO; goto stop_clk; } sinfo->irq_base = platform_get_irq(pdev, 0); if (sinfo->irq_base < 0) { dev_err(dev, "unable to get irq\n"); ret = sinfo->irq_base; goto stop_clk; } /* Initialize video memory */ map = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (map) { /* use a pre-allocated memory buffer */ info->fix.smem_start = map->start; info->fix.smem_len = map->end - map->start + 1; if (!request_mem_region(info->fix.smem_start, info->fix.smem_len, pdev->name)) { ret = -EBUSY; goto stop_clk; } info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len); if (!info->screen_base) goto release_intmem; } else { /* alocate memory buffer */ ret = atmel_lcdfb_alloc_video_memory(sinfo); if (ret < 0) { dev_err(dev, "cannot allocate framebuffer: %d\n", ret); goto stop_clk; } } /* LCDC registers */ info->fix.mmio_start = regs->start; info->fix.mmio_len = regs->end - regs->start + 1; if (!request_mem_region(info->fix.mmio_start, info->fix.mmio_len, pdev->name)) { ret = -EBUSY; goto free_fb; } sinfo->mmio = ioremap(info->fix.mmio_start, info->fix.mmio_len); if (!sinfo->mmio) { dev_err(dev, "cannot map LCDC registers\n"); goto release_mem; } /* interrupt */ ret = request_irq(sinfo->irq_base, atmel_lcdfb_interrupt, 0, pdev->name, info); if (ret) { dev_err(dev, "request_irq failed: %d\n", ret); goto unmap_mmio; } ret = atmel_lcdfb_init_fbinfo(sinfo); if (ret < 0) { dev_err(dev, "init fbinfo failed: %d\n", ret); goto unregister_irqs; } /* * This makes sure that our colour bitfield * descriptors are correctly initialised. */ atmel_lcdfb_check_var(&info->var, info); ret = fb_set_var(info, &info->var); if (ret) { dev_warn(dev, "unable to set display parameters\n"); goto free_cmap; } dev_set_drvdata(dev, info); /* * Tell the world that we're ready to go */ ret = register_framebuffer(info); if (ret < 0) { dev_err(dev, "failed to register framebuffer device: %d\n", ret); goto free_cmap; } /* Power up the LCDC screen */ if (sinfo->atmel_lcdfb_power_control) sinfo->atmel_lcdfb_power_control(1); dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %lu\n", info->node, info->fix.mmio_start, sinfo->mmio, sinfo->irq_base); return 0;free_cmap: fb_dealloc_cmap(&info->cmap);unregister_irqs: free_irq(sinfo->irq_base, info);unmap_mmio: iounmap(sinfo->mmio);release_mem: release_mem_region(info->fix.mmio_start, info->fix.mmio_len);free_fb: if (map) iounmap(info->screen_base); else atmel_lcdfb_free_video_memory(sinfo);release_intmem: if (map) release_mem_region(info->fix.smem_start, info->fix.smem_len);stop_clk: atmel_lcdfb_stop_clock(sinfo); clk_put(sinfo->lcdc_clk);put_bus_clk: if (sinfo->bus_clk) clk_put(sinfo->bus_clk);free_info: framebuffer_release(info);out: dev_dbg(dev, "%s FAILED\n", __func__); return ret;}static int __exit atmel_lcdfb_remove(struct platform_device *pdev){ struct device *dev = &pdev->dev; struct fb_info *info = dev_get_drvdata(dev); struct atmel_lcdfb_info *sinfo = info->par; if (!sinfo) return 0; if (sinfo->atmel_lcdfb_power_control) sinfo->atmel_lcdfb_power_control(0); unregister_framebuffer(info); atmel_lcdfb_stop_clock(sinfo); clk_put(sinfo->lcdc_clk); if (sinfo->bus_clk) clk_put(sinfo->bus_clk); fb_dealloc_cmap(&info->cmap); free_irq(sinfo->irq_base, info); iounmap(sinfo->mmio); release_mem_region(info->fix.mmio_start, info->fix.mmio_len); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) { iounmap(info->screen_base); release_mem_region(info->fix.smem_start, info->fix.smem_len); } else { atmel_lcdfb_free_video_memory(sinfo); } dev_set_drvdata(dev, NULL); framebuffer_release(info); return 0;}static struct platform_driver atmel_lcdfb_driver = { .remove = __exit_p(atmel_lcdfb_remove), .driver = { .name = "atmel_lcdfb", .owner = THIS_MODULE, },};static int __init atmel_lcdfb_init(void){ return platform_driver_probe(&atmel_lcdfb_driver, atmel_lcdfb_probe);}static void __exit atmel_lcdfb_exit(void){ platform_driver_unregister(&atmel_lcdfb_driver);}module_init(atmel_lcdfb_init);module_exit(atmel_lcdfb_exit);MODULE_DESCRIPTION("AT91/AT32 LCD Controller framebuffer driver");MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?