mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 12:17:12 +09:00
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:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user