aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanny Nold <dannynold@freescale.com>2011-10-24 11:11:17 -0500
committerEric Miao <eric.miao@canonical.com>2011-11-10 07:38:51 +0800
commit6ed6226eaa55a58d729342bc78f109c6146c6f5e (patch)
tree56c97a1aa05a7e8fac6d6a9163ae29c25cd41bd1
parenteacfe81721b83680a41f09ef2a94fc7a178c735f (diff)
ENGR00160692 - MXC HDMI: Unbalanced clk disable
HDMI IAHB clock was capable of being disabled twice consecutively due to a race condition between enabling the HDMI interrupts and disabling the HDMI clock. A spinlock has been added to protect against the race condition. Another race condition was present due to the driver handling HPD (hotplug detect) and RX Sense interrupts separately. Only HPD interrupts handled now, simplifying enable/disable flow and eliminating the race condition. One final race condition fixed during HDMI driver initialization. IRQ request moved to after place where IAHB is disabled, to ensure balanced enable/disable of IAHB clock. Signed-off-by: Danny Nold <dannynold@freescale.com>
-rw-r--r--drivers/video/mxc_hdmi.c136
1 files changed, 49 insertions, 87 deletions
diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c
index ca3f7fa4201..c3c0164b224 100644
--- a/drivers/video/mxc_hdmi.c
+++ b/drivers/video/mxc_hdmi.c
@@ -68,13 +68,6 @@
#define NUM_CEA_VIDEO_MODES 64
#define DEFAULT_VIDEO_MODE 16 /* 1080P */
-#define HDMI_IH_PHY_RX_SENSE (HDMI_IH_PHY_STAT0_RX_SENSE0 |\
- HDMI_IH_PHY_STAT0_RX_SENSE1 |\
- HDMI_IH_PHY_STAT0_RX_SENSE2 |\
- HDMI_IH_PHY_STAT0_RX_SENSE3)
-#define HDMI_PHY_RX_SENSE (HDMI_PHY_RX_SENSE0 | HDMI_PHY_RX_SENSE1 |\
- HDMI_PHY_RX_SENSE2 | HDMI_PHY_RX_SENSE3)
-
#define RGB 0
#define YCBCR444 1
#define YCBCR422_16BITS 2
@@ -140,6 +133,7 @@ struct mxc_hdmi {
bool cable_plugin;
u8 latest_intr_stat;
bool irq_enabled;
+ spinlock_t irq_lock;
};
struct i2c_client *hdmi_i2c;
@@ -1262,11 +1256,11 @@ static void mxc_hdmi_poweroff(struct mxc_hdmi *hdmi)
plat->disable_pins();
}
-static void mxc_hdmi_rx_powerup(struct mxc_hdmi *hdmi)
+static void mxc_hdmi_enable(struct mxc_hdmi *hdmi)
{
u8 val;
- dev_dbg(&hdmi->pdev->dev, "rx power up\n");
+ dev_dbg(&hdmi->pdev->dev, "hdmi enable\n");
clk_enable(hdmi->hdmi_iahb_clk);
@@ -1290,11 +1284,11 @@ static void mxc_hdmi_rx_powerup(struct mxc_hdmi *hdmi)
}
}
-static void mxc_hdmi_rx_powerdown(struct mxc_hdmi *hdmi)
+static void mxc_hdmi_disable(struct mxc_hdmi *hdmi)
{
u8 val;
- dev_dbg(&hdmi->pdev->dev, "rx power down\n");
+ dev_dbg(&hdmi->pdev->dev, "hdmi disable\n");
/* Disable HDMI PHY - Set PDDQ=1 and TXPWRON=0 */
val = hdmi_readb(HDMI_PHY_CONF0);
@@ -1370,16 +1364,16 @@ static void det_worker(struct work_struct *work)
container_of(delay_work, struct mxc_hdmi, det_work);
u32 phy_int_stat, phy_int_pol, phy_int_mask;
u8 val;
- bool rx_powerdown = false;
+ bool hdmi_disable = false;
int irq = platform_get_irq(hdmi->pdev, 0);
+ unsigned long flags;
if (!hdmi->irq_enabled) {
clk_enable(hdmi->hdmi_iahb_clk);
/* Capture status - used in det_worker ISR */
phy_int_stat = hdmi_readb(HDMI_IH_PHY_STAT0);
- if ((phy_int_stat &
- (HDMI_IH_PHY_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) == 0) {
+ if ((phy_int_stat & HDMI_IH_PHY_STAT0_HPD) == 0) {
clk_disable(hdmi->hdmi_iahb_clk);
return; /* No interrupts to handle */
}
@@ -1388,17 +1382,11 @@ static void det_worker(struct work_struct *work)
/* Unmask interrupts until handled */
val = hdmi_readb(HDMI_PHY_MASK0);
- val |= HDMI_PHY_RX_SENSE | HDMI_PHY_HPD;
+ val |= HDMI_PHY_HPD;
hdmi_writeb(val, HDMI_PHY_MASK0);
- /* Clear Hotplug/RX Sense interrupts */
- hdmi_writeb(HDMI_IH_PHY_STAT0_HPD |
- HDMI_IH_PHY_STAT0_TX_PHY_LOCK |
- HDMI_IH_PHY_STAT0_RX_SENSE0 |
- HDMI_IH_PHY_STAT0_RX_SENSE1 |
- HDMI_IH_PHY_STAT0_RX_SENSE2 |
- HDMI_IH_PHY_STAT0_RX_SENSE3,
- HDMI_IH_PHY_STAT0);
+ /* Clear Hotplug interrupts */
+ hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
phy_int_pol = hdmi_readb(HDMI_PHY_POL0);
@@ -1418,6 +1406,7 @@ static void det_worker(struct work_struct *work)
if (phy_int_pol & HDMI_PHY_HPD) {
dev_dbg(&hdmi->pdev->dev, "EVENT=plugin\n");
mxc_hdmi_cable_connected(hdmi);
+ mxc_hdmi_enable(hdmi);
/* Make HPD intr active low to capture unplug event */
val = hdmi_readb(HDMI_PHY_POL0);
@@ -1426,6 +1415,7 @@ static void det_worker(struct work_struct *work)
} else if (!(phy_int_pol & HDMI_PHY_HPD)) {
dev_dbg(&hdmi->pdev->dev, "EVENT=plugout\n");
mxc_hdmi_cable_disconnected(hdmi);
+ hdmi_disable = true;
/* Make HPD intr active high to capture plugin event */
val = hdmi_readb(HDMI_PHY_POL0);
@@ -1435,34 +1425,19 @@ static void det_worker(struct work_struct *work)
dev_dbg(&hdmi->pdev->dev, "EVENT=none?\n");
}
- /* check rx power */
- if (phy_int_stat & HDMI_IH_PHY_RX_SENSE) {
- if ((phy_int_pol & HDMI_PHY_RX_SENSE)) {
- mxc_hdmi_rx_powerup(hdmi);
-
- /* Change RX Sense pol to capture RX disable */
- val = hdmi_readb(HDMI_PHY_POL0);
- val &= ~HDMI_PHY_RX_SENSE;
- hdmi_writeb(val, HDMI_PHY_POL0);
- } else if (!(phy_int_pol & HDMI_PHY_RX_SENSE)) {
- rx_powerdown = true;
-
- /* Change RX Sense pol to capture RX enable */
- val = hdmi_readb(HDMI_PHY_POL0);
- val |= HDMI_PHY_RX_SENSE;
- hdmi_writeb(val, HDMI_PHY_POL0);
- } else
- dev_err(&hdmi->pdev->dev,
- "Received RX sense event but no change\n");
- }
+ /* Lock here to ensure full powerdown sequence
+ * completed before next interrupt processed */
+ spin_lock_irqsave(&hdmi->irq_lock, flags);
- /* Re-enable RX Sense and HPD interrupts */
+ /* Re-enable HPD interrupts */
phy_int_mask = hdmi_readb(HDMI_PHY_MASK0);
- phy_int_mask &= ~(HDMI_PHY_RX_SENSE | HDMI_PHY_HPD);
+ phy_int_mask &= ~HDMI_PHY_HPD;
hdmi_writeb(phy_int_mask, HDMI_PHY_MASK0);
- if (rx_powerdown)
- mxc_hdmi_rx_powerdown(hdmi);
+ if (hdmi_disable)
+ mxc_hdmi_disable(hdmi);
+
+ spin_unlock_irqrestore(&hdmi->irq_lock, flags);
}
static irqreturn_t mxc_hdmi_hotplug(int irq, void *data)
@@ -1470,6 +1445,9 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data)
struct mxc_hdmi *hdmi = data;
unsigned int ret;
u8 val, intr_stat;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hdmi->irq_lock, flags);
/*
* We have to disable the irq, rather than just masking
@@ -1483,8 +1461,7 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data)
if (ret == IRQ_DISABLE_FAIL) {
/* Capture status - used in det_worker ISR */
intr_stat = hdmi_readb(HDMI_IH_PHY_STAT0);
- if ((intr_stat &
- (HDMI_IH_PHY_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) == 0)
+ if ((intr_stat & HDMI_IH_PHY_STAT0_HPD) == 0)
return IRQ_HANDLED;
dev_dbg(&hdmi->pdev->dev, "Hotplug interrupt received\n");
@@ -1492,17 +1469,11 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data)
/* Unmask interrupts until handled */
val = hdmi_readb(HDMI_PHY_MASK0);
- val |= HDMI_PHY_RX_SENSE | HDMI_PHY_HPD;
+ val |= HDMI_PHY_HPD;
hdmi_writeb(val, HDMI_PHY_MASK0);
- /* Clear Hotplug/RX Sense interrupts */
- hdmi_writeb(HDMI_IH_PHY_STAT0_HPD |
- HDMI_IH_PHY_STAT0_TX_PHY_LOCK |
- HDMI_IH_PHY_STAT0_RX_SENSE0 |
- HDMI_IH_PHY_STAT0_RX_SENSE1 |
- HDMI_IH_PHY_STAT0_RX_SENSE2 |
- HDMI_IH_PHY_STAT0_RX_SENSE3,
- HDMI_IH_PHY_STAT0);
+ /* Clear Hotplug interrupts */
+ hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
hdmi->irq_enabled = true;
} else
@@ -1510,6 +1481,8 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data)
schedule_delayed_work(&(hdmi->det_work), msecs_to_jiffies(20));
+ spin_unlock_irqrestore(&hdmi->irq_lock, flags);
+
return IRQ_HANDLED;
}
@@ -1736,44 +1709,22 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_entry *disp)
}
}
- ret = request_irq(irq, mxc_hdmi_hotplug, IRQF_SHARED,
- dev_name(&hdmi->pdev->dev), hdmi);
- if (ret < 0) {
- dev_err(&hdmi->pdev->dev,
- "Unable to request irq: %d\n", ret);
- goto ereqirq;
- }
-
- /* Initialize IRQ at HDMI core level */
- hdmi_irq_init();
+ spin_lock_init(&hdmi->irq_lock);
INIT_DELAYED_WORK(&(hdmi->det_work), det_worker);
/* Configure registers related to HDMI interrupt
* generation before registering IRQ. */
- hdmi_writeb(HDMI_PHY_RX_SENSE | HDMI_PHY_HPD,
- HDMI_PHY_POL0);
+ hdmi_writeb(HDMI_PHY_HPD, HDMI_PHY_POL0);
/* enable cable hot plug irq */
- val = HDMI_PHY_RX_SENSE | HDMI_PHY_HPD;
- val = ~val;
+ val = ~HDMI_PHY_HPD;
hdmi_writeb(val, HDMI_PHY_MASK0);
- /* Clear Hotplug/RX Sense interrupts */
- hdmi_writeb(HDMI_IH_PHY_STAT0_HPD |
- HDMI_IH_PHY_STAT0_TX_PHY_LOCK |
- HDMI_IH_PHY_STAT0_RX_SENSE0 |
- HDMI_IH_PHY_STAT0_RX_SENSE1 |
- HDMI_IH_PHY_STAT0_RX_SENSE2 |
- HDMI_IH_PHY_STAT0_RX_SENSE3,
- HDMI_IH_PHY_STAT0);
-
- hdmi_writeb(~(HDMI_IH_PHY_STAT0_HPD |
- HDMI_IH_PHY_STAT0_RX_SENSE0 |
- HDMI_IH_PHY_STAT0_RX_SENSE1 |
- HDMI_IH_PHY_STAT0_RX_SENSE2 |
- HDMI_IH_PHY_STAT0_RX_SENSE3),
- HDMI_IH_MUTE_PHY_STAT0);
+ /* Clear Hotplug interrupts */
+ hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+
+ hdmi_writeb(~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
hdmi->nb.notifier_call = mxc_hdmi_fb_event;
ret = fb_register_client(&hdmi->nb);
@@ -1788,6 +1739,17 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_entry *disp)
* ISFR clock must remain enabled for hotplug to work. */
clk_disable(hdmi->hdmi_iahb_clk);
+ /* Initialize IRQ at HDMI core level */
+ hdmi_irq_init();
+
+ ret = request_irq(irq, mxc_hdmi_hotplug, IRQF_SHARED,
+ dev_name(&hdmi->pdev->dev), hdmi);
+ if (ret < 0) {
+ dev_err(&hdmi->pdev->dev,
+ "Unable to request irq: %d\n", ret);
+ goto ereqirq;
+ }
+
return ret;
efbclient: