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