aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Chen <jason.chen@linaro.org>2011-12-05 17:41:05 +0800
committerEric Miao <eric.miao@linaro.org>2012-01-11 21:39:07 +0800
commitb96c105cf80e19a8abd0e183189865b9b86b36b1 (patch)
tree4d0cc9ecac134fc756e02a8a2609c996f1374353
parent8bc98972c8aaa80e50d29478fc8bfa45d4359083 (diff)
IPU3 FB: merge to fsl imx_2.6.38 branch base
to base commit 433c6306fe9455163cff3591b4cf8e2f22bc6cc8 add ipuv3 fb driver. add mxc display driver. add mxc edid driver. add display device driver for lcd/ldb/tve/dvi/sii902x Signed-off-by: Jason Chen <jason.chen@linaro.org>
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc_edid.h52
-rw-r--r--drivers/video/mxc/Kconfig7
-rw-r--r--drivers/video/mxc/Makefile2
-rw-r--r--drivers/video/mxc/ldb.c138
-rw-r--r--drivers/video/mxc/mxc_dvi.c380
-rw-r--r--drivers/video/mxc/mxc_edid.c103
-rw-r--r--drivers/video/mxc/mxc_ipuv3_fb.c2
-rw-r--r--drivers/video/mxc/mxcfb_sii902x.c8
-rw-r--r--drivers/video/mxc/tve.c13
-rw-r--r--include/linux/fsl_devices.h8
10 files changed, 679 insertions, 34 deletions
diff --git a/arch/arm/plat-mxc/include/mach/mxc_edid.h b/arch/arm/plat-mxc/include/mach/mxc_edid.h
new file mode 100644
index 00000000000..59688f657a2
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mxc_edid.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc. 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
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxc_edid.h
+ *
+ * @brief MXC EDID tools
+ *
+ * @ingroup Framebuffer
+ */
+
+#ifndef MXC_EDID_H
+#define MXC_EDID_H
+
+#define FB_VMODE_ASPECT_4_3 0x10
+#define FB_VMODE_ASPECT_16_9 0x20
+
+struct mxc_edid_cfg {
+ bool cea_underscan;
+ bool cea_basicaudio;
+ bool cea_ycbcr444;
+ bool cea_ycbcr422;
+ bool hdmi_cap;
+
+ /*VSD*/
+ bool vsd_dc_48bit;
+ bool vsd_dc_36bit;
+ bool vsd_dc_30bit;
+ bool vsd_dc_y444;
+ bool vsd_dvi_dual;
+};
+
+int mxc_edid_var_to_vic(struct fb_var_screeninfo *var);
+int mxc_edid_mode_to_vic(const struct fb_videomode *mode);
+int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr,
+ unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi);
+
+#endif
diff --git a/drivers/video/mxc/Kconfig b/drivers/video/mxc/Kconfig
index bb836c3a63f..053f62b7632 100644
--- a/drivers/video/mxc/Kconfig
+++ b/drivers/video/mxc/Kconfig
@@ -14,6 +14,11 @@ config FB_MXC
If you plan to use the LCD display with your MXC system, say
Y here.
+config FB_MXC_EDID
+ depends on FB_MXC && I2C
+ tristate "MXC EDID support"
+ default y
+
config FB_MXC_SYNC_PANEL
depends on FB_MXC
tristate "Synchronous Panel Framebuffer"
@@ -25,7 +30,7 @@ config FB_MXC_TVOUT_TVE
depends on MXC_IPU_V3
config FB_MXC_SII902X
- depends on FB_MXC_SYNC_PANEL
+ depends on FB_MXC_SYNC_PANEL && I2C
tristate "Si Image SII9022 DVI/HDMI Interface Chip"
config FB_MXC_LDB
diff --git a/drivers/video/mxc/Makefile b/drivers/video/mxc/Makefile
index 4c75e1be2d3..e0d47ed90b4 100644
--- a/drivers/video/mxc/Makefile
+++ b/drivers/video/mxc/Makefile
@@ -1,5 +1,5 @@
obj-$(CONFIG_FB_MXC_TVOUT_TVE) += tve.o
obj-$(CONFIG_FB_MXC_SII902X) += mxcfb_sii902x.o
obj-$(CONFIG_FB_MXC_LDB) += ldb.o
-obj-$(CONFIG_FB_MODE_HELPERS) += mxc_edid.o
+obj-$(CONFIG_FB_MXC_EDID) += mxc_edid.o mxc_dvi.o
obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_dispdrv.o mxc_lcdif.o mxc_ipuv3_fb.o
diff --git a/drivers/video/mxc/ldb.c b/drivers/video/mxc/ldb.c
index 180bf7f6f8d..b3a75a5868c 100644
--- a/drivers/video/mxc/ldb.c
+++ b/drivers/video/mxc/ldb.c
@@ -297,6 +297,116 @@ int ldb_fb_event(struct notifier_block *nb, unsigned long val, void *v)
return 0;
}
+#define LVDS0_MUX_CTL_MASK (3 << 6)
+#define LVDS1_MUX_CTL_MASK (3 << 8)
+#define LVDS0_MUX_CTL_OFFS 6
+#define LVDS1_MUX_CTL_OFFS 8
+#define ROUTE_IPU0_DI0 0
+#define ROUTE_IPU0_DI1 1
+#define ROUTE_IPU1_DI0 2
+#define ROUTE_IPU1_DI1 3
+static int ldb_ipu_ldb_route(int ipu, int di, struct ldb_data *ldb)
+{
+ uint32_t reg;
+ int mode = ldb->mode;
+
+ reg = readl(ldb->gpr3_reg);
+ if ((mode == LDB_SPL_DI0) || (mode == LDB_DUL_DI0)) {
+ reg &= ~(LVDS0_MUX_CTL_MASK | LVDS1_MUX_CTL_MASK);
+ if (ipu == 0)
+ reg |= (ROUTE_IPU0_DI0 << LVDS0_MUX_CTL_OFFS) |
+ (ROUTE_IPU0_DI0 << LVDS1_MUX_CTL_OFFS);
+ else
+ reg |= (ROUTE_IPU1_DI0 << LVDS0_MUX_CTL_OFFS) |
+ (ROUTE_IPU1_DI0 << LVDS1_MUX_CTL_OFFS);
+ dev_dbg(&ldb->pdev->dev,
+ "Dual/Split mode both channels route to IPU%d-DI0\n", ipu);
+ } else if ((mode == LDB_SPL_DI1) || (mode == LDB_DUL_DI1)) {
+ reg &= ~(LVDS0_MUX_CTL_MASK | LVDS1_MUX_CTL_MASK);
+ if (ipu == 0)
+ reg |= (ROUTE_IPU0_DI1 << LVDS0_MUX_CTL_OFFS) |
+ (ROUTE_IPU0_DI1 << LVDS1_MUX_CTL_OFFS);
+ else
+ reg |= (ROUTE_IPU1_DI1 << LVDS0_MUX_CTL_OFFS) |
+ (ROUTE_IPU1_DI1 << LVDS1_MUX_CTL_OFFS);
+ dev_dbg(&ldb->pdev->dev,
+ "Dual/Split mode both channels route to IPU%d-DI1\n", ipu);
+ } else if (mode == LDB_SIN0) {
+ reg &= ~LVDS0_MUX_CTL_MASK;
+ if ((ipu == 0) && (di == 0))
+ reg |= ROUTE_IPU0_DI0 << LVDS0_MUX_CTL_OFFS;
+ else if ((ipu == 0) && (di == 1))
+ reg |= ROUTE_IPU0_DI1 << LVDS0_MUX_CTL_OFFS;
+ else if ((ipu == 1) && (di == 0))
+ reg |= ROUTE_IPU1_DI0 << LVDS0_MUX_CTL_OFFS;
+ else
+ reg |= ROUTE_IPU1_DI1 << LVDS0_MUX_CTL_OFFS;
+ dev_dbg(&ldb->pdev->dev,
+ "Single mode channel 0 route to IPU%d-DI%d\n", ipu, di);
+ } else if (mode == LDB_SIN1) {
+ reg &= ~LVDS1_MUX_CTL_MASK;
+ if ((ipu == 0) && (di == 0))
+ reg |= ROUTE_IPU0_DI0 << LVDS1_MUX_CTL_OFFS;
+ else if ((ipu == 0) && (di == 1))
+ reg |= ROUTE_IPU0_DI1 << LVDS1_MUX_CTL_OFFS;
+ else if ((ipu == 1) && (di == 0))
+ reg |= ROUTE_IPU1_DI0 << LVDS1_MUX_CTL_OFFS;
+ else
+ reg |= ROUTE_IPU1_DI1 << LVDS1_MUX_CTL_OFFS;
+ dev_dbg(&ldb->pdev->dev,
+ "Single mode channel 1 route to IPU%d-DI%d\n", ipu, di);
+ } else {
+ static bool first = true;
+ int channel;
+
+ if (first) {
+ if (mode == LDB_SEP0) {
+ reg &= ~LVDS0_MUX_CTL_MASK;
+ channel = 0;
+ } else {
+ reg &= ~LVDS1_MUX_CTL_MASK;
+ channel = 1;
+ }
+ first = false;
+ } else {
+ if (mode == LDB_SEP0) {
+ reg &= ~LVDS1_MUX_CTL_MASK;
+ channel = 1;
+ } else {
+ reg &= ~LVDS0_MUX_CTL_MASK;
+ channel = 0;
+ }
+ }
+
+ if ((ipu == 0) && (di == 0)) {
+ if (channel == 0)
+ reg |= ROUTE_IPU0_DI0 << LVDS0_MUX_CTL_OFFS;
+ else
+ reg |= ROUTE_IPU0_DI0 << LVDS1_MUX_CTL_OFFS;
+ } else if ((ipu == 0) && (di == 1)) {
+ if (channel == 0)
+ reg |= ROUTE_IPU0_DI1 << LVDS0_MUX_CTL_OFFS;
+ else
+ reg |= ROUTE_IPU0_DI1 << LVDS1_MUX_CTL_OFFS;
+ } else if ((ipu == 1) && (di == 0)) {
+ if (channel == 0)
+ reg |= ROUTE_IPU1_DI0 << LVDS0_MUX_CTL_OFFS;
+ else
+ reg |= ROUTE_IPU1_DI0 << LVDS1_MUX_CTL_OFFS;
+ } else {
+ if (channel == 0)
+ reg |= ROUTE_IPU1_DI1 << LVDS0_MUX_CTL_OFFS;
+ else
+ reg |= ROUTE_IPU1_DI1 << LVDS1_MUX_CTL_OFFS;
+ }
+
+ dev_dbg(&ldb->pdev->dev, "Separate mode channel %d route to IPU%d-DI%d\n", channel, ipu, di);
+ }
+ writel(reg, ldb->gpr3_reg);
+
+ return 0;
+}
+
static int ldb_disp_init(struct mxc_dispdrv_entry *disp)
{
int ret = 0, i;
@@ -430,7 +540,11 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp)
writel(reg, ldb->control_reg);
/* clock setting */
- ldb_clk[6] += setting->disp_id;
+ if (cpu_is_mx6q() &&
+ ((ldb->mode == LDB_SEP0) || (ldb->mode == LDB_SEP1)))
+ ldb_clk[6] += lvds_channel;
+ else
+ ldb_clk[6] += setting->disp_id;
ldb->ldb_di_clk[0] = clk_get(&ldb->pdev->dev, ldb_clk);
if (IS_ERR(ldb->ldb_di_clk[0])) {
dev_err(&ldb->pdev->dev, "get ldb clk0 failed\n");
@@ -474,8 +588,13 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp)
return -EINVAL;
}
- setting->dev_id = plat_data->ipu_id;
- setting->disp_id = !plat_data->disp_id;
+ if (cpu_is_mx6q()) {
+ setting->dev_id = plat_data->sec_ipu_id;
+ setting->disp_id = plat_data->sec_disp_id;
+ } else {
+ setting->dev_id = plat_data->ipu_id;
+ setting->disp_id = !plat_data->disp_id;
+ }
/* second output is LVDS0 or LVDS1 */
if (ldb->mode == LDB_SEP0)
@@ -507,7 +626,10 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp)
writel(reg, ldb->control_reg);
/* clock setting */
- ldb_clk[6] += setting->disp_id;
+ if (cpu_is_mx6q())
+ ldb_clk[6] += lvds_channel;
+ else
+ ldb_clk[6] += setting->disp_id;
ldb->ldb_di_clk[1] = clk_get(&ldb->pdev->dev, ldb_clk);
if (IS_ERR(ldb->ldb_di_clk[1])) {
dev_err(&ldb->pdev->dev, "get ldb clk1 failed\n");
@@ -526,6 +648,14 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp)
setting_idx = 1;
}
+ if (cpu_is_mx6q()) {
+ reg = readl(ldb->control_reg);
+ reg &= ~(LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK);
+ reg |= LDB_CH0_MODE_EN_TO_DI0 | LDB_CH1_MODE_EN_TO_DI1;
+ writel(reg, ldb->control_reg);
+ ldb_ipu_ldb_route(setting->dev_id, setting->disp_id, ldb);
+ }
+
/*
* ldb_di0_clk -> ipux_di0_clk
* ldb_di1_clk -> ipux_di1_clk
diff --git a/drivers/video/mxc/mxc_dvi.c b/drivers/video/mxc/mxc_dvi.c
new file mode 100644
index 00000000000..765670c8098
--- /dev/null
+++ b/drivers/video/mxc/mxc_dvi.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*!
+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
+ */
+
+/*!
+ * @file mxc_dvi.c
+ *
+ * @brief MXC DVI driver
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/ipu.h>
+#include <linux/mxcfb.h>
+#include <linux/fsl_devices.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/regulator/consumer.h>
+#include <mach/mxc_edid.h>
+#include "mxc_dispdrv.h"
+#include "../edid.h"
+
+#define MXC_EDID_LENGTH (EDID_LENGTH*4)
+
+#define DISPDRV_DVI "dvi"
+
+struct mxc_dvi_data {
+ struct i2c_client *client;
+ struct platform_device *pdev;
+ struct mxc_dispdrv_entry *disp_dvi;
+ struct delayed_work det_work;
+ struct fb_info *fbi;
+ struct mxc_edid_cfg edid_cfg;
+ u8 cable_plugin;
+ u8 edid[MXC_EDID_LENGTH];
+
+ u32 ipu;
+ u32 di;
+ void (*init)(void);
+ int (*update)(void);
+ struct regulator *analog_reg;
+};
+
+static ssize_t mxc_dvi_show_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxc_dvi_data *dvi = dev_get_drvdata(dev);
+
+ if (dvi->cable_plugin == 0)
+ strcpy(buf, "plugout\n");
+ else
+ strcpy(buf, "plugin\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(cable_state, S_IRUGO, mxc_dvi_show_state, NULL);
+
+static ssize_t mxc_dvi_show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxc_dvi_data *dvi = dev_get_drvdata(dev);
+
+ strcpy(buf, dvi->fbi->fix.id);
+ sprintf(buf+strlen(buf), "\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(fb_name, S_IRUGO, mxc_dvi_show_name, NULL);
+
+static ssize_t mxc_dvi_show_edid(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxc_dvi_data *dvi = dev_get_drvdata(dev);
+ int i, j, len = 0;
+
+ for (j = 0; j < MXC_EDID_LENGTH/16; j++) {
+ for (i = 0; i < 16; i++)
+ len += sprintf(buf+len, "0x%02X ",
+ dvi->edid[j*16 + i]);
+ len += sprintf(buf+len, "\n");
+ }
+
+ return len;
+}
+
+static DEVICE_ATTR(edid, S_IRUGO, mxc_dvi_show_edid, NULL);
+
+static void det_worker(struct work_struct *work)
+{
+ struct delayed_work *delay_work = to_delayed_work(work);
+ struct mxc_dvi_data *dvi =
+ container_of(delay_work, struct mxc_dvi_data, det_work);
+ char event_string[16];
+ char *envp[] = { event_string, NULL };
+
+ /* cable connection changes */
+ if (dvi->update()) {
+ u8 edid_old[MXC_EDID_LENGTH];
+ dvi->cable_plugin = 1;
+ sprintf(event_string, "EVENT=plugin");
+
+ memcpy(edid_old, dvi->edid, MXC_EDID_LENGTH);
+
+ if (mxc_edid_read(dvi->client->adapter, dvi->client->addr,
+ dvi->edid, &dvi->edid_cfg, dvi->fbi) < 0)
+ dev_err(&dvi->client->dev,
+ "MXC dvi: read edid fail\n");
+ else {
+ if (!memcmp(edid_old, dvi->edid, MXC_EDID_LENGTH))
+ dev_info(&dvi->client->dev,
+ "Sii902x: same edid\n");
+ else if (dvi->fbi->monspecs.modedb_len > 0) {
+ int i;
+ const struct fb_videomode *mode;
+ struct fb_videomode m;
+
+ fb_destroy_modelist(&dvi->fbi->modelist);
+
+ for (i = 0; i < dvi->fbi->monspecs.modedb_len; i++)
+ /*FIXME now we do not support interlaced mode */
+ if (!(dvi->fbi->monspecs.modedb[i].vmode & FB_VMODE_INTERLACED))
+ fb_add_videomode(&dvi->fbi->monspecs.modedb[i],
+ &dvi->fbi->modelist);
+
+ fb_var_to_videomode(&m, &dvi->fbi->var);
+ mode = fb_find_nearest_mode(&m,
+ &dvi->fbi->modelist);
+
+ fb_videomode_to_var(&dvi->fbi->var, mode);
+
+ dvi->fbi->var.activate |= FB_ACTIVATE_FORCE;
+ console_lock();
+ dvi->fbi->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(dvi->fbi, &dvi->fbi->var);
+ dvi->fbi->flags &= ~FBINFO_MISC_USEREVENT;
+ console_unlock();
+ }
+ }
+ } else {
+ dvi->cable_plugin = 0;
+ sprintf(event_string, "EVENT=plugout");
+ }
+
+ kobject_uevent_env(&dvi->pdev->dev.kobj, KOBJ_CHANGE, envp);
+}
+
+static irqreturn_t mxc_dvi_detect_handler(int irq, void *data)
+{
+ struct mxc_dvi_data *dvi = data;
+ schedule_delayed_work(&(dvi->det_work), msecs_to_jiffies(300));
+ return IRQ_HANDLED;
+}
+
+static int dvi_init(struct mxc_dispdrv_entry *disp)
+{
+ int ret = 0;
+ struct mxc_dvi_data *dvi = mxc_dispdrv_getdata(disp);
+ struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp);
+ struct fsl_mxc_dvi_platform_data *plat = dvi->client->dev.platform_data;
+
+ setting->dev_id = dvi->ipu = plat->ipu_id;
+ setting->disp_id = dvi->di = plat->disp_id;
+ setting->if_fmt = IPU_PIX_FMT_RGB24;
+ dvi->fbi = setting->fbi;
+ dvi->init = plat->init;
+ dvi->update = plat->update;
+
+ dvi->analog_reg = regulator_get(&dvi->pdev->dev, plat->analog_regulator);
+ if (!IS_ERR(dvi->analog_reg)) {
+ regulator_set_voltage(dvi->analog_reg, 2775000, 2775000);
+ regulator_enable(dvi->analog_reg);
+ }
+
+ if (dvi->init)
+ dvi->init();
+
+ /* get video mode from edid */
+ if (!dvi->update)
+ return -EINVAL;
+ else {
+ bool found = false;
+
+ INIT_LIST_HEAD(&dvi->fbi->modelist);
+ if (dvi->update()) {
+ dvi->cable_plugin = 1;
+ /* try to read edid */
+ if (mxc_edid_read(dvi->client->adapter, dvi->client->addr,
+ dvi->edid, &dvi->edid_cfg, dvi->fbi) < 0)
+ dev_warn(&dvi->client->dev, "Can not read edid\n");
+ else if (dvi->fbi->monspecs.modedb_len > 0) {
+ int i;
+ const struct fb_videomode *mode;
+ struct fb_videomode m;
+
+ for (i = 0; i < dvi->fbi->monspecs.modedb_len; i++) {
+ /*FIXME now we do not support interlaced mode */
+ if (!(dvi->fbi->monspecs.modedb[i].vmode
+ & FB_VMODE_INTERLACED))
+ fb_add_videomode(
+ &dvi->fbi->monspecs.modedb[i],
+ &dvi->fbi->modelist);
+ }
+
+ fb_find_mode(&dvi->fbi->var, dvi->fbi, setting->dft_mode_str,
+ NULL, 0, NULL, setting->default_bpp);
+
+ fb_var_to_videomode(&m, &dvi->fbi->var);
+ mode = fb_find_nearest_mode(&m,
+ &dvi->fbi->modelist);
+ fb_videomode_to_var(&dvi->fbi->var, mode);
+ found = 1;
+ }
+ } else
+ dvi->cable_plugin = 0;
+
+ if (!found) {
+ ret = fb_find_mode(&dvi->fbi->var, dvi->fbi, setting->dft_mode_str,
+ NULL, 0, NULL, setting->default_bpp);
+ if (!ret)
+ return -EINVAL;
+ }
+ }
+
+ /* cable detection */
+ if (dvi->client->irq) {
+ ret = request_irq(dvi->client->irq, mxc_dvi_detect_handler,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "dvi_det", dvi);
+ if (ret < 0) {
+ dev_warn(&dvi->client->dev,
+ "MXC dvi: cound not request det irq %d\n",
+ dvi->client->irq);
+ goto err;
+ } else {
+ INIT_DELAYED_WORK(&(dvi->det_work), det_worker);
+ ret = device_create_file(&dvi->pdev->dev, &dev_attr_fb_name);
+ if (ret < 0)
+ dev_warn(&dvi->client->dev,
+ "MXC dvi: cound not create sys node for fb name\n");
+ ret = device_create_file(&dvi->pdev->dev, &dev_attr_cable_state);
+ if (ret < 0)
+ dev_warn(&dvi->client->dev,
+ "MXC dvi: cound not create sys node for cable state\n");
+ ret = device_create_file(&dvi->pdev->dev, &dev_attr_edid);
+ if (ret < 0)
+ dev_warn(&dvi->client->dev,
+ "MXC dvi: cound not create sys node for edid\n");
+
+ dev_set_drvdata(&dvi->pdev->dev, dvi);
+ }
+ }
+
+err:
+ return ret;
+}
+
+static void dvi_deinit(struct mxc_dispdrv_entry *disp)
+{
+ struct mxc_dvi_data *dvi = mxc_dispdrv_getdata(disp);
+
+ if (!IS_ERR(dvi->analog_reg))
+ regulator_disable(dvi->analog_reg);
+
+ free_irq(dvi->client->irq, dvi);
+}
+
+static struct mxc_dispdrv_driver dvi_drv = {
+ .name = DISPDRV_DVI,
+ .init = dvi_init,
+ .deinit = dvi_deinit,
+};
+
+static int __devinit mxc_dvi_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mxc_dvi_data *dvi;
+ int ret = 0;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
+ return -ENODEV;
+
+ dvi = kzalloc(sizeof(struct mxc_dvi_data), GFP_KERNEL);
+ if (!dvi) {
+ ret = -ENOMEM;
+ goto alloc_failed;
+ }
+
+ dvi->pdev = platform_device_register_simple("mxc_dvi", 0, NULL, 0);
+ if (IS_ERR(dvi->pdev)) {
+ printk(KERN_ERR
+ "Unable to register MXC DVI as a platform device\n");
+ ret = PTR_ERR(dvi->pdev);
+ goto pdev_reg_failed;
+ }
+
+ dvi->client = client;
+ dvi->disp_dvi = mxc_dispdrv_register(&dvi_drv);
+ mxc_dispdrv_setdata(dvi->disp_dvi, dvi);
+
+ i2c_set_clientdata(client, dvi);
+
+ return ret;
+
+pdev_reg_failed:
+ kfree(dvi);
+alloc_failed:
+ return ret;
+}
+
+static int __devexit mxc_dvi_remove(struct i2c_client *client)
+{
+ struct mxc_dvi_data *dvi = i2c_get_clientdata(client);
+
+ mxc_dispdrv_unregister(dvi->disp_dvi);
+ platform_device_unregister(dvi->pdev);
+ kfree(dvi);
+ return 0;
+}
+
+static const struct i2c_device_id mxc_dvi_id[] = {
+ { "mxc_dvi", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, mxc_dvi_id);
+
+static struct i2c_driver mxc_dvi_i2c_driver = {
+ .driver = {
+ .name = "mxc_dvi",
+ },
+ .probe = mxc_dvi_probe,
+ .remove = mxc_dvi_remove,
+ .id_table = mxc_dvi_id,
+};
+
+static int __init mxc_dvi_init(void)
+{
+ return i2c_add_driver(&mxc_dvi_i2c_driver);
+}
+
+static void __exit mxc_dvi_exit(void)
+{
+ i2c_del_driver(&mxc_dvi_i2c_driver);
+}
+
+module_init(mxc_dvi_init);
+module_exit(mxc_dvi_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC DVI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c
index 0fa5cf97602..85a9063037a 100644
--- a/drivers/video/mxc/mxc_edid.c
+++ b/drivers/video/mxc/mxc_edid.c
@@ -29,7 +29,7 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fb.h>
-#include "mxc_edid.h"
+#include <mach/mxc_edid.h>
#include "../edid.h"
#undef DEBUG /* define this for verbose EDID parsing output */
@@ -41,80 +41,120 @@
#endif
const struct fb_videomode mxc_cea_mode[64] = {
- /* #1: 640x480p@59.94/60Hz */
+ /* #1: 640x480p@59.94/60Hz 4:3 */
[1] = {
NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
},
- /* #3: 720x480p@59.94/60Hz */
+ /* #2: 720x480p@59.94/60Hz 4:3 */
+ [2] = {
+ NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
+ },
+ /* #3: 720x480p@59.94/60Hz 16:9 */
[3] = {
NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
- /* #4: 1280x720p@59.94/60Hz */
+ /* #4: 1280x720p@59.94/60Hz 16:9 */
[4] = {
NULL, 60, 1280, 720, 13468, 220, 110, 20, 5, 40, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0
},
- /* #5: 1920x1080i@59.94/60Hz */
+ /* #5: 1920x1080i@59.94/60Hz 16:9 */
[5] = {
NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_INTERLACED, 0,
+ FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #6: 720(1440)x480iH@59.94/60Hz 4:3 */
+ [6] = {
+ NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
+ FB_VMODE_INTERLACED | FB_VMODE_ASPECT_4_3, 0,
},
- /* #7: 720(1440)x480iH@59.94/60Hz */
+ /* #7: 720(1440)x480iH@59.94/60Hz 16:9 */
[7] = {
NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
- FB_VMODE_INTERLACED, 0,
+ FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
- /* #9: 720(1440)x240pH@59.94/60Hz */
+ /* #8: 720(1440)x240pH@59.94/60Hz 4:3 */
+ [8] = {
+ NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #9: 720(1440)x240pH@59.94/60Hz 16:9 */
[9] = {
NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
- /* #16: 1920x1080p@60Hz */
+ /* #16: 1920x1080p@60Hz 16:9 */
[16] = {
NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
+ },
+ /* #17: 720x576pH@50Hz 4:3 */
+ [17] = {
+ NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
},
- /* #18: 720x576pH@50Hz */
+ /* #18: 720x576pH@50Hz 16:9 */
[18] = {
NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #19: 1280x720p@50Hz */
[19] = {
NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #20: 1920x1080i@50Hz */
[20] = {
NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_INTERLACED, 0,
+ FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #31: 1920x1080p@50Hz */
[31] = {
NULL, 50, 1920, 1080, 6734, 148, 528, 36, 4, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #32: 1920x1080p@23.98/24Hz */
[32] = {
NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
},
/* #35: (2880)x480p4x@59.94/60Hz */
[35] = {
NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
},
};
+/*
+ * We have a special version of fb_mode_is_equal that ignores
+ * pixclock, since for many CEA modes, 2 frequencies are supported
+ * e.g. 640x480 @ 60Hz or 59.94Hz
+ */
+int mxc_edid_fb_mode_is_equal(const struct fb_videomode *mode1,
+ const struct fb_videomode *mode2)
+{
+ return (mode1->xres == mode2->xres &&
+ mode1->yres == mode2->yres &&
+ mode1->hsync_len == mode2->hsync_len &&
+ mode1->vsync_len == mode2->vsync_len &&
+ mode1->left_margin == mode2->left_margin &&
+ mode1->right_margin == mode2->right_margin &&
+ mode1->upper_margin == mode2->upper_margin &&
+ mode1->lower_margin == mode2->lower_margin &&
+ mode1->sync == mode2->sync &&
+ mode1->vmode == mode2->vmode);
+}
+
static void get_detailed_timing(unsigned char *block,
struct fb_videomode *mode)
{
@@ -399,7 +439,7 @@ int mxc_edid_var_to_vic(struct fb_var_screeninfo *var)
for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
fb_var_to_videomode(&m, var);
- if (fb_mode_is_equal(&m, &mxc_cea_mode[i]))
+ if (mxc_edid_fb_mode_is_equal(&m, &mxc_cea_mode[i]))
break;
}
@@ -408,8 +448,25 @@ int mxc_edid_var_to_vic(struct fb_var_screeninfo *var)
return i;
}
+
EXPORT_SYMBOL(mxc_edid_var_to_vic);
+int mxc_edid_mode_to_vic(const struct fb_videomode *mode)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
+ if (mxc_edid_fb_mode_is_equal(mode, &mxc_cea_mode[i]))
+ break;
+ }
+
+ if (i == ARRAY_SIZE(mxc_cea_mode))
+ return 0;
+
+ return i;
+}
+EXPORT_SYMBOL(mxc_edid_mode_to_vic);
+
/* make sure edid has 512 bytes*/
int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr,
unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi)
diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c
index 260b41a3ddf..63807917350 100644
--- a/drivers/video/mxc/mxc_ipuv3_fb.c
+++ b/drivers/video/mxc/mxc_ipuv3_fb.c
@@ -1787,8 +1787,6 @@ static int mxcfb_register(struct fb_info *fbi)
ret = fb_set_var(fbi, &fbi->var);
fbi->flags &= ~FBINFO_MISC_USEREVENT;
console_unlock();
- if (ret < 0)
- goto err2;
if (mxcfbi->next_blank == FB_BLANK_UNBLANK) {
console_lock();
diff --git a/drivers/video/mxc/mxcfb_sii902x.c b/drivers/video/mxc/mxcfb_sii902x.c
index 17ce8fca68b..f626f649f9c 100644
--- a/drivers/video/mxc/mxcfb_sii902x.c
+++ b/drivers/video/mxc/mxcfb_sii902x.c
@@ -50,7 +50,7 @@
#include <linux/uaccess.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
-#include "mxc_edid.h"
+#include <mach/mxc_edid.h>
#include "mxc_dispdrv.h"
#define DISPDRV_SII "hdmi"
@@ -671,7 +671,11 @@ static void sii902x_setup(struct sii902x_data *sii902x, struct fb_info *fbi)
/* reg 0x0a: set output format to RGB */
sii902x->tpivmode[2] = 0x00;
- if (fbi->var.xres/16 == fbi->var.yres/9)
+ if (fbi->var.vmode & FB_VMODE_ASPECT_16_9)
+ sii902x->aspect_ratio = VMD_ASPECT_RATIO_16x9;
+ else if (fbi->var.vmode & FB_VMODE_ASPECT_4_3)
+ sii902x->aspect_ratio = VMD_ASPECT_RATIO_4x3;
+ else if (fbi->var.xres/16 == fbi->var.yres/9)
sii902x->aspect_ratio = VMD_ASPECT_RATIO_16x9;
else
sii902x->aspect_ratio = VMD_ASPECT_RATIO_4x3;
diff --git a/drivers/video/mxc/tve.c b/drivers/video/mxc/tve.c
index 35b100349fe..98b506d3730 100644
--- a/drivers/video/mxc/tve.c
+++ b/drivers/video/mxc/tve.c
@@ -38,7 +38,6 @@
#include <linux/uaccess.h>
#include <asm/atomic.h>
#include <mach/hardware.h>
-#include <mach/ipu-v3.h>
#include "mxc_dispdrv.h"
#define TVE_ENABLE (1UL)
@@ -1105,6 +1104,18 @@ static int tve_drv_init(struct mxc_dispdrv_entry *disp, bool vga)
tve->reg_fields = &tve_reg_fields_v2;
}
+ /* adjust video mode for mx37 */
+ if (cpu_is_mx37()) {
+ video_modes_tve[0].left_margin = 121;
+ video_modes_tve[0].right_margin = 16;
+ video_modes_tve[0].upper_margin = 17;
+ video_modes_tve[0].lower_margin = 5;
+ video_modes_tve[1].left_margin = 131;
+ video_modes_tve[1].right_margin = 12;
+ video_modes_tve[1].upper_margin = 21;
+ video_modes_tve[1].lower_margin = 3;
+ }
+
if (vga && cpu_is_mx53()) {
setting->if_fmt = IPU_PIX_FMT_GBR24;
modedb = video_modes_vga;
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index cb624eaff19..b342652135f 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -316,6 +316,14 @@ struct fsl_mxc_lcd_platform_data {
int disp_id;
};
+struct fsl_mxc_dvi_platform_data {
+ void (*init) (void);
+ int (*update) (void);
+ char *analog_regulator;
+ int ipu_id;
+ int disp_id;
+};
+
struct fsl_mxc_ldb_platform_data {
char *lvds_bg_reg;
u32 ext_ref;