From a57c188e6c341ada7da5e526be7ae34f644b1424 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 16 Oct 2012 17:33:42 -0500 Subject: MAINTAINERS: Make Timur Tabi the maintainer for the Freescale DIU driver Timur has been cleaning up this driver, so just make it official. Signed-off-by: Timur Tabi diff --git a/MAINTAINERS b/MAINTAINERS index fdc0119..1eeaaf5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2895,6 +2895,12 @@ F: drivers/video/ F: include/video/ F: include/linux/fb.h +FREESCALE DIU FRAMEBUFFER DRIVER +M: Timur Tabi +L: linux-fbdev@vger.kernel.org +S: Supported +F: drivers/video/fsl-diu-fb.* + FREESCALE DMA DRIVER M: Li Yang M: Zhang Wei -- cgit v0.10.2 From 2867173572d5ca5084e8836c8a1d0cafa95b729b Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 16 Oct 2012 17:33:43 -0500 Subject: drivers/video: fsl-diu-fb: simplify platforms that have only one port Allow the platform code not to define a value for diu_ops.set_monitor_port. This would be for platforms that only have one monitor port. set_monitor_port() will never be called with an unsupported port anyway. Signed-off-by: Timur Tabi diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index ede9e55..5b12e17 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -792,7 +792,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 */ -- cgit v0.10.2 From e95c17e9caff4b106cc95b761f3e07964a5a0d9f Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 16 Oct 2012 17:33:44 -0500 Subject: drivers/video: fsl-diu-fb: add support for set_gamma ioctls The MPC5121 BSP comes with a gamma_set utility that initializes the gamma table via an ioctl. Unfortunately, the ioctl number that utility uses is defined improperly, but we can still support it. Signed-off-by: Timur Tabi diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 5b12e17..9c40547 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -1181,6 +1181,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; diff --git a/include/linux/fsl-diu-fb.h b/include/linux/fsl-diu-fb.h index 11c16a1..a1e8277 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 -- cgit v0.10.2 From f74de500a1e8d82f29b59b2b8f0ce87cf3d5ea54 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 16 Oct 2012 17:33:45 -0500 Subject: drivers/video: fsl-diu-fb: streamline enabling of interrupts Remove functions request_irq_local() and free_irq_local(), and merge their code into the functions that handle opening and closing the framebuffer. We also improve the way that interrupts are enabled. Also implement a work-around for broken U-Boots that leave the DIU interrupts enabled. Signed-off-by: Timur Tabi diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 9c40547..f388c3a 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -1224,8 +1224,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); @@ -1241,8 +1255,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; @@ -1380,7 +1399,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 */ @@ -1405,40 +1424,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, @@ -1621,7 +1606,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; } @@ -1656,7 +1650,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]); -- cgit v0.10.2 From 5cc2a36fe8aad05457f15d5908a980b67e0e945d Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 16 Oct 2012 17:33:46 -0500 Subject: drivers/video: fsl-diu-fb: improve message displays Add some debug and error messages that are useful during debugging. Signed-off-by: Timur Tabi diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index f388c3a..6e32ba8 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -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 * @@ -1108,6 +1124,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, @@ -1754,6 +1776,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' " @@ -1763,10 +1788,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); -- cgit v0.10.2 From 8deac723f7b6528e8ab3dec5ce1c913ce3d3009c Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 16 Oct 2012 17:33:47 -0500 Subject: drivers/video: fsl-diu-fb: remove unused 'cursor_reset' variable Probably left over from initial development in hardware cursor support that never made it upstream. Signed-off-by: Timur Tabi diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 6e32ba8..fa7f200 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -337,7 +337,6 @@ 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 */ @@ -982,7 +981,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 */ -- cgit v0.10.2 From 7af3b136d4e035d71c4e70c4b4a2017a10623fc0 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 16 Oct 2012 17:33:48 -0500 Subject: drivers/video: fsl-diu-fb: clean up reset of primary display Commit 4b5006ec ("shared DIU framebuffer support") added the ability to retain the splash screen until the framebuffer is opened by user space. Clean up this code to eliminate redundant writes to registers, and eliminate the use of dummy area descriptor. Signed-off-by: Timur Tabi diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index fa7f200..37175ac 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -496,8 +496,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]; @@ -549,8 +548,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]; @@ -1519,7 +1517,6 @@ 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 */ unsigned int i; int ret; @@ -1584,10 +1581,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); @@ -1609,11 +1602,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); -- cgit v0.10.2 From ceb001b077af64b20a8027132cfe015c80f4319d Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 16 Oct 2012 17:33:49 -0500 Subject: drivers/video: fsl-diu-fb: don't touch registers for unused features We don't use the writeback buffer, so don't initialize the registers for that feature. The default value for SYN_POL is already zero, so don't re-initialize it. Writing the INT_STATUS register does nothing. The value that we write to the PLUT register only makes sense on the MPC8610 and P1022, so don't touch that register on the MPC5121. Signed-off-by: Timur Tabi diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 37175ac..0883bc4 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -825,12 +825,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 */ @@ -847,9 +843,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); -- cgit v0.10.2 From e7b427187547bff5b845f3a7137312c6d1ae8153 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Tue, 16 Oct 2012 17:33:50 -0500 Subject: drivers/video: fsl-diu-fb: store EDID data in the global object Although the DIU driver creates five framebuffer devices, only the first one controls the physical display. The remaining four are virtual "AOIs". Therefore, the EDID data should be stored in the global fsl_diu_data object, instead of the per-framebuffer object. Signed-off-by: Timur Tabi diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 0883bc4..d3fc92e 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -342,7 +342,6 @@ struct mfb_info { 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; }; /** @@ -377,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 */ @@ -1310,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); @@ -1326,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); @@ -1346,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; @@ -1410,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) @@ -1525,6 +1524,7 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev) struct mfb_info *mfbi; struct fsl_diu_data *data; dma_addr_t dma_addr; /* DMA addr of fsl_diu_data struct */ + const void *prop; unsigned int i; int ret; @@ -1568,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); -- cgit v0.10.2