summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorJason Chen <b02280@freescale.com>2011-07-13 15:24:19 +0800
committerJason Chen <b02280@freescale.com>2011-07-18 12:18:32 +0800
commit59fc940821ec69b3be9446ef72ab625fff893436 (patch)
treee4e4054313c3a83bcb8c837301d13feedcab0366 /drivers
parentfe1ddea276f2c86d856754a830bc524a163b2a35 (diff)
ENGR00152359-3 sii902x hdmi: add AVI and AIF support for common changes
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/video/mxc/Makefile2
-rw-r--r--drivers/video/mxc/mxc_ddc.c347
-rw-r--r--drivers/video/mxc/mxc_edid.c657
-rw-r--r--drivers/video/mxc/mxcfb_sii902x.c1181
4 files changed, 1658 insertions, 529 deletions
diff --git a/drivers/video/mxc/Makefile b/drivers/video/mxc/Makefile
index 501107974b60..89f19269f233 100644
--- a/drivers/video/mxc/Makefile
+++ b/drivers/video/mxc/Makefile
@@ -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
index 000000000000..60b74cd6e38b
--- /dev/null
+++ b/drivers/video/mxc/mxc_ddc.c
@@ -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");
diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c
index 50355b550bba..dae7b4f6c9cd 100644
--- a/drivers/video/mxc/mxc_edid.c
+++ b/drivers/video/mxc/mxc_edid.c
@@ -28,19 +28,9 @@
*/
#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
@@ -49,56 +39,277 @@
#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");
diff --git a/drivers/video/mxc/mxcfb_sii902x.c b/drivers/video/mxc/mxcfb_sii902x.c
index 08d8c2ce05d5..43613bbd9e26 100644
--- a/drivers/video/mxc/mxcfb_sii902x.c
+++ b/drivers/video/mxc/mxcfb_sii902x.c
@@ -43,15 +43,145 @@
#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)