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.
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
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
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
#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>
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
{
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;
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);
{
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)) {
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;
}
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;
}
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)
{
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;
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);
hdmi->wait = 0;
}
kobject_uevent_env(&hdmi->dev->kobj, KOBJ_REMOVE, envp);
+ mutex_unlock(&work_mutex);
return;
}
else if(hotplug == HDMI_HPD_REMOVED) {
}
}
hdmi_dbg(hdmi->dev, "[%s] done\n", __FUNCTION__);
+ mutex_unlock(&work_mutex);
}
\ No newline at end of file