]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00152359-3 sii902x hdmi: add AVI and AIF support for common changes
authorJason Chen <b02280@freescale.com>
Wed, 13 Jul 2011 07:24:19 +0000 (15:24 +0800)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:32:59 +0000 (08:32 +0200)
1.add AVI and AIF support.
2.add edid 4-block reading support.(not test)

For RGB input fmt support, pls input cmdline like:
video=mxcdixfb:RGB24,1024x768M@60
For YUV input fmt support, pls input cmdline like:
video=mxcdixfb:VYU444,1024x768M@60

Signed-off-by: Jason Chen <b02280@freescale.com>
drivers/video/mxc/Makefile
drivers/video/mxc/mxc_ddc.c [new file with mode: 0644]
drivers/video/mxc/mxc_edid.c
drivers/video/mxc/mxcfb_sii902x.c

index 501107974b603bf315e354a425ecd1d695215c56..89f19269f233cb593027b24362cc81b80100083b 100644 (file)
@@ -1,7 +1,7 @@
 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_MODE_HELPERS)                 += mxc_edid.o mxc_ddc.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
diff --git a/drivers/video/mxc/mxc_ddc.c b/drivers/video/mxc/mxc_ddc.c
new file mode 100644 (file)
index 0000000..60b74cd
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 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.c
+ *
+ * @brief MXC EDID driver
+ *
+ * @ingroup Framebuffer
+ */
+
+/*!
+ * Include files
+ */
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/platform_device.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 "../edid.h"
+
+#define MXC_EDID_LENGTH (EDID_LENGTH*4)
+
+struct mxc_ddc_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[MXC_EDID_LENGTH];
+
+       u32 di;
+       void (*init)(void);
+       int (*update)(void);
+       struct regulator *analog_reg;
+} mxc_ddc;
+
+#define MXC_ENABLE     1
+#define MXC_DISABLE    2
+static int g_enable_ddc;
+
+static ssize_t mxc_ddc_show_state(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       if (mxc_ddc.cable_plugin == 0)
+               strcpy(buf, "plugout\n");
+       else
+               strcpy(buf, "plugin\n");
+
+       return strlen(buf);
+}
+
+static DEVICE_ATTR(cable_state, S_IRUGO, mxc_ddc_show_state, NULL);
+
+static ssize_t mxc_ddc_show_name(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       strcpy(buf, mxc_ddc.fbi->fix.id);
+       sprintf(buf+strlen(buf), "\n");
+
+       return strlen(buf);
+}
+
+static DEVICE_ATTR(fb_name, S_IRUGO, mxc_ddc_show_name, NULL);
+
+static ssize_t mxc_ddc_show_edid(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       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 ",
+                                       mxc_ddc.edid[j*16 + i]);
+               len += sprintf(buf+len, "\n");
+       }
+
+       return len;
+}
+
+static DEVICE_ATTR(edid, S_IRUGO, mxc_ddc_show_edid, NULL);
+
+static void det_worker(struct work_struct *work)
+{
+       char event_string[16];
+       char *envp[] = { event_string, NULL };
+
+       /* cable connection changes */
+       if (mxc_ddc.update()) {
+               u8 edid_old[MXC_EDID_LENGTH];
+               mxc_ddc.cable_plugin = 1;
+               sprintf(event_string, "EVENT=plugin");
+
+               memcpy(edid_old, mxc_ddc.edid, MXC_EDID_LENGTH);
+
+               if (mxc_edid_read(mxc_ddc.client->adapter, mxc_ddc.client->addr,
+                               mxc_ddc.edid, &mxc_ddc.edid_cfg, mxc_ddc.fbi) < 0)
+                       dev_err(&mxc_ddc.client->dev,
+                                       "MXC ddc: read edid fail\n");
+               else {
+                       if (!memcmp(edid_old, mxc_ddc.edid, MXC_EDID_LENGTH))
+                               dev_info(&mxc_ddc.client->dev,
+                                       "Sii902x: same edid\n");
+                       else if (mxc_ddc.fbi->monspecs.modedb_len > 0) {
+                               int i;
+                               const struct fb_videomode *mode;
+                               struct fb_videomode m;
+
+                               /* make sure fb is powerdown */
+                               acquire_console_sem();
+                               fb_blank(mxc_ddc.fbi, FB_BLANK_POWERDOWN);
+                               release_console_sem();
+
+                               fb_destroy_modelist(&mxc_ddc.fbi->modelist);
+
+                               for (i = 0; i < mxc_ddc.fbi->monspecs.modedb_len; i++)
+                                       /*FIXME now we do not support interlaced mode */
+                                       if (!(mxc_ddc.fbi->monspecs.modedb[i].vmode & FB_VMODE_INTERLACED))
+                                               fb_add_videomode(&mxc_ddc.fbi->monspecs.modedb[i],
+                                                               &mxc_ddc.fbi->modelist);
+
+                               fb_var_to_videomode(&m, &mxc_ddc.fbi->var);
+                               mode = fb_find_nearest_mode(&m,
+                                               &mxc_ddc.fbi->modelist);
+
+                               fb_videomode_to_var(&mxc_ddc.fbi->var, mode);
+
+                               mxc_ddc.fbi->var.activate |= FB_ACTIVATE_FORCE;
+                               acquire_console_sem();
+                               mxc_ddc.fbi->flags |= FBINFO_MISC_USEREVENT;
+                               fb_set_var(mxc_ddc.fbi, &mxc_ddc.fbi->var);
+                               mxc_ddc.fbi->flags &= ~FBINFO_MISC_USEREVENT;
+                               release_console_sem();
+
+                               acquire_console_sem();
+                               fb_blank(mxc_ddc.fbi, FB_BLANK_UNBLANK);
+                               release_console_sem();
+                       }
+               }
+       } else {
+               mxc_ddc.cable_plugin = 0;
+               sprintf(event_string, "EVENT=plugout");
+       }
+
+       kobject_uevent_env(&mxc_ddc.pdev->dev.kobj, KOBJ_CHANGE, envp);
+}
+
+static irqreturn_t mxc_ddc_detect_handler(int irq, void *data)
+{
+       if (mxc_ddc.fbi)
+               schedule_delayed_work(&(mxc_ddc.det_work), msecs_to_jiffies(300));
+       return IRQ_HANDLED;
+}
+
+static int mxc_ddc_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+{
+       struct fb_event *event = v;
+       struct fb_info *fbi = event->info;
+
+       if ((mxc_ddc.di)) {
+               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 (mxc_ddc.fbi != NULL)
+                       break;
+               mxc_ddc.fbi = fbi;
+               break;
+       }
+       return 0;
+}
+
+static struct notifier_block nb = {
+       .notifier_call = mxc_ddc_fb_event,
+};
+
+static int __devinit mxc_ddc_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       int ret = 0;
+       struct fb_info edid_fbi;
+       struct mxc_ddc_platform_data *plat = client->dev.platform_data;
+
+       if (plat->boot_enable && !g_enable_ddc)
+               g_enable_ddc = MXC_ENABLE;
+       if (!g_enable_ddc)
+               g_enable_ddc = MXC_DISABLE;
+
+       if (g_enable_ddc == MXC_DISABLE) {
+               printk(KERN_WARNING "By setting, DDC driver will not be enabled\n");
+               return -ENODEV;
+       }
+
+       mxc_ddc.client = client;
+       mxc_ddc.di = plat->di;
+       mxc_ddc.init = plat->init;
+       mxc_ddc.update = plat->update;
+
+       if (!mxc_ddc.update)
+               return -EINVAL;
+
+       mxc_ddc.analog_reg = regulator_get(&mxc_ddc.pdev->dev, plat->analog_regulator);
+       if (!IS_ERR(mxc_ddc.analog_reg)) {
+               regulator_set_voltage(mxc_ddc.analog_reg, 2775000, 2775000);
+               regulator_enable(mxc_ddc.analog_reg);
+       }
+
+       if (mxc_ddc.init)
+               mxc_ddc.init();
+
+       if (mxc_ddc.update()) {
+               mxc_ddc.cable_plugin = 1;
+               /* try to read edid */
+               if (mxc_edid_read(client->adapter, client->addr,
+                                       mxc_ddc.edid, &mxc_ddc.edid_cfg, &edid_fbi) < 0)
+                       dev_warn(&client->dev, "Can not read edid\n");
+#if defined(CONFIG_MXC_IPU_V3) && defined(CONFIG_FB_MXC_SYNC_PANEL)
+               else
+                       mxcfb_register_mode(mxc_ddc.di, edid_fbi.monspecs.modedb,
+                                       edid_fbi.monspecs.modedb_len, MXC_DISP_DDC_DEV);
+#endif
+       } else
+               mxc_ddc.cable_plugin = 0;
+
+       if (client->irq) {
+               ret = request_irq(client->irq, mxc_ddc_detect_handler,
+                               IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+                               "ddc_det", &mxc_ddc);
+               if (ret < 0) {
+                       dev_warn(&client->dev,
+                               "MXC ddc: cound not request det irq %d\n",
+                               client->irq);
+                       goto err;
+               } else {
+                       INIT_DELAYED_WORK(&(mxc_ddc.det_work), det_worker);
+                       ret = device_create_file(&mxc_ddc.pdev->dev, &dev_attr_fb_name);
+                       if (ret < 0)
+                               dev_warn(&client->dev,
+                                       "MXC ddc: cound not create sys node for fb name\n");
+                       ret = device_create_file(&mxc_ddc.pdev->dev, &dev_attr_cable_state);
+                       if (ret < 0)
+                               dev_warn(&client->dev,
+                                       "MXC ddc: cound not create sys node for cable state\n");
+                       ret = device_create_file(&mxc_ddc.pdev->dev, &dev_attr_edid);
+                       if (ret < 0)
+                               dev_warn(&client->dev,
+                                       "MXC ddc: cound not create sys node for edid\n");
+               }
+       }
+
+       fb_register_client(&nb);
+
+err:
+       return ret;
+}
+
+static int __devexit mxc_ddc_remove(struct i2c_client *client)
+{
+       fb_unregister_client(&nb);
+       if (!IS_ERR(mxc_ddc.analog_reg))
+               regulator_disable(mxc_ddc.analog_reg);
+       return 0;
+}
+
+static int __init enable_ddc_setup(char *options)
+{
+       if (!strcmp(options, "=off"))
+               g_enable_ddc = MXC_DISABLE;
+       else
+               g_enable_ddc = MXC_ENABLE;
+
+       return 1;
+}
+__setup("ddc", enable_ddc_setup);
+
+static const struct i2c_device_id mxc_ddc_id[] = {
+       { "mxc_ddc", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, mxc_ddc_id);
+
+static struct i2c_driver mxc_ddc_i2c_driver = {
+       .driver = {
+                  .name = "mxc_ddc",
+                  },
+       .probe = mxc_ddc_probe,
+       .remove = mxc_ddc_remove,
+       .id_table = mxc_ddc_id,
+};
+
+static int __init mxc_ddc_init(void)
+{
+       int ret;
+
+       memset(&mxc_ddc, 0, sizeof(mxc_ddc));
+
+       mxc_ddc.pdev = platform_device_register_simple("mxc_ddc", 0, NULL, 0);
+       if (IS_ERR(mxc_ddc.pdev)) {
+               printk(KERN_ERR
+                               "Unable to register MXC DDC as a platform device\n");
+               ret = PTR_ERR(mxc_ddc.pdev);
+               goto err;
+       }
+
+       return i2c_add_driver(&mxc_ddc_i2c_driver);
+err:
+       return ret;
+}
+
+static void __exit mxc_ddc_exit(void)
+{
+       i2c_del_driver(&mxc_ddc_i2c_driver);
+       platform_device_unregister(mxc_ddc.pdev);
+}
+
+module_init(mxc_ddc_init);
+module_exit(mxc_ddc_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC DDC driver");
+MODULE_LICENSE("GPL");
index 50355b550bbadda098150deb292fd0bc6dd8f2ba..dae7b4f6c9cdd55b0c201fa00d77dc29b9b7e4e5 100644 (file)
  */
 #include <linux/i2c.h>
 #include <linux/fb.h>
-#include <linux/console.h>
-#include <linux/init.h>
-#include <linux/platform_device.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 "../edid.h"
 
-#define MXC_EDID_LENGTH (EDID_LENGTH*2)
-
 #undef DEBUG  /* define this for verbose EDID parsing output */
 
 #ifdef DEBUG
 #define DPRINTK(fmt, args...)
 #endif
 
-struct mxc_ddc_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[MXC_EDID_LENGTH];
-
-       u32 di;
-       void (*init)(void);
-       int (*update)(void);
-       struct regulator *analog_reg;
-} mxc_ddc;
-
-#define MXC_ENABLE     1
-#define MXC_DISABLE    2
-static int g_enable_ddc;
-
-void mxc_edid_parse_ext_blk(unsigned char *edid,
+const struct fb_videomode cea_modes[64] = {
+       /* #1: 640x480p@59.94/60Hz */
+       [1] = {
+               NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
+               FB_VMODE_NONINTERLACED, 0,
+       },
+       /* #3: 720x480p@59.94/60Hz */
+       [3] = {
+               NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
+               FB_VMODE_NONINTERLACED, 0,
+       },
+       /* #4: 1280x720p@59.94/60Hz */
+       [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,
+       },
+       /* #5: 1920x1080i@59.94/60Hz */
+       [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,
+       },
+       /* #7: 720(1440)x480iH@59.94/60Hz */
+       [7] = {
+               NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
+               FB_VMODE_INTERLACED, 0,
+       },
+       /* #9: 720(1440)x240pH@59.94/60Hz */
+       [9] = {
+               NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
+               FB_VMODE_NONINTERLACED, 0,
+       },
+       /* #16: 1920x1080p@60Hz */
+       [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,
+       },
+       /* #18: 720x576pH@50Hz */
+       [18] = {
+               NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
+               FB_VMODE_NONINTERLACED, 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,
+       },
+       /* #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,
+       },
+       /* #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,
+       },
+       /* #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,
+       },
+       /* #35: (2880)x480p4x@59.94/60Hz */
+       [35] = {
+               NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0,
+               FB_VMODE_NONINTERLACED, 0,
+       },
+};
+
+static void get_detailed_timing(unsigned char *block,
+                               struct fb_videomode *mode)
+{
+       mode->xres = H_ACTIVE;
+       mode->yres = V_ACTIVE;
+       mode->pixclock = PIXEL_CLOCK;
+       mode->pixclock /= 1000;
+       mode->pixclock = KHZ2PICOS(mode->pixclock);
+       mode->right_margin = H_SYNC_OFFSET;
+       mode->left_margin = (H_ACTIVE + H_BLANKING) -
+               (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
+       mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
+               V_SYNC_WIDTH;
+       mode->lower_margin = V_SYNC_OFFSET;
+       mode->hsync_len = H_SYNC_WIDTH;
+       mode->vsync_len = V_SYNC_WIDTH;
+       if (HSYNC_POSITIVE)
+               mode->sync |= FB_SYNC_HOR_HIGH_ACT;
+       if (VSYNC_POSITIVE)
+               mode->sync |= FB_SYNC_VERT_HIGH_ACT;
+       mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
+                                    (V_ACTIVE + V_BLANKING));
+       if (INTERLACED) {
+               mode->yres *= 2;
+               mode->upper_margin *= 2;
+               mode->lower_margin *= 2;
+               mode->vsync_len *= 2;
+               mode->vmode |= FB_VMODE_INTERLACED;
+       }
+       mode->flag = FB_MODE_IS_DETAILED;
+
+       DPRINTK("      %d MHz ",  PIXEL_CLOCK/1000000);
+       DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
+              H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
+       DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
+              V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
+       DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
+              (VSYNC_POSITIVE) ? "+" : "-");
+}
+
+int mxc_edid_parse_ext_blk(unsigned char *edid,
                struct mxc_edid_cfg *cfg,
                struct fb_monspecs *specs)
 {
+       char detail_timming_desc_offset;
+       struct fb_videomode *mode, *m;
        unsigned char index = 0x0;
+       unsigned char *block;
+       int i, num = 0;
 
        if (edid[index++] != 0x2) /* only support cea ext block now */
-               return;
+               return -1;
        if (edid[index++] != 0x3) /* only support version 3*/
-               return;
+               return -1;
+       mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
+       if (mode == NULL)
+               return -1;
+
+       detail_timming_desc_offset = edid[index++];
 
        cfg->cea_underscan = (edid[index] >> 7) & 0x1;
        cfg->cea_basicaudio = (edid[index] >> 6) & 0x1;
        cfg->cea_ycbcr444 = (edid[index] >> 5) & 0x1;
        cfg->cea_ycbcr422 = (edid[index] >> 4) & 0x1;
 
-       return fb_edid_add_monspecs(edid, specs);
+       DPRINTK("CEA underscan %d\n", cfg->cea_underscan);
+       DPRINTK("CEA basicaudio %d\n", cfg->cea_basicaudio);
+       DPRINTK("CEA ycbcr444 %d\n", cfg->cea_ycbcr444);
+       DPRINTK("CEA ycbcr422 %d\n", cfg->cea_ycbcr422);
+
+       /* short desc */
+       DPRINTK("CEA Short desc timmings\n");
+       index++;
+       while (index < detail_timming_desc_offset) {
+               unsigned char tagcode, blklen;
+
+               tagcode = (edid[index] >> 5) & 0x7;
+               blklen = (edid[index]) & 0x1f;
+
+               DPRINTK("Tagcode %x Len %d\n", tagcode, blklen);
+
+               switch (tagcode) {
+               case 0x2: /*Video data block*/
+               {
+                       int cea_idx;
+                       i = 0;
+                       while (i < blklen) {
+                               index++;
+                               cea_idx = edid[index] & 0x7f;
+                               if (cea_idx < ARRAY_SIZE(cea_modes) &&
+                                       (cea_modes[cea_idx].xres)) {
+                                       DPRINTK("Support CEA Format #%d\n", cea_idx);
+                                       mode[num] = cea_modes[cea_idx];
+                                       mode[num].flag |= FB_MODE_IS_STANDARD;
+                                       num++;
+                               }
+                               i++;
+                       }
+                       break;
+               }
+               case 0x3: /*Vendor specific data*/
+               {
+                       unsigned char IEEE_reg_iden[3];
+                       unsigned char deep_color;
+                       IEEE_reg_iden[0] = edid[index+1];
+                       IEEE_reg_iden[1] = edid[index+2];
+                       IEEE_reg_iden[2] = edid[index+3];
+                       deep_color = edid[index+6];
+
+                       if ((IEEE_reg_iden[0] == 0x03) &&
+                               (IEEE_reg_iden[1] == 0x0c) &&
+                               (IEEE_reg_iden[2] == 0x00))
+                               cfg->hdmi_cap = 1;
+
+                       if (deep_color & 0x40)
+                               cfg->vsd_dc_48bit = true;
+                       if (deep_color & 0x20)
+                               cfg->vsd_dc_36bit = true;
+                       if (deep_color & 0x10)
+                               cfg->vsd_dc_30bit = true;
+                       if (deep_color & 0x08)
+                               cfg->vsd_dc_y444 = true;
+                       if (deep_color & 0x01)
+                               cfg->vsd_dvi_dual = true;
+
+                       DPRINTK("VSD hdmi capability %d\n", cfg->hdmi_cap);
+                       DPRINTK("VSD support deep color 48bit %d\n", cfg->vsd_dc_48bit);
+                       DPRINTK("VSD support deep color 36bit %d\n", cfg->vsd_dc_36bit);
+                       DPRINTK("VSD support deep color 30bit %d\n", cfg->vsd_dc_30bit);
+                       DPRINTK("VSD support deep color y444 %d\n", cfg->vsd_dc_y444);
+                       DPRINTK("VSD support dvi dual %d\n", cfg->vsd_dvi_dual);
+
+                       index += blklen;
+                       break;
+               }
+               case 0x1: /*Audio data block*/
+               case 0x4: /*Speaker allocation block*/
+               case 0x7: /*User extended block*/
+               default:
+                       /* skip */
+                       index += blklen;
+                       break;
+               }
+
+               index++;
+       }
+
+       /* long desc */
+       DPRINTK("CEA long desc timmings\n");
+       index = detail_timming_desc_offset;
+       block = edid + index;
+       while (index < (EDID_LENGTH - DETAILED_TIMING_DESCRIPTION_SIZE)) {
+               if (!(block[0] == 0x00 && block[1] == 0x00)) {
+                       get_detailed_timing(block, &mode[num]);
+                       num++;
+               }
+               block += DETAILED_TIMING_DESCRIPTION_SIZE;
+               index += DETAILED_TIMING_DESCRIPTION_SIZE;
+       }
+
+       if (!num) {
+               kfree(mode);
+               return 0;
+       }
+
+       m = kmalloc((num + specs->modedb_len) *
+                       sizeof(struct fb_videomode), GFP_KERNEL);
+       if (!m)
+               return 0;
+
+       if (specs->modedb_len) {
+               memmove(m, specs->modedb,
+                       specs->modedb_len * sizeof(struct fb_videomode));
+               kfree(specs->modedb);
+       }
+       memmove(m+specs->modedb_len, mode,
+               num * sizeof(struct fb_videomode));
+       kfree(mode);
+
+       specs->modedb_len += num;
+       specs->modedb = m;
+
+       return 0;
 }
 
-/* make sure edid has 256 bytes*/
-int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr,
-       unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi)
+static int mxc_edid_readblk(struct i2c_adapter *adp,
+               unsigned short addr, unsigned char *edid)
 {
-       u8 buf0[2] = {0, 0};
-       int dat = 0;
+       int ret = 0, extblknum = 0;
+       unsigned char regaddr = 0x0;
        struct i2c_msg msg[2] = {
                {
                .addr   = addr,
                .flags  = 0,
                .len    = 1,
-               .buf    = buf0,
+               .buf    = &regaddr,
                }, {
                .addr   = addr,
                .flags  = I2C_M_RD,
@@ -107,328 +318,130 @@ int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr,
                },
        };
 
-       if (adp == NULL)
-               return -EINVAL;
-
-       memset(edid, 0, 256);
-       memset(cfg, 0, sizeof(struct mxc_edid_cfg));
-
-       buf0[0] = 0x00;
-       dat = i2c_transfer(adp, msg, 2);
-
-       /* If 0x50 fails, try 0x37. */
-       if (edid[1] == 0x00) {
-               msg[0].addr = msg[1].addr = 0x37;
-               dat = i2c_transfer(adp, msg, 2);
-               if (dat < 0)
-                       return dat;
+       ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
+       if (ret != ARRAY_SIZE(msg)) {
+               DPRINTK("unable to read EDID block\n");
+               return -EIO;
        }
 
        if (edid[1] == 0x00)
                return -ENOENT;
 
-       /* edid first block parsing */
-       memset(&fbi->monspecs, 0, sizeof(fbi->monspecs));
-       fb_edid_to_monspecs(edid, &fbi->monspecs);
+       extblknum = edid[0x7E];
 
-       /* need read ext block? Only support one more blk now*/
-       if (edid[0x7E]) {
-               if (edid[0x7E] > 1)
-                       DPRINTK("Edid has %d ext block, \
-                               but now only support 1 ext blk\n", edid[0x7E]);
-               buf0[0] = 0x80;
+       if (extblknum) {
+               regaddr = 128;
                msg[1].buf = edid + EDID_LENGTH;
-               dat = i2c_transfer(adp, msg, 2);
-               if (dat < 0)
-                       return dat;
 
-               /* edid ext block parsing */
-               mxc_edid_parse_ext_blk(edid + 128, cfg, &fbi->monspecs);
+               ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
+               if (ret != ARRAY_SIZE(msg)) {
+                       DPRINTK("unable to read EDID ext block\n");
+                       return -EIO;
+               }
        }
 
-       return 0;
-}
-EXPORT_SYMBOL(mxc_edid_read);
-
-static ssize_t mxc_ddc_show_state(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       if (mxc_ddc.cable_plugin == 0)
-               strcpy(buf, "plugout\n");
-       else
-               strcpy(buf, "plugin\n");
-
-       return strlen(buf);
-}
-
-static DEVICE_ATTR(cable_state, S_IRUGO, mxc_ddc_show_state, NULL);
-
-static ssize_t mxc_ddc_show_name(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       strcpy(buf, mxc_ddc.fbi->fix.id);
-       sprintf(buf+strlen(buf), "\n");
-
-       return strlen(buf);
+       return extblknum;
 }
 
-static DEVICE_ATTR(fb_name, S_IRUGO, mxc_ddc_show_name, NULL);
-
-static ssize_t mxc_ddc_show_edid(struct device *dev,
-               struct device_attribute *attr, char *buf)
+static int mxc_edid_readsegblk(struct i2c_adapter *adp, unsigned short addr,
+                       unsigned char *edid, int seg_num)
 {
-       int i, j, len = 0;
+       int ret = 0;
+       unsigned char segment = 0x1, regaddr = 0;
+       struct i2c_msg msg[3] = {
+               {
+               .addr   = 0x30,
+               .flags  = 0,
+               .len    = 1,
+               .buf    = &segment,
+               }, {
+               .addr   = addr,
+               .flags  = 0,
+               .len    = 1,
+               .buf    = &regaddr,
+               }, {
+               .addr   = addr,
+               .flags  = I2C_M_RD,
+               .len    = EDID_LENGTH,
+               .buf    = edid,
+               },
+       };
 
-       for (j = 0; j < MXC_EDID_LENGTH/16; j++) {
-               for (i = 0; i < 16; i++)
-                       len += sprintf(buf+len, "0x%02X ",
-                                       mxc_ddc.edid[j*16 + i]);
-               len += sprintf(buf+len, "\n");
+       ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
+       if (ret != ARRAY_SIZE(msg)) {
+               DPRINTK("unable to read EDID block\n");
+               return -EIO;
        }
 
-       return len;
-}
-
-static DEVICE_ATTR(edid, S_IRUGO, mxc_ddc_show_edid, NULL);
-
-static void det_worker(struct work_struct *work)
-{
-       char event_string[16];
-       char *envp[] = { event_string, NULL };
-
-       /* cable connection changes */
-       if (mxc_ddc.update()) {
-               mxc_ddc.cable_plugin = 1;
-               sprintf(event_string, "EVENT=plugin");
-
-               /* make sure fb is powerdown */
-               console_lock();
-               fb_blank(mxc_ddc.fbi, FB_BLANK_POWERDOWN);
-               console_unlock();
-
-               if (mxc_edid_read(mxc_ddc.client->adapter, mxc_ddc.client->addr,
-                               mxc_ddc.edid, &mxc_ddc.edid_cfg, mxc_ddc.fbi) < 0)
-                       dev_err(&mxc_ddc.client->dev,
-                                       "MXC ddc: read edid fail\n");
-               else {
-                       if (mxc_ddc.fbi->monspecs.modedb_len > 0) {
-                               int i;
-                               const struct fb_videomode *mode;
-                               struct fb_videomode m;
-
-                               fb_destroy_modelist(&mxc_ddc.fbi->modelist);
-
-                               for (i = 0; i < mxc_ddc.fbi->monspecs.modedb_len; i++)
-                                       /*FIXME now we do not support interlaced mode */
-                                       if (!(mxc_ddc.fbi->monspecs.modedb[i].vmode & FB_VMODE_INTERLACED))
-                                               fb_add_videomode(&mxc_ddc.fbi->monspecs.modedb[i],
-                                                               &mxc_ddc.fbi->modelist);
-
-                               fb_var_to_videomode(&m, &mxc_ddc.fbi->var);
-                               mode = fb_find_nearest_mode(&m,
-                                               &mxc_ddc.fbi->modelist);
-
-                               fb_videomode_to_var(&mxc_ddc.fbi->var, mode);
-
-                               mxc_ddc.fbi->var.activate |= FB_ACTIVATE_FORCE;
-                               console_lock();
-                               mxc_ddc.fbi->flags |= FBINFO_MISC_USEREVENT;
-                               fb_set_var(mxc_ddc.fbi, &mxc_ddc.fbi->var);
-                               mxc_ddc.fbi->flags &= ~FBINFO_MISC_USEREVENT;
-                               console_unlock();
-                       }
+       if (seg_num == 2) {
+               regaddr = 128;
+               msg[2].buf = edid + EDID_LENGTH;
 
-                       console_lock();
-                       fb_blank(mxc_ddc.fbi, FB_BLANK_UNBLANK);
-                       console_unlock();
+               ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
+               if (ret != ARRAY_SIZE(msg)) {
+                       DPRINTK("unable to read EDID block\n");
+                       return -EIO;
                }
-       } else {
-               mxc_ddc.cable_plugin = 0;
-               sprintf(event_string, "EVENT=plugout");
-               console_lock();
-               fb_blank(mxc_ddc.fbi, FB_BLANK_POWERDOWN);
-               console_unlock();
        }
 
-       kobject_uevent_env(&mxc_ddc.pdev->dev.kobj, KOBJ_CHANGE, envp);
-}
-
-static irqreturn_t mxc_ddc_detect_handler(int irq, void *data)
-{
-       if (mxc_ddc.fbi)
-               schedule_delayed_work(&(mxc_ddc.det_work), msecs_to_jiffies(300));
-       return IRQ_HANDLED;
+       return ret;
 }
 
-static int mxc_ddc_fb_event(struct notifier_block *nb, unsigned long val, void *v)
+int mxc_edid_var_to_vic(struct fb_var_screeninfo *var)
 {
-       struct fb_event *event = v;
-       struct fb_info *fbi = event->info;
-
-       if ((mxc_ddc.di)) {
-               if (strcmp(event->info->fix.id, "DISP3 BG - DI1"))
-                       return 0;
-       } else {
-               if (strcmp(event->info->fix.id, "DISP3 BG"))
-                       return 0;
-       }
+       int i;
+       struct fb_videomode m;
 
-       switch (val) {
-       case FB_EVENT_FB_REGISTERED:
-               if (mxc_ddc.fbi != NULL)
+       for (i = 0; i < ARRAY_SIZE(cea_modes); i++) {
+               fb_var_to_videomode(&m, var);
+               if (fb_mode_is_equal(&m, &cea_modes[i]))
                        break;
-               mxc_ddc.fbi = fbi;
-               break;
        }
-       return 0;
-}
-
-static struct notifier_block nb = {
-       .notifier_call = mxc_ddc_fb_event,
-};
-
-static int __devinit mxc_ddc_probe(struct i2c_client *client,
-               const struct i2c_device_id *id)
-{
-       int ret = 0;
-       struct fb_info edid_fbi;
-       struct fsl_mxc_ddc_platform_data *plat = client->dev.platform_data;
-
-       if (plat->boot_enable && !g_enable_ddc)
-               g_enable_ddc = MXC_ENABLE;
-       if (!g_enable_ddc)
-               g_enable_ddc = MXC_DISABLE;
 
-       if (g_enable_ddc == MXC_DISABLE) {
-               printk(KERN_WARNING "By setting, DDC driver will not be enabled\n");
+       if (i == ARRAY_SIZE(cea_modes))
                return 0;
-       }
-
-       mxc_ddc.client = client;
-       mxc_ddc.di = plat->di;
-       mxc_ddc.init = plat->init;
-       mxc_ddc.update = plat->update;
-
-       if (!mxc_ddc.update)
-               return -EINVAL;
-
-       mxc_ddc.analog_reg = regulator_get(&mxc_ddc.pdev->dev, plat->analog_regulator);
-       if (!IS_ERR(mxc_ddc.analog_reg)) {
-               regulator_set_voltage(mxc_ddc.analog_reg, 2775000, 2775000);
-               regulator_enable(mxc_ddc.analog_reg);
-       }
 
-       if (mxc_ddc.init)
-               mxc_ddc.init();
-
-       if (mxc_ddc.update()) {
-               mxc_ddc.cable_plugin = 1;
-               /* try to read edid */
-               if (mxc_edid_read(client->adapter, client->addr,
-                                       mxc_ddc.edid, &mxc_ddc.edid_cfg, &edid_fbi) < 0)
-                       dev_warn(&client->dev, "Can not read edid\n");
-#if defined(CONFIG_MXC_IPU_V3) && defined(CONFIG_FB_MXC_SYNC_PANEL)
-               else
-                       mxcfb_register_mode(mxc_ddc.di, edid_fbi.monspecs.modedb,
-                                       edid_fbi.monspecs.modedb_len, MXC_DISP_DDC_DEV);
-#endif
-       } else
-               mxc_ddc.cable_plugin = 0;
-
-       if (client->irq) {
-               ret = request_irq(client->irq, mxc_ddc_detect_handler,
-                               IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-                               "ddc_det", &mxc_ddc);
-               if (ret < 0) {
-                       dev_warn(&client->dev,
-                               "MXC ddc: cound not request det irq %d\n",
-                               client->irq);
-                       goto err;
-               } else {
-                       INIT_DELAYED_WORK(&(mxc_ddc.det_work), det_worker);
-                       ret = device_create_file(&mxc_ddc.pdev->dev, &dev_attr_fb_name);
-                       if (ret < 0)
-                               dev_warn(&client->dev,
-                                       "MXC ddc: cound not create sys node for fb name\n");
-                       ret = device_create_file(&mxc_ddc.pdev->dev, &dev_attr_cable_state);
-                       if (ret < 0)
-                               dev_warn(&client->dev,
-                                       "MXC ddc: cound not create sys node for cable state\n");
-                       ret = device_create_file(&mxc_ddc.pdev->dev, &dev_attr_edid);
-                       if (ret < 0)
-                               dev_warn(&client->dev,
-                                       "MXC ddc: cound not create sys node for edid\n");
-               }
-       }
-
-       fb_register_client(&nb);
-
-err:
-       return ret;
+       return i;
 }
+EXPORT_SYMBOL(mxc_edid_var_to_vic);
 
-static int __devexit mxc_ddc_remove(struct i2c_client *client)
+/* 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)
 {
-       fb_unregister_client(&nb);
-       if (!IS_ERR(mxc_ddc.analog_reg))
-               regulator_disable(mxc_ddc.analog_reg);
-       return 0;
-}
+       int ret = 0, extblknum;
+       if (!adp || !edid || !cfg || !fbi)
+               return -EINVAL;
 
-static int __init enable_ddc_setup(char *options)
-{
-       if (!strcmp(options, "=off"))
-               g_enable_ddc = MXC_DISABLE;
-       else
-               g_enable_ddc = MXC_ENABLE;
+       memset(edid, 0, EDID_LENGTH*4);
+       memset(cfg, 0, sizeof(struct mxc_edid_cfg));
 
-       return 1;
-}
-__setup("ddc", enable_ddc_setup);
+       extblknum = mxc_edid_readblk(adp, addr, edid);
+       if (extblknum < 0)
+               return extblknum;
 
-static const struct i2c_device_id mxc_ddc_id[] = {
-       { "mxc_ddc", 0 },
-       {},
-};
-MODULE_DEVICE_TABLE(i2c, mxc_ddc_id);
-
-static struct i2c_driver mxc_ddc_i2c_driver = {
-       .driver = {
-                  .name = "mxc_ddc",
-                  },
-       .probe = mxc_ddc_probe,
-       .remove = mxc_ddc_remove,
-       .id_table = mxc_ddc_id,
-};
+       /* edid first block parsing */
+       memset(&fbi->monspecs, 0, sizeof(fbi->monspecs));
+       fb_edid_to_monspecs(edid, &fbi->monspecs);
 
-static int __init mxc_ddc_init(void)
-{
-       int ret;
+       if (extblknum) {
+               int i;
 
-       memset(&mxc_ddc, 0, sizeof(mxc_ddc));
+               /* need read segment block? */
+               if (extblknum > 1) {
+                       ret = mxc_edid_readsegblk(adp, addr,
+                               edid + EDID_LENGTH*2, extblknum - 1);
+                       if (ret < 0)
+                               return ret;
+               }
 
-       mxc_ddc.pdev = platform_device_register_simple("mxc_ddc", 0, NULL, 0);
-       if (IS_ERR(mxc_ddc.pdev)) {
-               printk(KERN_ERR
-                               "Unable to register MXC DDC as a platform device\n");
-               ret = PTR_ERR(mxc_ddc.pdev);
-               goto err;
+               for (i = 1; i <= extblknum; i++)
+                       /* edid ext block parsing */
+                       mxc_edid_parse_ext_blk(edid + i*EDID_LENGTH,
+                                       cfg, &fbi->monspecs);
        }
 
-       return i2c_add_driver(&mxc_ddc_i2c_driver);
-err:
-       return ret;
-}
-
-static void __exit mxc_ddc_exit(void)
-{
-       i2c_del_driver(&mxc_ddc_i2c_driver);
-       platform_device_unregister(mxc_ddc.pdev);
+       return 0;
 }
+EXPORT_SYMBOL(mxc_edid_read);
 
-module_init(mxc_ddc_init);
-module_exit(mxc_ddc_exit);
-
-MODULE_AUTHOR("Freescale Semiconductor, Inc.");
-MODULE_DESCRIPTION("MXC DDC driver");
-MODULE_LICENSE("GPL");
index 08d8c2ce05d53ae0819c9ae3d83e52b6f9b04f42..43613bbd9e2606e38e102efeb9b7b513314dc188 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/i2c.h>
+#include <linux/ipu.h>
 #include <linux/mxcfb.h>
 #include <linux/fsl_devices.h>
 #include <linux/interrupt.h>
+#include <linux/uaccess.h>
 #include <asm/mach-types.h>
 #include <mach/hardware.h>
 #include <mach/mxc_edid.h>
 
-#define IPU_DISP_PORT 0
-#define SII_EDID_LEN   256
+#define TPI_PIX_CLK_LSB                 (0x00)
+#define TPI_PIX_CLK_MSB                 (0x01)
+#define TPI_VERT_FREQ_LSB               (0x02)
+#define TPI_VERT_FREQ_MSB               (0x03)
+#define TPI_TOTAL_PIX_LSB               (0x04)
+#define TPI_TOTAL_PIX_MSB              (0x05)
+#define TPI_TOTAL_LINES_LSB            (0x06)
+#define TPI_TOTAL_LINES_MSB            (0x07)
+#define TPI_PIX_REPETITION              (0x08)
+#define TPI_INPUT_FORMAT_REG            (0x09)
+#define TPI_OUTPUT_FORMAT_REG           (0x0A)
+
+#define TPI_AVI_BYTE_0                  (0x0C)
+#define TPI_AVI_BYTE_1                  (0x0D)
+#define TPI_AVI_BYTE_2                  (0x0E)
+#define TPI_AVI_BYTE_3                  (0x0F)
+#define TPI_AVI_BYTE_4                  (0x10)
+#define TPI_AVI_BYTE_5                  (0x11)
+
+#define TPI_END_TOP_BAR_LSB             (0x12)
+#define TPI_END_TOP_BAR_MSB             (0x13)
+
+#define TPI_START_BTM_BAR_LSB           (0x14)
+#define TPI_START_BTM_BAR_MSB           (0x15)
+
+#define TPI_END_LEFT_BAR_LSB            (0x16)
+#define TPI_END_LEFT_BAR_MSB            (0x17)
+
+#define TPI_END_RIGHT_BAR_LSB           (0x18)
+#define TPI_END_RIGHT_BAR_MSB           (0x19)
+
+#define TPI_SYSTEM_CONTROL_DATA_REG    (0x1A)
+#define TPI_DEVICE_ID                  (0x1B)
+#define TPI_DEVICE_REV_ID              (0x1C)
+#define TPI_RESERVED2                  (0x1D)
+#define TPI_DEVICE_POWER_STATE_CTRL_REG        (0x1E)
+
+#define TPI_I2S_EN                      (0x1F)
+#define TPI_I2S_IN_CFG                  (0x20)
+#define TPI_I2S_CHST_0                  (0x21)
+#define TPI_I2S_CHST_1                  (0x22)
+#define TPI_I2S_CHST_2                  (0x23)
+#define TPI_I2S_CHST_3                  (0x24)
+#define TPI_I2S_CHST_4                  (0x25)
+
+#define TPI_AUDIO_HANDLING              (0x25)
+#define TPI_AUDIO_INTERFACE_REG         (0x26)
+#define TPI_AUDIO_SAMPLE_CTRL           (0x27)
+
+#define TPI_INTERRUPT_ENABLE_REG       (0x3C)
+#define TPI_INTERRUPT_STATUS_REG       (0x3D)
+
+#define TPI_INTERNAL_PAGE_REG          0xBC
+#define TPI_INDEXED_OFFSET_REG         0xBD
+#define TPI_INDEXED_VALUE_REG          0xBE
+
+#define MISC_INFO_FRAMES_CTRL           (0xBF)
+#define MISC_INFO_FRAMES_TYPE           (0xC0)
+#define EN_AND_RPT_AUDIO                0xC2
+#define DISABLE_AUDIO                   0x02
+
+#define TPI_ENABLE                     (0xC7)
+
+#define INDEXED_PAGE_0         0x01
+#define INDEXED_PAGE_1         0x02
+#define INDEXED_PAGE_2         0x03
+
+#define HOT_PLUG_EVENT          0x01
+#define RX_SENSE_EVENT          0x02
+#define HOT_PLUG_STATE          0x04
+#define RX_SENSE_STATE          0x08
+
+#define OUTPUT_MODE_MASK       (0x01)
+#define OUTPUT_MODE_DVI                (0x00)
+#define OUTPUT_MODE_HDMI       (0x01)
+
+#define LINK_INTEGRITY_MODE_MASK        0x40
+#define LINK_INTEGRITY_STATIC           (0x00)
+#define LINK_INTEGRITY_DYNAMIC          (0x40)
+
+#define TMDS_OUTPUT_CONTROL_MASK       0x10
+#define TMDS_OUTPUT_CONTROL_ACTIVE      (0x00)
+#define TMDS_OUTPUT_CONTROL_POWER_DOWN  (0x10)
+
+#define AV_MUTE_MASK                    0x08
+#define AV_MUTE_NORMAL                  (0x00)
+#define AV_MUTE_MUTED                   (0x08)
+
+#define TX_POWER_STATE_MASK             0x3
+#define TX_POWER_STATE_D0               (0x00)
+#define TX_POWER_STATE_D1               (0x01)
+#define TX_POWER_STATE_D2               (0x02)
+#define TX_POWER_STATE_D3               (0x03)
+
+#define AUDIO_MUTE_MASK                  0x10
+#define AUDIO_MUTE_NORMAL                (0x00)
+#define AUDIO_MUTE_MUTED                 (0x10)
+
+#define AUDIO_SEL_MASK                   0xC0
+#define AUD_IF_SPDIF                     0x40
+#define AUD_IF_I2S                       0x80
+#define AUD_IF_DSD                       0xC0
+#define AUD_IF_HBR                       0x04
+
+#define REFER_TO_STREAM_HDR            0x00
+
+#define AUD_PASS_BASIC      0x00
+#define AUD_PASS_ALL        0x01
+#define AUD_DOWN_SAMPLE     0x02
+#define AUD_DO_NOT_CHECK    0x03
+
+#define BITS_IN_RGB         0x00
+#define BITS_IN_YCBCR444    0x01
+#define BITS_IN_YCBCR422    0x02
+
+#define BITS_IN_AUTO_RANGE  0x00
+#define BITS_IN_FULL_RANGE  0x04
+#define BITS_IN_LTD_RANGE   0x08
+
+#define BIT_EN_DITHER_10_8  0x40
+#define BIT_EXTENDED_MODE   0x80
+
+#define SII_EDID_LEN           512
+#define SIZE_AVI_INFOFRAME     0x0E
+#define SIZE_AUDIO_INFOFRAME    0x0F
+
+#define _4_To_3                 0x10
+#define _16_To_9                0x20
+#define SAME_AS_AR              0x08
+
 #define MXC_ENABLE     1
 #define MXC_DISABLE    2
 static int g_enable_hdmi;
@@ -64,29 +194,101 @@ struct sii902x_data {
        struct delayed_work det_work;
        struct fb_info *fbi;
        struct mxc_edid_cfg edid_cfg;
-       u8 cable_plugin;
+       char *fb_id;
+       bool cable_plugin;
+       bool rx_powerup;
+       bool need_mode_change;
        u8 edid[SII_EDID_LEN];
-} sii902x;
 
-static void sii902x_poweron(void);
-static void sii902x_poweroff(void);
-static void (*sii902x_reset) (void);
+       u8 power_state;
+       u8 tpivmode[3];
+       u8 pixrep;
+
+       /* SII902x video setting:
+        * 1. hdmi video fmt:
+        *      0 = CEA-861 VIC; 1 = HDMI_VIC; 2 = 3D
+        * 2. vic: video mode index
+        * 3. aspect ratio:
+        *      4x3 or 16x9
+        * 4. color space:
+        *      0 = RGB; 1 = YCbCr4:4:4; 2 = YCbCr4:2:2_16bits;
+        *      3 = YCbCr4:2:2_8bits;4 = xvYCC4:4:4
+        * 5. color depth:
+        *      0 = 8bits; 1 = 10bits; 2 = 12bits; 3 = 16bits
+        * 6. colorimetry:
+        *      0 = 601; 1 = 709
+        * 7. syncmode:
+        *      0 = external HS/VS/DE; 1 = external HS/VS and internal DE;
+        *      2 = embedded sync
+        */
+#define VMD_HDMIFORMAT_CEA_VIC                  0x00
+#define VMD_HDMIFORMAT_HDMI_VIC                 0x01
+#define VMD_HDMIFORMAT_3D                       0x02
+#define VMD_HDMIFORMAT_PC                       0x03
+       u8 hdmi_vid_fmt;
+       u8 vic;
+#define VMD_ASPECT_RATIO_4x3                    0x01
+#define VMD_ASPECT_RATIO_16x9                   0x02
+       u8 aspect_ratio;
+#define RGB                    0
+#define YCBCR444               1
+#define YCBCR422_16BITS                2
+#define YCBCR422_8BITS         3
+#define XVYCC444               4
+       u8 icolor_space;
+       u8 ocolor_space;
+#define VMD_COLOR_DEPTH_8BIT                    0x00
+#define VMD_COLOR_DEPTH_10BIT                   0x01
+#define VMD_COLOR_DEPTH_12BIT                   0x02
+#define VMD_COLOR_DEPTH_16BIT                   0x03
+       u8 color_depth;
+#define COLORIMETRY_601                0
+#define COLORIMETRY_709                1
+       u8 colorimetry;
+#define EXTERNAL_HSVSDE                0
+#define INTERNAL_DE             1
+#define EMBEDDED_SYNC           2
+       u8 syncmode;
+       u8 threeDstruct;
+       u8 threeDextdata;
+
+#define AMODE_I2S               0
+#define AMODE_SPDIF             1
+#define AMODE_HBR               2
+#define AMODE_DSD               3
+       u8 audio_mode;
+#define ACHANNEL_2CH            1
+#define ACHANNEL_3CH            2
+#define ACHANNEL_4CH            3
+#define ACHANNEL_5CH            4
+#define ACHANNEL_6CH            5
+#define ACHANNEL_7CH            6
+#define ACHANNEL_8CH            7
+       u8 audio_channels;
+       u8 audiofs;
+       u8 audio_word_len;
+       u8 audio_i2s_fmt;
+};
+static struct sii902x_data *g_sii902x;
 
-static __attribute__ ((unused)) void dump_regs(u8 reg, int len)
+static __attribute__ ((unused)) void dump_regs(struct sii902x_data *sii902x,
+                                       u8 reg, int len)
 {
        u8 buf[50];
        int i;
 
-       i2c_smbus_read_i2c_block_data(sii902x.client, reg, len, buf);
+       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",
+               dev_dbg(&sii902x->client->dev, "reg[0x%02X]: 0x%02X\n",
                                i+reg, buf[i]);
 }
 
 static ssize_t sii902x_show_name(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       strcpy(buf, sii902x.fbi->fix.id);
+       struct sii902x_data *sii902x = dev_get_drvdata(dev);
+
+       strcpy(buf, sii902x->fbi->fix.id);
        sprintf(buf+strlen(buf), "\n");
 
        return strlen(buf);
@@ -97,7 +299,9 @@ static DEVICE_ATTR(fb_name, S_IRUGO, sii902x_show_name, NULL);
 static ssize_t sii902x_show_state(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       if (sii902x.cable_plugin == 0)
+       struct sii902x_data *sii902x = dev_get_drvdata(dev);
+
+       if (sii902x->cable_plugin == false)
                strcpy(buf, "plugout\n");
        else
                strcpy(buf, "plugin\n");
@@ -110,12 +314,13 @@ static DEVICE_ATTR(cable_state, S_IRUGO, sii902x_show_state, NULL);
 static ssize_t sii902x_show_edid(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
+       struct sii902x_data *sii902x = dev_get_drvdata(dev);
        int i, j, len = 0;
 
        for (j = 0; j < SII_EDID_LEN/16; j++) {
                for (i = 0; i < 16; i++)
                        len += sprintf(buf+len, "0x%02X ",
-                                       sii902x.edid[j*16 + i]);
+                                       sii902x->edid[j*16 + i]);
                len += sprintf(buf+len, "\n");
        }
 
@@ -124,17 +329,304 @@ static ssize_t sii902x_show_edid(struct device *dev,
 
 static DEVICE_ATTR(edid, S_IRUGO, sii902x_show_edid, NULL);
 
-static void sii902x_setup(struct fb_info *fbi)
+/*------------------------------------------------------------------------------
+ * Function Description: Write "0" to all bits in TPI offset "Offset" that are set
+ *                 to "1" in "Pattern"; Leave all other bits in "Offset"
+ *                 unchanged.
+ *-----------------------------------------------------------------------------
+ */
+void read_clr_write_tpi(struct i2c_client *client, u8 offset, u8 mask)
+{
+       u8 tmp;
+
+       tmp = i2c_smbus_read_byte_data(client, offset);
+       tmp &= ~mask;
+       i2c_smbus_write_byte_data(client, offset, tmp);
+}
+
+/*------------------------------------------------------------------------------
+ * Function Description: Write "1" to all bits in TPI offset "Offset" that are set
+ *                 to "1" in "Pattern"; Leave all other bits in "Offset"
+ *                 unchanged.
+ *-----------------------------------------------------------------------------
+ */
+void read_set_write_tpi(struct i2c_client *client, u8 offset, u8 mask)
+{
+       u8 tmp;
+
+       tmp = i2c_smbus_read_byte_data(client, offset);
+       tmp |= mask;
+       i2c_smbus_write_byte_data(client, offset, tmp);
+}
+
+/*------------------------------------------------------------------------------
+ * Function Description: Write "Value" to all bits in TPI offset "Offset" that are set
+ *                 to "1" in "Mask"; Leave all other bits in "Offset"
+ *                 unchanged.
+ *----------------------------------------------------------------------------
+ */
+void read_modify_tpi(struct i2c_client *client, u8 offset, u8 mask, u8 value)
+{
+       u8 tmp;
+
+       tmp = i2c_smbus_read_byte_data(client, offset);
+       tmp &= ~mask;
+       tmp |= (value & mask);
+       i2c_smbus_write_byte_data(client, offset, tmp);
+}
+
+/*------------------------------------------------------------------------------
+ * Function Description: Read an indexed register value
+ *                 Write:
+ *                     1. 0xBC => Internal page num
+ *                     2. 0xBD => Indexed register offset
+ *                 Read:
+ *                     3. 0xBE => Returns the indexed register value
+ *----------------------------------------------------------------------------
+ */
+int read_idx_reg(struct i2c_client *client, u8 page, u8 regoffset)
+{
+       i2c_smbus_write_byte_data(client, TPI_INTERNAL_PAGE_REG, page);
+       i2c_smbus_write_byte_data(client, TPI_INDEXED_OFFSET_REG, regoffset);
+       return i2c_smbus_read_byte_data(client, TPI_INDEXED_VALUE_REG);
+}
+
+/*------------------------------------------------------------------------------
+ * Function Description: Write a value to an indexed register
+ *
+ *                  Write:
+ *                      1. 0xBC => Internal page num
+ *                      2. 0xBD => Indexed register offset
+ *                      3. 0xBE => Set the indexed register value
+ *------------------------------------------------------------------------------
+ */
+void write_idx_reg(struct i2c_client *client, u8 page, u8 regoffset, u8 regval)
+{
+       i2c_smbus_write_byte_data(client, TPI_INTERNAL_PAGE_REG, page);
+       i2c_smbus_write_byte_data(client, TPI_INDEXED_OFFSET_REG, regoffset);
+       i2c_smbus_write_byte_data(client, TPI_INDEXED_VALUE_REG, regval);
+}
+
+/*------------------------------------------------------------------------------
+ * Function Description: Write "Value" to all bits in TPI offset "Offset" that are set
+ *                 to "1" in "Mask"; Leave all other bits in "Offset"
+ *                 unchanged.
+ *----------------------------------------------------------------------------
+ */
+void read_modify_idx_reg(struct i2c_client *client, u8 page, u8 regoffset, u8 mask, u8 value)
+{
+       u8 tmp;
+
+       i2c_smbus_write_byte_data(client, TPI_INTERNAL_PAGE_REG, page);
+       i2c_smbus_write_byte_data(client, TPI_INDEXED_OFFSET_REG, regoffset);
+       tmp = i2c_smbus_read_byte_data(client, TPI_INDEXED_VALUE_REG);
+       tmp &= ~mask;
+       tmp |= (value & mask);
+       i2c_smbus_write_byte_data(client, TPI_INDEXED_VALUE_REG, tmp);
+}
+
+static void sii902x_set_powerstate(struct sii902x_data *sii902x, u8 state)
+{
+       if (sii902x->power_state != state) {
+               read_modify_tpi(sii902x->client, TPI_DEVICE_POWER_STATE_CTRL_REG,
+                               TX_POWER_STATE_MASK, state);
+               sii902x->power_state = state;
+       }
+}
+
+static void sii902x_setAVI(struct sii902x_data *sii902x)
+{
+       u8 avi_data[SIZE_AVI_INFOFRAME];
+       u8 tmp;
+       int i;
+
+       dev_dbg(&sii902x->client->dev, "set AVI frame\n");
+
+       memset(avi_data, 0, SIZE_AVI_INFOFRAME);
+
+       if (sii902x->edid_cfg.cea_ycbcr444)
+               tmp = 2;
+       else if (sii902x->edid_cfg.cea_ycbcr422)
+               tmp = 1;
+       else
+               tmp = 0;
+
+       /* AVI byte1: Y1Y0 (output format) */
+       avi_data[1] = (tmp << 5) & 0x60;
+       /* A0 = 1; Active format identification data is present in the AVI InfoFrame.
+        * S1:S0 = 00;
+        */
+       avi_data[1] |= 0x10;
+
+       if (sii902x->ocolor_space == XVYCC444) {
+               avi_data[2] = 0xC0;
+               if (sii902x->colorimetry == COLORIMETRY_601)
+                       avi_data[3] &= ~0x70;
+               else if (sii902x->colorimetry == COLORIMETRY_709)
+                       avi_data[3] = (avi_data[3] & ~0x70) | 0x10;
+       } else if (sii902x->ocolor_space != RGB) {
+               if (sii902x->colorimetry == COLORIMETRY_709)
+                       avi_data[2] = 0x80;/* AVI byte2: C1C0*/
+               else if (sii902x->colorimetry == COLORIMETRY_601)
+                       avi_data[2] = 0x40;/* AVI byte2: C1C0 */
+       } else {/* Carries no data */
+               /* AVI Byte2: C1C0 */
+               avi_data[2] &= ~0xc0; /* colorimetry = 0 */
+               avi_data[3] &= ~0x70; /* Extended colorimetry = 0 */
+       }
+
+       avi_data[4] = sii902x->vic;
+
+       /*  Set the Aspect Ration info into the Infoframe Byte 2 */
+       if (sii902x->aspect_ratio == VMD_ASPECT_RATIO_16x9)
+               avi_data[2] |= _16_To_9; /* AVI Byte2: M1M0 */
+       else
+               avi_data[2] |= _4_To_3;
+
+       avi_data[2] |= SAME_AS_AR;  /* AVI Byte2: R3..R1 - Set to "Same as Picture Aspect Ratio" */
+       avi_data[5] = sii902x->pixrep; /* AVI Byte5: Pixel Replication - PR3..PR0 */
+
+       /* Calculate AVI InfoFrame ChecKsum */
+       avi_data[0] = 0x82 + 0x02 + 0x0D;
+       for (i = 1; i < SIZE_AVI_INFOFRAME; i++)
+              avi_data[0] += avi_data[i];
+       avi_data[0] = 0x100 - avi_data[0];
+
+       /* Write the Inforframe data to the TPI Infoframe registers */
+       for (i = 0; i < SIZE_AVI_INFOFRAME; i++)
+               i2c_smbus_write_byte_data(sii902x->client,
+                               TPI_AVI_BYTE_0 + i, avi_data[i]);
+
+       dump_regs(sii902x, TPI_AVI_BYTE_0, SIZE_AVI_INFOFRAME);
+}
+
+#define TYPE_AUDIO_INFOFRAMES       0x84
+#define AUDIO_INFOFRAMES_VERSION    0x01
+#define AUDIO_INFOFRAMES_LENGTH     0x0A
+/*------------------------------------------------------------------------------
+* Function Description: Load Audio InfoFrame data into registers and send to sink
+*
+* Accepts: (1) Channel count
+*              (2) speaker configuration per CEA-861D Tables 19, 20
+*              (3) Coding type: 0x09 for DSD Audio. 0 (refer to stream header) for all the rest
+*              (4) Sample Frequency. Non zero for HBR only
+*              (5) Audio Sample Length. Non zero for HBR only.
+*------------------------------------------------------------------------------
+*/
+static void  sii902x_setAIF(struct sii902x_data *sii902x,
+               u8 codingtype, u8 sample_size, u8 sample_freq,
+               u8 speaker_cfg)
+{
+       u8 aif_data[SIZE_AUDIO_INFOFRAME];
+       u8 channel_count = sii902x->audio_channels & 0x07;
+       int i;
+
+       dev_dbg(&sii902x->client->dev, "set AIF frame\n");
+
+       memset(aif_data, 0, SIZE_AUDIO_INFOFRAME);
+
+       /*  Disbale MPEG/Vendor Specific InfoFrames */
+       i2c_smbus_write_byte_data(sii902x->client, MISC_INFO_FRAMES_CTRL, DISABLE_AUDIO);
+
+       aif_data[0] = TYPE_AUDIO_INFOFRAMES;
+       aif_data[1] = AUDIO_INFOFRAMES_VERSION;
+       aif_data[2] = AUDIO_INFOFRAMES_LENGTH;
+       /* Calculate checksum - 0x84 + 0x01 + 0x0A */
+       aif_data[3] = TYPE_AUDIO_INFOFRAMES +
+               AUDIO_INFOFRAMES_VERSION + AUDIO_INFOFRAMES_LENGTH;
+
+       aif_data[4] = channel_count; /* 0 for "Refer to Stream Header" or for 2 Channels. 0x07 for 8 Channels*/
+       aif_data[4] |= (codingtype << 4); /* 0xC7[7:4] == 0b1001 for DSD Audio */
+       aif_data[5] = ((sample_freq & 0x07) << 2) | (sample_size & 0x03);
+       aif_data[7] = speaker_cfg;
+
+       for (i = 4; i < SIZE_AUDIO_INFOFRAME; i++)
+               aif_data[3] += aif_data[i];
+
+       aif_data[3] = 0x100 - aif_data[3];
+
+       /* Re-enable Audio InfoFrame transmission and repeat */
+       i2c_smbus_write_byte_data(sii902x->client, MISC_INFO_FRAMES_CTRL, EN_AND_RPT_AUDIO);
+
+       for (i = 0; i < SIZE_AUDIO_INFOFRAME; i++)
+               i2c_smbus_write_byte_data(sii902x->client,
+                               MISC_INFO_FRAMES_TYPE + i, aif_data[i]);
+
+       dump_regs(sii902x, MISC_INFO_FRAMES_TYPE, SIZE_AUDIO_INFOFRAME);
+}
+
+static void sii902x_setaudio(struct sii902x_data *sii902x)
+{
+       dev_dbg(&sii902x->client->dev, "set audio\n");
+
+       /* mute audio */
+       read_modify_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG,
+                       AUDIO_MUTE_MASK, AUDIO_MUTE_MUTED);
+       if (sii902x->audio_mode == AMODE_I2S) {
+               read_modify_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG,
+                               AUDIO_SEL_MASK, AUD_IF_I2S);
+               i2c_smbus_write_byte_data(sii902x->client, TPI_AUDIO_HANDLING,
+                               0x08 | AUD_DO_NOT_CHECK);
+       } else {
+               read_modify_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG,
+                               AUDIO_SEL_MASK, AUD_IF_SPDIF);
+               i2c_smbus_write_byte_data(sii902x->client, TPI_AUDIO_HANDLING,
+                               AUD_PASS_BASIC);
+       }
+
+       if (sii902x->audio_channels == ACHANNEL_2CH)
+               read_clr_write_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG, 0x20);
+       else
+               read_set_write_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG, 0x20);
+
+       if (sii902x->audio_mode == AMODE_I2S) {
+               /* I2S - Map channels */
+               i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_EN, 0x80);
+
+               if (sii902x->audio_channels > ACHANNEL_2CH)
+                       i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_EN, 0x91);
+
+               if (sii902x->audio_channels > ACHANNEL_4CH)
+                       i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_EN, 0xA2);
+
+               if (sii902x->audio_channels > ACHANNEL_6CH)
+                       i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_EN, 0xB3);
+
+               /* I2S - Stream Header Settings */
+               i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_CHST_0, 0x00);
+               i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_CHST_1, 0x00);
+               i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_CHST_2, 0x00);
+               i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_CHST_3, sii902x->audiofs);
+               i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_CHST_4,
+                                       (sii902x->audiofs << 4) | sii902x->audio_word_len);
+
+               /* added for 16bit auido noise issue */
+               write_idx_reg(sii902x->client, INDEXED_PAGE_1, 0x24, sii902x->audio_word_len);
+
+               /* I2S - Input Configuration */
+               i2c_smbus_write_byte_data(sii902x->client, TPI_I2S_IN_CFG, sii902x->audio_i2s_fmt);
+       }
+
+       i2c_smbus_write_byte_data(sii902x->client, TPI_AUDIO_SAMPLE_CTRL, REFER_TO_STREAM_HDR);
+
+       sii902x_setAIF(sii902x, REFER_TO_STREAM_HDR, REFER_TO_STREAM_HDR, REFER_TO_STREAM_HDR, 0x00);
+
+       /* unmute audio */
+       read_modify_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG, AUDIO_MUTE_MASK, AUDIO_MUTE_NORMAL);
+}
+
+static void sii902x_setup(struct sii902x_data *sii902x, struct fb_info *fbi)
 {
        u16 data[4];
        u32 refresh;
        u8 *tmp;
+       mm_segment_t old_fs;
+       unsigned int fmt;
        int i;
 
-       dev_dbg(&sii902x.client->dev, "Sii902x: setup..\n");
+       dev_dbg(&sii902x->client->dev, "setup..\n");
 
-       /* Power up */
-       i2c_smbus_write_byte_data(sii902x.client, 0x1E, 0x00);
+       sii902x->vic = mxc_edid_var_to_vic(&fbi->var);
 
        /* set TPI video mode */
        data[0] = PICOS2KHZ(fbi->var.pixclock) / 10;
@@ -147,33 +639,132 @@ static void sii902x_setup(struct fb_info *fbi)
        data[1] = refresh * 100;
        tmp = (u8 *)data;
        for (i = 0; i < 8; i++)
-               i2c_smbus_write_byte_data(sii902x.client, i, tmp[i]);
+               i2c_smbus_write_byte_data(sii902x->client, i, tmp[i]);
+
+       dump_regs(sii902x, 0, 8);
+
+       if (fbi->fbops->fb_ioctl) {
+               old_fs = get_fs();
+               set_fs(KERNEL_DS);
+               fbi->fbops->fb_ioctl(fbi, MXCFB_GET_DIFMT, (unsigned long)&fmt);
+               set_fs(old_fs);
+               if (fmt == IPU_PIX_FMT_VYU444) {
+                       sii902x->icolor_space = YCBCR444;
+                       dev_dbg(&sii902x->client->dev, "input color space YUV\n");
+               } else {
+                       sii902x->icolor_space = RGB;
+                       dev_dbg(&sii902x->client->dev, "input color space RGB\n");
+               }
+       }
+
+       /* reg 0x08: input bus/pixel: full pixel wide (24bit), rising edge */
+       sii902x->tpivmode[0] = 0x70;
+       /* reg 0x09: Set input format */
+       if (sii902x->icolor_space == RGB)
+               sii902x->tpivmode[1] =
+                       (((BITS_IN_RGB | BITS_IN_AUTO_RANGE) & ~BIT_EN_DITHER_10_8) & ~BIT_EXTENDED_MODE);
+       else if (sii902x->icolor_space == YCBCR444)
+               sii902x->tpivmode[1] =
+                       (((BITS_IN_YCBCR444 | BITS_IN_AUTO_RANGE) & ~BIT_EN_DITHER_10_8) & ~BIT_EXTENDED_MODE);
+       else if ((sii902x->icolor_space == YCBCR422_16BITS) || (sii902x->icolor_space == YCBCR422_8BITS))
+               sii902x->tpivmode[1] =
+                       (((BITS_IN_YCBCR422 | BITS_IN_AUTO_RANGE) & ~BIT_EN_DITHER_10_8) & ~BIT_EXTENDED_MODE);
+       /* reg 0x0a: set output format to RGB */
+       sii902x->tpivmode[2] = 0x00;
+
+       if (fbi->var.xres/16 == fbi->var.yres/9)
+               sii902x->aspect_ratio = VMD_ASPECT_RATIO_16x9;
+       else
+               sii902x->aspect_ratio = VMD_ASPECT_RATIO_4x3;
+
+       if ((sii902x->vic == 6) || (sii902x->vic == 7) ||
+               (sii902x->vic == 21) || (sii902x->vic == 22) ||
+               (sii902x->vic == 2) || (sii902x->vic == 3) ||
+               (sii902x->vic == 17) || (sii902x->vic == 18)) {
+               sii902x->tpivmode[2] &= ~0x10; /*BT.601*/
+               sii902x->colorimetry = COLORIMETRY_601;
+               sii902x->aspect_ratio = VMD_ASPECT_RATIO_4x3;
+       } else {
+               sii902x->tpivmode[2] |= 0x10; /*BT.709*/
+               sii902x->colorimetry = COLORIMETRY_709;
+       }
+
+       if ((sii902x->vic == 10) || (sii902x->vic == 11) ||
+               (sii902x->vic == 12) || (sii902x->vic == 13) ||
+               (sii902x->vic == 14) || (sii902x->vic == 15) ||
+               (sii902x->vic == 25) || (sii902x->vic == 26) ||
+               (sii902x->vic == 27) || (sii902x->vic == 28) ||
+               (sii902x->vic == 29) || (sii902x->vic == 30) ||
+               (sii902x->vic == 35) || (sii902x->vic == 36) ||
+               (sii902x->vic == 37) || (sii902x->vic == 38))
+               sii902x->pixrep = 1;
+       else
+               sii902x->pixrep = 0;
+
+       dev_dbg(&sii902x->client->dev, "vic %d\n", sii902x->vic);
+       dev_dbg(&sii902x->client->dev, "pixrep %d\n", sii902x->pixrep);
+       if (sii902x->aspect_ratio == VMD_ASPECT_RATIO_4x3) {
+               dev_dbg(&sii902x->client->dev, "aspect 4:3\n");
+       } else {
+               dev_dbg(&sii902x->client->dev, "aspect 16:9\n");
+       }
+       if (sii902x->colorimetry == COLORIMETRY_601) {
+               dev_dbg(&sii902x->client->dev, "COLORIMETRY_601\n");
+       } else {
+               dev_dbg(&sii902x->client->dev, "COLORIMETRY_709\n");
+       }
+       dev_dbg(&sii902x->client->dev, "hdmi capbility %d\n", sii902x->edid_cfg.hdmi_cap);
+
+       sii902x->ocolor_space = RGB;
+       if (sii902x->edid_cfg.hdmi_cap) {
+               if (sii902x->edid_cfg.cea_ycbcr444) {
+                       sii902x->ocolor_space = YCBCR444;
+                       sii902x->tpivmode[2] |= 0x1; /*Ycbcr444*/
+               } else if (sii902x->edid_cfg.cea_ycbcr422) {
+                       sii902x->ocolor_space = YCBCR422_8BITS;
+                       sii902x->tpivmode[2] |= 0x2; /*Ycbcr422*/
+               }
+       }
+
+       dev_dbg(&sii902x->client->dev, "write reg 0x08 0X%2X\n", sii902x->tpivmode[0]);
+       dev_dbg(&sii902x->client->dev, "write reg 0x09 0X%2X\n", sii902x->tpivmode[1]);
+       dev_dbg(&sii902x->client->dev, "write reg 0x0a 0X%2X\n", sii902x->tpivmode[2]);
+
+       i2c_smbus_write_byte_data(sii902x->client, TPI_PIX_REPETITION, sii902x->tpivmode[0]);
+       i2c_smbus_write_byte_data(sii902x->client, TPI_INPUT_FORMAT_REG, sii902x->tpivmode[1]);
+       i2c_smbus_write_byte_data(sii902x->client, TPI_OUTPUT_FORMAT_REG, sii902x->tpivmode[2]);
+
+       /* goto state D0*/
+       sii902x_set_powerstate(sii902x, TX_POWER_STATE_D0);
+
+       if (sii902x->edid_cfg.hdmi_cap) {
+               sii902x_setAVI(sii902x);
+               sii902x_setaudio(sii902x);
+       } else {
+               /* set last byte of TPI AVI InfoFrame for TPI AVI I/O format to take effect ?? */
+               i2c_smbus_write_byte_data(sii902x->client, TPI_END_RIGHT_BAR_MSB, 0x00);
 
-       /* 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);
+               /* mute audio */
+               read_modify_tpi(sii902x->client, TPI_AUDIO_INTERFACE_REG,
+                               AUDIO_MUTE_MASK, AUDIO_MUTE_MUTED);
+       }
 }
 
 #ifdef CONFIG_FB_MODE_HELPERS
-static int sii902x_read_edid(struct fb_info *fbi)
+static int sii902x_read_edid(struct sii902x_data *sii902x,
+                       struct fb_info *fbi)
 {
        int old, dat, ret, cnt = 100;
        unsigned short addr = 0x50;
+       u8 edid_old[SII_EDID_LEN];
 
-       old = i2c_smbus_read_byte_data(sii902x.client, 0x1A);
+       old = i2c_smbus_read_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG);
 
-       i2c_smbus_write_byte_data(sii902x.client, 0x1A, old | 0x4);
+       i2c_smbus_write_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG, old | 0x4);
        do {
                cnt--;
                msleep(10);
-               dat = i2c_smbus_read_byte_data(sii902x.client, 0x1A);
+               dat = i2c_smbus_read_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG);
        } while ((!(dat & 0x2)) && cnt);
 
        if (!cnt) {
@@ -181,104 +772,228 @@ static int sii902x_read_edid(struct fb_info *fbi)
                goto done;
        }
 
-       i2c_smbus_write_byte_data(sii902x.client, 0x1A, old | 0x06);
+       i2c_smbus_write_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG, old | 0x06);
+
+       /* save old edid */
+       memcpy(edid_old, sii902x->edid, SII_EDID_LEN);
 
        /* edid reading */
-       ret = mxc_edid_read(sii902x.client->adapter, addr,
-                               sii902x.edid, &sii902x.edid_cfg, fbi);
+       ret = mxc_edid_read(sii902x->client->adapter, addr,
+                               sii902x->edid, &sii902x->edid_cfg, fbi);
 
        cnt = 100;
        do {
                cnt--;
-               i2c_smbus_write_byte_data(sii902x.client, 0x1A, old & ~0x6);
+               i2c_smbus_write_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG, old & ~0x6);
                msleep(10);
-               dat = i2c_smbus_read_byte_data(sii902x.client, 0x1A);
+               dat = i2c_smbus_read_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG);
        } while ((dat & 0x6) && cnt);
 
        if (!cnt)
                ret = -1;
 
 done:
-       i2c_smbus_write_byte_data(sii902x.client, 0x1A, old);
+       i2c_smbus_write_byte_data(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG, old);
+
+       if (!memcmp(edid_old, sii902x->edid, SII_EDID_LEN))
+               ret = -2;
        return ret;
 }
 #else
-static int sii902x_read_edid(struct fb_info *fbi)
+static int sii902x_read_edid(struct sii902x_data *sii902x,
+                       struct fb_info *fbi)
 {
        return -1;
 }
 #endif
 
+static void sii902x_enable_tmds(struct sii902x_data *sii902x)
+{
+       /* goto state D0*/
+       sii902x_set_powerstate(sii902x, TX_POWER_STATE_D0);
+
+       /* Turn on DVI or HDMI */
+       if (sii902x->edid_cfg.hdmi_cap)
+               read_modify_tpi(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG,
+                               OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI);
+       else
+               read_modify_tpi(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG,
+                               OUTPUT_MODE_MASK, OUTPUT_MODE_DVI);
+
+       read_modify_tpi(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG,
+               LINK_INTEGRITY_MODE_MASK | TMDS_OUTPUT_CONTROL_MASK | AV_MUTE_MASK,
+               LINK_INTEGRITY_DYNAMIC | TMDS_OUTPUT_CONTROL_ACTIVE | AV_MUTE_NORMAL);
+
+       i2c_smbus_write_byte_data(sii902x->client, TPI_PIX_REPETITION,
+                               sii902x->tpivmode[0]);
+}
+
+static void sii902x_disable_tmds(struct sii902x_data *sii902x)
+{
+       read_modify_tpi(sii902x->client, TPI_SYSTEM_CONTROL_DATA_REG,
+               TMDS_OUTPUT_CONTROL_MASK | AV_MUTE_MASK | OUTPUT_MODE_MASK,
+               TMDS_OUTPUT_CONTROL_POWER_DOWN | AV_MUTE_MUTED | OUTPUT_MODE_DVI);
+
+       /* goto state D2*/
+       sii902x_set_powerstate(sii902x, TX_POWER_STATE_D2);
+}
+
+static void sii902x_poweron(struct sii902x_data *sii902x)
+{
+       struct mxc_lcd_platform_data *plat = sii902x->client->dev.platform_data;
+
+       dev_dbg(&sii902x->client->dev, "power on\n");
+
+       /* Enable pins to HDMI */
+       if (plat->enable_pins)
+               plat->enable_pins();
+
+       if (sii902x->rx_powerup)
+               sii902x_enable_tmds(sii902x);
+}
+
+static void sii902x_poweroff(struct sii902x_data *sii902x)
+{
+       struct mxc_lcd_platform_data *plat = sii902x->client->dev.platform_data;
+
+       dev_dbg(&sii902x->client->dev, "power off\n");
+
+       /* Disable pins to HDMI */
+       if (plat->disable_pins)
+               plat->disable_pins();
+
+       if (sii902x->rx_powerup)
+               sii902x_disable_tmds(sii902x);
+}
+
+static void sii902x_rx_powerup(struct sii902x_data *sii902x)
+{
+
+       dev_dbg(&sii902x->client->dev, "rx power up\n");
+
+       if (sii902x->need_mode_change) {
+               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();
+               sii902x->need_mode_change = false;
+       }
+
+       sii902x_enable_tmds(sii902x);
+
+       sii902x->rx_powerup = true;
+}
+
+static void sii902x_rx_powerdown(struct sii902x_data *sii902x)
+{
+       dev_dbg(&sii902x->client->dev, "rx power down\n");
+
+       sii902x_disable_tmds(sii902x);
+
+       sii902x->rx_powerup = false;
+}
+
+static int sii902x_cable_connected(struct sii902x_data *sii902x)
+{
+       int ret;
+
+       dev_dbg(&sii902x->client->dev, "cable connected\n");
+
+       sii902x->cable_plugin = true;
+
+       /* edid read */
+       ret = sii902x_read_edid(sii902x, sii902x->fbi);
+       if (ret == -1)
+               dev_err(&sii902x->client->dev,
+                               "read edid fail\n");
+       else if (ret == -2)
+               dev_info(&sii902x->client->dev,
+                               "same edid\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++) {
+                               /*FIXME now we do not support interlaced mode */
+                               if (!(sii902x->fbi->monspecs.modedb[i].vmode & FB_VMODE_INTERLACED))
+                                       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->need_mode_change = true;
+               }
+       }
+
+       /* ?? remain it for control back door register */
+       read_modify_idx_reg(sii902x->client, INDEXED_PAGE_0, 0x0a, 0x08, 0x08);
+
+       return 0;
+}
+
+static void sii902x_cable_disconnected(struct sii902x_data *sii902x)
+{
+       dev_dbg(&sii902x->client->dev, "cable disconnected\n");
+       sii902x_rx_powerdown(sii902x);
+       sii902x->cable_plugin = false;
+}
+
 static void det_worker(struct work_struct *work)
 {
-       int dat;
+       struct delayed_work *delay_work = to_delayed_work(work);
+       struct sii902x_data *sii902x =
+               container_of(delay_work, struct sii902x_data, det_work);
+       int status;
        char event_string[16];
        char *envp[] = { event_string, NULL };
 
-       dat = i2c_smbus_read_byte_data(sii902x.client, 0x3D);
-       if (dat & 0x1) {
+       status = i2c_smbus_read_byte_data(sii902x->client, TPI_INTERRUPT_STATUS_REG);
+
+       /* check cable status */
+       if (status & HOT_PLUG_EVENT) {
                /* cable connection changes */
-               if (dat & 0x4) {
-                       sii902x.cable_plugin = 1;
-                       sprintf(event_string, "EVENT=plugin");
-
-                       /* make sure fb is powerdown */
-                       console_lock();
-                       fb_blank(sii902x.fbi, FB_BLANK_POWERDOWN);
-                       console_unlock();
-
-                       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++) {
-                                               /*FIXME now we do not support interlaced mode */
-                                               if (!(sii902x.fbi->monspecs.modedb[i].vmode & FB_VMODE_INTERLACED))
-                                                       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;
-                                       console_lock();
-                                       sii902x.fbi->flags |= FBINFO_MISC_USEREVENT;
-                                       fb_set_var(sii902x.fbi, &sii902x.fbi->var);
-                                       sii902x.fbi->flags &= ~FBINFO_MISC_USEREVENT;
-                                       console_unlock();
-                               }
-
-                               console_lock();
-                               fb_blank(sii902x.fbi, FB_BLANK_UNBLANK);
-                               console_unlock();
+               if ((status & HOT_PLUG_STATE) != sii902x->cable_plugin) {
+                       if (status & HOT_PLUG_STATE) {
+                               sprintf(event_string, "EVENT=plugin");
+                               sii902x_cable_connected(sii902x);
+                       } else {
+                               sprintf(event_string, "EVENT=plugout");
+                               sii902x_cable_disconnected(sii902x);
                        }
-               } else {
-                       sii902x.cable_plugin = 0;
-                       sprintf(event_string, "EVENT=plugout");
-                       console_lock();
-                       fb_blank(sii902x.fbi, FB_BLANK_POWERDOWN);
-                       console_unlock();
+                       kobject_uevent_env(&sii902x->pdev->dev.kobj, KOBJ_CHANGE, envp);
+               }
+       }
+
+       /* check rx power */
+       if (((status & RX_SENSE_STATE) >> 3) != sii902x->rx_powerup) {
+               if (sii902x->cable_plugin) {
+                       if (status & RX_SENSE_STATE)
+                               sii902x_rx_powerup(sii902x);
+                       else
+                               sii902x_rx_powerdown(sii902x);
                }
-               kobject_uevent_env(&sii902x.pdev->dev.kobj, KOBJ_CHANGE, envp);
        }
-       i2c_smbus_write_byte_data(sii902x.client, 0x3D, dat);
+
+       /* clear interrupt pending status */
+       i2c_smbus_write_byte_data(sii902x->client, TPI_INTERRUPT_STATUS_REG, status);
 }
 
 static irqreturn_t sii902x_detect_handler(int irq, void *data)
 {
-       if (sii902x.fbi)
-               schedule_delayed_work(&(sii902x.det_work), msecs_to_jiffies(20));
+       struct sii902x_data *sii902x = data;
+       if (sii902x->fbi)
+               schedule_delayed_work(&(sii902x->det_work), msecs_to_jiffies(20));
+
        return IRQ_HANDLED;
 }
 
@@ -287,29 +1002,23 @@ static int sii902x_fb_event(struct notifier_block *nb, unsigned long val, void *
        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;
-       }
+       if (strcmp(event->info->fix.id, g_sii902x->fb_id))
+               return 0;
 
        switch (val) {
        case FB_EVENT_FB_REGISTERED:
-               if (sii902x.fbi != NULL)
+               if (g_sii902x->fbi != NULL)
                        break;
-               sii902x.fbi = fbi;
+               g_sii902x->fbi = fbi;
                break;
        case FB_EVENT_MODE_CHANGE:
-               sii902x_setup(fbi);
+               sii902x_setup(g_sii902x, fbi);
                break;
        case FB_EVENT_BLANK:
                if (*((int *)event->data) == FB_BLANK_UNBLANK)
-                       sii902x_poweron();
+                       sii902x_poweron(g_sii902x);
                else
-                       sii902x_poweroff();
+                       sii902x_poweroff(g_sii902x);
                break;
        }
        return 0;
@@ -319,12 +1028,54 @@ static struct notifier_block nb = {
        .notifier_call = sii902x_fb_event,
 };
 
+static int __devinit sii902x_TPI_init(struct i2c_client *client)
+{
+       struct mxc_lcd_platform_data *plat = client->dev.platform_data;
+       u8 devid = 0;
+       u16 wid = 0;
+
+       if (plat->reset)
+               plat->reset();
+
+       /* sii902x back door register - Set terminations to default */
+       i2c_smbus_write_byte_data(client, 0x82, 0x25);
+       /* sii902x back door register - HW debounce to 64ms (0x14) */
+       i2c_smbus_write_byte_data(client, 0x7c, 0x14);
+
+       /* Set 902x in hardware TPI mode on and jump out of D3 state */
+       if (i2c_smbus_write_byte_data(client, TPI_ENABLE, 0x00) < 0) {
+               dev_err(&client->dev,
+                       "cound not find device\n");
+               return -ENODEV;
+       }
+
+       msleep(100);
+
+       /* read device ID */
+       devid = read_idx_reg(client, INDEXED_PAGE_0, 0x03);
+       wid = devid;
+       wid <<= 8;
+       devid = read_idx_reg(client, INDEXED_PAGE_0, 0x02);
+       wid |= devid;
+       devid = i2c_smbus_read_byte_data(client, TPI_DEVICE_ID);
+
+       if (devid == 0xB0)
+               dev_info(&client->dev, "found device %04X", wid);
+       else {
+               dev_err(&client->dev, "cound not find device\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
 static int __devinit sii902x_probe(struct i2c_client *client,
                const struct i2c_device_id *id)
 {
-       int i, dat, ret;
-       struct fsl_mxc_lcd_platform_data *plat = client->dev.platform_data;
+       int ret = 0;
+       struct mxc_lcd_platform_data *plat = client->dev.platform_data;
        struct fb_info edid_fbi;
+       struct sii902x_data *sii902x;
 
        if (plat->boot_enable &&
                !g_enable_hdmi)
@@ -334,99 +1085,153 @@ static int __devinit sii902x_probe(struct i2c_client *client,
 
        if (g_enable_hdmi == MXC_DISABLE) {
                printk(KERN_WARNING "By setting, SII driver will not be enabled\n");
-               return 0;
+               return -ENODEV;
        }
 
-       sii902x.client = client;
+       if (!i2c_check_functionality(client->adapter,
+                               I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
+               return -ENODEV;
 
-       sii902x.io_reg = regulator_get(&sii902x.client->dev, plat->io_reg);
-       if (!IS_ERR(sii902x.io_reg)) {
-               regulator_set_voltage(sii902x.io_reg, 3300000, 3300000);
-               regulator_enable(sii902x.io_reg);
-       }
-       sii902x.analog_reg = regulator_get(&sii902x.client->dev, plat->analog_reg);
-       if (!IS_ERR(sii902x.analog_reg)) {
-               regulator_set_voltage(sii902x.analog_reg, 1300000, 1300000);
-               regulator_enable(sii902x.analog_reg);
+       sii902x = kzalloc(sizeof(struct sii902x_data), GFP_KERNEL);
+       if (!sii902x) {
+               ret = -ENOMEM;
+               goto alloc_failed;
        }
 
-       if (plat->reset) {
-               sii902x_reset = plat->reset;
-               sii902x_reset();
-       }
+       sii902x->client = client;
+       sii902x->fb_id = plat->fb_id;
+       g_sii902x = sii902x;
 
-       /* 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;
+       sii902x->power_state = TX_POWER_STATE_D2;
+       sii902x->icolor_space = RGB;
+       sii902x->audio_mode = AMODE_SPDIF;
+       sii902x->audio_channels = ACHANNEL_2CH;
+
+       sii902x->pdev = platform_device_register_simple("sii902x", 0, NULL, 0);
+       if (IS_ERR(sii902x->pdev)) {
+               dev_err(&sii902x->client->dev,
+                               "Unable to register Sii902x as a platform device\n");
+               ret = PTR_ERR(sii902x->pdev);
+               goto register_pltdev_failed;
        }
 
-       /* 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 (plat->io_reg) {
+               sii902x->io_reg = regulator_get(&sii902x->client->dev, plat->io_reg);
+               if (!IS_ERR(sii902x->io_reg)) {
+                       regulator_set_voltage(sii902x->io_reg, 3300000, 3300000);
+                       regulator_enable(sii902x->io_reg);
                }
        }
-       if (i == 0) {
-               dev_err(&sii902x.client->dev,
-                       "Sii902x: cound not find device\n");
-               return -ENODEV;
+       if (plat->analog_reg) {
+               sii902x->analog_reg = regulator_get(&sii902x->client->dev, plat->analog_reg);
+               if (!IS_ERR(sii902x->analog_reg)) {
+                       regulator_set_voltage(sii902x->analog_reg, 1300000, 1300000);
+                       regulator_enable(sii902x->analog_reg);
+               }
        }
 
+       /* Claim HDMI pins */
+       if (plat->get_pins)
+               if (!plat->get_pins()) {
+                       ret = -EACCES;
+                       goto get_pins_failed;
+               }
+
+       ret = sii902x_TPI_init(client);
+       if (ret < 0)
+               goto init_failed;
+
        /* try to read edid */
-       if (sii902x_read_edid(&edid_fbi) < 0)
-               dev_warn(&sii902x.client->dev, "Can not read edid\n");
+       ret = sii902x_read_edid(sii902x, &edid_fbi);
+       if (ret < 0)
+               dev_warn(&sii902x->client->dev, "Can not read edid\n");
+
 #if defined(CONFIG_MXC_IPU_V3) && defined(CONFIG_FB_MXC_SYNC_PANEL)
-       else
-               mxcfb_register_mode(IPU_DISP_PORT, edid_fbi.monspecs.modedb,
+       if (ret >= 0) {
+               int di = 0;
+               if (!strcmp(sii902x->fb_id, "DISP3 BG - DI1"))
+                       di = 1;
+               mxcfb_register_mode(di, edid_fbi.monspecs.modedb,
+                               edid_fbi.monspecs.modedb_len, MXC_DISP_DDC_DEV);
+       }
+#endif
+#if defined(CONFIG_FB_MXC_ELCDIF_FB)
+       if (ret >= 0)
+               mxcfb_elcdif_register_mode(edid_fbi.monspecs.modedb,
                                edid_fbi.monspecs.modedb_len, MXC_DISP_DDC_DEV);
 #endif
 
-       if (sii902x.client->irq) {
-               ret = request_irq(sii902x.client->irq, sii902x_detect_handler,
+       if (sii902x->client->irq) {
+               ret = request_irq(sii902x->client->irq, sii902x_detect_handler,
                                IRQF_TRIGGER_FALLING,
-                               "SII902x_det", &sii902x);
+                               "SII902x_det", sii902x);
                if (ret < 0)
-                       dev_warn(&sii902x.client->dev,
-                               "Sii902x: cound not request det irq %d\n",
-                               sii902x.client->irq);
+                       dev_warn(&sii902x->client->dev,
+                               "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);
+                       i2c_smbus_write_byte_data(sii902x->client,
+                                       TPI_INTERRUPT_ENABLE_REG,
+                                       HOT_PLUG_EVENT | RX_SENSE_EVENT);
+                       INIT_DELAYED_WORK(&(sii902x->det_work), det_worker);
+                       /*clear hot plug event status*/
+                       i2c_smbus_write_byte_data(sii902x->client,
+                                       TPI_INTERRUPT_STATUS_REG,
+                                       HOT_PLUG_EVENT | RX_SENSE_EVENT);
                }
-               ret = device_create_file(&sii902x.pdev->dev, &dev_attr_fb_name);
+
+               ret = device_create_file(&sii902x->pdev->dev, &dev_attr_fb_name);
                if (ret < 0)
-                       dev_warn(&sii902x.client->dev,
-                               "Sii902x: cound not create sys node for fb name\n");
-               ret = device_create_file(&sii902x.pdev->dev, &dev_attr_cable_state);
+                       dev_warn(&sii902x->client->dev,
+                               "cound not create sys node for fb name\n");
+               ret = device_create_file(&sii902x->pdev->dev, &dev_attr_cable_state);
                if (ret < 0)
-                       dev_warn(&sii902x.client->dev,
-                               "Sii902x: cound not create sys node for cable state\n");
-               ret = device_create_file(&sii902x.pdev->dev, &dev_attr_edid);
+                       dev_warn(&sii902x->client->dev,
+                               "cound not create sys node for cable state\n");
+               ret = device_create_file(&sii902x->pdev->dev, &dev_attr_edid);
                if (ret < 0)
-                       dev_warn(&sii902x.client->dev,
-                               "Sii902x: cound not create sys node for edid\n");
+                       dev_warn(&sii902x->client->dev,
+                               "cound not create sys node for edid\n");
+
+               dev_set_drvdata(&sii902x->pdev->dev, sii902x);
        }
 
        fb_register_client(&nb);
 
-       return 0;
+       i2c_set_clientdata(client, sii902x);
+
+       return ret;
+
+init_failed:
+get_pins_failed:
+       platform_device_unregister(sii902x->pdev);
+register_pltdev_failed:
+       kfree(sii902x);
+alloc_failed:
+       return ret;
 }
 
 static int __devexit sii902x_remove(struct i2c_client *client)
 {
+       struct sii902x_data *sii902x = i2c_get_clientdata(client);
+       struct mxc_lcd_platform_data *plat = client->dev.platform_data;
+
+       if (sii902x->client->irq)
+               free_irq(sii902x->client->irq, sii902x);
+
        fb_unregister_client(&nb);
-       sii902x_poweroff();
+
+       sii902x_poweroff(sii902x);
+
+       /* Release HDMI pins */
+       if (plat->put_pins)
+               plat->put_pins();
+
+       platform_device_unregister(sii902x->pdev);
+
+       kfree(sii902x);
+
        return 0;
 }
 
@@ -442,27 +1247,6 @@ static int sii902x_resume(struct i2c_client *client)
        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 },
        {},
@@ -482,27 +1266,12 @@ static struct i2c_driver sii902x_i2c_driver = {
 
 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)