rk30 hdmi:

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.
This commit is contained in:
Zheng Yang
2012-04-25 18:20:43 +08:00
parent fcff15d5fc
commit c66cf4b662
5 changed files with 67 additions and 25 deletions

View File

@@ -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)
{
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;
}
disable_irq(hdmi->irq);
hdmi->enable = 0;
mutex_unlock(&hdmi->enable_mutex);
hdmi->command = HDMI_CONFIG_ENABLE;
/* wait for hdmi configuration finish */
while(hdmi->wait)
msleep(10);
init_completion(&hdmi->complete);
hdmi->wait = 1;
queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0);
@@ -48,8 +52,12 @@ static void hdmi_early_suspend(struct early_suspend *h)
static void hdmi_early_resume(struct early_suspend *h)
{
hdmi_dbg(hdmi->dev, "hdmi exit early resume\n");
hdmi->enable = 1;
enable_irq(hdmi->irq);
mutex_lock(&hdmi->enable_mutex);
hdmi->suspend = 0;
if(hdmi->enable) {
enable_irq(hdmi->irq);
}
mutex_unlock(&hdmi->enable_mutex);
return;
}
#endif
@@ -151,7 +159,8 @@ static int __devinit rk30_hdmi_probe (struct platform_device *pdev)
hdmi_register_display_sysfs(hdmi, hdmi->dev);
spin_lock_init(&hdmi->irq_lock);
mutex_init(&hdmi->enable_mutex);
/* get the IRQ */
hdmi->irq = platform_get_irq(pdev, 0);
if(hdmi->irq <= 0) {

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}