summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorJason Chen <b02280@freescale.com>2011-02-12 11:15:30 +0800
committerAndy Voltz <andy.voltz@timesys.com>2011-06-01 13:20:50 -0400
commit563e1612a15260cc41785cd1482ced0e64a3d778 (patch)
tree97204cece4e7f73dc789ee146e58b7194cbdd9d6 /drivers
parentcd2010b9e285cb5cefeb7997a947381d2325da4e (diff)
ENGR00138644-5 HDMI: change the video mode setting method
HDMI Sii902x support for changing the video mode setting method. Signed-off-by: Jason Chen <b02280@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/video/mxc/Kconfig2
-rw-r--r--drivers/video/mxc/Makefile2
-rw-r--r--drivers/video/mxc/mxcfb_sii9022.c410
-rw-r--r--drivers/video/mxc/mxcfb_sii902x.c447
4 files changed, 449 insertions, 412 deletions
diff --git a/drivers/video/mxc/Kconfig b/drivers/video/mxc/Kconfig
index 74f06ba1f2ab..e2b79faeaf35 100644
--- a/drivers/video/mxc/Kconfig
+++ b/drivers/video/mxc/Kconfig
@@ -42,7 +42,7 @@ config FB_MXC_SEIKO_WVGA_SYNC_PANEL
depends on FB_MXC_SYNC_PANEL
tristate "SEIKO WVGA Panel"
-config FB_MXC_SII9022
+config FB_MXC_SII902X
depends on FB_MXC_SYNC_PANEL
tristate "Si Image SII9022 DVI/HDMI Interface Chip"
diff --git a/drivers/video/mxc/Makefile b/drivers/video/mxc/Makefile
index 3b58b2cf85a4..02b3797a262f 100644
--- a/drivers/video/mxc/Makefile
+++ b/drivers/video/mxc/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_FB_MODE_HELPERS) += mxc_edid.o
obj-$(CONFIG_FB_MXC_TVOUT_TVE) += tve.o
+obj-$(CONFIG_FB_MXC_SII902X) += mxcfb_sii902x.o
ifeq ($(CONFIG_ARCH_MX21)$(CONFIG_ARCH_MX27)$(CONFIG_ARCH_MX25),y)
obj-$(CONFIG_FB_MXC_TVOUT) += fs453.o
obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mx2fb.o mxcfb_modedb.o
@@ -20,7 +21,6 @@ obj-$(CONFIG_FB_MXC_CLAA_WVGA_SYNC_PANEL) += mxcfb_claa_wvga.o
obj-$(CONFIG_FB_MXC_SEIKO_WVGA_SYNC_PANEL) += mxcfb_seiko_wvga.o
obj-$(CONFIG_FB_MXC_TVOUT_CH7024) += ch7024.o
obj-$(CONFIG_FB_MXC_LDB) += ldb.o
-obj-$(CONFIG_FB_MXC_SII9022) += mxcfb_sii9022.o
obj-$(CONFIG_FB_MXC_CH7026) += mxcfb_ch7026.o
obj-$(CONFIG_FB_MXC_EINK_PANEL) += mxc_epdc_fb.o
obj-$(CONFIG_FB_MXC_ELCDIF_FB) += mxc_elcdif_fb.o
diff --git a/drivers/video/mxc/mxcfb_sii9022.c b/drivers/video/mxc/mxcfb_sii9022.c
deleted file mode 100644
index e80306340b3f..000000000000
--- a/drivers/video/mxc/mxcfb_sii9022.c
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright (C) 2010-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 mxcfb_sii9022.c
- *
- * @brief MXC Frame buffer driver for SII9022
- *
- * @ingroup Framebuffer
- */
-
-/*!
- * Include files
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/console.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fb.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
-#include <linux/mxcfb.h>
-#include <linux/fsl_devices.h>
-#include <linux/interrupt.h>
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <mach/mxc_edid.h>
-
-struct sii9022_data {
- struct platform_device *pdev;
- struct i2c_client *client;
- struct delayed_work det_work;
- struct fb_info *fbi;
- struct mxc_edid_cfg edid_cfg;
- u8 cable_plugin;
- u8 edid[256];
-} sii9022;
-
-static void sii9022_poweron(void);
-static void sii9022_poweroff(void);
-static void (*sii9022_reset) (void);
-
-static __attribute__ ((unused)) void dump_regs(u8 reg, int len)
-{
- u8 buf[50];
- int i;
-
- i2c_smbus_read_i2c_block_data(sii9022.client, reg, len, buf);
- for (i = 0; i < len; i++)
- dev_dbg(&sii9022.client->dev, "reg[0x%02X]: 0x%02X\n",
- i+reg, buf[i]);
-}
-
-static ssize_t sii9022_show_state(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- if (sii9022.cable_plugin == 0)
- strcpy(buf, "plugout\n");
- else
- strcpy(buf, "plugin\n");
-
- return strlen(buf);
-}
-
-static DEVICE_ATTR(cable_state, S_IRUGO | S_IWUSR, sii9022_show_state, NULL);
-
-static void sii9022_setup(struct fb_info *fbi)
-{
- u16 data[4];
- u32 refresh;
- u8 *tmp;
- int i;
-
- dev_dbg(&sii9022.client->dev, "SII9022: setup..\n");
-
- /* Power up */
- i2c_smbus_write_byte_data(sii9022.client, 0x1E, 0x00);
-
- /* set TPI video mode */
- data[0] = PICOS2KHZ(fbi->var.pixclock) / 10;
- data[2] = fbi->var.hsync_len + fbi->var.left_margin +
- fbi->var.xres + fbi->var.right_margin;
- data[3] = fbi->var.vsync_len + fbi->var.upper_margin +
- fbi->var.yres + fbi->var.lower_margin;
- refresh = data[2] * data[3];
- refresh = (PICOS2KHZ(fbi->var.pixclock) * 1000) / refresh;
- data[1] = refresh * 100;
- tmp = (u8 *)data;
- for (i = 0; i < 8; i++)
- i2c_smbus_write_byte_data(sii9022.client, i, tmp[i]);
-
- /* input bus/pixel: full pixel wide (24bit), rising edge */
- i2c_smbus_write_byte_data(sii9022.client, 0x08, 0x70);
- /* Set input format to RGB */
- i2c_smbus_write_byte_data(sii9022.client, 0x09, 0x00);
- /* set output format to RGB */
- i2c_smbus_write_byte_data(sii9022.client, 0x0A, 0x00);
- /* audio setup */
- i2c_smbus_write_byte_data(sii9022.client, 0x25, 0x00);
- i2c_smbus_write_byte_data(sii9022.client, 0x26, 0x40);
- i2c_smbus_write_byte_data(sii9022.client, 0x27, 0x00);
-}
-
-static int sii9022_read_edid(void)
-{
- int old, dat, ret, cnt = 100;
-
- old = i2c_smbus_read_byte_data(sii9022.client, 0x1A);
-
- i2c_smbus_write_byte_data(sii9022.client, 0x1A, old | 0x4);
- do {
- cnt--;
- msleep(10);
- dat = i2c_smbus_read_byte_data(sii9022.client, 0x1A);
- } while ((!(dat & 0x2)) && cnt);
-
- if (!cnt) {
- ret = -1;
- goto done;
- }
-
- i2c_smbus_write_byte_data(sii9022.client, 0x1A, old | 0x06);
-
- /* edid reading */
- ret = mxc_edid_read(sii9022.client->adapter, sii9022.edid,
- &sii9022.edid_cfg, sii9022.fbi);
-
- cnt = 100;
- do {
- cnt--;
- i2c_smbus_write_byte_data(sii9022.client, 0x1A, old & ~0x6);
- msleep(10);
- dat = i2c_smbus_read_byte_data(sii9022.client, 0x1A);
- } while ((dat & 0x6) && cnt);
-
- if (!cnt)
- ret = -1;
-
-done:
- i2c_smbus_write_byte_data(sii9022.client, 0x1A, old);
- return ret;
-}
-
-static void det_worker(struct work_struct *work)
-{
- int dat;
- char event_string[16];
- char *envp[] = { event_string, NULL };
-
- dat = i2c_smbus_read_byte_data(sii9022.client, 0x3D);
- if (dat & 0x1) {
- /* cable connection changes */
- if (dat & 0x4) {
- sii9022.cable_plugin = 1;
- sprintf(event_string, "EVENT=plugin");
- if (sii9022_read_edid() < 0)
- dev_err(&sii9022.client->dev,
- "SII9022: read edid fail\n");
- else {
- if (sii9022.fbi->monspecs.modedb_len > 0) {
- int i;
-
- for (i = 0; i < sii9022.fbi->monspecs.modedb_len; i++)
- fb_add_videomode(&sii9022.fbi->monspecs.modedb[i],
- &sii9022.fbi->modelist);
- }
- sii9022_poweron();
- }
- } else {
- sii9022.cable_plugin = 0;
- sprintf(event_string, "EVENT=plugout");
- sii9022_poweroff();
- }
- kobject_uevent_env(&sii9022.pdev->dev.kobj, KOBJ_CHANGE, envp);
- }
- i2c_smbus_write_byte_data(sii9022.client, 0x3D, dat);
-}
-
-static irqreturn_t sii9022_detect_handler(int irq, void *data)
-{
- schedule_delayed_work(&(sii9022.det_work), msecs_to_jiffies(100));
- return IRQ_HANDLED;
-}
-
-static int lcd_fb_event(struct notifier_block *nb, unsigned long val, void *v)
-{
- struct fb_event *event = v;
- struct fb_info *fbi = event->info;
-
- /* assume sii9022 on DI0 only */
- if (strcmp(event->info->fix.id, "DISP3 BG"))
- return 0;
-
- switch (val) {
- case FB_EVENT_MODE_CHANGE:
- sii9022_setup(fbi);
- break;
- case FB_EVENT_BLANK:
- if (*((int *)event->data) == FB_BLANK_UNBLANK)
- sii9022_poweron();
- else
- sii9022_poweroff();
- break;
- }
- return 0;
-}
-
-static struct notifier_block nb = {
- .notifier_call = lcd_fb_event,
-};
-
-static int __devinit sii9022_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- int i, dat;
- struct mxc_lcd_platform_data *plat = client->dev.platform_data;
-
- sii9022.client = client;
-
- if (plat->reset) {
- sii9022_reset = plat->reset;
- sii9022_reset();
- }
-
- /* Set 9022 in hardware TPI mode on and jump out of D3 state */
- if (i2c_smbus_write_byte_data(sii9022.client, 0xc7, 0x00) < 0) {
- dev_err(&sii9022.client->dev,
- "SII9022: cound not find device\n");
- return -ENODEV;
- }
-
- /* read device ID */
- for (i = 10; i > 0; i--) {
- dat = i2c_smbus_read_byte_data(sii9022.client, 0x1B);
- printk(KERN_DEBUG "Sii9022: read id = 0x%02X", dat);
- if (dat == 0xb0) {
- dat = i2c_smbus_read_byte_data(sii9022.client, 0x1C);
- printk(KERN_DEBUG "-0x%02X", dat);
- dat = i2c_smbus_read_byte_data(sii9022.client, 0x1D);
- printk(KERN_DEBUG "-0x%02X", dat);
- dat = i2c_smbus_read_byte_data(sii9022.client, 0x30);
- printk(KERN_DEBUG "-0x%02X\n", dat);
- break;
- }
- }
- if (i == 0) {
- dev_err(&sii9022.client->dev,
- "SII9022: cound not find device\n");
- return -ENODEV;
- }
-
- if (sii9022.client->irq) {
- int ret;
-
- ret = request_irq(sii9022.client->irq, sii9022_detect_handler,
- IRQF_TRIGGER_FALLING,
- "sii9022_det", &sii9022.client->dev);
- if (ret < 0)
- dev_warn(&sii9022.client->dev,
- "SII9022: cound not request det irq %d\n",
- sii9022.client->irq);
- else {
- /*enable cable hot plug irq*/
- i2c_smbus_write_byte_data(sii9022.client, 0x3c, 0x01);
- INIT_DELAYED_WORK(&(sii9022.det_work), det_worker);
- }
- ret = device_create_file(&sii9022.pdev->dev, &dev_attr_cable_state);
- if (ret < 0)
- dev_warn(&sii9022.client->dev,
- "SII9022: cound not crate sys node\n");
- }
-
- fb_register_client(&nb);
-
- for (i = 0; i < num_registered_fb; i++) {
- /* assume sii9022 on DI0 only */
- if (strcmp(registered_fb[i]->fix.id, "DISP3 BG") == 0) {
- sii9022.fbi = registered_fb[i];
-
- acquire_console_sem();
- fb_blank(sii9022.fbi, FB_BLANK_POWERDOWN);
- release_console_sem();
-
- sii9022_setup(sii9022.fbi);
-
- /* primary display? */
- if (i == 0) {
- acquire_console_sem();
- fb_blank(sii9022.fbi, FB_BLANK_UNBLANK);
- release_console_sem();
-
- fb_show_logo(sii9022.fbi, 0);
- }
- }
- }
-
- return 0;
-}
-
-static int __devexit sii9022_remove(struct i2c_client *client)
-{
- fb_unregister_client(&nb);
- sii9022_poweroff();
- return 0;
-}
-
-static int sii9022_suspend(struct i2c_client *client, pm_message_t message)
-{
- /*TODO*/
- return 0;
-}
-
-static int sii9022_resume(struct i2c_client *client)
-{
- /*TODO*/
- return 0;
-}
-
-static void sii9022_poweron(void)
-{
- /* Turn on DVI or HDMI */
- if (sii9022.edid_cfg.hdmi_cap)
- i2c_smbus_write_byte_data(sii9022.client, 0x1A, 0x01);
- else
- i2c_smbus_write_byte_data(sii9022.client, 0x1A, 0x00);
- return;
-}
-
-static void sii9022_poweroff(void)
-{
- /* disable tmds before changing resolution */
- if (sii9022.edid_cfg.hdmi_cap)
- i2c_smbus_write_byte_data(sii9022.client, 0x1A, 0x11);
- else
- i2c_smbus_write_byte_data(sii9022.client, 0x1A, 0x10);
-
- return;
-}
-
-static const struct i2c_device_id sii9022_id[] = {
- { "sii9022", 0 },
- {},
-};
-MODULE_DEVICE_TABLE(i2c, sii9022_id);
-
-static struct i2c_driver sii9022_i2c_driver = {
- .driver = {
- .name = "sii9022",
- },
- .probe = sii9022_probe,
- .remove = sii9022_remove,
- .suspend = sii9022_suspend,
- .resume = sii9022_resume,
- .id_table = sii9022_id,
-};
-
-static int __init sii9022_init(void)
-{
- int ret;
-
- memset(&sii9022, 0, sizeof(sii9022));
-
- sii9022.pdev = platform_device_register_simple("sii9022", 0, NULL, 0);
- if (IS_ERR(sii9022.pdev)) {
- printk(KERN_ERR
- "Unable to register Sii9022 as a platform device\n");
- ret = PTR_ERR(sii9022.pdev);
- goto err;
- }
-
- return i2c_add_driver(&sii9022_i2c_driver);
-err:
- return ret;
-}
-
-static void __exit sii9022_exit(void)
-{
- i2c_del_driver(&sii9022_i2c_driver);
- platform_device_unregister(sii9022.pdev);
-}
-
-module_init(sii9022_init);
-module_exit(sii9022_exit);
-
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_DESCRIPTION("SI9022 DVI/HDMI driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxcfb_sii902x.c b/drivers/video/mxc/mxcfb_sii902x.c
new file mode 100644
index 000000000000..4082db0246c0
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_sii902x.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2010-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 mxcfb_sii902x.c
+ *
+ * @brief MXC Frame buffer driver for SII902x
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/mxcfb.h>
+#include <linux/fsl_devices.h>
+#include <linux/interrupt.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/mxc_edid.h>
+
+#define IPU_DISP_PORT 0
+static bool g_enable_hdmi;
+
+struct sii902x_data {
+ struct platform_device *pdev;
+ struct i2c_client *client;
+ struct delayed_work det_work;
+ struct fb_info *fbi;
+ struct mxc_edid_cfg edid_cfg;
+ u8 cable_plugin;
+ u8 edid[256];
+} sii902x;
+
+static void sii902x_poweron(void);
+static void sii902x_poweroff(void);
+static void (*sii902x_reset) (void);
+
+static __attribute__ ((unused)) void dump_regs(u8 reg, int len)
+{
+ u8 buf[50];
+ int i;
+
+ i2c_smbus_read_i2c_block_data(sii902x.client, reg, len, buf);
+ for (i = 0; i < len; i++)
+ dev_dbg(&sii902x.client->dev, "reg[0x%02X]: 0x%02X\n",
+ i+reg, buf[i]);
+}
+
+static ssize_t sii902x_show_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (sii902x.cable_plugin == 0)
+ strcpy(buf, "plugout\n");
+ else
+ strcpy(buf, "plugin\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(cable_state, S_IRUGO | S_IWUSR, sii902x_show_state, NULL);
+
+static void sii902x_setup(struct fb_info *fbi)
+{
+ u16 data[4];
+ u32 refresh;
+ u8 *tmp;
+ int i;
+
+ dev_dbg(&sii902x.client->dev, "Sii902x: setup..\n");
+
+ /* Power up */
+ i2c_smbus_write_byte_data(sii902x.client, 0x1E, 0x00);
+
+ /* set TPI video mode */
+ data[0] = PICOS2KHZ(fbi->var.pixclock) / 10;
+ data[2] = fbi->var.hsync_len + fbi->var.left_margin +
+ fbi->var.xres + fbi->var.right_margin;
+ data[3] = fbi->var.vsync_len + fbi->var.upper_margin +
+ fbi->var.yres + fbi->var.lower_margin;
+ refresh = data[2] * data[3];
+ refresh = (PICOS2KHZ(fbi->var.pixclock) * 1000) / refresh;
+ data[1] = refresh * 100;
+ tmp = (u8 *)data;
+ for (i = 0; i < 8; i++)
+ i2c_smbus_write_byte_data(sii902x.client, i, tmp[i]);
+
+ /* input bus/pixel: full pixel wide (24bit), rising edge */
+ i2c_smbus_write_byte_data(sii902x.client, 0x08, 0x70);
+ /* Set input format to RGB */
+ i2c_smbus_write_byte_data(sii902x.client, 0x09, 0x00);
+ /* set output format to RGB */
+ i2c_smbus_write_byte_data(sii902x.client, 0x0A, 0x00);
+ /* audio setup */
+ i2c_smbus_write_byte_data(sii902x.client, 0x25, 0x00);
+ i2c_smbus_write_byte_data(sii902x.client, 0x26, 0x40);
+ i2c_smbus_write_byte_data(sii902x.client, 0x27, 0x00);
+}
+
+static int sii902x_read_edid(struct fb_info *fbi)
+{
+ int old, dat, ret, cnt = 100;
+
+ old = i2c_smbus_read_byte_data(sii902x.client, 0x1A);
+
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, old | 0x4);
+ do {
+ cnt--;
+ msleep(10);
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x1A);
+ } while ((!(dat & 0x2)) && cnt);
+
+ if (!cnt) {
+ ret = -1;
+ goto done;
+ }
+
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, old | 0x06);
+
+ /* edid reading */
+ ret = mxc_edid_read(sii902x.client->adapter, sii902x.edid,
+ &sii902x.edid_cfg, fbi);
+
+ cnt = 100;
+ do {
+ cnt--;
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, old & ~0x6);
+ msleep(10);
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x1A);
+ } while ((dat & 0x6) && cnt);
+
+ if (!cnt)
+ ret = -1;
+
+done:
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, old);
+ return ret;
+}
+
+static void det_worker(struct work_struct *work)
+{
+ int dat;
+ char event_string[16];
+ char *envp[] = { event_string, NULL };
+
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x3D);
+ if (dat & 0x1) {
+ /* cable connection changes */
+ if (dat & 0x4) {
+ sii902x.cable_plugin = 1;
+ sprintf(event_string, "EVENT=plugin");
+
+ /* make sure fb is powerdown */
+ acquire_console_sem();
+ fb_blank(sii902x.fbi, FB_BLANK_POWERDOWN);
+ release_console_sem();
+
+ if (sii902x_read_edid(sii902x.fbi) < 0)
+ dev_err(&sii902x.client->dev,
+ "Sii902x: read edid fail\n");
+ else {
+ if (sii902x.fbi->monspecs.modedb_len > 0) {
+ int i;
+ const struct fb_videomode *mode;
+ struct fb_videomode m;
+
+ fb_destroy_modelist(&sii902x.fbi->modelist);
+
+ for (i = 0; i < sii902x.fbi->monspecs.modedb_len; i++)
+ fb_add_videomode(&sii902x.fbi->monspecs.modedb[i],
+ &sii902x.fbi->modelist);
+
+ fb_var_to_videomode(&m, &sii902x.fbi->var);
+ mode = fb_find_nearest_mode(&m,
+ &sii902x.fbi->modelist);
+
+ fb_videomode_to_var(&sii902x.fbi->var, mode);
+
+ sii902x.fbi->var.activate |= FB_ACTIVATE_FORCE;
+ acquire_console_sem();
+ sii902x.fbi->flags |= FBINFO_MISC_USEREVENT;
+ fb_set_var(sii902x.fbi, &sii902x.fbi->var);
+ sii902x.fbi->flags &= ~FBINFO_MISC_USEREVENT;
+ release_console_sem();
+ }
+
+ acquire_console_sem();
+ fb_blank(sii902x.fbi, FB_BLANK_UNBLANK);
+ release_console_sem();
+ }
+ } else {
+ sii902x.cable_plugin = 0;
+ sprintf(event_string, "EVENT=plugout");
+ acquire_console_sem();
+ fb_blank(sii902x.fbi, FB_BLANK_POWERDOWN);
+ release_console_sem();
+ }
+ kobject_uevent_env(&sii902x.pdev->dev.kobj, KOBJ_CHANGE, envp);
+ }
+ i2c_smbus_write_byte_data(sii902x.client, 0x3D, dat);
+}
+
+static irqreturn_t sii902x_detect_handler(int irq, void *data)
+{
+ if (sii902x.fbi)
+ schedule_delayed_work(&(sii902x.det_work), msecs_to_jiffies(20));
+ return IRQ_HANDLED;
+}
+
+static int sii902x_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+ struct fb_event *event = v;
+ struct fb_info *fbi = event->info;
+
+ /* assume sii902x on DI0 only */
+ if ((IPU_DISP_PORT)) {
+ if (strcmp(event->info->fix.id, "DISP3 BG - DI1"))
+ return 0;
+ } else {
+ if (strcmp(event->info->fix.id, "DISP3 BG"))
+ return 0;
+ }
+
+ switch (val) {
+ case FB_EVENT_FB_REGISTERED:
+ if (sii902x.fbi != NULL)
+ break;
+ sii902x.fbi = fbi;
+ break;
+ case FB_EVENT_MODE_CHANGE:
+ sii902x_setup(fbi);
+ break;
+ case FB_EVENT_BLANK:
+ if (*((int *)event->data) == FB_BLANK_UNBLANK)
+ sii902x_poweron();
+ else
+ sii902x_poweroff();
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block nb = {
+ .notifier_call = sii902x_fb_event,
+};
+
+static int __devinit sii902x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int i, dat, ret;
+ struct mxc_lcd_platform_data *plat = client->dev.platform_data;
+ struct fb_info edid_fbi;
+
+ if (g_enable_hdmi == false)
+ return -EPERM;
+
+ sii902x.client = client;
+
+ if (plat->reset) {
+ sii902x_reset = plat->reset;
+ sii902x_reset();
+ }
+
+ /* Set 902x in hardware TPI mode on and jump out of D3 state */
+ if (i2c_smbus_write_byte_data(sii902x.client, 0xc7, 0x00) < 0) {
+ dev_err(&sii902x.client->dev,
+ "Sii902x: cound not find device\n");
+ return -ENODEV;
+ }
+
+ /* read device ID */
+ for (i = 10; i > 0; i--) {
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x1B);
+ printk(KERN_DEBUG "Sii902x: read id = 0x%02X", dat);
+ if (dat == 0xb0) {
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x1C);
+ printk(KERN_DEBUG "-0x%02X", dat);
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x1D);
+ printk(KERN_DEBUG "-0x%02X", dat);
+ dat = i2c_smbus_read_byte_data(sii902x.client, 0x30);
+ printk(KERN_DEBUG "-0x%02X\n", dat);
+ break;
+ }
+ }
+ if (i == 0) {
+ dev_err(&sii902x.client->dev,
+ "Sii902x: cound not find device\n");
+ return -ENODEV;
+ }
+
+ /* try to read edid */
+ if (sii902x_read_edid(&edid_fbi) < 0)
+ dev_warn(&sii902x.client->dev, "Can not read edid\n");
+ else
+ mxcfb_register_mode(IPU_DISP_PORT, edid_fbi.monspecs.modedb,
+ edid_fbi.monspecs.modedb_len, MXC_DISP_DDC_DEV);
+
+ if (sii902x.client->irq) {
+ ret = request_irq(sii902x.client->irq, sii902x_detect_handler,
+ IRQF_TRIGGER_FALLING,
+ "sII902x_det", &sii902x.client->dev);
+ if (ret < 0)
+ dev_warn(&sii902x.client->dev,
+ "Sii902x: cound not request det irq %d\n",
+ sii902x.client->irq);
+ else {
+ /*enable cable hot plug irq*/
+ i2c_smbus_write_byte_data(sii902x.client, 0x3c, 0x01);
+ INIT_DELAYED_WORK(&(sii902x.det_work), det_worker);
+ }
+ ret = device_create_file(&sii902x.pdev->dev, &dev_attr_cable_state);
+ if (ret < 0)
+ dev_warn(&sii902x.client->dev,
+ "Sii902x: cound not crate sys node\n");
+ }
+
+ fb_register_client(&nb);
+
+ return 0;
+}
+
+static int __devexit sii902x_remove(struct i2c_client *client)
+{
+ fb_unregister_client(&nb);
+ sii902x_poweroff();
+ return 0;
+}
+
+static int sii902x_suspend(struct i2c_client *client, pm_message_t message)
+{
+ /*TODO*/
+ return 0;
+}
+
+static int sii902x_resume(struct i2c_client *client)
+{
+ /*TODO*/
+ return 0;
+}
+
+static void sii902x_poweron(void)
+{
+ /* Turn on DVI or HDMI */
+ if (sii902x.edid_cfg.hdmi_cap)
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x01);
+ else
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x00);
+ return;
+}
+
+static void sii902x_poweroff(void)
+{
+ /* disable tmds before changing resolution */
+ if (sii902x.edid_cfg.hdmi_cap)
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x11);
+ else
+ i2c_smbus_write_byte_data(sii902x.client, 0x1A, 0x10);
+
+ return;
+}
+
+static const struct i2c_device_id sii902x_id[] = {
+ { "sii902x", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, sii902x_id);
+
+static struct i2c_driver sii902x_i2c_driver = {
+ .driver = {
+ .name = "sii902x",
+ },
+ .probe = sii902x_probe,
+ .remove = sii902x_remove,
+ .suspend = sii902x_suspend,
+ .resume = sii902x_resume,
+ .id_table = sii902x_id,
+};
+
+static int __init sii902x_init(void)
+{
+ int ret;
+
+ memset(&sii902x, 0, sizeof(sii902x));
+
+ sii902x.pdev = platform_device_register_simple("sii902x", 0, NULL, 0);
+ if (IS_ERR(sii902x.pdev)) {
+ printk(KERN_ERR
+ "Unable to register Sii902x as a platform device\n");
+ ret = PTR_ERR(sii902x.pdev);
+ goto err;
+ }
+
+ return i2c_add_driver(&sii902x_i2c_driver);
+err:
+ return ret;
+}
+
+static void __exit sii902x_exit(void)
+{
+ i2c_del_driver(&sii902x_i2c_driver);
+ platform_device_unregister(sii902x.pdev);
+}
+
+static int __init enable_hdmi_setup(char *options)
+{
+ g_enable_hdmi = true;
+
+ return 1;
+}
+__setup("hdmi", enable_hdmi_setup);
+
+module_init(sii902x_init);
+module_exit(sii902x_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("SII902x DVI/HDMI driver");
+MODULE_LICENSE("GPL");