it66121: update and remove debug log
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / hdmi / chips / cat66121 / cat66121_hdmi.c
index c3a6834bdeeca819f86bf8f1383c4bfa67e8bdd5..3756b9a24bd0925d112fda384ba0af85aab6afdc 100755 (executable)
-#include <linux/kernel.h>\r
-#include <linux/delay.h>\r
-#include <linux/module.h>\r
-#include <linux/platform_device.h>\r
-#include <linux/interrupt.h>\r
-#include <mach/gpio.h>\r
-#include <mach/iomux.h>\r
-#include <linux/i2c.h>\r
-#include "cat66121_hdmi.h"\r
-\r
-struct cat66121_hdmi_pdata *cat66121_hdmi = NULL;\r
-struct hdmi *hdmi=NULL;\r
-\r
-extern struct rk_lcdc_device_driver * rk_get_lcdc_drv(char *name);\r
-extern void hdmi_register_display_sysfs(struct hdmi *hdmi, struct device *parent);\r
-extern void hdmi_unregister_display_sysfs(struct hdmi *hdmi);\r
-\r
-int cat66121_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void),\r
-                                        void (*hdcp_irq_cb)(int status),\r
-                                        int (*hdcp_power_on_cb)(void),\r
-                                        void (*hdcp_power_off_cb)(void))\r
-{\r
-       hdmi->hdcp_cb = hdcp_cb;\r
-       hdmi->hdcp_irq_cb = hdcp_irq_cb;\r
-       hdmi->hdcp_power_on_cb = hdcp_power_on_cb;\r
-       hdmi->hdcp_power_off_cb = hdcp_power_off_cb;\r
-       \r
-       return HDMI_ERROR_SUCESS;\r
-}\r
-\r
-#ifdef CONFIG_HAS_EARLYSUSPEND\r
-static void hdmi_early_suspend(struct early_suspend *h)\r
-{\r
-       hdmi_dbg(hdmi->dev, "hdmi enter early suspend pwr %d state %d\n", hdmi->pwr_mode, hdmi->state);\r
-       flush_delayed_work(&hdmi->delay_work);  \r
-       mutex_lock(&hdmi->enable_mutex);\r
-       hdmi->suspend = 1;\r
-       if(!hdmi->enable) {\r
-               mutex_unlock(&hdmi->enable_mutex);\r
-               return;\r
-       }\r
-       \r
-       #ifdef HDMI_USE_IRQ\r
-       if(hdmi->irq)\r
-               disable_irq(hdmi->irq);\r
-       #endif\r
-       \r
-       mutex_unlock(&hdmi->enable_mutex);\r
-       hdmi->command = HDMI_CONFIG_ENABLE;\r
-       init_completion(&hdmi->complete);\r
-       hdmi->wait = 1;\r
-       queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0);\r
-       wait_for_completion_interruptible_timeout(&hdmi->complete,\r
-                                                       msecs_to_jiffies(5000));\r
-       flush_delayed_work(&hdmi->delay_work);\r
-       return;\r
-}\r
-\r
-static void hdmi_early_resume(struct early_suspend *h)\r
-{\r
-       hdmi_dbg(hdmi->dev, "hdmi exit early resume\n");\r
-       mutex_lock(&hdmi->enable_mutex);\r
-       \r
-       hdmi->suspend = 0;\r
-       #ifdef HDMI_USE_IRQ\r
-       if(hdmi->enable && hdmi->irq) {\r
-               enable_irq(hdmi->irq);\r
-       }\r
-       #else\r
-       queue_delayed_work(cat66121_hdmi->workqueue, &cat66121_hdmi->delay_work, 100);\r
-       #endif\r
-       queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10));   \r
-       mutex_unlock(&hdmi->enable_mutex);\r
-       return;\r
-}\r
-#endif\r
-\r
-static void cat66121_irq_work_func(struct work_struct *work)\r
-{\r
-       if(hdmi->suspend == 0) {\r
-               if(hdmi->enable == 1) {\r
-                       cat66121_hdmi_interrupt();\r
-                       if(hdmi->hdcp_irq_cb)\r
-                               hdmi->hdcp_irq_cb(0);\r
-               }\r
-               #ifndef HDMI_USE_IRQ\r
-               queue_delayed_work(cat66121_hdmi->workqueue, &cat66121_hdmi->delay_work, 50);\r
-               #endif\r
-       }\r
-}\r
-\r
-#ifdef HDMI_USE_IRQ\r
-static irqreturn_t cat66121_irq(int irq, void *dev_id)\r
-{\r
-       printk(KERN_INFO "cat66121 irq triggered.\n");\r
-       schedule_work(&cat66121_hdmi->irq_work);\r
-    return IRQ_HANDLED;\r
-}\r
-#endif\r
-static int rk610_read_p0_reg(struct i2c_client *client, char reg, char *val)\r
-{\r
-       return i2c_master_reg8_recv(client, reg, val, 1, 100*1000) > 0? 0: -EINVAL;\r
-}\r
-\r
-static int rk610_write_p0_reg(struct i2c_client *client, char reg, char *val)\r
-{\r
-       return i2c_master_reg8_send(client, reg, val, 1, 100*1000) > 0? 0: -EINVAL;\r
-}\r
-static ssize_t rk610_show_reg_attrs(struct device *dev,\r
-                                             struct device_attribute *attr,\r
-                                             char *buf)\r
-{\r
-\r
-       int i,size=0;\r
-       char val;\r
-       struct i2c_client *client=cat66121_hdmi->client;\r
-\r
-       for(i=0;i<256;i++)\r
-       {\r
-               rk610_read_p0_reg(client, i,  &val);\r
-               if(i%16==0)\r
-                       size += sprintf(buf+size,"\n>>>rk610_hdmi %x:",i);\r
-               size += sprintf(buf+size," %2x",val);\r
-       }\r
-\r
-       return size;\r
-}\r
-static ssize_t rk610_store_reg_attrs(struct device *dev,\r
-                                               struct device_attribute *attr,\r
-                                               const char *buf, size_t size)\r
-{\r
-       struct i2c_client *client=NULL;\r
-       static char val=0,reg=0;\r
-       client = cat66121_hdmi->client;\r
-       printk("/**********rk610 reg config******/");\r
-\r
-       sscanf(buf, "%x%x", &val,&reg);\r
-       printk("reg=%x val=%x\n",reg,val);\r
-       rk610_write_p0_reg(client, reg,  &val);\r
-       printk("val=%x\n",val);\r
-       return size;\r
-}\r
-\r
-static struct device_attribute rk610_attrs[] = {\r
-       __ATTR(reg_ctl, 0777,rk610_show_reg_attrs,rk610_store_reg_attrs),\r
-};\r
-static int cat66121_hdmi_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id)\r
-{\r
-    int rc = 0;\r
-       \r
-       cat66121_hdmi = kzalloc(sizeof(struct cat66121_hdmi_pdata), GFP_KERNEL);\r
-       if(!cat66121_hdmi)\r
-       {\r
-        dev_err(&client->dev, "no memory for state\n");\r
-       return -ENOMEM;\r
-    }\r
-       cat66121_hdmi->client = client;\r
-       i2c_set_clientdata(client, cat66121_hdmi);\r
-       \r
-       hdmi = kmalloc(sizeof(struct hdmi), GFP_KERNEL);\r
-       if(!hdmi)\r
-       {\r
-       dev_err(&client->dev, "cat66121 hdmi kmalloc fail!");\r
-       goto err_kzalloc_hdmi;\r
-       }\r
-       memset(hdmi, 0, sizeof(struct hdmi));\r
-       hdmi->dev = &client->dev;\r
-       \r
-       if(HDMI_SOURCE_DEFAULT == HDMI_SOURCE_LCDC0)\r
-               hdmi->lcdc = rk_get_lcdc_drv("lcdc0");\r
-       else\r
-               hdmi->lcdc = rk_get_lcdc_drv("lcdc1");\r
-       if(hdmi->lcdc == NULL)\r
-       {\r
-               dev_err(hdmi->dev, "can not connect to video source lcdc\n");\r
-               rc = -ENXIO;\r
-               goto err_request_lcdc;\r
-       }\r
-       hdmi->xscale = 100;\r
-       hdmi->yscale = 100;\r
-       hdmi->insert = cat66121_hdmi_sys_insert;\r
-       hdmi->remove = cat66121_hdmi_sys_remove;\r
-       hdmi->control_output = cat66121_hdmi_sys_enalbe_output;\r
-       hdmi->config_video = cat66121_hdmi_sys_config_video;\r
-       hdmi->config_audio = cat66121_hdmi_sys_config_audio;\r
-       hdmi->detect_hotplug = cat66121_hdmi_sys_detect_hpd;\r
-       hdmi->read_edid = cat66121_hdmi_sys_read_edid;\r
-       hdmi_sys_init();\r
-       \r
-       hdmi->workqueue = create_singlethread_workqueue("hdmi");\r
-       INIT_DELAYED_WORK(&(hdmi->delay_work), hdmi_work);\r
-       \r
-       #ifdef CONFIG_HAS_EARLYSUSPEND\r
-       hdmi->early_suspend.suspend = hdmi_early_suspend;\r
-       hdmi->early_suspend.resume = hdmi_early_resume;\r
-       hdmi->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 10;\r
-       register_early_suspend(&hdmi->early_suspend);\r
-       #endif\r
-       \r
-       hdmi_register_display_sysfs(hdmi, NULL);\r
-       #ifdef CONFIG_SWITCH\r
-       hdmi->switch_hdmi.name="hdmi";\r
-       switch_dev_register(&(hdmi->switch_hdmi));\r
-       #endif\r
-               \r
-       spin_lock_init(&hdmi->irq_lock);\r
-       mutex_init(&hdmi->enable_mutex);\r
-       \r
-       cat66121_hdmi_sys_init();\r
-       rc = gpio_request(client->irq, "cat66121 rst");\r
-       if (rc != 0) {\r
-               gpio_free(client->irq);\r
-               printk("goodix power error\n");\r
-               return -EIO;\r
-       }\r
-       gpio_direction_output(client->irq, GPIO_HIGH);\r
-       gpio_set_value(client->irq, GPIO_HIGH);\r
-       msleep(10);\r
-       gpio_set_value(client->irq, GPIO_LOW);\r
-       msleep(200);\r
-       gpio_set_value(client->irq, GPIO_HIGH);\r
-#ifdef HDMI_USE_IRQ\r
-       if(client->irq != INVALID_GPIO) {\r
-               INIT_WORK(&cat66121_hdmi->irq_work, cat66121_irq_work_func);\r
-               schedule_work(&cat66121_hdmi->irq_work);\r
-               if((rc = gpio_request(client->irq, "hdmi gpio")) < 0)\r
-           {\r
-               dev_err(&client->dev, "fail to request gpio %d\n", client->irq);\r
-               goto err_request_lcdc;\r
-           }\r
-           hdmi->irq = gpio_to_irq(client->irq);\r
-               cat66121_hdmi->gpio = client->irq;\r
-           gpio_pull_updown(client->irq, GPIOPullUp);\r
-           gpio_direction_input(client->irq);\r
-           if((rc = request_irq(hdmi->irq, cat66121_irq, IRQF_TRIGGER_RISING, NULL, hdmi)) < 0)\r
-           {\r
-               dev_err(&client->dev, "fail to request hdmi irq\n");\r
-               goto err_request_irq;\r
-           }\r
-       }\r
-       else\r
-#else\r
-       {\r
-               cat66121_hdmi->workqueue = create_singlethread_workqueue("cat66121 irq");\r
-               INIT_DELAYED_WORK(&(cat66121_hdmi->delay_work), cat66121_irq_work_func);\r
-               cat66121_irq_work_func(NULL);\r
-       }\r
-#endif\r
-\r
-       device_create_file(&(client->dev), &rk610_attrs[0]);\r
-       dev_info(&client->dev, "cat66121 hdmi i2c probe ok\n");\r
-       \r
-    return 0;\r
-       \r
-err_request_irq:\r
-       gpio_free(client->irq);\r
-err_request_lcdc:\r
-       kfree(hdmi);\r
-       hdmi = NULL;\r
-err_kzalloc_hdmi:\r
-       kfree(cat66121_hdmi);\r
-       cat66121_hdmi = NULL;\r
-       dev_err(&client->dev, "cat66121 hdmi probe error\n");\r
-       return rc;\r
-\r
-}\r
-\r
-static int __devexit cat66121_hdmi_i2c_remove(struct i2c_client *client)\r
-{      \r
-       hdmi_dbg(hdmi->dev, "%s\n", __func__);\r
-       if(hdmi) {\r
-               mutex_lock(&hdmi->enable_mutex);\r
-               if(!hdmi->suspend && hdmi->enable && hdmi->irq)\r
-                       disable_irq(hdmi->irq);\r
-               mutex_unlock(&hdmi->enable_mutex);\r
-               if(hdmi->irq)\r
-                       free_irq(hdmi->irq, NULL);\r
-               flush_workqueue(hdmi->workqueue);\r
-               destroy_workqueue(hdmi->workqueue);\r
-               #ifdef CONFIG_SWITCH\r
-               switch_dev_unregister(&(hdmi->switch_hdmi));\r
-               #endif\r
-               hdmi_unregister_display_sysfs(hdmi);\r
-               #ifdef CONFIG_HAS_EARLYSUSPEND\r
-               unregister_early_suspend(&hdmi->early_suspend);\r
-               #endif\r
-               fb_destroy_modelist(&hdmi->edid.modelist);\r
-               if(hdmi->edid.audio)\r
-                       kfree(hdmi->edid.audio);\r
-               if(hdmi->edid.specs)\r
-               {\r
-                       if(hdmi->edid.specs->modedb)\r
-                               kfree(hdmi->edid.specs->modedb);\r
-                       kfree(hdmi->edid.specs);\r
-               }\r
-               kfree(hdmi);\r
-               hdmi = NULL;\r
-       }\r
-    return 0;\r
-}\r
-\r
-static void cat66121_hdmi_i2c_shutdown(struct i2c_client *client)\r
-{\r
-       if(hdmi) {\r
-               #ifdef CONFIG_HAS_EARLYSUSPEND\r
-               unregister_early_suspend(&hdmi->early_suspend);\r
-               #endif\r
-       }\r
-       printk(KERN_INFO "cat66121 hdmi shut down.\n");\r
-}\r
-\r
-static const struct i2c_device_id cat66121_hdmi_id[] = {\r
-       { "cat66121_hdmi", 0 },\r
-       { }\r
-};\r
-\r
-static struct i2c_driver cat66121_hdmi_i2c_driver = {\r
-    .driver = {\r
-        .name  = "cat66121_hdmi",\r
-        .owner = THIS_MODULE,\r
-    },\r
-    .probe      = cat66121_hdmi_i2c_probe,\r
-    .remove     = cat66121_hdmi_i2c_remove,\r
-    .shutdown  = cat66121_hdmi_i2c_shutdown,\r
-    .id_table  = cat66121_hdmi_id,\r
-};\r
-\r
-static int __init cat66121_hdmi_init(void)\r
-{\r
-    return i2c_add_driver(&cat66121_hdmi_i2c_driver);\r
-}\r
-\r
-static void __exit cat66121_hdmi_exit(void)\r
-{\r
-    i2c_del_driver(&cat66121_hdmi_i2c_driver);\r
-}\r
-\r
-module_init(cat66121_hdmi_init);\r
-//fs_initcall(cat66121_init);\r
-module_exit(cat66121_hdmi_exit);\r
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <mach/gpio.h>
+#include <mach/iomux.h>
+#include <linux/i2c.h>
+#include "cat66121_hdmi.h"
+
+struct cat66121_hdmi_pdata *cat66121_hdmi = NULL;
+struct hdmi *hdmi=NULL;
+
+extern struct rk_lcdc_device_driver * rk_get_lcdc_drv(char *name);
+extern void hdmi_register_display_sysfs(struct hdmi *hdmi, struct device *parent);
+extern void hdmi_unregister_display_sysfs(struct hdmi *hdmi);
+
+int cat66121_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void),
+                                        void (*hdcp_irq_cb)(int status),
+                                        int (*hdcp_power_on_cb)(void),
+                                        void (*hdcp_power_off_cb)(void))
+{
+       hdmi->hdcp_cb = hdcp_cb;
+       hdmi->hdcp_irq_cb = hdcp_irq_cb;
+       hdmi->hdcp_power_on_cb = hdcp_power_on_cb;
+       hdmi->hdcp_power_off_cb = hdcp_power_off_cb;
+       
+       return HDMI_ERROR_SUCESS;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void hdmi_early_suspend(struct early_suspend *h)
+{
+       hdmi_dbg(hdmi->dev, "hdmi enter early suspend pwr %d state %d\n", hdmi->pwr_mode, hdmi->state);
+       flush_delayed_work(&hdmi->delay_work);  
+       mutex_lock(&hdmi->enable_mutex);
+       hdmi->suspend = 1;
+       if(!hdmi->enable) {
+               mutex_unlock(&hdmi->enable_mutex);
+               return;
+       }
+       
+       #ifdef HDMI_USE_IRQ
+       if(hdmi->irq)
+               disable_irq(hdmi->irq);
+       #endif
+       
+       mutex_unlock(&hdmi->enable_mutex);
+       hdmi->command = HDMI_CONFIG_ENABLE;
+       init_completion(&hdmi->complete);
+       hdmi->wait = 1;
+       queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0);
+       wait_for_completion_interruptible_timeout(&hdmi->complete,
+                                                       msecs_to_jiffies(5000));
+       flush_delayed_work(&hdmi->delay_work);
+       return;
+}
+
+static void hdmi_early_resume(struct early_suspend *h)
+{
+       hdmi_dbg(hdmi->dev, "hdmi exit early resume\n");
+       mutex_lock(&hdmi->enable_mutex);
+       
+       hdmi->suspend = 0;
+       #ifdef HDMI_USE_IRQ
+       if(hdmi->enable && hdmi->irq) {
+               enable_irq(hdmi->irq);
+       }
+       #else
+       queue_delayed_work(cat66121_hdmi->workqueue, &cat66121_hdmi->delay_work, 100);
+       #endif
+       queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10));   
+       mutex_unlock(&hdmi->enable_mutex);
+       return;
+}
+#endif
+
+static void cat66121_irq_work_func(struct work_struct *work)
+{
+       if(hdmi->suspend == 0) {
+               if(hdmi->enable == 1) {
+                       cat66121_hdmi_interrupt();
+                       if(hdmi->hdcp_irq_cb)
+                               hdmi->hdcp_irq_cb(0);
+               }
+               #ifndef HDMI_USE_IRQ
+               queue_delayed_work(cat66121_hdmi->workqueue, &cat66121_hdmi->delay_work, 50);
+               #endif
+       }
+}
+
+#ifdef HDMI_USE_IRQ
+static irqreturn_t cat66121_irq(int irq, void *dev_id)
+{
+       printk(KERN_INFO "cat66121 irq triggered.\n");
+       schedule_work(&cat66121_hdmi->irq_work);
+    return IRQ_HANDLED;
+}
+#endif
+#ifdef HDMI_DEBUG
+static int hdmi_read_p0_reg(struct i2c_client *client, char reg, char *val)
+{
+       return i2c_master_reg8_recv(client, reg, val, 1, 100*1000) > 0? 0: -EINVAL;
+}
+
+static int hdmi_write_p0_reg(struct i2c_client *client, char reg, char *val)
+{
+       return i2c_master_reg8_send(client, reg, val, 1, 100*1000) > 0? 0: -EINVAL;
+}
+static ssize_t hdmi_show_reg_attrs(struct device *dev,
+                                             struct device_attribute *attr,
+                                             char *buf)
+{
+
+       int i,size=0;
+       char val;
+       struct i2c_client *client=cat66121_hdmi->client;
+
+       for(i=0;i<256;i++)
+       {
+               hdmi_read_p0_reg(client, i,  &val);
+               if(i%16==0)
+                       size += sprintf(buf+size,"\n>>>hdmi_hdmi %x:",i);
+               size += sprintf(buf+size," %2x",val);
+       }
+
+       return size;
+}
+static ssize_t hdmi_store_reg_attrs(struct device *dev,
+                                               struct device_attribute *attr,
+                                               const char *buf, size_t size)
+{
+       struct i2c_client *client=NULL;
+       static char val=0,reg=0;
+       client = cat66121_hdmi->client;
+       printk("/**********hdmi reg config******/");
+
+       sscanf(buf, "%x%x", &val,&reg);
+       hdmi_write_p0_reg(client, reg,  &val);
+       return size;
+}
+
+static struct device_attribute hdmi_attrs[] = {
+       __ATTR(reg_ctl, 0777,hdmi_show_reg_attrs,hdmi_store_reg_attrs),
+};
+#endif
+static int cat66121_hdmi_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id)
+{
+    int rc = 0;
+       struct rk_hdmi_platform_data *pdata = client->dev.platform_data;
+       
+       cat66121_hdmi = kzalloc(sizeof(struct cat66121_hdmi_pdata), GFP_KERNEL);
+       if(!cat66121_hdmi)
+       {
+        dev_err(&client->dev, "no memory for state\n");
+       return -ENOMEM;
+    }
+       cat66121_hdmi->client = client;
+       i2c_set_clientdata(client, cat66121_hdmi);
+       
+       hdmi = kmalloc(sizeof(struct hdmi), GFP_KERNEL);
+       if(!hdmi)
+       {
+       dev_err(&client->dev, "cat66121 hdmi kmalloc fail!");
+       goto err_kzalloc_hdmi;
+       }
+       memset(hdmi, 0, sizeof(struct hdmi));
+       hdmi->dev = &client->dev;
+       
+       if(HDMI_SOURCE_DEFAULT == HDMI_SOURCE_LCDC0)
+               hdmi->lcdc = rk_get_lcdc_drv("lcdc0");
+       else
+               hdmi->lcdc = rk_get_lcdc_drv("lcdc1");
+       if(hdmi->lcdc == NULL)
+       {
+               dev_err(hdmi->dev, "can not connect to video source lcdc\n");
+               rc = -ENXIO;
+               goto err_request_lcdc;
+       }
+       hdmi->xscale = 100;
+       hdmi->yscale = 100;
+       hdmi->insert = cat66121_hdmi_sys_insert;
+       hdmi->remove = cat66121_hdmi_sys_remove;
+       hdmi->control_output = cat66121_hdmi_sys_enalbe_output;
+       hdmi->config_video = cat66121_hdmi_sys_config_video;
+       hdmi->config_audio = cat66121_hdmi_sys_config_audio;
+       hdmi->detect_hotplug = cat66121_hdmi_sys_detect_hpd;
+       hdmi->read_edid = cat66121_hdmi_sys_read_edid;
+       hdmi_sys_init();
+       
+       hdmi->workqueue = create_singlethread_workqueue("hdmi");
+       INIT_DELAYED_WORK(&(hdmi->delay_work), hdmi_work);
+       
+       #ifdef CONFIG_HAS_EARLYSUSPEND
+       hdmi->early_suspend.suspend = hdmi_early_suspend;
+       hdmi->early_suspend.resume = hdmi_early_resume;
+       hdmi->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 10;
+       register_early_suspend(&hdmi->early_suspend);
+       #endif
+       
+       hdmi_register_display_sysfs(hdmi, NULL);
+       #ifdef CONFIG_SWITCH
+       hdmi->switch_hdmi.name="hdmi";
+       switch_dev_register(&(hdmi->switch_hdmi));
+       #endif
+               
+       spin_lock_init(&hdmi->irq_lock);
+       mutex_init(&hdmi->enable_mutex);
+       
+       if(pdata->io_init){
+               if(pdata->io_init()<0){
+                       dev_err(&client->dev, "fail to rst chip\n");
+                       goto err_request_lcdc;
+               }
+       }
+       cat66121_hdmi_sys_init();
+#ifdef HDMI_USE_IRQ
+       if(client->irq != INVALID_GPIO) {
+               INIT_WORK(&cat66121_hdmi->irq_work, cat66121_irq_work_func);
+               schedule_work(&cat66121_hdmi->irq_work);
+               if((rc = gpio_request(client->irq, "hdmi gpio")) < 0)
+           {
+               dev_err(&client->dev, "fail to request gpio %d\n", client->irq);
+               goto err_request_lcdc;
+           }
+           hdmi->irq = gpio_to_irq(client->irq);
+               cat66121_hdmi->gpio = client->irq;
+           gpio_pull_updown(client->irq, GPIOPullUp);
+           gpio_direction_input(client->irq);
+           if((rc = request_irq(hdmi->irq, cat66121_irq, IRQF_TRIGGER_RISING, NULL, hdmi)) < 0)
+           {
+               dev_err(&client->dev, "fail to request hdmi irq\n");
+               goto err_request_irq;
+           }
+       }
+       else
+#else
+       {
+               cat66121_hdmi->workqueue = create_singlethread_workqueue("cat66121 irq");
+               INIT_DELAYED_WORK(&(cat66121_hdmi->delay_work), cat66121_irq_work_func);
+               cat66121_irq_work_func(NULL);
+       }
+#endif
+
+#ifdef HDMI_DEBUG
+       device_create_file(&(client->dev), &hdmi_attrs[0]);
+#endif
+       dev_info(&client->dev, "cat66121 hdmi i2c probe ok\n");
+       
+    return 0;
+       
+err_request_irq:
+       gpio_free(client->irq);
+err_request_lcdc:
+       kfree(hdmi);
+       hdmi = NULL;
+err_kzalloc_hdmi:
+       kfree(cat66121_hdmi);
+       cat66121_hdmi = NULL;
+       dev_err(&client->dev, "cat66121 hdmi probe error\n");
+       return rc;
+
+}
+
+static int __devexit cat66121_hdmi_i2c_remove(struct i2c_client *client)
+{      
+       hdmi_dbg(hdmi->dev, "%s\n", __func__);
+       if(hdmi) {
+               mutex_lock(&hdmi->enable_mutex);
+               if(!hdmi->suspend && hdmi->enable && hdmi->irq)
+                       disable_irq(hdmi->irq);
+               mutex_unlock(&hdmi->enable_mutex);
+               if(hdmi->irq)
+                       free_irq(hdmi->irq, NULL);
+               flush_workqueue(hdmi->workqueue);
+               destroy_workqueue(hdmi->workqueue);
+               #ifdef CONFIG_SWITCH
+               switch_dev_unregister(&(hdmi->switch_hdmi));
+               #endif
+               hdmi_unregister_display_sysfs(hdmi);
+               #ifdef CONFIG_HAS_EARLYSUSPEND
+               unregister_early_suspend(&hdmi->early_suspend);
+               #endif
+               fb_destroy_modelist(&hdmi->edid.modelist);
+               if(hdmi->edid.audio)
+                       kfree(hdmi->edid.audio);
+               if(hdmi->edid.specs)
+               {
+                       if(hdmi->edid.specs->modedb)
+                               kfree(hdmi->edid.specs->modedb);
+                       kfree(hdmi->edid.specs);
+               }
+               kfree(hdmi);
+               hdmi = NULL;
+       }
+    return 0;
+}
+
+static void cat66121_hdmi_i2c_shutdown(struct i2c_client *client)
+{
+       if(hdmi) {
+               #ifdef CONFIG_HAS_EARLYSUSPEND
+               unregister_early_suspend(&hdmi->early_suspend);
+               #endif
+       }
+       printk(KERN_INFO "cat66121 hdmi shut down.\n");
+}
+
+static const struct i2c_device_id cat66121_hdmi_id[] = {
+       { "cat66121_hdmi", 0 },
+       { }
+};
+
+static struct i2c_driver cat66121_hdmi_i2c_driver = {
+    .driver = {
+        .name  = "cat66121_hdmi",
+        .owner = THIS_MODULE,
+    },
+    .probe      = cat66121_hdmi_i2c_probe,
+    .remove     = cat66121_hdmi_i2c_remove,
+    .shutdown  = cat66121_hdmi_i2c_shutdown,
+    .id_table  = cat66121_hdmi_id,
+};
+
+static int __init cat66121_hdmi_init(void)
+{
+    return i2c_add_driver(&cat66121_hdmi_i2c_driver);
+}
+
+static void __exit cat66121_hdmi_exit(void)
+{
+    i2c_del_driver(&cat66121_hdmi_i2c_driver);
+}
+
+module_init(cat66121_hdmi_init);
+//fs_initcall(cat66121_init);
+module_exit(cat66121_hdmi_exit);