diff options
Diffstat (limited to 'drivers/video/fbdev/mxc')
-rw-r--r-- | drivers/video/fbdev/mxc/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/fbdev/mxc/ldb.c | 59 | ||||
-rw-r--r-- | drivers/video/fbdev/mxc/mxc_hdmi.c | 156 | ||||
-rw-r--r-- | drivers/video/fbdev/mxc/mxc_ipuv3_fb.c | 26 | ||||
-rw-r--r-- | drivers/video/fbdev/mxc/mxc_lcdif.c | 60 | ||||
-rw-r--r-- | drivers/video/fbdev/mxc/mxc_vdacif.c | 356 |
6 files changed, 572 insertions, 87 deletions
diff --git a/drivers/video/fbdev/mxc/Makefile b/drivers/video/fbdev/mxc/Makefile index 5b619152673f..d93eeb7b56a4 100644 --- a/drivers/video/fbdev/mxc/Makefile +++ b/drivers/video/fbdev/mxc/Makefile @@ -11,7 +11,7 @@ obj-$(CONFIG_FB_MXC_HDMI) += mxc_hdmi.o obj-$(CONFIG_FB_MXC_EDID) += mxc_edid.o obj-$(CONFIG_FB_MXC_ADV7535) += adv7535.o obj-$(CONFIG_FB_MXC_DISP_FRAMEWORK) += mxc_dispdrv.o -obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_lcdif.o mxc_ipuv3_fb.o +obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_lcdif.o mxc_vdacif.o mxc_ipuv3_fb.o obj-$(CONFIG_FB_MXC_EINK_PANEL) += mxc_epdc_fb.o obj-$(CONFIG_FB_MXC_EINK_V2_PANEL) += mxc_epdc_v2_fb.o obj-$(CONFIG_FB_MXS_SII902X) += mxsfb_sii902x.o diff --git a/drivers/video/fbdev/mxc/ldb.c b/drivers/video/fbdev/mxc/ldb.c index 1d6c9e08590c..66a8fe51a84d 100644 --- a/drivers/video/fbdev/mxc/ldb.c +++ b/drivers/video/fbdev/mxc/ldb.c @@ -22,6 +22,7 @@ #include <linux/regmap.h> #include <linux/types.h> #include <video/of_videomode.h> +#include <video/of_display_timing.h> #include <video/videomode.h> #include "mxc_dispdrv.h" @@ -77,7 +78,8 @@ struct ldb_data; struct ldb_chan { struct ldb_data *ldb; struct fb_info *fbi; - struct videomode vm; + unsigned long serial_clk; + struct display_timings *timings; enum crtc crtc; int chno; bool is_used; @@ -301,8 +303,10 @@ static int ldb_init(struct mxc_dispdrv_handle *mddh, struct device *dev = ldb->dev; struct fb_info *fbi = setting->fbi; struct ldb_chan *chan; - struct fb_videomode fb_vm; - int chno; + struct fb_videomode fb_vm, native_mode; + int chno, i, ret; + struct videomode vm; + unsigned long pixelclock; chno = ldb->chan[ldb->primary_chno].is_used ? !ldb->primary_chno : ldb->primary_chno; @@ -322,10 +326,40 @@ static int ldb_init(struct mxc_dispdrv_handle *mddh, chan->fbi = fbi; - fb_videomode_from_videomode(&chan->vm, &fb_vm); - fb_videomode_to_var(&fbi->var, &fb_vm); - setting->crtc = chan->crtc; + + INIT_LIST_HEAD(&fbi->modelist); + for (i = 0; i < chan->timings->num_timings; i++) { + ret = videomode_from_timings(chan->timings, &vm, i); + if (ret < 0) { + dev_err(ldb->dev, + "failed to get video mode from timings\n"); + return ret; + } + + ret = fb_videomode_from_videomode(&vm, &fb_vm); + if (ret < 0) { + dev_err(ldb->dev, + "failed to get fb video mode from video mode\n"); + return ret; + } + + if (i == chan->timings->native_mode) + fb_videomode_from_videomode(&vm, &native_mode); + + fb_add_videomode(&fb_vm, &fbi->modelist); + fb_videomode_to_var(&fbi->var, &fb_vm); + } + + fb_find_mode(&fbi->var, fbi, setting->dft_mode_str, &fb_vm, + chan->timings->num_timings, &native_mode, + setting->default_bpp); + + /* Calculate the LVDS clock */ + fb_var_to_videomode(&fb_vm, &fbi->var); + + pixelclock = fb_vm.pixclock ? (1000000000UL/(fb_vm.pixclock)) * 1000: 0; + chan->serial_clk = ldb->spl_mode ? pixelclock * 7 / 2 : pixelclock * 7; return 0; } @@ -399,7 +433,6 @@ static int ldb_setup(struct mxc_dispdrv_handle *mddh, struct clk *other_ldb_di_sel = NULL; struct bus_mux bus_mux; int ret = 0, id = 0, chno, other_chno; - unsigned long serial_clk; u32 mux_val; ret = find_ldb_chno(ldb, fbi, &chno); @@ -453,9 +486,7 @@ static int ldb_setup(struct mxc_dispdrv_handle *mddh, clk_set_parent(ldb->div_sel_clk[chno], ldb_di_parent); ldb_di_sel = clk_get_parent(ldb_di_parent); ldb_di_sel_parent = clk_get_parent(ldb_di_sel); - serial_clk = ldb->spl_mode ? chan.vm.pixelclock * 7 / 2 : - chan.vm.pixelclock * 7; - clk_set_rate(ldb_di_sel_parent, serial_clk); + clk_set_rate(ldb_di_sel_parent, chan.serial_clk); /* * split mode or dual mode: @@ -834,9 +865,11 @@ static int ldb_probe(struct platform_device *pdev) return -EINVAL; } - ret = of_get_videomode(child, &chan->vm, 0); - if (ret) - return -EINVAL; + chan->timings = of_get_display_timings(child); + if (!chan->timings) { + dev_err(ldb->dev, "failed to get display timings\n"); + ret = -ENOENT; + } sprintf(clkname, "ldb_di%d", i); ldb->ldb_di_clk[i] = devm_clk_get(dev, clkname); diff --git a/drivers/video/fbdev/mxc/mxc_hdmi.c b/drivers/video/fbdev/mxc/mxc_hdmi.c index 6bb39f9795d1..db75e32493f5 100644 --- a/drivers/video/fbdev/mxc/mxc_hdmi.c +++ b/drivers/video/fbdev/mxc/mxc_hdmi.c @@ -72,6 +72,18 @@ #define YCBCR422_8BITS 3 #define XVYCC444 4 +static bool only_cea; +module_param(only_cea, bool, 0644); +MODULE_PARM_DESC(only_cea, "Allow only CEA modes"); + +#if 0 +/* rely solely on the HPD pin for hot plug detection */ +#undef HDMI_DVI_STAT +#define HDMI_DVI_STAT 2 + +#undef HDMI_DVI_IH_STAT +#define HDMI_DVI_IH_STAT 1 +#endif /* * We follow a flowchart which is in the "Synopsys DesignWare Courses * HDMI Transmitter Controller User Guide, 1.30a", section 3.1 @@ -162,11 +174,11 @@ struct mxc_hdmi { bool dft_mode_set; char *dft_mode_str; int default_bpp; - u8 latest_intr_stat; bool irq_enabled; spinlock_t irq_lock; bool phy_enabled; struct fb_videomode default_mode; + struct fb_videomode previous_mode; struct fb_videomode previous_non_vga_mode; bool requesting_vga_for_initialization; @@ -945,6 +957,9 @@ static u8 hdmi_edid_i2c_read(struct mxc_hdmi *hdmi, return data; } +static int keepalive=1; +module_param(keepalive, int, 0644); +MODULE_PARM_DESC(keepalive, "Keep HDMI alive (don't detect disconnect)"); /* "Power-down enable (active low)" * That mean that power up == 1! */ @@ -1264,9 +1279,6 @@ static void mxc_hdmi_phy_init(struct mxc_hdmi *hdmi) || (hdmi->blank != FB_BLANK_UNBLANK)) return; - if (!hdmi->hdmi_data.video_mode.mDVI) - hdmi_enable_overflow_interrupts(); - /*check csc whether needed activated in HDMI mode */ cscon = (isColorSpaceConversion(hdmi) && !hdmi->hdmi_data.video_mode.mDVI); @@ -1283,6 +1295,8 @@ static void mxc_hdmi_phy_init(struct mxc_hdmi *hdmi) } hdmi->phy_enabled = true; + if (!hdmi->hdmi_data.video_mode.mDVI) + hdmi_enable_overflow_interrupts(); } static void hdmi_config_AVI(struct mxc_hdmi *hdmi) @@ -1805,7 +1819,7 @@ static void mxc_hdmi_edid_rebuild_modelist(struct mxc_hdmi *hdmi) mode = &hdmi->fbi->monspecs.modedb[i]; if (!(mode->vmode & FB_VMODE_INTERLACED) && - (mxc_edid_mode_to_vic(mode) != 0)) { + (!only_cea || mxc_edid_mode_to_vic(mode))) { dev_dbg(&hdmi->pdev->dev, "Added mode %d:", i); dev_dbg(&hdmi->pdev->dev, @@ -1948,13 +1962,19 @@ static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) } /* HDMI Initialization Steps D, E, F */ + /* + * Without this the kernel hangs during boot with HDMI attached. + * It looks like we get an overflow IRQ storm. + */ + mxc_hdmi_set_mode_to_vga_dvi(hdmi); switch (edid_status) { case HDMI_EDID_SUCCESS: mxc_hdmi_edid_rebuild_modelist(hdmi); break; - /* Nothing to do if EDID same */ + /* Rebuild even if they're the same in case only_cea changed */ case HDMI_EDID_SAME: + mxc_hdmi_edid_rebuild_modelist(hdmi); break; case HDMI_EDID_FAIL: @@ -2009,74 +2029,70 @@ static void hotplug_worker(struct work_struct *work) struct delayed_work *delay_work = to_delayed_work(work); struct mxc_hdmi *hdmi = container_of(delay_work, struct mxc_hdmi, hotplug_work); - u32 phy_int_stat, phy_int_pol, phy_int_mask; - u8 val; + u32 hdmi_phy_stat0, hdmi_phy_pol0, hdmi_phy_mask0; unsigned long flags; char event_string[32]; + int isalive = 0; char *envp[] = { event_string, NULL }; - phy_int_stat = hdmi->latest_intr_stat; - phy_int_pol = hdmi_readb(HDMI_PHY_POL0); - dev_dbg(&hdmi->pdev->dev, "phy_int_stat=0x%x, phy_int_pol=0x%x\n", - phy_int_stat, phy_int_pol); + hdmi_phy_stat0 = hdmi_readb(HDMI_PHY_STAT0); + hdmi_phy_pol0 = hdmi_readb(HDMI_PHY_POL0); + + dev_dbg(&hdmi->pdev->dev, "hdmi_phy_stat0=0x%x, hdmi_phy_pol0=0x%x\n", + hdmi_phy_stat0, hdmi_phy_pol0); + + /* Make HPD intr active low to capture unplug event or + * active high to capture plugin event */ + hdmi_writeb((HDMI_DVI_STAT & ~hdmi_phy_stat0), HDMI_PHY_POL0); /* check cable status */ - if (phy_int_stat & HDMI_IH_PHY_STAT0_HPD) { - /* cable connection changes */ - if (phy_int_pol & HDMI_PHY_HPD) { - /* Plugin event */ - dev_dbg(&hdmi->pdev->dev, "EVENT=plugin\n"); - mxc_hdmi_cable_connected(hdmi); - - /* Make HPD intr active low to capture unplug event */ - val = hdmi_readb(HDMI_PHY_POL0); - val &= ~HDMI_PHY_HPD; - hdmi_writeb(val, HDMI_PHY_POL0); - - hdmi_set_cable_state(1); - - sprintf(event_string, "EVENT=plugin"); - kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp); -#ifdef CONFIG_MXC_HDMI_CEC - mxc_hdmi_cec_handle(0x80); -#endif - } else if (!(phy_int_pol & HDMI_PHY_HPD)) { - /* Plugout event */ - dev_dbg(&hdmi->pdev->dev, "EVENT=plugout\n"); - hdmi_set_cable_state(0); - mxc_hdmi_abort_stream(); - mxc_hdmi_cable_disconnected(hdmi); + if (hdmi_phy_stat0 & HDMI_DVI_STAT) { + /* Plugin event */ + dev_dbg(&hdmi->pdev->dev, "EVENT=plugin\n"); + mxc_hdmi_cable_connected(hdmi); - /* Make HPD intr active high to capture plugin event */ - val = hdmi_readb(HDMI_PHY_POL0); - val |= HDMI_PHY_HPD; - hdmi_writeb(val, HDMI_PHY_POL0); + hdmi_set_cable_state(1); - sprintf(event_string, "EVENT=plugout"); - kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp); + sprintf(event_string, "EVENT=plugin"); + kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp); #ifdef CONFIG_MXC_HDMI_CEC - mxc_hdmi_cec_handle(0x100); + mxc_hdmi_cec_handle(0x80); +#endif + if (keepalive) + hdmi_writeb(HDMI_DVI_STAT, HDMI_PHY_POL0); + isalive=1; + } else if (!keepalive) { + /* Plugout event */ + dev_dbg(&hdmi->pdev->dev, "EVENT=plugout\n"); + hdmi_set_cable_state(0); + mxc_hdmi_abort_stream(); + mxc_hdmi_cable_disconnected(hdmi); + + sprintf(event_string, "EVENT=plugout"); + kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp); +#ifdef CONFIG_MXC_HDMI_CEC + mxc_hdmi_cec_handle(0x100); #endif - } else - dev_dbg(&hdmi->pdev->dev, "EVENT=none?\n"); } /* Lock here to ensure full powerdown sequence * completed before next interrupt processed */ spin_lock_irqsave(&hdmi->irq_lock, flags); - /* Re-enable HPD interrupts */ - phy_int_mask = hdmi_readb(HDMI_PHY_MASK0); - phy_int_mask &= ~HDMI_PHY_HPD; - hdmi_writeb(phy_int_mask, HDMI_PHY_MASK0); + if (!(keepalive && isalive)) { + /* Re-enable HPD interrupts */ + hdmi_phy_mask0 = hdmi_readb(HDMI_PHY_MASK0); + hdmi_phy_mask0 &= ~HDMI_DVI_STAT; + hdmi_writeb(hdmi_phy_mask0, HDMI_PHY_MASK0); - /* Unmute interrupts */ - hdmi_writeb(~HDMI_IH_MUTE_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); + /* Unmute interrupts */ + hdmi_writeb(~HDMI_DVI_IH_STAT, HDMI_IH_MUTE_PHY_STAT0); - if (hdmi_readb(HDMI_IH_FC_STAT2) & HDMI_IH_FC_STAT2_OVERFLOW_MASK) - mxc_hdmi_clear_overflow(hdmi); + if (hdmi_readb(HDMI_IH_FC_STAT2) & HDMI_IH_FC_STAT2_OVERFLOW_MASK) + mxc_hdmi_clear_overflow(hdmi); + } spin_unlock_irqrestore(&hdmi->irq_lock, flags); } @@ -2099,7 +2115,7 @@ static void hdcp_hdp_worker(struct work_struct *work) static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) { struct mxc_hdmi *hdmi = data; - u8 val, intr_stat; + u8 val; unsigned long flags; spin_lock_irqsave(&hdmi->irq_lock, flags); @@ -2121,25 +2137,22 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) * HDMI registers. */ /* Capture status - used in hotplug_worker ISR */ - intr_stat = hdmi_readb(HDMI_IH_PHY_STAT0); - - if (intr_stat & HDMI_IH_PHY_STAT0_HPD) { + if (hdmi_readb(HDMI_IH_PHY_STAT0) & HDMI_DVI_IH_STAT) { dev_dbg(&hdmi->pdev->dev, "Hotplug interrupt received\n"); - hdmi->latest_intr_stat = intr_stat; /* Mute interrupts until handled */ val = hdmi_readb(HDMI_IH_MUTE_PHY_STAT0); - val |= HDMI_IH_MUTE_PHY_STAT0_HPD; + val |= HDMI_DVI_IH_STAT; hdmi_writeb(val, HDMI_IH_MUTE_PHY_STAT0); val = hdmi_readb(HDMI_PHY_MASK0); - val |= HDMI_PHY_HPD; + val |= HDMI_DVI_STAT; hdmi_writeb(val, HDMI_PHY_MASK0); /* Clear Hotplug interrupts */ - hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + hdmi_writeb(HDMI_DVI_IH_STAT, HDMI_IH_PHY_STAT0); schedule_delayed_work(&(hdmi->hotplug_work), msecs_to_jiffies(20)); } @@ -2171,6 +2184,9 @@ static void mxc_hdmi_setup(struct mxc_hdmi *hdmi, unsigned long event) dev_dbg(&hdmi->pdev->dev, "%s - video mode changed\n", __func__); + /* Save mode as 'previous_mode' so that we can know if mode changed. */ + memcpy(&hdmi->previous_mode, &m, sizeof(struct fb_videomode)); + hdmi->vic = 0; if (!hdmi->requesting_vga_for_initialization) { /* Save mode if this isn't the result of requesting @@ -2291,13 +2307,13 @@ static void mxc_hdmi_fb_registered(struct mxc_hdmi *hdmi) HDMI_PHY_I2CM_CTLINT_ADDR); /* enable cable hot plug irq */ - hdmi_writeb((u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0); + hdmi_writeb((u8)~HDMI_DVI_STAT, HDMI_PHY_MASK0); /* Clear Hotplug interrupts */ - hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + hdmi_writeb(HDMI_DVI_IH_STAT, HDMI_IH_PHY_STAT0); /* Unmute interrupts */ - hdmi_writeb(~HDMI_IH_MUTE_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); + hdmi_writeb(~HDMI_DVI_IH_STAT, HDMI_IH_MUTE_PHY_STAT0); hdmi->fb_reg = true; @@ -2310,6 +2326,7 @@ static int mxc_hdmi_fb_event(struct notifier_block *nb, { struct fb_event *event = v; struct mxc_hdmi *hdmi = container_of(nb, struct mxc_hdmi, nb); + struct fb_videomode *mode; if (strcmp(event->info->fix.id, hdmi->fbi->fix.id)) return 0; @@ -2329,7 +2346,10 @@ static int mxc_hdmi_fb_event(struct notifier_block *nb, case FB_EVENT_MODE_CHANGE: dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_MODE_CHANGE\n"); - if (hdmi->fb_reg) + mode = (struct fb_videomode *)event->data; + if ((hdmi->fb_reg) && + (mode != NULL) && + !fb_mode_is_equal(&hdmi->previous_mode, mode)) mxc_hdmi_setup(hdmi, val); break; @@ -2628,10 +2648,10 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, /* Configure registers related to HDMI interrupt * generation before registering IRQ. */ - hdmi_writeb(HDMI_PHY_HPD, HDMI_PHY_POL0); + hdmi_writeb(HDMI_DVI_STAT, HDMI_PHY_POL0); /* Clear Hotplug interrupts */ - hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + hdmi_writeb(HDMI_DVI_IH_STAT, HDMI_IH_PHY_STAT0); hdmi->nb.notifier_call = mxc_hdmi_fb_event; ret = fb_register_client(&hdmi->nb); diff --git a/drivers/video/fbdev/mxc/mxc_ipuv3_fb.c b/drivers/video/fbdev/mxc/mxc_ipuv3_fb.c index c6001b6a73d2..926990316639 100644 --- a/drivers/video/fbdev/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/fbdev/mxc/mxc_ipuv3_fb.c @@ -1317,6 +1317,15 @@ static int mxcfb_set_par(struct fb_info *fbi) dev_dbg(fbi->device, "pixclock = %ul Hz\n", (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL)); + dev_info(fbi->device,"%dx%d h_sync,r,l: %d,%d,%d v_sync,l,u: %d,%d,%d pixclock=%u Hz\n", + fbi->var.xres, fbi->var.yres, + fbi->var.hsync_len, + fbi->var.right_margin, + fbi->var.left_margin, + fbi->var.vsync_len, + fbi->var.lower_margin, + fbi->var.upper_margin, + (u32)(PICOS2KHZ(fbi->var.pixclock) * 1000UL)); if (ipu_init_sync_panel(mxc_fbi->ipu, mxc_fbi->ipu_di, (PICOS2KHZ(fbi->var.pixclock)) * 1000UL, @@ -3050,6 +3059,10 @@ static int mxcfb_option_setup(struct platform_device *pdev, struct fb_info *fbi) name[5] += pdev->id; if (fb_get_options(name, &options)) { + if (options && !strncmp(options, "off", 3)) { + dev_info(&pdev->dev, "%s is turned off!\n", name); + return -ENODEV; + } dev_err(&pdev->dev, "Can't get fb option for %s!\n", name); return -ENODEV; } @@ -3128,8 +3141,7 @@ static int mxcfb_option_setup(struct platform_device *pdev, struct fb_info *fbi) fb_mode_str = opt; } - if (fb_mode_str) - pdata->mode_str = fb_mode_str; + pdata->mode_str = fb_mode_str; return 0; } @@ -3343,8 +3355,10 @@ static void mxcfb_unsetup_overlay(struct fb_info *fbi_bg) } static bool ipu_usage[2][2]; -static int ipu_test_set_usage(int ipu, int di) +static int ipu_test_set_usage(unsigned ipu, unsigned di) { + if ((ipu >= 2) || (di >= 2)) + return -EINVAL; if (ipu_usage[ipu][di]) return -EBUSY; else @@ -3354,6 +3368,8 @@ static int ipu_test_set_usage(int ipu, int di) static void ipu_clear_usage(int ipu, int di) { + if ((ipu >= 2) || (di >= 2)) + return; ipu_usage[ipu][di] = false; } @@ -3525,7 +3541,7 @@ static int mxcfb_probe(struct platform_device *pdev) mxcfbi->ipu_alp_ch_irq = IPU_IRQ_BG_ALPHA_SYNC_EOF; mxcfbi->ipu_ch = MEM_BG_SYNC; /* Unblank the primary fb only by default */ - if (pdev->id == 0) + if (1) //(pdev->id == 0) mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_UNBLANK; else mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN; @@ -3566,7 +3582,7 @@ static int mxcfb_probe(struct platform_device *pdev) mxcfbi->ipu_ch_nf_irq = IPU_IRQ_DC_SYNC_NFACK; mxcfbi->ipu_alp_ch_irq = -1; mxcfbi->ipu_ch = MEM_DC_SYNC; - mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN; + mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_UNBLANK; ret = mxcfb_register(fbi); if (ret < 0) diff --git a/drivers/video/fbdev/mxc/mxc_lcdif.c b/drivers/video/fbdev/mxc/mxc_lcdif.c index 59d429c82017..1636bae7b8d0 100644 --- a/drivers/video/fbdev/mxc/mxc_lcdif.c +++ b/drivers/video/fbdev/mxc/mxc_lcdif.c @@ -37,6 +37,42 @@ struct mxc_lcdif_data { static struct fb_videomode lcdif_modedb[] = { { + /* 1024x600 @ 59 Hz , pixel clk @ 45 MHz */ /* 22222 ps*/ + "FusionF10A", 59, 1024, 600, 22222, + .left_margin = 104, + .right_margin = 43, + .hsync_len = 5, + .upper_margin = 24, + .lower_margin = 20, + .vsync_len = 5, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0,}, + { + /* 800x480 @ 60 Hz , pixel clk @ 33.26MHz */ + "FusionF07A", 60, 800, 480, 30066, + .left_margin = 88, + .right_margin = 40, + .hsync_len = 128, + .upper_margin = 33, + .lower_margin = 10, + .vsync_len = 2, + .sync = FB_SYNC_CLK_LAT_FALL, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0,}, + { + /* 800x480 @ 60 Hz , pixel clk @ 33.26MHz */ + "EDT-WVGA", 60, 800, 480, 30066, + .left_margin = 88, + .right_margin = 40, + .hsync_len = 128, + .upper_margin = 33, + .lower_margin = 10, + .vsync_len = 2, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0,}, + { /* 800x480 @ 57 Hz , pixel clk @ 27MHz */ "CLAA-WVGA", 57, 800, 480, 37037, 40, 60, 10, 10, 20, 10, FB_SYNC_CLK_LAT_FALL, @@ -48,6 +84,30 @@ static struct fb_videomode lcdif_modedb[] = { FB_SYNC_CLK_LAT_FALL, FB_VMODE_NONINTERLACED, 0,}, + { + /* 640x480 @ 60 Hz , pixel clk @ 25.175MHz */ + "EDT-VGA", 60, 640, 480, 39721, + .left_margin = 114, + .right_margin = 16, + .hsync_len = 30, + .upper_margin = 32, + .lower_margin = 10, + .vsync_len = 3, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0,}, + { + /* 480x272 @ 60 Hz , pixel clk @ 9MHz */ + "EDT-480x272", 60, 480, 272, 111111, + .left_margin = 2, + .right_margin = 2, + .hsync_len = 41, + .upper_margin = 2, + .lower_margin = 2, + .vsync_len = 10, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0,}, }; static int lcdif_modedb_sz = ARRAY_SIZE(lcdif_modedb); diff --git a/drivers/video/fbdev/mxc/mxc_vdacif.c b/drivers/video/fbdev/mxc/mxc_vdacif.c new file mode 100644 index 000000000000..873c02a4ef51 --- /dev/null +++ b/drivers/video/fbdev/mxc/mxc_vdacif.c @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2011-2015 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2014-2016 Toradex AG. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * copy of mxc_lcdif.c + * adds a second parallel output which drives a Video DAC + * available on the second IPU, first DI on a + * Apalis iMX6 module + */ + +#include <linux/init.h> +#include <linux/ipu.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mxcfb.h> +#include <linux/of_device.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> + +#include "mxc_dispdrv.h" + +struct mxc_vdac_platform_data { + u32 default_ifmt; + u32 ipu_id; + u32 disp_id; +}; + +struct mxc_vdacif_data { + struct platform_device *pdev; + struct mxc_dispdrv_handle *disp_vdacif; +}; + +#define DISPDRV_LCD "vdac" + +static struct fb_videomode vdacif_modedb[] = { + /* 0 640x350-85 VESA */ + { NULL, 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_OE_LOW_ACT, FB_VMODE_NONINTERLACED, + FB_MODE_IS_VESA}, + /* 1 640x400-85 VESA */ + { NULL, 85, 640, 400, 31746, 96, 32, 41, 01, 64, 3, + FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, FB_VMODE_NONINTERLACED, + FB_MODE_IS_VESA }, + /* 2 720x400-85 VESA */ + { NULL, 85, 721, 400, 28169, 108, 36, 42, 01, 72, 3, + FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, FB_VMODE_NONINTERLACED, + FB_MODE_IS_VESA }, + /* 3 640x480-60 VESA */ + { NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2, + FB_SYNC_OE_LOW_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 4 640x480-72 VESA */ + { NULL, 72, 640, 480, 31746, 128, 24, 29, 9, 40, 2, + FB_SYNC_OE_LOW_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 5 640x480-75 VESA */ + { NULL, 75, 640, 480, 31746, 120, 16, 16, 01, 64, 3, + FB_SYNC_OE_LOW_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 6 640x480-85 VESA */ + { NULL, 85, 640, 480, 27777, 80, 56, 25, 01, 56, 3, + FB_SYNC_OE_LOW_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 7 800x600-56 VESA */ + { NULL, 56, 800, 600, 27777, 128, 24, 22, 01, 72, 2, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 8 800x600-60 VESA */ + { NULL, 60, 800, 600, 25000, 88, 40, 23, 01, 128, 4, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 9 800x600-72 VESA */ + { NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 10 800x600-75 VESA */ + { NULL, 75, 800, 600, 20202, 160, 16, 21, 01, 80, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 11 800x600-85 VESA */ + { NULL, 85, 800, 600, 17761, 152, 32, 27, 01, 64, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 12 1024x768i-43 VESA */ + { NULL, 43, 1024, 768, 22271, 56, 8, 41, 0, 176, 8, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_INTERLACED, FB_MODE_IS_VESA }, + /* 13 1024x768-60 VESA */ + { NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6, + FB_SYNC_OE_LOW_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 14 1024x768-70 VESA */ + { NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6, + FB_SYNC_OE_LOW_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 15 1024x768-75 VESA */ + { NULL, 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 16 1024x768-85 VESA */ + { NULL, 85, 1024, 768, 10582, 208, 48, 36, 1, 96, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 17 1152x864-75 VESA */ + { NULL, 75, 1152, 864, 9259, 256, 64, 32, 1, 128, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 18 1280x960-60 VESA */ + { NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 19 1280x960-85 VESA */ + { NULL, 85, 1280, 960, 6734, 224, 64, 47, 1, 160, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 20 1280x1024-60 VESA */ + { NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 21 1280x1024-75 VESA */ + { NULL, 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 22 1280x1024-85 VESA */ + { NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 23 1600x1200-60 VESA */ + { NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 24 1600x1200-65 VESA */ + { NULL, 65, 1600, 1200, 5698, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 25 1600x1200-70 VESA */ + { NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 26 1600x1200-75 VESA */ + { NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 27 1600x1200-85 VESA */ + { NULL, 85, 1600, 1200, 4357, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 28 1792x1344-60 VESA */ + { NULL, 60, 1792, 1344, 4882, 328, 128, 46, 1, 200, 3, + FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 29 1792x1344-75 VESA */ + { NULL, 75, 1792, 1344, 3831, 352, 96, 69, 1, 216, 3, + FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 30 1856x1392-60 VESA */ + { NULL, 60, 1856, 1392, 4580, 352, 96, 43, 1, 224, 3, + FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 31 1856x1392-75 VESA */ + { NULL, 75, 1856, 1392, 3472, 352, 128, 104, 1, 224, 3, + FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 32 1920x1440-60 VESA */ + { NULL, 60, 1920, 1440, 4273, 344, 128, 56, 1, 200, 3, + FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 33 1920x1440-75 VESA */ + { NULL, 75, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3, + FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 1920x1200 @ 60 Hz, 74.5 Khz hsync */ + { NULL, 60, 1920, 1200, 5177, 128, 336, 1, 38, 208, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED }, + /* #16: 1920x1080p@60Hz 16:9 */ + { NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_LOW_ACT, + FB_VMODE_NONINTERLACED, 0 }, +}; +static int vdacif_modedb_sz = ARRAY_SIZE(vdacif_modedb); + +static int vdacif_init(struct mxc_dispdrv_handle *disp, + struct mxc_dispdrv_setting *setting) +{ + int ret, i; + struct mxc_vdacif_data *vdacif = mxc_dispdrv_getdata(disp); + struct device *dev = &vdacif->pdev->dev; + struct mxc_vdac_platform_data *plat_data = dev->platform_data; + struct fb_videomode *modedb = vdacif_modedb; + int modedb_sz = vdacif_modedb_sz; + + /* use platform defined ipu/di */ + ret = ipu_di_to_crtc(dev, plat_data->ipu_id, + plat_data->disp_id, &setting->crtc); + if (ret < 0) + return ret; + + ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str, + modedb, modedb_sz, NULL, setting->default_bpp); + if (!ret) { + fb_videomode_to_var(&setting->fbi->var, &modedb[0]); + setting->if_fmt = plat_data->default_ifmt; + } + /* Peter's VDAC requires OE to be low active */ + setting->fbi->var.sync |= FB_SYNC_OE_LOW_ACT; + + INIT_LIST_HEAD(&setting->fbi->modelist); + for (i = 0; i < modedb_sz; i++) { + struct fb_videomode m; + fb_var_to_videomode(&m, &setting->fbi->var); + if (fb_mode_is_equal(&m, &modedb[i])) { + fb_add_videomode(&modedb[i], + &setting->fbi->modelist); + break; + } + } + + return ret; +} + +void vdacif_deinit(struct mxc_dispdrv_handle *disp) +{ + /*TODO*/ +} + +static struct mxc_dispdrv_driver vdacif_drv = { + .name = DISPDRV_LCD, + .init = vdacif_init, + .deinit = vdacif_deinit, +}; + +static int vdac_get_of_property(struct platform_device *pdev, + struct mxc_vdac_platform_data *plat_data) +{ + struct device_node *np = pdev->dev.of_node; + int err; + u32 ipu_id, disp_id; + const char *default_ifmt; + + err = of_property_read_string(np, "default_ifmt", &default_ifmt); + if (err) { + dev_dbg(&pdev->dev, "get of property default_ifmt fail\n"); + return err; + } + err = of_property_read_u32(np, "ipu_id", &ipu_id); + if (err) { + dev_dbg(&pdev->dev, "get of property ipu_id fail\n"); + return err; + } + err = of_property_read_u32(np, "disp_id", &disp_id); + if (err) { + dev_dbg(&pdev->dev, "get of property disp_id fail\n"); + return err; + } + + if ( (ipu_id == 0) || (disp_id == 1) ) { + dev_err(&pdev->dev, "wrong ipu_id %x or disp_id %x, expected 1, 0\n", ipu_id, disp_id); + } + plat_data->ipu_id = ipu_id; + plat_data->disp_id = disp_id; + if (!strncmp(default_ifmt, "RGB565", 6)) + plat_data->default_ifmt = IPU_PIX_FMT_RGB565; + else { + dev_err(&pdev->dev, "err default_ifmt!\n"); + return -ENOENT; + } + + return err; +} + +static int mxc_vdacif_probe(struct platform_device *pdev) +{ + int ret; + struct pinctrl *pinctrl; + struct mxc_vdacif_data *vdacif; + struct mxc_vdac_platform_data *plat_data; + + dev_dbg(&pdev->dev, "%s enter\n", __func__); + vdacif = devm_kzalloc(&pdev->dev, sizeof(struct mxc_vdacif_data), + GFP_KERNEL); + if (!vdacif) + return -ENOMEM; + plat_data = devm_kzalloc(&pdev->dev, + sizeof(struct mxc_vdac_platform_data), + GFP_KERNEL); + if (!plat_data) + return -ENOMEM; + pdev->dev.platform_data = plat_data; + + ret = vdac_get_of_property(pdev, plat_data); + if (ret < 0) { + dev_err(&pdev->dev, "get vdac of property fail\n"); + return ret; + } + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + dev_err(&pdev->dev, "can't get/select pinctrl\n"); + return PTR_ERR(pinctrl); + } + + vdacif->pdev = pdev; + vdacif->disp_vdacif = mxc_dispdrv_register(&vdacif_drv); + mxc_dispdrv_setdata(vdacif->disp_vdacif, vdacif); + + dev_set_drvdata(&pdev->dev, vdacif); + dev_dbg(&pdev->dev, "%s exit\n", __func__); + + return ret; +} + +static int mxc_vdacif_remove(struct platform_device *pdev) +{ + struct mxc_vdacif_data *vdacif = dev_get_drvdata(&pdev->dev); + + mxc_dispdrv_puthandle(vdacif->disp_vdacif); + mxc_dispdrv_unregister(vdacif->disp_vdacif); + kfree(vdacif); + return 0; +} + +static const struct of_device_id imx_vdac_dt_ids[] = { + { .compatible = "fsl,vdac"}, + { /* sentinel */ } +}; +static struct platform_driver mxc_vdacif_driver = { + .driver = { + .name = "mxc_vdacif", + .of_match_table = imx_vdac_dt_ids, + }, + .probe = mxc_vdacif_probe, + .remove = mxc_vdacif_remove, +}; + +static int __init mxc_vdacif_init(void) +{ + return platform_driver_register(&mxc_vdacif_driver); +} + +static void __exit mxc_vdacif_exit(void) +{ + platform_driver_unregister(&mxc_vdacif_driver); +} + +module_init(mxc_vdacif_init); +module_exit(mxc_vdacif_exit); + +MODULE_AUTHOR("Toradex AG"); +MODULE_DESCRIPTION("i.MX ipuv3 VDAC extern port driver"); +MODULE_LICENSE("GPL"); |