📄 fsl-diu-fb.c
字号:
info->var.activate = FB_ACTIVATE_NOW; info->fbops = &fsl_diu_ops; info->flags = FBINFO_FLAG_DEFAULT; info->pseudo_palette = &mfbi->pseudo_palette; /* Allocate colormap */ fb_alloc_cmap(&info->cmap, 16, 0); return 0;}static int __devinit install_fb(struct fb_info *info){ int rc; struct mfb_info *mfbi = info->par; const char *aoi_mode, *init_aoi_mode = "320x240"; if (init_fbinfo(info)) return -EINVAL; if (mfbi->index == 0) /* plane 0 */ aoi_mode = fb_mode; else aoi_mode = init_aoi_mode; pr_debug("mode used = %s\n", aoi_mode); rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db, ARRAY_SIZE(fsl_diu_mode_db), &fsl_diu_default_mode, default_bpp); switch (rc) { case 1: pr_debug("using mode specified in @mode\n"); break; case 2: pr_debug("using mode specified in @mode " "with ignored refresh rate\n"); break; case 3: pr_debug("using mode default mode\n"); break; case 4: pr_debug("using mode from list\n"); break; default: pr_debug("rc = %d\n", rc); pr_debug("failed to find mode\n"); return -EINVAL; break; } pr_debug("xres_virtual %d\n", info->var.xres_virtual); pr_debug("bits_per_pixel %d\n", info->var.bits_per_pixel); pr_debug("info->var.yres_virtual = %d\n", info->var.yres_virtual); pr_debug("info->fix.line_length = %d\n", info->fix.line_length); if (mfbi->type == MFB_TYPE_OFF) mfbi->blank = FB_BLANK_NORMAL; else mfbi->blank = FB_BLANK_UNBLANK; if (fsl_diu_check_var(&info->var, info)) { printk(KERN_ERR "fb_check_var failed"); fb_dealloc_cmap(&info->cmap); return -EINVAL; } if (fsl_diu_set_par(info)) { printk(KERN_ERR "fb_set_par failed"); fb_dealloc_cmap(&info->cmap); return -EINVAL; } if (register_framebuffer(info) < 0) { printk(KERN_ERR "register_framebuffer failed"); unmap_video_memory(info); fb_dealloc_cmap(&info->cmap); return -EINVAL; } mfbi->registered = 1; printk(KERN_INFO "fb%d: %s fb device registered successfully.\n", info->node, info->fix.id); return 0;}static void uninstall_fb(struct fb_info *info){ struct mfb_info *mfbi = info->par; if (!mfbi->registered) return; unregister_framebuffer(info); unmap_video_memory(info); if (&info->cmap) fb_dealloc_cmap(&info->cmap); mfbi->registered = 0;}static irqreturn_t fsl_diu_isr(int irq, void *dev_id){ struct diu *hw = dr.diu_reg; unsigned int status = in_be32(&hw->int_status); if (status) { /* This is the workaround for underrun */ if (status & INT_UNDRUN) { out_be32(&hw->diu_mode, 0); pr_debug("Err: DIU occurs underrun!\n"); udelay(1); out_be32(&hw->diu_mode, 1); }#if defined(CONFIG_NOT_COHERENT_CACHE) else if (status & INT_VSYNC) { unsigned int i; for (i = 0; i < coherence_data_size; i += d_cache_line_size) __asm__ __volatile__ ( "dcbz 0, %[input]" ::[input]"r"(&coherence_data[i])); }#endif return IRQ_HANDLED; } return IRQ_NONE;}static int request_irq_local(int irq){ unsigned long status, ints; struct diu *hw; int ret; hw = dr.diu_reg; /* Read to clear the status */ status = in_be32(&hw->int_status); ret = request_irq(irq, fsl_diu_isr, 0, "diu", NULL); if (ret) pr_info("Request diu IRQ failed.\n"); else { ints = INT_PARERR | INT_LS_BF_VS;#if !defined(CONFIG_NOT_COHERENT_CACHE) ints |= INT_VSYNC;#endif if (dr.mode == MFB_MODE2 || dr.mode == MFB_MODE3) ints |= INT_VSYNC_WB; /* Read to clear the status */ status = in_be32(&hw->int_status); out_be32(&hw->int_mask, ints); } return ret;}static void free_irq_local(int irq){ struct diu *hw = dr.diu_reg; /* Disable all LCDC interrupt */ out_be32(&hw->int_mask, 0x1f); free_irq(irq, NULL);}#ifdef CONFIG_PM/* * Power management hooks. Note that we won't be called from IRQ context, * unlike the blank functions above, so we may sleep. */static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state){ struct fsl_diu_data *machine_data; machine_data = dev_get_drvdata(&ofdev->dev); disable_lcdc(machine_data->fsl_diu_info[0]); return 0;}static int fsl_diu_resume(struct of_device *ofdev){ struct fsl_diu_data *machine_data; machine_data = dev_get_drvdata(&ofdev->dev); enable_lcdc(machine_data->fsl_diu_info[0]); return 0;}#else#define fsl_diu_suspend NULL#define fsl_diu_resume NULL#endif /* CONFIG_PM *//* Align to 64-bit(8-byte), 32-byte, etc. */static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align){ u32 offset, ssize; u32 mask; dma_addr_t paddr = 0; ssize = size + bytes_align; buf->vaddr = dma_alloc_coherent(NULL, ssize, &paddr, GFP_DMA | __GFP_ZERO); if (!buf->vaddr) return -ENOMEM; buf->paddr = (__u32) paddr; mask = bytes_align - 1; offset = (u32)buf->paddr & mask; if (offset) { buf->offset = bytes_align - offset; buf->paddr = (u32)buf->paddr + offset; } else buf->offset = 0; return 0;}static void free_buf(struct diu_addr *buf, u32 size, u32 bytes_align){ dma_free_coherent(NULL, size + bytes_align, buf->vaddr, (buf->paddr - buf->offset)); return;}static ssize_t store_monitor(struct device *device, struct device_attribute *attr, const char *buf, size_t count){ int old_monitor_port; unsigned long val; struct fsl_diu_data *machine_data = container_of(attr, struct fsl_diu_data, dev_attr); if (strict_strtoul(buf, 10, &val)) return 0; old_monitor_port = machine_data->monitor_port; machine_data->monitor_port = diu_ops.set_sysfs_monitor_port(val); if (old_monitor_port != machine_data->monitor_port) { /* All AOIs need adjust pixel format * fsl_diu_set_par only change the pixsel format here * unlikely to fail. */ fsl_diu_set_par(machine_data->fsl_diu_info[0]); fsl_diu_set_par(machine_data->fsl_diu_info[1]); fsl_diu_set_par(machine_data->fsl_diu_info[2]); fsl_diu_set_par(machine_data->fsl_diu_info[3]); fsl_diu_set_par(machine_data->fsl_diu_info[4]); } return count;}static ssize_t show_monitor(struct device *device, struct device_attribute *attr, char *buf){ struct fsl_diu_data *machine_data = container_of(attr, struct fsl_diu_data, dev_attr); return diu_ops.show_monitor_port(machine_data->monitor_port, buf);}static int __devinit fsl_diu_probe(struct of_device *ofdev, const struct of_device_id *match){ struct device_node *np = ofdev->node; struct mfb_info *mfbi; phys_addr_t dummy_ad_addr; int ret, i, error = 0; struct resource res; struct fsl_diu_data *machine_data; machine_data = kzalloc(sizeof(struct fsl_diu_data), GFP_KERNEL); if (!machine_data) return -ENOMEM; for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) { machine_data->fsl_diu_info[i] = framebuffer_alloc(sizeof(struct mfb_info), &ofdev->dev); if (!machine_data->fsl_diu_info[i]) { dev_err(&ofdev->dev, "cannot allocate memory\n"); ret = -ENOMEM; goto error2; } mfbi = machine_data->fsl_diu_info[i]->par; memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info)); mfbi->parent = machine_data; } ret = of_address_to_resource(np, 0, &res); if (ret) { dev_err(&ofdev->dev, "could not obtain DIU address\n"); goto error; } if (!res.start) { dev_err(&ofdev->dev, "invalid DIU address\n"); goto error; } dev_dbg(&ofdev->dev, "%s, res.start: 0x%08x\n", __func__, res.start); dr.diu_reg = ioremap(res.start, sizeof(struct diu)); if (!dr.diu_reg) { dev_err(&ofdev->dev, "Err: can't map DIU registers!\n"); ret = -EFAULT; goto error2; } out_be32(&dr.diu_reg->diu_mode, 0); /* disable DIU anyway*/ /* Get the IRQ of the DIU */ machine_data->irq = irq_of_parse_and_map(np, 0); if (!machine_data->irq) { dev_err(&ofdev->dev, "could not get DIU IRQ\n"); ret = -EINVAL; goto error; } machine_data->monitor_port = monitor_port; /* Area descriptor memory pool aligns to 64-bit boundary */ if (allocate_buf(&pool.ad, sizeof(struct diu_ad) * FSL_AOI_NUM, 8)) return -ENOMEM; /* Get memory for Gamma Table - 32-byte aligned memory */ if (allocate_buf(&pool.gamma, 768, 32)) { ret = -ENOMEM; goto error; } /* For performance, cursor bitmap buffer aligns to 32-byte boundary */ if (allocate_buf(&pool.cursor, MAX_CURS * MAX_CURS * 2, 32)) { ret = -ENOMEM; goto error; } i = ARRAY_SIZE(machine_data->fsl_diu_info); machine_data->dummy_ad = (struct diu_ad *) ((u32)pool.ad.vaddr + pool.ad.offset) + i; machine_data->dummy_ad->paddr = pool.ad.paddr + i * sizeof(struct diu_ad); machine_data->dummy_aoi_virt = fsl_diu_alloc(64, &dummy_ad_addr); if (!machine_data->dummy_aoi_virt) { ret = -ENOMEM; goto error; } machine_data->dummy_ad->addr = cpu_to_le32(dummy_ad_addr); machine_data->dummy_ad->pix_fmt = 0x88882317; machine_data->dummy_ad->src_size_g_alpha = cpu_to_le32((4 << 12) | 4); machine_data->dummy_ad->aoi_size = cpu_to_le32((4 << 16) | 2); machine_data->dummy_ad->offset_xyi = 0; machine_data->dummy_ad->offset_xyd = 0; machine_data->dummy_ad->next_ad = 0; out_be32(&dr.diu_reg->desc[0], machine_data->dummy_ad->paddr); out_be32(&dr.diu_reg->desc[1], machine_data->dummy_ad->paddr); out_be32(&dr.diu_reg->desc[2], machine_data->dummy_ad->paddr); for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) { machine_data->fsl_diu_info[i]->fix.smem_start = 0; mfbi = machine_data->fsl_diu_info[i]->par; mfbi->ad = (struct diu_ad *)((u32)pool.ad.vaddr + pool.ad.offset) + i; mfbi->ad->paddr = pool.ad.paddr + i * sizeof(struct diu_ad); ret = install_fb(machine_data->fsl_diu_info[i]); if (ret) { dev_err(&ofdev->dev, "Failed to register framebuffer %d\n", i); goto error; } } if (request_irq_local(machine_data->irq)) { dev_err(machine_data->fsl_diu_info[0]->dev, "could not request irq for diu."); goto error; } machine_data->dev_attr.attr.name = "monitor"; machine_data->dev_attr.attr.mode = S_IRUGO|S_IWUSR; machine_data->dev_attr.show = show_monitor; machine_data->dev_attr.store = store_monitor; error = device_create_file(machine_data->fsl_diu_info[0]->dev, &machine_data->dev_attr); if (error) { dev_err(machine_data->fsl_diu_info[0]->dev, "could not create sysfs %s file\n", machine_data->dev_attr.attr.name); } dev_set_drvdata(&ofdev->dev, machine_data); return 0;error: for (i = ARRAY_SIZE(machine_data->fsl_diu_info); i > 0; i--) uninstall_fb(machine_data->fsl_diu_info[i - 1]); if (pool.ad.vaddr) free_buf(&pool.ad, sizeof(struct diu_ad) * FSL_AOI_NUM, 8); if (pool.gamma.vaddr) free_buf(&pool.gamma, 768, 32); if (pool.cursor.vaddr) free_buf(&pool.cursor, MAX_CURS * MAX_CURS * 2, 32); if (machine_data->dummy_aoi_virt) fsl_diu_free(machine_data->dummy_aoi_virt, 64); iounmap(dr.diu_reg);error2: for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) if (machine_data->fsl_diu_info[i]) framebuffer_release(machine_data->fsl_diu_info[i]); kfree(machine_data); return ret;}static int fsl_diu_remove(struct of_device *ofdev){ struct fsl_diu_data *machine_data; int i; machine_data = dev_get_drvdata(&ofdev->dev); disable_lcdc(machine_data->fsl_diu_info[0]); free_irq_local(machine_data->irq); for (i = ARRAY_SIZE(machine_data->fsl_diu_info); i > 0; i--) uninstall_fb(machine_data->fsl_diu_info[i - 1]); if (pool.ad.vaddr) free_buf(&pool.ad, sizeof(struct diu_ad) * FSL_AOI_NUM, 8); if (pool.gamma.vaddr) free_buf(&pool.gamma, 768, 32); if (pool.cursor.vaddr) free_buf(&pool.cursor, MAX_CURS * MAX_CURS * 2, 32); if (machine_data->dummy_aoi_virt) fsl_diu_free(machine_data->dummy_aoi_virt, 64); iounmap(dr.diu_reg); for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) if (machine_data->fsl_diu_info[i]) framebuffer_release(machine_data->fsl_diu_info[i]); kfree(machine_data); return 0;}#ifndef MODULEstatic int __init fsl_diu_setup(char *options){ char *opt; unsigned long val; if (!options || !*options) return 0; while ((opt = strsep(&options, ",")) != NULL) { if (!*opt) continue; if (!strncmp(opt, "monitor=", 8)) { if (!strict_strtoul(opt + 8, 10, &val) && (val <= 2)) monitor_port = val; } else if (!strncmp(opt, "bpp=", 4)) { if (!strict_strtoul(opt + 4, 10, &val)) default_bpp = val; } else fb_mode = opt; } return 0;}#endifstatic struct of_device_id fsl_diu_match[] = { { .compatible = "fsl,diu", }, {}};MODULE_DEVICE_TABLE(of, fsl_diu_match);static struct of_platform_driver fsl_diu_driver = { .owner = THIS_MODULE, .name = "fsl_diu", .match_table = fsl_diu_match, .probe = fsl_diu_probe, .remove = fsl_diu_remove, .suspend = fsl_diu_suspend, .resume = fsl_diu_resume,};static int __init fsl_diu_init(void){#ifdef CONFIG_NOT_COHERENT_CACHE struct device_node *np; const u32 *prop;#endif int ret;#ifndef MODULE char *option; /* * For kernel boot options (in 'video=xxxfb:<options>' format) */ if (fb_get_options("fslfb", &option)) return -ENODEV; fsl_diu_setup(option);#endif printk(KERN_INFO "Freescale DIU driver\n");#ifdef CONFIG_NOT_COHERENT_CACHE np = of_find_node_by_type(NULL, "cpu"); if (!np) { printk(KERN_ERR "Err: can't find device node 'cpu'\n"); return -ENODEV; } prop = of_get_property(np, "d-cache-size", NULL); if (prop == NULL) { of_node_put(np); return -ENODEV; } /* Freescale PLRU requires 13/8 times the cache size to do a proper displacement flush */ coherence_data_size = *prop * 13; coherence_data_size /= 8; prop = of_get_property(np, "d-cache-line-size", NULL); if (prop == NULL) { of_node_put(np); return -ENODEV; } d_cache_line_size = *prop; of_node_put(np); coherence_data = vmalloc(coherence_data_size); if (!coherence_data) return -ENOMEM;#endif ret = of_register_platform_driver(&fsl_diu_driver); if (ret) { printk(KERN_ERR "fsl-diu: failed to register platform driver\n");#if defined(CONFIG_NOT_COHERENT_CACHE) vfree(coherence_data);#endif iounmap(dr.diu_reg); } return ret;}static void __exit fsl_diu_exit(void){ of_unregister_platform_driver(&fsl_diu_driver);#if defined(CONFIG_NOT_COHERENT_CACHE) vfree(coherence_data);#endif}module_init(fsl_diu_init);module_exit(fsl_diu_exit);MODULE_AUTHOR("York Sun <yorksun@freescale.com>");MODULE_DESCRIPTION("Freescale DIU framebuffer driver");MODULE_LICENSE("GPL");module_param_named(mode, fb_mode, charp, 0);MODULE_PARM_DESC(mode, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");module_param_named(bpp, default_bpp, ulong, 0);MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified mode");module_param_named(monitor, monitor_port, int, 0);MODULE_PARM_DESC(monitor, "Specify the monitor port (0, 1 or 2) if supported by the platform");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -