Merge "msm8974: mhl_sii8334: disable intrs in low-pwr"
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index fe10189..287f2cd 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -391,6 +391,10 @@
 		return rc;
 	}
 
+	if (hdmi_ctrl->mhl_max_pclk && hpd &&
+	    (!hdmi_ctrl->mhl_hpd_on || hdmi_ctrl->hpd_feature_on))
+		return 0;
+
 	if (0 == hpd && hdmi_ctrl->hpd_feature_on) {
 		rc = hdmi_tx_sysfs_enable_hpd(hdmi_ctrl, false);
 	} else if (1 == hpd && !hdmi_ctrl->hpd_feature_on) {
@@ -2508,6 +2512,9 @@
 		return -EINVAL;
 	}
 
+	/* mhl status should override */
+	hdmi_ctrl->mhl_hpd_on = on;
+
 	if (!on && hdmi_ctrl->hpd_feature_on) {
 		rc = hdmi_tx_sysfs_enable_hpd(hdmi_ctrl, false);
 	} else if (on && !hdmi_ctrl->hpd_feature_on) {
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.h b/drivers/video/msm/mdss/mdss_hdmi_tx.h
index ce3c00c..d4f8e67 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.h
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -62,6 +62,7 @@
 	u32 hpd_initialized;
 	u8  timing_gen_on;
 	u32 mhl_max_pclk;
+	u8  mhl_hpd_on;
 	struct completion hpd_done;
 	struct work_struct hpd_int_work;
 
diff --git a/drivers/video/msm/mdss/mhl_sii8334.c b/drivers/video/msm/mdss/mhl_sii8334.c
index a1053fb..9cae431 100644
--- a/drivers/video/msm/mdss/mhl_sii8334.c
+++ b/drivers/video/msm/mdss/mhl_sii8334.c
@@ -361,7 +361,7 @@
 	pr_debug("%s:%u\n", __func__, __LINE__);
 	INIT_COMPLETION(mhl_ctrl->rgnd_done);
 	timeout = wait_for_completion_interruptible_timeout
-		(&mhl_ctrl->rgnd_done, HZ/2);
+		(&mhl_ctrl->rgnd_done, HZ);
 	if (!timeout) {
 		/* most likely nothing plugged in USB */
 		/* USB HOST connected or already in USB mode */
@@ -377,6 +377,12 @@
 {
 	int rc;
 	struct mhl_tx_ctrl *mhl_ctrl = data;
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+	unsigned long flags;
+
+	enable_irq(client->irq);
+	/* wait for i2c interrupt line to be activated */
+	msleep(300);
 
 	if (id) {
 		/* When MHL cable is disconnected we get a sii8334
@@ -396,11 +402,16 @@
 		mhl_ctrl->notify_usb_online = usb_notify_cb;
 
 	if (!mhl_ctrl->disc_enabled) {
+		spin_lock_irqsave(&mhl_ctrl->lock, flags);
+		mhl_ctrl->tx_powered_off = false;
+		spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
 		mhl_sii_reset_pin(mhl_ctrl, 0);
 		msleep(50);
 		mhl_sii_reset_pin(mhl_ctrl, 1);
-		/* TX PR-guide requires a 100 ms wait here */
-		msleep(100);
+		/* chipset PR recommends waiting for at least 100 ms
+		 * the chipset needs longer to come out of D3 state.
+		 */
+		msleep(300);
 		mhl_init_reg_settings(mhl_ctrl, true);
 		rc = mhl_sii_wait_for_rgnd(mhl_ctrl);
 	} else {
@@ -690,8 +701,8 @@
 
 	MHL_SII_PAGE3_WR(0x3C, 0x80);
 
-	if (mhl_ctrl->cur_state != POWER_STATE_D3)
-		MHL_SII_REG_NAME_MOD(REG_INT_CTRL, BIT6 | BIT5 | BIT4, BIT4);
+	MHL_SII_REG_NAME_MOD(REG_INT_CTRL,
+			     (BIT6 | BIT5 | BIT4), (BIT6 | BIT4));
 
 	/* Enable Auto Soft RESET */
 	MHL_SII_REG_NAME_WR(REG_SRST, 0x084);
@@ -858,11 +869,6 @@
 static void mhl_msm_disconnection(struct mhl_tx_ctrl *mhl_ctrl)
 {
 	struct i2c_client *client = mhl_ctrl->i2c_handle;
-	unsigned long flags;
-
-	spin_lock_irqsave(&mhl_ctrl->lock, flags);
-	mhl_ctrl->dwnstream_hpd &= ~BIT6;
-	spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
 
 	/* disabling Tx termination */
 	MHL_SII_REG_NAME_WR(REG_MHLTX_CTL1, 0xD0);
@@ -870,7 +876,7 @@
 	mhl_msc_clear(mhl_ctrl);
 }
 
-static int  mhl_msm_read_rgnd_int(struct mhl_tx_ctrl *mhl_ctrl)
+static int mhl_msm_read_rgnd_int(struct mhl_tx_ctrl *mhl_ctrl)
 {
 	uint8_t rgnd_imp;
 	struct i2c_client *client = mhl_ctrl->i2c_handle;
@@ -967,12 +973,12 @@
 
 	if ((0x00 == status) &&\
 	    (mhl_ctrl->cur_state == POWER_STATE_D3)) {
-		pr_err("%s: invalid intr\n", __func__);
+		pr_warn("%s: invalid intr\n", __func__);
 		return 0;
 	}
 
 	if (0xFF == status) {
-		pr_debug("%s: invalid intr 0xff\n", __func__);
+		pr_warn("%s: invalid intr 0xff\n", __func__);
 		MHL_SII_REG_NAME_WR(REG_INTR4, status);
 		return 0;
 	}
@@ -999,13 +1005,14 @@
 		power_supply_changed(&mhl_ctrl->mhl_psy);
 		if (mhl_ctrl->notify_usb_online)
 			mhl_ctrl->notify_usb_online(0);
-		return -EACCES;
+		return 0;
 	}
 
 	if (status & BIT5) {
 		/* clr intr - reg int4 */
 		pr_debug("%s: mhl discon: int4 st=%02X\n", __func__,
 			 (int)status);
+		mhl_ctrl->mhl_det_discon = true;
 
 		reg = MHL_SII_REG_NAME_RD(REG_INTR4);
 		MHL_SII_REG_NAME_WR(REG_INTR4, reg);
@@ -1013,7 +1020,7 @@
 		power_supply_changed(&mhl_ctrl->mhl_psy);
 		if (mhl_ctrl->notify_usb_online)
 			mhl_ctrl->notify_usb_online(0);
-		return -EACCES;
+		return 0;
 	}
 
 	if ((mhl_ctrl->cur_state != POWER_STATE_D0_NO_MHL) &&\
@@ -1054,6 +1061,32 @@
 	MHL_SII_REG_NAME_WR(REG_INTR5,  intr_5_stat);
 }
 
+static void mhl_tx_down(struct mhl_tx_ctrl *mhl_ctrl)
+{
+	struct i2c_client *client = mhl_ctrl->i2c_handle;
+	unsigned long flags;
+	uint8_t reg;
+
+	switch_mode(mhl_ctrl, POWER_STATE_D3, true);
+
+	reg = MHL_SII_REG_NAME_RD(REG_INTR1);
+	MHL_SII_REG_NAME_WR(REG_INTR1, reg);
+
+	reg = MHL_SII_REG_NAME_RD(REG_INTR4);
+	MHL_SII_REG_NAME_WR(REG_INTR4, reg);
+
+	/* disable INTR1 and INTR4 */
+	MHL_SII_REG_NAME_MOD(REG_INTR1_MASK, BIT6, 0x0);
+	MHL_SII_REG_NAME_MOD(REG_INTR4_MASK,
+		(BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6), 0x0);
+
+	MHL_SII_PAGE1_MOD(0x003D, BIT0, 0x00);
+	spin_lock_irqsave(&mhl_ctrl->lock, flags);
+	mhl_ctrl->tx_powered_off = true;
+	spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+	pr_debug("%s: disabled\n", __func__);
+	disable_irq_nosync(client->irq);
+}
 
 static void mhl_hpd_stat_isr(struct mhl_tx_ctrl *mhl_ctrl)
 {
@@ -1085,20 +1118,24 @@
 
 		spin_lock_irqsave(&mhl_ctrl->lock, flags);
 		t = mhl_ctrl->dwnstream_hpd;
+		pr_debug("%s: %u: dwnstrm_hpd=0x%02x\n",
+			 __func__, __LINE__, mhl_ctrl->dwnstream_hpd);
 		spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
 
 		if (BIT6 & (cbus_stat ^ t)) {
 			u8 status = cbus_stat & BIT6;
 			mhl_drive_hpd(mhl_ctrl, status ? HPD_UP : HPD_DOWN);
-			if (!status) {
-				MHL_SII_PAGE1_MOD(0x003D, BIT0, 0x00);
-				spin_lock_irqsave(&mhl_ctrl->lock, flags);
-				mhl_ctrl->tx_powered_off = true;
-				spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+			if (!status && mhl_ctrl->mhl_det_discon) {
+				pr_debug("%s:%u: power_down\n",
+					 __func__, __LINE__);
+				mhl_tx_down(mhl_ctrl);
 			}
 			spin_lock_irqsave(&mhl_ctrl->lock, flags);
 			mhl_ctrl->dwnstream_hpd = cbus_stat;
+			pr_debug("%s: %u: dwnstrm_hpd=0x%02x\n",
+				 __func__, __LINE__, mhl_ctrl->dwnstream_hpd);
 			spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+			mhl_ctrl->mhl_det_discon = false;
 		}
 	}
 }
@@ -1252,6 +1289,7 @@
 	uint8_t cmd_data = 0x0;
 	int msc_msg_recved = 0;
 	int rc = -1;
+	unsigned long flags;
 	struct i2c_client *client = mhl_ctrl->i2c_handle;
 
 	regval = MHL_SII_REG_NAME_RD(REG_CBUS_INTR_STATUS);
@@ -1292,6 +1330,14 @@
 		intr = MHL_SII_REG_NAME_RD(REG_CBUS_SET_INT_0);
 		MHL_SII_REG_NAME_WR(REG_CBUS_SET_INT_0, intr);
 		mhl_msc_recv_set_int(mhl_ctrl, 0, intr);
+		if (intr & MHL_INT_DCAP_CHG) {
+			/* No need to go to low power mode */
+			spin_lock_irqsave(&mhl_ctrl->lock, flags);
+			mhl_ctrl->dwnstream_hpd = 0x00;
+			pr_debug("%s: %u: dwnstrm_hpd=0x%02x\n",
+				 __func__, __LINE__, mhl_ctrl->dwnstream_hpd);
+			spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+		}
 
 		pr_debug("%s: MHL_INT_0 = %02x\n", __func__, intr);
 		intr = MHL_SII_REG_NAME_RD(REG_CBUS_SET_INT_1);
@@ -1337,8 +1383,18 @@
 {
 	int rc;
 	struct mhl_tx_ctrl *mhl_ctrl = (struct mhl_tx_ctrl *)data;
+	unsigned long flags;
+
 	pr_debug("%s: Getting Interrupts\n", __func__);
 
+	spin_lock_irqsave(&mhl_ctrl->lock, flags);
+	if (mhl_ctrl->tx_powered_off) {
+		pr_warn("%s: powered off\n", __func__);
+		spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+		return IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&mhl_ctrl->lock, flags);
+
 	/*
 	 * Check RGND, MHL_EST, CBUS_LOCKOUT, SCDT
 	 * interrupts. In D3, we get only RGND
@@ -1398,6 +1454,8 @@
 	 */
 	mhl_init_reg_settings(mhl_ctrl, true);
 	switch_mode(mhl_ctrl, POWER_STATE_D3, true);
+	pr_debug("%s:%u: power_down\n", __func__, __LINE__);
+	mhl_tx_down(mhl_ctrl);
 	return 0;
 }
 
diff --git a/include/linux/mhl_8334.h b/include/linux/mhl_8334.h
index f04bf80..7297ff3 100644
--- a/include/linux/mhl_8334.h
+++ b/include/linux/mhl_8334.h
@@ -162,6 +162,7 @@
 	spinlock_t lock;
 	bool tx_powered_off;
 	uint8_t dwnstream_hpd;
+	bool mhl_det_discon;
 };
 
 int mhl_i2c_reg_read(struct i2c_client *client,