summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev/mxc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/fbdev/mxc')
-rw-r--r--drivers/video/fbdev/mxc/Makefile2
-rw-r--r--drivers/video/fbdev/mxc/ldb.c59
-rw-r--r--drivers/video/fbdev/mxc/mxc_hdmi.c156
-rw-r--r--drivers/video/fbdev/mxc/mxc_ipuv3_fb.c26
-rw-r--r--drivers/video/fbdev/mxc/mxc_lcdif.c60
-rw-r--r--drivers/video/fbdev/mxc/mxc_vdacif.c356
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");