diff options
-rw-r--r-- | MAINTAINERS | 6 | ||||
-rw-r--r-- | drivers/video/fsl-diu-fb.c | 201 | ||||
-rw-r--r-- | include/linux/fsl-diu-fb.h | 9 |
3 files changed, 135 insertions, 81 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 9386a63ea8f6..477a20fc3436 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3108,6 +3108,12 @@ F: drivers/video/ F: include/video/ F: include/linux/fb.h +FREESCALE DIU FRAMEBUFFER DRIVER +M: Timur Tabi <timur@freescale.com> +L: linux-fbdev@vger.kernel.org +S: Supported +F: drivers/video/fsl-diu-fb.* + FREESCALE DMA DRIVER M: Li Yang <leoli@freescale.com> M: Zhang Wei <zw@zh-kernel.org> diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index ede9e55413f8..d3fc92eaee89 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -337,13 +337,11 @@ struct mfb_info { int registered; unsigned long pseudo_palette[16]; struct diu_ad *ad; - int cursor_reset; unsigned char g_alpha; unsigned int count; int x_aoi_d; /* aoi display x offset to physical screen */ int y_aoi_d; /* aoi display y offset to physical screen */ struct fsl_diu_data *parent; - u8 *edid_data; }; /** @@ -378,6 +376,8 @@ struct fsl_diu_data { struct diu_ad ad[NUM_AOIS] __aligned(8); u8 gamma[256 * 3] __aligned(32); u8 cursor[MAX_CURS * MAX_CURS * 2] __aligned(32); + uint8_t edid_data[EDID_LENGTH]; + bool has_edid; } __aligned(32); /* Determine the DMA address of a member of the fsl_diu_data structure */ @@ -430,6 +430,22 @@ static struct mfb_info mfb_template[] = { }, }; +#ifdef DEBUG +static void __attribute__ ((unused)) fsl_diu_dump(struct diu __iomem *hw) +{ + mb(); + pr_debug("DIU: desc=%08x,%08x,%08x, gamma=%08x pallete=%08x " + "cursor=%08x curs_pos=%08x diu_mode=%08x bgnd=%08x " + "disp_size=%08x hsyn_para=%08x vsyn_para=%08x syn_pol=%08x " + "thresholds=%08x int_mask=%08x plut=%08x\n", + hw->desc[0], hw->desc[1], hw->desc[2], hw->gamma, + hw->pallete, hw->cursor, hw->curs_pos, hw->diu_mode, + hw->bgnd, hw->disp_size, hw->hsyn_para, hw->vsyn_para, + hw->syn_pol, hw->thresholds, hw->int_mask, hw->plut); + rmb(); +} +#endif + /** * fsl_diu_name_to_port - convert a port name to a monitor port enum * @@ -481,8 +497,7 @@ static void fsl_diu_enable_panel(struct fb_info *info) switch (mfbi->index) { case PLANE0: - if (hw->desc[0] != ad->paddr) - wr_reg_wa(&hw->desc[0], ad->paddr); + wr_reg_wa(&hw->desc[0], ad->paddr); break; case PLANE1_AOI0: cmfbi = &data->mfb[2]; @@ -534,8 +549,7 @@ static void fsl_diu_disable_panel(struct fb_info *info) switch (mfbi->index) { case PLANE0: - if (hw->desc[0] != data->dummy_ad.paddr) - wr_reg_wa(&hw->desc[0], data->dummy_ad.paddr); + wr_reg_wa(&hw->desc[0], 0); break; case PLANE1_AOI0: cmfbi = &data->mfb[2]; @@ -792,7 +806,8 @@ static void update_lcdc(struct fb_info *info) hw = data->diu_reg; - diu_ops.set_monitor_port(data->monitor_port); + if (diu_ops.set_monitor_port) + diu_ops.set_monitor_port(data->monitor_port); gamma_table_base = data->gamma; /* Prep for DIU init - gamma table, cursor table */ @@ -811,12 +826,8 @@ static void update_lcdc(struct fb_info *info) out_be32(&hw->gamma, DMA_ADDR(data, gamma)); out_be32(&hw->cursor, DMA_ADDR(data, cursor)); - out_be32(&hw->bgnd, 0x007F7F7F); /* BGND */ - out_be32(&hw->bgnd_wb, 0); /* BGND_WB */ - out_be32(&hw->disp_size, (var->yres << 16 | var->xres)); - /* DISP SIZE */ - out_be32(&hw->wb_size, 0); /* WB SIZE */ - out_be32(&hw->wb_mem_addr, 0); /* WB MEM ADDR */ + out_be32(&hw->bgnd, 0x007F7F7F); /* Set background to grey */ + out_be32(&hw->disp_size, (var->yres << 16) | var->xres); /* Horizontal and vertical configuration register */ temp = var->left_margin << 22 | /* BP_H */ @@ -833,9 +844,20 @@ static void update_lcdc(struct fb_info *info) diu_ops.set_pixel_clock(var->pixclock); - out_be32(&hw->syn_pol, 0); /* SYNC SIGNALS POLARITY */ - out_be32(&hw->int_status, 0); /* INTERRUPT STATUS */ +#ifndef CONFIG_PPC_MPC512x + /* + * The PLUT register is defined differently on the MPC5121 than it + * is on other SOCs. Unfortunately, there's no documentation that + * explains how it's supposed to be programmed, so for now, we leave + * it at the default value on the MPC5121. + * + * For other SOCs, program it for the highest priority, which will + * reduce the chance of underrun. Technically, we should scale the + * priority to match the screen resolution, but doing that properly + * requires delicate fine-tuning for each use-case. + */ out_be32(&hw->plut, 0x01F5F666); +#endif /* Enable the DIU */ enable_lcdc(info); @@ -965,7 +987,6 @@ static int fsl_diu_set_par(struct fb_info *info) hw = data->diu_reg; set_fix(info); - mfbi->cursor_reset = 1; len = info->var.yres_virtual * info->fix.line_length; /* Alloc & dealloc each time resolution/bpp change */ @@ -1107,6 +1128,12 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, if (!arg) return -EINVAL; + + dev_dbg(info->dev, "ioctl %08x (dir=%s%s type=%u nr=%u size=%u)\n", cmd, + _IOC_DIR(cmd) & _IOC_READ ? "R" : "", + _IOC_DIR(cmd) & _IOC_WRITE ? "W" : "", + _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd)); + switch (cmd) { case MFB_SET_PIXFMT_OLD: dev_warn(info->dev, @@ -1180,6 +1207,23 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd, ad->ckmin_b = ck.blue_min; } break; +#ifdef CONFIG_PPC_MPC512x + case MFB_SET_GAMMA: { + struct fsl_diu_data *data = mfbi->parent; + + if (copy_from_user(data->gamma, buf, sizeof(data->gamma))) + return -EFAULT; + setbits32(&data->diu_reg->gamma, 0); /* Force table reload */ + break; + } + case MFB_GET_GAMMA: { + struct fsl_diu_data *data = mfbi->parent; + + if (copy_to_user(buf, data->gamma, sizeof(data->gamma))) + return -EFAULT; + break; + } +#endif default: dev_err(info->dev, "unknown ioctl command (0x%08X)\n", cmd); return -ENOIOCTLCMD; @@ -1206,8 +1250,22 @@ static int fsl_diu_open(struct fb_info *info, int user) res = fsl_diu_set_par(info); if (res < 0) mfbi->count--; - else + else { + struct fsl_diu_data *data = mfbi->parent; + +#ifdef CONFIG_NOT_COHERENT_CACHE + /* + * Enable underrun detection and vertical sync + * interrupts. + */ + clrbits32(&data->diu_reg->int_mask, + INT_UNDRUN | INT_VSYNC); +#else + /* Enable underrun detection */ + clrbits32(&data->diu_reg->int_mask, INT_UNDRUN); +#endif fsl_diu_enable_panel(info); + } } spin_unlock(&diu_lock); @@ -1223,8 +1281,13 @@ static int fsl_diu_release(struct fb_info *info, int user) spin_lock(&diu_lock); mfbi->count--; - if (mfbi->count == 0) + if (mfbi->count == 0) { + struct fsl_diu_data *data = mfbi->parent; + + /* Disable interrupts */ + out_be32(&data->diu_reg->int_mask, 0xffffffff); fsl_diu_disable_panel(info); + } spin_unlock(&diu_lock); return res; @@ -1248,6 +1311,7 @@ static int __devinit install_fb(struct fb_info *info) { int rc; struct mfb_info *mfbi = info->par; + struct fsl_diu_data *data = mfbi->parent; const char *aoi_mode, *init_aoi_mode = "320x240"; struct fb_videomode *db = fsl_diu_mode_db; unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db); @@ -1264,9 +1328,9 @@ static int __devinit install_fb(struct fb_info *info) return rc; if (mfbi->index == PLANE0) { - if (mfbi->edid_data) { + if (data->has_edid) { /* Now build modedb from EDID */ - fb_edid_to_monspecs(mfbi->edid_data, &info->monspecs); + fb_edid_to_monspecs(data->edid_data, &info->monspecs); fb_videomode_to_modelist(info->monspecs.modedb, info->monspecs.modedb_len, &info->modelist); @@ -1284,7 +1348,7 @@ static int __devinit install_fb(struct fb_info *info) * For plane 0 we continue and look into * driver's internal modedb. */ - if ((mfbi->index == PLANE0) && mfbi->edid_data) + if ((mfbi->index == PLANE0) && data->has_edid) has_default_mode = 0; else return -EINVAL; @@ -1348,9 +1412,6 @@ static void uninstall_fb(struct fb_info *info) if (!mfbi->registered) return; - if (mfbi->index == PLANE0) - kfree(mfbi->edid_data); - unregister_framebuffer(info); unmap_video_memory(info); if (&info->cmap) @@ -1362,7 +1423,7 @@ static void uninstall_fb(struct fb_info *info) static irqreturn_t fsl_diu_isr(int irq, void *dev_id) { struct diu __iomem *hw = dev_id; - unsigned int status = in_be32(&hw->int_status); + uint32_t status = in_be32(&hw->int_status); if (status) { /* This is the workaround for underrun */ @@ -1387,40 +1448,6 @@ static irqreturn_t fsl_diu_isr(int irq, void *dev_id) return IRQ_NONE; } -static int request_irq_local(struct fsl_diu_data *data) -{ - struct diu __iomem *hw = data->diu_reg; - u32 ints; - int ret; - - /* Read to clear the status */ - in_be32(&hw->int_status); - - ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb", hw); - if (!ret) { - ints = INT_PARERR | INT_LS_BF_VS; -#if !defined(CONFIG_NOT_COHERENT_CACHE) - ints |= INT_VSYNC; -#endif - - /* Read to clear the status */ - in_be32(&hw->int_status); - out_be32(&hw->int_mask, ints); - } - - return ret; -} - -static void free_irq_local(struct fsl_diu_data *data) -{ - struct diu __iomem *hw = data->diu_reg; - - /* Disable all LCDC interrupt */ - out_be32(&hw->int_mask, 0x1f); - - free_irq(data->irq, NULL); -} - #ifdef CONFIG_PM /* * Power management hooks. Note that we won't be called from IRQ context, @@ -1496,8 +1523,8 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct mfb_info *mfbi; struct fsl_diu_data *data; - int diu_mode; dma_addr_t dma_addr; /* DMA addr of fsl_diu_data struct */ + const void *prop; unsigned int i; int ret; @@ -1541,17 +1568,13 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev) memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info)); mfbi->parent = data; mfbi->ad = &data->ad[i]; + } - if (mfbi->index == PLANE0) { - const u8 *prop; - int len; - - /* Get EDID */ - prop = of_get_property(np, "edid", &len); - if (prop && len == EDID_LENGTH) - mfbi->edid_data = kmemdup(prop, EDID_LENGTH, - GFP_KERNEL); - } + /* Get the EDID data from the device tree, if present */ + prop = of_get_property(np, "edid", &ret); + if (prop && ret == EDID_LENGTH) { + memcpy(data->edid_data, prop, EDID_LENGTH); + data->has_edid = true; } data->diu_reg = of_iomap(np, 0); @@ -1561,10 +1584,6 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev) goto error; } - diu_mode = in_be32(&data->diu_reg->diu_mode); - if (diu_mode == MFB_MODE0) - out_be32(&data->diu_reg->diu_mode, 0); /* disable DIU */ - /* Get the IRQ of the DIU */ data->irq = irq_of_parse_and_map(np, 0); @@ -1586,11 +1605,11 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev) data->dummy_ad.paddr = DMA_ADDR(data, dummy_ad); /* - * Let DIU display splash screen if it was pre-initialized - * by the bootloader, set dummy area descriptor otherwise. + * Let DIU continue to display splash screen if it was pre-initialized + * by the bootloader; otherwise, clear the display. */ - if (diu_mode == MFB_MODE0) - out_be32(&data->diu_reg->desc[0], data->dummy_ad.paddr); + if (in_be32(&data->diu_reg->diu_mode) == MFB_MODE0) + out_be32(&data->diu_reg->desc[0], 0); out_be32(&data->diu_reg->desc[1], data->dummy_ad.paddr); out_be32(&data->diu_reg->desc[2], data->dummy_ad.paddr); @@ -1603,7 +1622,16 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev) } } - if (request_irq_local(data)) { + /* + * Older versions of U-Boot leave interrupts enabled, so disable + * all of them and clear the status register. + */ + out_be32(&data->diu_reg->int_mask, 0xffffffff); + in_be32(&data->diu_reg->int_status); + + ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb", + &data->diu_reg); + if (ret) { dev_err(&pdev->dev, "could not claim irq\n"); goto error; } @@ -1638,7 +1666,8 @@ static int fsl_diu_remove(struct platform_device *pdev) data = dev_get_drvdata(&pdev->dev); disable_lcdc(&data->fsl_diu_info[0]); - free_irq_local(data); + + free_irq(data->irq, &data->diu_reg); for (i = 0; i < NUM_AOIS; i++) uninstall_fb(&data->fsl_diu_info[i]); @@ -1741,6 +1770,9 @@ static int __init fsl_diu_init(void) coherence_data_size = be32_to_cpup(prop) * 13; coherence_data_size /= 8; + pr_debug("fsl-diu-fb: coherence data size is %zu bytes\n", + coherence_data_size); + prop = of_get_property(np, "d-cache-line-size", NULL); if (prop == NULL) { pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' " @@ -1750,10 +1782,17 @@ static int __init fsl_diu_init(void) } d_cache_line_size = be32_to_cpup(prop); + pr_debug("fsl-diu-fb: cache lines size is %u bytes\n", + d_cache_line_size); + of_node_put(np); coherence_data = vmalloc(coherence_data_size); - if (!coherence_data) + if (!coherence_data) { + pr_err("fsl-diu-fb: could not allocate coherence data " + "(size=%zu)\n", coherence_data_size); return -ENOMEM; + } + #endif ret = platform_driver_register(&fsl_diu_driver); diff --git a/include/linux/fsl-diu-fb.h b/include/linux/fsl-diu-fb.h index 11c16a1fb9e3..a1e8277120c7 100644 --- a/include/linux/fsl-diu-fb.h +++ b/include/linux/fsl-diu-fb.h @@ -47,6 +47,15 @@ struct aoi_display_offset { #define MFB_GET_PIXFMT _IOR('M', 8, __u32) /* + * The MPC5121 BSP comes with a gamma_set utility that initializes the + * gamma table. Unfortunately, it uses bad values for the IOCTL commands, + * but there's nothing we can do about it now. These ioctls are only + * supported on the MPC5121. + */ +#define MFB_SET_GAMMA _IOW('M', 1, __u8) +#define MFB_GET_GAMMA _IOR('M', 1, __u8) + +/* * The original definitions of MFB_SET_PIXFMT and MFB_GET_PIXFMT used the * wrong value for 'size' field of the ioctl. The current macros above use the * right size, but we still need to provide backwards compatibility, at least |