|
@@ -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);
|