rk30 hdmi:
authorZheng Yang <zhengyang@rock-chips.com>
Wed, 25 Apr 2012 10:20:43 +0000 (18:20 +0800)
committerZheng Yang <zhengyang@rock-chips.com>
Wed, 25 Apr 2012 10:20:43 +0000 (18:20 +0800)
1. When hdmi was disabled in early_suspend, sometimes it will be enabld agian by user control.
   So the hdmi irq will be enabled twice in early_resume, this action will caurse kernel crash.
   To fix this bug, we need to distinguish suspend mode and user control mode, and add mutex to
   protect enable/disable hdmi irq.
2. Use spin_lock_irqsave/spin_unlock_irqrestore replace spin_lock/spin_unlock.

drivers/video/rockchip/hdmi/rk30_hdmi.c
drivers/video/rockchip/hdmi/rk30_hdmi.h
drivers/video/rockchip/hdmi/rk30_hdmi_hw.c
drivers/video/rockchip/hdmi/rk30_hdmi_sysfs.c
drivers/video/rockchip/hdmi/rk30_hdmi_task.c

index 6fc842c9be81ad89456f9683577b4c9e0866dd9d..c0038bf0274cfff364e312e6c0ed83050389b00d 100755 (executable)
@@ -30,12 +30,16 @@ extern void hdmi_register_display_sysfs(struct hdmi *hdmi, struct device *parent
 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
        disable_irq(hdmi->irq);\r
-       hdmi->enable = 0;\r
+       mutex_unlock(&hdmi->enable_mutex);\r
        hdmi->command = HDMI_CONFIG_ENABLE;\r
-       /* wait for hdmi configuration finish */\r
-       while(hdmi->wait)\r
-               msleep(10);\r
        init_completion(&hdmi->complete);\r
        hdmi->wait = 1;\r
        queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0);\r
@@ -48,8 +52,12 @@ static void hdmi_early_suspend(struct early_suspend *h)
 static void hdmi_early_resume(struct early_suspend *h)\r
 {\r
        hdmi_dbg(hdmi->dev, "hdmi exit early resume\n");\r
-       hdmi->enable = 1;\r
-       enable_irq(hdmi->irq);\r
+       mutex_lock(&hdmi->enable_mutex);\r
+       hdmi->suspend = 0;\r
+       if(hdmi->enable) {\r
+               enable_irq(hdmi->irq);\r
+       }\r
+       mutex_unlock(&hdmi->enable_mutex);\r
        return;\r
 }\r
 #endif\r
@@ -151,7 +159,8 @@ static int __devinit rk30_hdmi_probe (struct platform_device *pdev)
        hdmi_register_display_sysfs(hdmi, hdmi->dev);\r
        \r
        spin_lock_init(&hdmi->irq_lock);\r
-\r
+       mutex_init(&hdmi->enable_mutex);\r
+       \r
        /* get the IRQ */\r
        hdmi->irq = platform_get_irq(pdev, 0);\r
        if(hdmi->irq <= 0) {\r
index d39c4b0db8f23a1cf9429aab59e83a8d22226bfb..caf8b495f0fe16845510f3b1c24901ce81c037dd 100755 (executable)
@@ -3,6 +3,7 @@
 
 #include <linux/fb.h>
 #include <linux/spinlock.h>
+#include <linux/mutex.h>
 #include <linux/device.h>
 #include <linux/workqueue.h>
 #include <linux/display-sys.h>
@@ -48,10 +49,13 @@ struct hdmi {
        struct delayed_work delay_work;
        
        spinlock_t      irq_lock;
+       struct mutex enable_mutex;
        
        int wait;
        struct completion       complete;
+       
 #ifdef CONFIG_HAS_EARLYSUSPEND
+       int suspend;
        struct early_suspend    early_suspend;
 #endif
        
index 6ce3f62f82d78dd45ee0443b7239abb30e4d9ee4..f3c2a3482af07a969efb2b1de1506e2a904865ff 100755 (executable)
@@ -63,11 +63,12 @@ int rk30_hdmi_read_edid(int block, unsigned char *buff)
 {
        int value, ret = -1, ddc_bus_freq = 0;
        char interrupt = 0, trytime = 2;
+       unsigned long flags;
        
        hdmi_dbg(hdmi->dev, "[%s] block %d\n", __FUNCTION__, block);
-       spin_lock(&hdmi->irq_lock);
+       spin_lock_irqsave(&hdmi->irq_lock, flags);
        edid_result = 0;
-       spin_unlock(&hdmi->irq_lock);
+       spin_unlock_irqrestore(&hdmi->irq_lock, flags);
        //Before Phy parameter was set, DDC_CLK is equal to PLLA freq which is 24MHz.
        //Set DDC I2C CLK which devided from DDC_CLK to 100KHz.
        ddc_bus_freq = (24000000/HDMI_EDID_DDC_CLK)/4;
@@ -85,10 +86,9 @@ int rk30_hdmi_read_edid(int block, unsigned char *buff)
                value = 100;
                while(value--)
                {
-                       spin_lock(&hdmi->irq_lock);
+                       spin_lock_irqsave(&hdmi->irq_lock, flags);
                        interrupt = edid_result;
-                       spin_unlock(&hdmi->irq_lock);
-//                     hdmi_dbg(hdmi->dev, "[%s] interrupt %02x value %d\n", __FUNCTION__, interrupt, value);
+                       spin_unlock_irqrestore(&hdmi->irq_lock, flags);
                        if(interrupt & (m_INT_EDID_ERR | m_INT_EDID_READY))
                                break;
                        msleep(10);
@@ -452,13 +452,12 @@ irqreturn_t hdmi_irq(int irq, void *priv)
 {              
        char interrupt1 = 0, interrupt2 = 0, interrupt3 = 0, interrupt4 = 0;
        
-       spin_lock(&hdmi->irq_lock);
        if(hdmi->pwr_mode == PWR_SAVE_MODE_A)
        {
-               hdmi_dbg(hdmi->dev, "hdmi irq wake up\n");
                HDMIWrReg(SYS_CTRL, 0x20);
                hdmi->pwr_mode = PWR_SAVE_MODE_B;
                
+               hdmi_dbg(hdmi->dev, "hdmi irq wake up\n");
                // HDMI was inserted when system is sleeping, irq was triggered only once
                // when wake up. So we need to check hotplug status.
                if(HDMIRdReg(HPD_MENS_STA) & (m_HOTPLUG_STATUS | m_MSEN_STATUS)) {                      
@@ -486,15 +485,17 @@ irqreturn_t hdmi_irq(int irq, void *priv)
                        interrupt1 &= ~(m_INT_HOTPLUG | m_INT_MSENS);
                        queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10));   
                }
-               else if(interrupt1 & (m_INT_EDID_READY | m_INT_EDID_ERR))
+               else if(interrupt1 & (m_INT_EDID_READY | m_INT_EDID_ERR)) {
+                       spin_lock(&hdmi->irq_lock);
                        edid_result = interrupt1;
+                       spin_unlock(&hdmi->irq_lock);
+               }
                else if(hdmi->state == HDMI_SLEEP) {
                        hdmi_dbg(hdmi->dev, "hdmi return to sleep mode\n");
                        HDMIWrReg(SYS_CTRL, 0x10);
                        hdmi->pwr_mode = PWR_SAVE_MODE_A;
                }
        }
-       spin_unlock(&hdmi->irq_lock);
        return IRQ_HANDLED;
 }
 
index cd206320086aa915da3a6567d4072901a9155872..873562687d09e2f781860c8d686249b81f68ff50 100755 (executable)
@@ -7,24 +7,41 @@
 static int hdmi_get_enable(struct rk_display_device *device)
 {
        struct hdmi *hdmi = device->priv_data;
-
-       return hdmi->enable;
+       int enable;
+       
+       mutex_lock(&hdmi->enable_mutex);
+       enable = hdmi->enable;
+       mutex_unlock(&hdmi->enable_mutex);
+       
+       return enable;
 }
 
 static int hdmi_set_enable(struct rk_display_device *device, int enable)
 {
        struct hdmi *hdmi = device->priv_data;
        
-       if(hdmi->enable == enable)
+       mutex_lock(&hdmi->enable_mutex);
+       if(hdmi->enable == enable) {
+               mutex_unlock(&hdmi->enable_mutex);
                return 0;
+       }
        hdmi->enable = enable;
+       
+       if(hdmi->suspend ) {
+               mutex_unlock(&hdmi->enable_mutex);
+               return 0;
+       }
+       
        if(enable == 0) {
                disable_irq(hdmi->irq);
+               mutex_unlock(&hdmi->enable_mutex);
                hdmi->command = HDMI_CONFIG_ENABLE;
                queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0);
        }
-       else
+       else {
                enable_irq(hdmi->irq);
+               mutex_unlock(&hdmi->enable_mutex);
+       }
        return 0;
 }
 
index 63cec992924121f6f8637651f29fb79aa006439f..72728864831c3eb21efbc584485570c955bce539 100755 (executable)
@@ -82,12 +82,14 @@ void hdmi_sys_remove(void)
 
 static void hdmi_sys_sleep(void)
 {
+       mutex_lock(&hdmi->enable_mutex);
        if(hdmi->enable)
-               disable_irq(hdmi->irq);
+               disable_irq(hdmi->irq);                         
        hdmi->state = HDMI_SLEEP;
        rk30_hdmi_removed();
        if(hdmi->enable)
                enable_irq(hdmi->irq);
+       mutex_unlock(&hdmi->enable_mutex);
 }
 
 static int hdmi_process_command(void)
@@ -102,15 +104,17 @@ static int hdmi_process_command(void)
                {       
                        case HDMI_CONFIG_ENABLE:
                                /* disable HDMI */
-                               if(!hdmi->enable)
+                               mutex_lock(&hdmi->enable_mutex);
+                               if(!hdmi->enable || hdmi->suspend)
                                {
-                                       if(hdmi->hotplug)
+                                       if(hdmi->hotplug == HDMI_HPD_ACTIVED)
                                                hdmi_sys_remove();
                                        hdmi->state = HDMI_SLEEP;
                                        hdmi->hotplug = HDMI_HPD_REMOVED;
                                        rk30_hdmi_removed();
                                        state = HDMI_SLEEP;
                                }
+                               mutex_unlock(&hdmi->enable_mutex);
                                if(hdmi->wait == 1) {
                                        complete(&hdmi->complete);
                                        hdmi->wait = 0; 
@@ -147,16 +151,21 @@ static int hdmi_process_command(void)
        return state;
 }
 
+static DEFINE_MUTEX(work_mutex);
+
 void hdmi_work(struct work_struct *work)
 {
        int hotplug, state_last;
        int rc = HDMI_ERROR_SUCESS, trytimes = 0;
+       
+       mutex_lock(&work_mutex);
        /* Process hdmi command */
        hdmi->state = hdmi_process_command();
        
-       if(!hdmi->enable)
+       if(!hdmi->enable || hdmi->suspend) {
+               mutex_unlock(&work_mutex);
                return;
-       
+       }
        hotplug = rk30_hdmi_detect_hotplug();
        hdmi_dbg(hdmi->dev, "[%s] hotplug %02x curvalue %d\n", __FUNCTION__, hotplug, hdmi->hotplug);
        
@@ -180,6 +189,7 @@ void hdmi_work(struct work_struct *work)
                                hdmi->wait = 0; 
                        }
                        kobject_uevent_env(&hdmi->dev->kobj, KOBJ_REMOVE, envp);
+                       mutex_unlock(&work_mutex);
                        return;
                }
                else if(hotplug == HDMI_HPD_REMOVED) {
@@ -262,4 +272,5 @@ void hdmi_work(struct work_struct *work)
                }
        }
        hdmi_dbg(hdmi->dev, "[%s] done\n", __FUNCTION__);
+       mutex_unlock(&work_mutex);
 }
\ No newline at end of file