diff options
Diffstat (limited to 'drivers/video/fsl-diu-fb.c')
-rw-r--r-- | drivers/video/fsl-diu-fb.c | 145 |
1 files changed, 117 insertions, 28 deletions
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 27455ce298b7..8bbbf08fa3ce 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c @@ -34,7 +34,8 @@ #include <linux/of_platform.h> #include <sysdev/fsl_soc.h> -#include "fsl-diu-fb.h" +#include <linux/fsl-diu-fb.h> +#include "edid.h" /* * These parameters give default parameters @@ -217,6 +218,7 @@ 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; }; @@ -317,6 +319,17 @@ static void fsl_diu_free(void *virt, size_t size) free_pages_exact(virt, size); } +/* + * Workaround for failed writing desc register of planes. + * Needed with MPC5121 DIU rev 2.0 silicon. + */ +void wr_reg_wa(u32 *reg, u32 val) +{ + do { + out_be32(reg, val); + } while (in_be32(reg) != val); +} + static int fsl_diu_enable_panel(struct fb_info *info) { struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par; @@ -330,7 +343,7 @@ static int fsl_diu_enable_panel(struct fb_info *info) switch (mfbi->index) { case 0: /* plane 0 */ if (hw->desc[0] != ad->paddr) - out_be32(&hw->desc[0], ad->paddr); + wr_reg_wa(&hw->desc[0], ad->paddr); break; case 1: /* plane 1 AOI 0 */ cmfbi = machine_data->fsl_diu_info[2]->par; @@ -340,7 +353,7 @@ static int fsl_diu_enable_panel(struct fb_info *info) cpu_to_le32(cmfbi->ad->paddr); else ad->next_ad = 0; - out_be32(&hw->desc[1], ad->paddr); + wr_reg_wa(&hw->desc[1], ad->paddr); } break; case 3: /* plane 2 AOI 0 */ @@ -351,14 +364,14 @@ static int fsl_diu_enable_panel(struct fb_info *info) cpu_to_le32(cmfbi->ad->paddr); else ad->next_ad = 0; - out_be32(&hw->desc[2], ad->paddr); + wr_reg_wa(&hw->desc[2], ad->paddr); } break; case 2: /* plane 1 AOI 1 */ pmfbi = machine_data->fsl_diu_info[1]->par; ad->next_ad = 0; if (hw->desc[1] == machine_data->dummy_ad->paddr) - out_be32(&hw->desc[1], ad->paddr); + wr_reg_wa(&hw->desc[1], ad->paddr); else /* AOI0 open */ pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); break; @@ -366,7 +379,7 @@ static int fsl_diu_enable_panel(struct fb_info *info) pmfbi = machine_data->fsl_diu_info[3]->par; ad->next_ad = 0; if (hw->desc[2] == machine_data->dummy_ad->paddr) - out_be32(&hw->desc[2], ad->paddr); + wr_reg_wa(&hw->desc[2], ad->paddr); else /* AOI0 was open */ pmfbi->ad->next_ad = cpu_to_le32(ad->paddr); break; @@ -390,27 +403,24 @@ static int fsl_diu_disable_panel(struct fb_info *info) switch (mfbi->index) { case 0: /* plane 0 */ if (hw->desc[0] != machine_data->dummy_ad->paddr) - out_be32(&hw->desc[0], - machine_data->dummy_ad->paddr); + wr_reg_wa(&hw->desc[0], machine_data->dummy_ad->paddr); break; case 1: /* plane 1 AOI 0 */ cmfbi = machine_data->fsl_diu_info[2]->par; if (cmfbi->count > 0) /* AOI1 is open */ - out_be32(&hw->desc[1], cmfbi->ad->paddr); + wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr); /* move AOI1 to the first */ else /* AOI1 was closed */ - out_be32(&hw->desc[1], - machine_data->dummy_ad->paddr); + wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr); /* close AOI 0 */ break; case 3: /* plane 2 AOI 0 */ cmfbi = machine_data->fsl_diu_info[4]->par; if (cmfbi->count > 0) /* AOI1 is open */ - out_be32(&hw->desc[2], cmfbi->ad->paddr); + wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr); /* move AOI1 to the first */ else /* AOI1 was closed */ - out_be32(&hw->desc[2], - machine_data->dummy_ad->paddr); + wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr); /* close AOI 0 */ break; case 2: /* plane 1 AOI 1 */ @@ -421,7 +431,7 @@ static int fsl_diu_disable_panel(struct fb_info *info) /* AOI0 is open, must be the first */ pmfbi->ad->next_ad = 0; } else /* AOI1 is the first in the chain */ - out_be32(&hw->desc[1], machine_data->dummy_ad->paddr); + wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr); /* close AOI 1 */ break; case 4: /* plane 2 AOI 1 */ @@ -432,7 +442,7 @@ static int fsl_diu_disable_panel(struct fb_info *info) /* AOI0 is open, must be the first */ pmfbi->ad->next_ad = 0; } else /* AOI1 is the first in the chain */ - out_be32(&hw->desc[2], machine_data->dummy_ad->paddr); + wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr); /* close AOI 1 */ break; default: @@ -1100,6 +1110,10 @@ static int fsl_diu_open(struct fb_info *info, int user) struct mfb_info *mfbi = info->par; int res = 0; + /* free boot splash memory on first /dev/fb0 open */ + if (!mfbi->index && diu_ops.release_bootmem) + diu_ops.release_bootmem(); + spin_lock(&diu_lock); mfbi->count++; if (mfbi->count == 1) { @@ -1173,18 +1187,30 @@ 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"; + struct fb_videomode *db = fsl_diu_mode_db; + unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db); + int has_default_mode = 1; if (init_fbinfo(info)) return -EINVAL; - if (mfbi->index == 0) /* plane 0 */ + if (mfbi->index == 0) { /* plane 0 */ + if (mfbi->edid_data) { + /* Now build modedb from EDID */ + fb_edid_to_monspecs(mfbi->edid_data, &info->monspecs); + fb_videomode_to_modelist(info->monspecs.modedb, + info->monspecs.modedb_len, + &info->modelist); + db = info->monspecs.modedb; + dbsize = info->monspecs.modedb_len; + } aoi_mode = fb_mode; - else + } 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); - + rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize, + &fsl_diu_default_mode, default_bpp); switch (rc) { case 1: pr_debug("using mode specified in @mode\n"); @@ -1202,10 +1228,50 @@ static int __devinit install_fb(struct fb_info *info) default: pr_debug("rc = %d\n", rc); pr_debug("failed to find mode\n"); - return -EINVAL; + /* + * For plane 0 we continue and look into + * driver's internal modedb. + */ + if (mfbi->index == 0 && mfbi->edid_data) + has_default_mode = 0; + else + return -EINVAL; break; } + if (!has_default_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); + if (rc > 0 && rc < 5) + has_default_mode = 1; + } + + /* Still not found, use preferred mode from database if any */ + if (!has_default_mode && info->monspecs.modedb) { + struct fb_monspecs *specs = &info->monspecs; + struct fb_videomode *modedb = &specs->modedb[0]; + + /* + * Get preferred timing. If not found, + * first mode in database will be used. + */ + if (specs->misc & FB_MISC_1ST_DETAIL) { + int i; + + for (i = 0; i < specs->modedb_len; i++) { + if (specs->modedb[i].flag & FB_MODE_IS_FIRST) { + modedb = &specs->modedb[i]; + break; + } + } + } + + info->var.bits_per_pixel = default_bpp; + fb_videomode_to_var(&info->var, modedb); + } + pr_debug("xres_virtual %d\n", info->var.xres_virtual); pr_debug("bits_per_pixel %d\n", info->var.bits_per_pixel); @@ -1244,6 +1310,9 @@ static void uninstall_fb(struct fb_info *info) if (!mfbi->registered) return; + if (mfbi->index == 0) + kfree(mfbi->edid_data); + unregister_framebuffer(info); unmap_video_memory(info); if (&info->cmap) @@ -1324,7 +1393,7 @@ static void free_irq_local(int irq) * 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) +static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state) { struct fsl_diu_data *machine_data; @@ -1334,7 +1403,7 @@ static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state) return 0; } -static int fsl_diu_resume(struct of_device *ofdev) +static int fsl_diu_resume(struct platform_device *ofdev) { struct fsl_diu_data *machine_data; @@ -1418,7 +1487,7 @@ static ssize_t show_monitor(struct device *device, return diu_ops.show_monitor_port(machine_data->monitor_port, buf); } -static int __devinit fsl_diu_probe(struct of_device *ofdev, +static int __devinit fsl_diu_probe(struct platform_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->dev.of_node; @@ -1427,6 +1496,7 @@ static int __devinit fsl_diu_probe(struct of_device *ofdev, int ret, i, error = 0; struct resource res; struct fsl_diu_data *machine_data; + int diu_mode; machine_data = kzalloc(sizeof(struct fsl_diu_data), GFP_KERNEL); if (!machine_data) @@ -1443,6 +1513,17 @@ static int __devinit fsl_diu_probe(struct of_device *ofdev, mfbi = machine_data->fsl_diu_info[i]->par; memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info)); mfbi->parent = machine_data; + + if (mfbi->index == 0) { + 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); + } } ret = of_address_to_resource(np, 0, &res); @@ -1463,7 +1544,9 @@ static int __devinit fsl_diu_probe(struct of_device *ofdev, goto error2; } - out_be32(&dr.diu_reg->diu_mode, 0); /* disable DIU anyway*/ + diu_mode = in_be32(&dr.diu_reg->diu_mode); + if (diu_mode != MFB_MODE1) + out_be32(&dr.diu_reg->diu_mode, 0); /* disable DIU */ /* Get the IRQ of the DIU */ machine_data->irq = irq_of_parse_and_map(np, 0); @@ -1511,7 +1594,13 @@ static int __devinit fsl_diu_probe(struct of_device *ofdev, 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); + /* + * Let DIU display splash screen if it was pre-initialized + * by the bootloader, set dummy area descriptor otherwise. + */ + if (diu_mode != MFB_MODE1) + 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); @@ -1578,7 +1667,7 @@ error2: } -static int fsl_diu_remove(struct of_device *ofdev) +static int fsl_diu_remove(struct platform_device *ofdev) { struct fsl_diu_data *machine_data; int i; |