msm_fb: MHL: Adding MHL api's for USB interaction.

Adding MHL api's for  device discovery and registering
callback mechanism.USB driver use these API's for
synchronization with MHL driver.

Change-Id: I0846951302e3b5f5d20d566c92c2f224528ca455
Signed-off-by: Manoj Rao <manojraj@codeaurora.org>
Signed-off-by: Abhishek Kharbanda <akharban@codeaurora.org>
diff --git a/drivers/video/msm/mhl/mhl_8334.c b/drivers/video/msm/mhl/mhl_8334.c
index d6e3f6f..646dd29 100644
--- a/drivers/video/msm/mhl/mhl_8334.c
+++ b/drivers/video/msm/mhl/mhl_8334.c
@@ -30,11 +30,11 @@
 #include <linux/regulator/consumer.h>
 #include <linux/device.h>
 #include <linux/platform_device.h>
+#include <linux/mhl_8334.h>
 
 #include "msm_fb.h"
 #include "external_common.h"
 #include "hdmi_msm.h"
-#include "mhl_8334.h"
 #include "mhl_i2c_utils.h"
 
 
@@ -53,6 +53,8 @@
 static void release_usb_switch_open(void);
 static void switch_mode(enum mhl_st_type to_mode);
 static irqreturn_t mhl_tx_isr(int irq, void *dev_id);
+void (*notify_usb_online)(int online);
+static void mhl_drive_hpd(uint8_t to_state);
 
 static struct i2c_driver mhl_sii_i2c_driver = {
 	.driver = {
@@ -227,6 +229,58 @@
 	return true;
 }
 
+
+/*  USB_HANDSHAKING FUNCTIONS */
+
+int mhl_device_discovery(const char *name, int *result)
+
+{
+	int timeout ;
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0010, 0x27);
+	msleep(50);
+	if (mhl_msm_state->cur_state == POWER_STATE_D3) {
+		/* give MHL driver chance to handle RGND interrupt */
+		INIT_COMPLETION(mhl_msm_state->rgnd_done);
+		timeout = wait_for_completion_interruptible_timeout
+			(&mhl_msm_state->rgnd_done, HZ/2);
+		if (!timeout) {
+			/* most likely nothing plugged in USB */
+			/* USB HOST connected or already in USB mode */
+			pr_debug("Timedout Returning from discovery mode\n");
+			*result = MHL_DISCOVERY_RESULT_USB;
+			return 0;
+		}
+		*result = mhl_msm_state->mhl_mode ?
+			MHL_DISCOVERY_RESULT_MHL : MHL_DISCOVERY_RESULT_USB;
+	} else
+		/* not in D3. already in MHL mode */
+		*result = MHL_DISCOVERY_RESULT_MHL;
+
+	return 0;
+}
+EXPORT_SYMBOL(mhl_device_discovery);
+
+int mhl_register_callback(const char *name, void (*callback)(int online))
+{
+	pr_debug("%s\n", __func__);
+	if (!callback)
+		return -EINVAL;
+	if (!notify_usb_online)
+		notify_usb_online = callback;
+	return 0;
+}
+EXPORT_SYMBOL(mhl_register_callback);
+
+int mhl_unregister_callback(const char *name)
+{
+	pr_debug("%s\n", __func__);
+	if (notify_usb_online)
+		notify_usb_online = NULL;
+	return 0;
+}
+EXPORT_SYMBOL(mhl_unregister_callback);
+
+
 static void cbus_reset(void)
 {
 	uint8_t i;
@@ -240,7 +294,7 @@
 	/*
 	 * REG_INTR1 and REG_INTR4
 	 */
-	mhl_i2c_reg_write(TX_PAGE_L0, 0x0075, BIT6 | BIT5);
+	mhl_i2c_reg_write(TX_PAGE_L0, 0x0075, BIT6);
 	mhl_i2c_reg_write(TX_PAGE_3, 0x0022,
 		BIT0 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6);
 	/* REG5 */
@@ -340,7 +394,7 @@
 /*
  * Configure the initial reg settings
  */
-static void mhl_init_reg_settings(void)
+static void mhl_init_reg_settings(bool mhl_disc_en)
 {
 
 	/*
@@ -419,15 +473,19 @@
 	/* Pull-up resistance off for IDLE state */
 	mhl_i2c_reg_write(TX_PAGE_3, 0x0013, 0x8C);
 	/* Enable CBUS Discovery */
-	mhl_i2c_reg_write(TX_PAGE_3, 0x0010, 0x27);
+	if (mhl_disc_en)
+		/* Enable MHL Discovery */
+		mhl_i2c_reg_write(TX_PAGE_3, 0x0010, 0x27);
+	else
+		/* Disable MHL Discovery */
+		mhl_i2c_reg_write(TX_PAGE_3, 0x0010, 0x26);
 	mhl_i2c_reg_write(TX_PAGE_3, 0x0016, 0x20);
 	/* MHL CBUS Discovery - immediate comm.  */
 	mhl_i2c_reg_write(TX_PAGE_3, 0x0012, 0x86);
 	/* Do not force HPD to 0 during wake-up from D3 */
-	if (mhl_msm_state->cur_state != POWER_STATE_D3) {
-		mhl_i2c_reg_modify(TX_PAGE_3, 0x0020,
-			       BIT5 | BIT4, BIT4);
-	}
+	if (mhl_msm_state->cur_state != POWER_STATE_D0_MHL)
+		mhl_drive_hpd(HPD_DOWN);
+
 	/* Enable Auto Soft RESET */
 	mhl_i2c_reg_write(TX_PAGE_3, 0x0000, 0x084);
 	/* HDMI Transcode mode enable */
@@ -452,7 +510,10 @@
 	/* MHL spec requires a 100 ms wait here.  */
 	msleep(100);
 
-	mhl_init_reg_settings();
+	/*
+	 * Need to disable MHL discovery
+	 */
+	mhl_init_reg_settings(true);
 
 	/*
 	 * Power down the chip to the
@@ -563,6 +624,7 @@
 
 	/* MHL SII 8334 chip specific init */
 	mhl_chip_init();
+	init_completion(&mhl_msm_state->rgnd_done);
 	return 0;
 
 init_exit:
@@ -583,10 +645,9 @@
 	case POWER_STATE_D0_NO_MHL:
 		break;
 	case POWER_STATE_D0_MHL:
-		mhl_init_reg_settings();
-
+		mhl_init_reg_settings(true);
 		/* REG_DISC_CTRL1 */
-		mhl_i2c_reg_modify(TX_PAGE_3, 0x0010, BIT1, 0);
+		mhl_i2c_reg_modify(TX_PAGE_3, 0x0010, BIT1 | BIT0, BIT0);
 
 		/*
 		 * TPI_DEVICE_POWER_STATE_CTRL_REG
@@ -597,16 +658,15 @@
 	case POWER_STATE_D3:
 		if (mhl_msm_state->cur_state != POWER_STATE_D3) {
 			/* Force HPD to 0 when not in MHL mode.  */
-			mhl_i2c_reg_modify(TX_PAGE_3, 0x0020,
-				BIT5 | BIT4, BIT4);
-
+			mhl_drive_hpd(HPD_DOWN);
 			/*
 			 * Change TMDS termination to high impedance
 			 * on disconnection.
 			 */
 			mhl_i2c_reg_write(TX_PAGE_3, 0x0030, 0xD0);
-			mhl_i2c_reg_modify(TX_PAGE_L1, 0x003D,
-				BIT1 | BIT0, BIT0);
+			msleep(50);
+			mhl_i2c_reg_modify(TX_PAGE_3, 0x0010,
+				BIT1 | BIT0, BIT1);
 			spin_lock_irqsave(&mhl_state_lock, flags);
 			mhl_msm_state->cur_state = POWER_STATE_D3;
 			spin_unlock_irqrestore(&mhl_state_lock, flags);
@@ -619,6 +679,11 @@
 
 static void mhl_drive_hpd(uint8_t to_state)
 {
+	if (mhl_msm_state->cur_state != POWER_STATE_D0_MHL) {
+		pr_err("MHL: invalid state to ctrl HPD\n");
+		return;
+	}
+
 	pr_debug("%s: To state=[0x%x]\n", __func__, to_state);
 	if (to_state == HPD_UP) {
 		/*
@@ -644,6 +709,7 @@
 		 * Disable TMDS Output on REG_TMDS_CCTRL
 		 * Enable/Disable TMDS output (MHL TMDS output only)
 		 */
+		mhl_i2c_reg_modify(TX_PAGE_3, 0x20, BIT4 | BIT5, BIT4);
 		mhl_i2c_reg_modify(TX_PAGE_L0, 0x0080, BIT4, 0x00);
 	}
 	return;
@@ -682,20 +748,11 @@
 
 static void mhl_msm_disconnection(void)
 {
-	uint8_t reg;
-	/* Clear interrupts - REG INTR4 */
-	reg = mhl_i2c_reg_read(TX_PAGE_3, 0x0021);
-	mhl_i2c_reg_write(TX_PAGE_3, 0x0021, reg);
 	/*
 	 * MHL TX CTL1
 	 * Disabling Tx termination
 	 */
 	mhl_i2c_reg_write(TX_PAGE_3, 0x30, 0xD0);
-	/*
-	 * MSC REQUESTOR ABORT REASON
-	 * Clear CBUS_HPD status
-	 */
-	mhl_i2c_reg_modify(TX_PAGE_CBUS, 0x000D, BIT6, 0x00);
 	/* Change HPD line to drive it low */
 	mhl_drive_hpd(HPD_DOWN);
 	/* switch power state to D3 */
@@ -704,11 +761,11 @@
 }
 
 /*
- * If hardware detected a change in impedence and raised an INTR
- * We check the range of this impedence to infer if the connected
+ * If hardware detected a change in impedance and raised an INTR
+ * We check the range of this impedance to infer if the connected
  * device is MHL or USB and take appropriate actions.
  */
-static void mhl_msm_read_rgnd_int(void)
+static int  mhl_msm_read_rgnd_int(void)
 {
 	uint8_t rgnd_imp;
 
@@ -720,20 +777,27 @@
 	 * 10  - 1 kOHM ***(MHL)**** It's range 800 - 1200 OHM from MHL spec
 	 * 11  - short (USB)
 	 */
-	rgnd_imp = mhl_i2c_reg_read(TX_PAGE_3, 0x001C);
+	rgnd_imp = (mhl_i2c_reg_read(TX_PAGE_3, 0x001C) & (BIT1 | BIT0));
 	pr_debug("Imp Range read = %02X\n", (int)rgnd_imp);
 
-
 	if (0x02 == rgnd_imp) {
 		pr_debug("MHL: MHL DEVICE!!!\n");
+		mhl_i2c_reg_modify(TX_PAGE_3, 0x0018, BIT0, BIT0);
 		/*
 		 * Handling the MHL event in driver
 		 */
-		mhl_i2c_reg_modify(TX_PAGE_3, 0x0018, BIT0, BIT0);
+		mhl_msm_state->mhl_mode = TRUE;
+		if (notify_usb_online)
+			notify_usb_online(1);
 	} else {
 		pr_debug("MHL: NON-MHL DEVICE!!!\n");
+		mhl_msm_state->mhl_mode = FALSE;
 		mhl_i2c_reg_modify(TX_PAGE_3, 0x0018, BIT3, BIT3);
+		switch_mode(POWER_STATE_D3);
 	}
+	complete(&mhl_msm_state->rgnd_done);
+	return mhl_msm_state->mhl_mode ?
+		MHL_DISCOVERY_RESULT_MHL : MHL_DISCOVERY_RESULT_USB;
 }
 
 static void force_usb_switch_open(void)
@@ -756,7 +820,7 @@
 
 static void int_4_isr(void)
 {
-	uint8_t status;
+	uint8_t status, reg ;
 
 	/* INTR_STATUS4 */
 	status = mhl_i2c_reg_read(TX_PAGE_3, 0x0021);
@@ -767,7 +831,7 @@
 	 * do nothing.
 	 */
 	if ((0x00 == status) && (mhl_msm_state->cur_state == POWER_STATE_D3)) {
-		mhl_chip_init();
+		pr_debug("MHL: spurious interrupt\n");
 		return;
 	}
 	if (0xFF != status) {
@@ -816,8 +880,13 @@
 
 		if (status & BIT5) {
 			mhl_connect_api(false);
+			/* Clear interrupts - REG INTR4 */
+			reg = mhl_i2c_reg_read(TX_PAGE_3, 0x0021);
+			mhl_i2c_reg_write(TX_PAGE_3, 0x0021, reg);
 			mhl_msm_disconnection();
-			pr_debug("MHL Disconn Drv: INT4 Status = %02X\n",
+			if (notify_usb_online)
+				notify_usb_online(0);
+			pr_debug("MHL Disconnect Drv: INT4 Status = %02X\n",
 				(int)status);
 		}
 
@@ -971,6 +1040,122 @@
 	return;
 }
 
+static void clear_all_intrs(void)
+{
+	uint8_t regval = 0x00;
+	/*
+	* intr status debug
+	*/
+	pr_debug("********* EXITING ISR MASK CHECK ?? *************\n");
+	pr_debug("Drv: INT1 MASK = %02X\n",
+		(int) mhl_i2c_reg_read(TX_PAGE_L0, 0x0071));
+	pr_debug("Drv: INT3 MASK = %02X\n",
+		(int) mhl_i2c_reg_read(TX_PAGE_L0, 0x0077));
+	pr_debug("Drv: INT4 MASK = %02X\n",
+		(int) mhl_i2c_reg_read(TX_PAGE_3, 0x0021));
+	pr_debug("Drv: INT5 MASK = %02X\n",
+		(int) mhl_i2c_reg_read(TX_PAGE_3, 0x0023));
+	pr_debug("Drv: CBUS1 MASK = %02X\n",
+		(int) mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0009));
+	pr_debug("Drv: CBUS2 MASK = %02X\n",
+		(int) mhl_i2c_reg_read(TX_PAGE_CBUS, 0x001F));
+	pr_debug("********* END OF ISR MASK CHECK *************\n");
+
+	pr_debug("********* EXITING IN ISR ?? *************\n");
+	regval = mhl_i2c_reg_read(TX_PAGE_L0, 0x0071);
+	pr_debug("Drv: INT1 Status = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_L0, 0x0071, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_L0, 0x0072);
+	pr_debug("Drv: INT2 Status = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_L0, 0x0072, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_L0, 0x0073);
+	pr_debug("Drv: INT3 Status = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_L0, 0x0073, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_3, 0x0021);
+	pr_debug("Drv: INT4 Status = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0021, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_3, 0x0023);
+	pr_debug("Drv: INT5 Status = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_3, 0x0023, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x0008);
+	pr_debug("Drv: cbusInt Status = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x0008, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x001E);
+	pr_debug("Drv: CBUS INTR_2: %d\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x001E, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00A0);
+	pr_debug("Drv: A0 INT Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00A0, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00A1);
+	pr_debug("Drv: A1 INT Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00A1, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00A2);
+	pr_debug("Drv: A2 INT Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00A2, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00A3);
+	pr_debug("Drv: A3 INT Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00A3, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00B0);
+	pr_debug("Drv: B0 STATUS Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00B0, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00B1);
+	pr_debug("Drv: B1 STATUS Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00B1, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00B2);
+	pr_debug("Drv: B2 STATUS Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00B2, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00B3);
+	pr_debug("Drv: B3 STATUS Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00B3, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00E0);
+	pr_debug("Drv: E0 STATUS Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00E0, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00E1);
+	pr_debug("Drv: E1 STATUS Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00E1, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00E2);
+	pr_debug("Drv: E2 STATUS Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00E2, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00E3);
+	pr_debug("Drv: E3 STATUS Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00E3, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00F0);
+	pr_debug("Drv: F0 INT Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00F0, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00F1);
+	pr_debug("Drv: F1 INT Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00F1, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00F2);
+	pr_debug("Drv: F2 INT Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00F2, regval);
+
+	regval =  mhl_i2c_reg_read(TX_PAGE_CBUS, 0x00F3);
+	pr_debug("Drv: F3 INT Set = %02X\n", (int)regval);
+	mhl_i2c_reg_write(TX_PAGE_CBUS, 0x00F3, regval);
+	pr_debug("********* END OF EXITING IN ISR *************\n");
+}
+
 static irqreturn_t mhl_tx_isr(int irq, void *dev_id)
 {
 	/*
@@ -997,6 +1182,7 @@
 		mhl_cbus_isr();
 		int_1_isr();
 	}
+	clear_all_intrs();
 	return IRQ_HANDLED;
 }
 
diff --git a/drivers/video/msm/mhl/mhl_i2c_utils.c b/drivers/video/msm/mhl/mhl_i2c_utils.c
index aab6e02..ee069bb 100644
--- a/drivers/video/msm/mhl/mhl_i2c_utils.c
+++ b/drivers/video/msm/mhl/mhl_i2c_utils.c
@@ -11,9 +11,9 @@
  *
  */
 #include <linux/i2c.h>
+#include <linux/mhl_8334.h>
 
 #include "mhl_i2c_utils.h"
-#include "mhl_8334.h"
 
 uint8_t slave_addrs[MAX_PAGES] = {
 	DEV_PAGE_TPI_0    ,
diff --git a/drivers/video/msm/mhl/mhl_i2c_utils.h b/drivers/video/msm/mhl/mhl_i2c_utils.h
index 76498d4..5a2d199 100644
--- a/drivers/video/msm/mhl/mhl_i2c_utils.h
+++ b/drivers/video/msm/mhl/mhl_i2c_utils.h
@@ -16,8 +16,7 @@
 
 #include <linux/i2c.h>
 #include <linux/types.h>
-
-#include "mhl_defs.h"
+#include <linux/mhl_defs.h>
 
 /*
  * I2C command to the adapter to append
diff --git a/drivers/video/msm/mhl/mhl_8334.h b/include/linux/mhl_8334.h
similarity index 66%
rename from drivers/video/msm/mhl/mhl_8334.h
rename to include/linux/mhl_8334.h
index eba544a..1b19103 100644
--- a/drivers/video/msm/mhl/mhl_8334.h
+++ b/include/linux/mhl_8334.h
@@ -17,9 +17,8 @@
 #include <linux/types.h>
 #include <linux/platform_device.h>
 #include <mach/board.h>
-
-#include "mhl_devcap.h"
-#include "mhl_defs.h"
+#include <linux/mhl_devcap.h>
+#include <linux/mhl_defs.h>
 
 #define MHL_DEVICE_NAME "sii8334"
 #define MHL_DRIVER_NAME "sii8334"
@@ -27,12 +26,48 @@
 #define HPD_UP               1
 #define HPD_DOWN             0
 
+enum discovery_result_enum {
+	MHL_DISCOVERY_RESULT_USB = 0,
+	MHL_DISCOVERY_RESULT_MHL,
+};
+
+/* USB driver interface  */
+
+#ifdef CONFIG_FB_MSM_HDMI_MHL_8334
+ /*  mhl_device_discovery */
+extern int mhl_device_discovery(const char *name, int *result);
+
+/* - register|unregister MHL cable plug callback. */
+extern int mhl_register_callback
+	(const char *name, void (*callback)(int online));
+extern int mhl_unregister_callback(const char *name);
+#else
+static inline int mhl_device_discovery(const char *name, int *result)
+{
+	return -ENODEV;
+}
+
+static inline int
+	mhl_register_callback(const char *name, void (*callback)(int online))
+{
+	return -ENODEV;
+}
+
+static inline int mhl_unregister_callback(const char *name)
+{
+	return -ENODEV;
+}
+#endif
+
 struct mhl_msm_state_t {
 	struct i2c_client *i2c_client;
 	struct i2c_driver *i2c_driver;
 	uint8_t      cur_state;
 	uint8_t chip_rev_id;
 	struct msm_mhl_platform_data *mhl_data;
+	/* Device Discovery stuff */
+	int mhl_mode;
+	struct completion rgnd_done;
 };
 
 enum {
diff --git a/drivers/video/msm/mhl/mhl_defs.h b/include/linux/mhl_defs.h
similarity index 100%
rename from drivers/video/msm/mhl/mhl_defs.h
rename to include/linux/mhl_defs.h
diff --git a/drivers/video/msm/mhl/mhl_devcap.h b/include/linux/mhl_devcap.h
similarity index 100%
rename from drivers/video/msm/mhl/mhl_devcap.h
rename to include/linux/mhl_devcap.h