[PATCH 21/21] usb: penwell_otg: add support for MHL-USB coexistence

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Hao Wu <hao.wu@xxxxxxxxx>

Add MHL support in penwell_otg transceiver driver including
pm related support. Use a new MHL state for MHL device connected
state, in this new state, usb will stop activity on the bus.

Signed-off-by: Hao Wu <hao.wu@xxxxxxxxx>
---
 drivers/usb/otg/penwell_otg.c   |  164 ++++++++++++++++++++++++++++++++++-----
 include/linux/usb/penwell_otg.h |    2 +-
 2 files changed, 145 insertions(+), 21 deletions(-)

diff --git a/drivers/usb/otg/penwell_otg.c b/drivers/usb/otg/penwell_otg.c
index e2b12d3..3b8fa77 100644
--- a/drivers/usb/otg/penwell_otg.c
+++ b/drivers/usb/otg/penwell_otg.c
@@ -63,6 +63,9 @@ static int penwell_otg_set_peripheral(struct otg_transceiver *otg,
 				struct usb_gadget *gadget);
 static int penwell_otg_start_srp(struct otg_transceiver *otg);
 static int penwell_otg_msic_write(u16 addr, u8 data);
+static void penwell_otg_intr(int on);
+static void penwell_otg_add_timer(enum penwell_otg_timer_type timers);
+static void update_hsm(void);
 
 static const char *state_string(enum usb_otg_state state)
 {
@@ -91,6 +94,8 @@ static const char *state_string(enum usb_otg_state state)
 		return "b_wait_acon";
 	case OTG_STATE_B_HOST:
 		return "b_host";
+	case OTG_STATE_MHL:
+		return "mhl";
 	default:
 		return "UNDEFINED";
 	}
@@ -540,6 +545,111 @@ static int penwell_otg_start_srp(struct otg_transceiver *otg)
 	return 0;
 }
 
+/* Enter MHL mode */
+static int penwell_otg_enter_mhl_mode(struct otg_transceiver *otg)
+{
+	struct penwell_otg	*pnw = the_transceiver;
+
+	dev_dbg(pnw->dev, "%s --->\n", __func__);
+
+	pm_runtime_get_sync(pnw->dev);
+
+	mutex_lock(&pnw->iotg.otg.state_mutex);
+
+	/* stop otg interrupt */
+	penwell_otg_intr(0);
+
+	/* move to OTG_MHL state */
+	pnw->iotg.otg.state = OTG_STATE_MHL;
+
+	mutex_unlock(&pnw->iotg.otg.state_mutex);
+
+	pm_runtime_put_sync(pnw->dev);
+
+	dev_dbg(pnw->dev, "%s <---\n", __func__);
+	return 0;
+}
+
+/* called with otg.state_mutex held */
+static int _penwell_otg_enter_mhl_mode(struct otg_transceiver *otg)
+{
+	struct penwell_otg	*pnw = the_transceiver;
+
+	dev_dbg(pnw->dev, "%s --->\n", __func__);
+
+	/* stop otg interrupt */
+	penwell_otg_intr(0);
+
+	/* move to OTG_MHL state */
+	pnw->iotg.otg.state = OTG_STATE_MHL;
+
+	dev_dbg(pnw->dev, "%s <---\n", __func__);
+	return 0;
+}
+
+/* Exit MHL mode */
+static int penwell_otg_exit_mhl_mode(struct otg_transceiver *otg)
+{
+	struct penwell_otg	*pnw = the_transceiver;
+
+	dev_dbg(pnw->dev, "%s --->\n", __func__);
+
+	mutex_lock(&pnw->iotg.otg.state_mutex);
+
+	if (pnw->iotg.otg.state != OTG_STATE_MHL)
+		return -EINVAL;
+
+	/* enable otg interrupt */
+	penwell_otg_intr(1);
+
+	pnw->iotg.hsm.power_up = 0;
+	pnw->iotg.hsm.adp_change = 0;
+	pnw->iotg.hsm.a_srp_det = 0;
+
+	if (pnw->iotg.otg.set_vbus)
+		pnw->iotg.otg.set_vbus(&pnw->iotg.otg, true);
+
+	penwell_otg_add_timer(TA_WAIT_VRISE_TMR);
+	pnw->iotg.otg.state = OTG_STATE_A_WAIT_VRISE;
+
+	update_hsm();
+	penwell_update_transceiver();
+
+	mutex_unlock(&pnw->iotg.otg.state_mutex);
+	dev_dbg(pnw->dev, "%s <---\n", __func__);
+	return 0;
+}
+
+/* called with otg.state_mutex held */
+static int _penwell_otg_exit_mhl_mode(struct otg_transceiver *otg)
+{
+	struct penwell_otg	*pnw = the_transceiver;
+
+	dev_dbg(pnw->dev, "%s --->\n", __func__);
+
+	if (pnw->iotg.otg.state != OTG_STATE_MHL)
+		return -EINVAL;
+
+	/* enable otg interrupt */
+	penwell_otg_intr(1);
+
+	pnw->iotg.hsm.power_up = 0;
+	pnw->iotg.hsm.adp_change = 0;
+	pnw->iotg.hsm.a_srp_det = 0;
+
+	if (pnw->iotg.otg.set_vbus)
+		pnw->iotg.otg.set_vbus(&pnw->iotg.otg, true);
+
+	penwell_otg_add_timer(TA_WAIT_VRISE_TMR);
+	pnw->iotg.otg.state = OTG_STATE_A_WAIT_VRISE;
+
+	update_hsm();
+	penwell_update_transceiver();
+
+	dev_dbg(pnw->dev, "%s <---\n", __func__);
+	return 0;
+}
+
 /* The timeout callback function to poll the host request flag */
 static void penwell_otg_hnp_poll_fn(unsigned long indicator)
 {
@@ -1011,6 +1121,8 @@ static void init_hsm(void)
 	struct intel_mid_otg_xceiv	*iotg = &pnw->iotg;
 	u32				val32;
 
+	mutex_lock(&iotg->otg.state_mutex);
+
 	/* read OTGSC after reset */
 	val32 = readl(iotg->base + CI_OTGSC);
 	dev_dbg(pnw->dev,
@@ -1051,6 +1163,7 @@ static void init_hsm(void)
 
 	penwell_otg_phy_low_power(1);
 
+	mutex_unlock(&iotg->otg.state_mutex);
 }
 
 static void update_hsm(void)
@@ -1293,6 +1406,7 @@ static void penwell_otg_work(struct work_struct *work)
 		"old state = %s\n", state_string(iotg->otg.state));
 
 	pm_runtime_get_sync(pnw->dev);
+	mutex_lock(&iotg->otg.state_mutex);
 
 	switch (iotg->otg.state) {
 	case OTG_STATE_UNDEFINED:
@@ -1744,22 +1858,14 @@ static void penwell_otg_work(struct work_struct *work)
 				|| hsm->power_up || hsm->adp_change)) {
 			/* power up / adp changes / srp detection should be
 			 * cleared at once after handled. */
-			if (hsm->power_up)
-				hsm->power_up = 0;
-
-			if (hsm->adp_change)
-				hsm->adp_change = 0;
-
-			if (hsm->a_srp_det)
-				hsm->a_srp_det = 0;
-
-			if (iotg->otg.set_vbus)
-				iotg->otg.set_vbus(&iotg->otg, true);
-
-			penwell_otg_add_timer(TA_WAIT_VRISE_TMR);
-			iotg->otg.state = OTG_STATE_A_WAIT_VRISE;
-
-			penwell_update_transceiver();
+			_penwell_otg_enter_mhl_mode(&iotg->otg);
+			if (otg_mhl_notify(&iotg->otg, 1)) {
+				_penwell_otg_exit_mhl_mode(&iotg->otg);
+				penwell_update_transceiver();
+			} else {
+				dev_dbg(pnw->dev, "Now in MHL mode\n");
+				break;
+			}
 		} else if (hsm->b_sess_end || hsm->a_sess_vld ||
 						!hsm->b_sess_vld) {
 			dev_dbg(pnw->dev,
@@ -1819,7 +1925,8 @@ static void penwell_otg_work(struct work_struct *work)
 		}
 		break;
 	case OTG_STATE_A_WAIT_BCON:
-		if (hsm->id == ID_B || hsm->id == ID_ACA_B || hsm->a_bus_drop) {
+		if (hsm->id == ID_B || hsm->id == ID_ACA_B || hsm->a_bus_drop ||
+			hsm->a_wait_bcon_tmout) {
 			/* Move to A_WAIT_VFALL state, user request */
 
 			/* Delete current timer and clear flags for B-Device */
@@ -1984,7 +2091,6 @@ static void penwell_otg_work(struct work_struct *work)
 			penwell_otg_del_timer(TA_AIDL_BDIS_TMR);
 
 			/* add kernel timer */
-			penwell_otg_add_timer(TA_WAIT_BCON_TMR);
 			iotg->otg.state = OTG_STATE_A_WAIT_BCON;
 		} else if (!hsm->b_conn && pnw->iotg.otg.host->b_hnp_enable) {
 			/* Move to A_PERIPHERAL state, HNP */
@@ -2082,7 +2188,6 @@ static void penwell_otg_work(struct work_struct *work)
 				dev_dbg(pnw->dev,
 						"host driver not loaded.\n");
 
-			penwell_otg_add_timer(TA_WAIT_BCON_TMR);
 			iotg->otg.state = OTG_STATE_A_WAIT_BCON;
 		} else if (!hsm->b_bus_suspend && hsm->a_bidl_adis_tmr) {
 			/* Client report suspend state end, delete timer */
@@ -2126,6 +2231,7 @@ static void penwell_otg_work(struct work_struct *work)
 		;
 	}
 
+	mutex_unlock(&iotg->otg.state_mutex);
 	pm_runtime_put_sync(pnw->dev);
 
 	dev_dbg(pnw->dev,
@@ -2559,6 +2665,8 @@ static int penwell_otg_probe(struct pci_dev *pdev,
 	pnw->iotg.otg.set_power = penwell_otg_set_power;
 	pnw->iotg.otg.set_vbus =  penwell_otg_set_vbus;
 	pnw->iotg.otg.start_srp = penwell_otg_start_srp;
+	pnw->iotg.otg.enter_mhl_mode = penwell_otg_enter_mhl_mode;
+	pnw->iotg.otg.exit_mhl_mode = penwell_otg_exit_mhl_mode;
 	pnw->iotg.set_adp_probe = NULL;
 	pnw->iotg.set_adp_sense = NULL;
 	pnw->iotg.otg.state = OTG_STATE_UNDEFINED;
@@ -2571,6 +2679,9 @@ static int penwell_otg_probe(struct pci_dev *pdev,
 	pnw->iotg.ulpi_ops.read = penwell_otg_ulpi_read;
 	pnw->iotg.ulpi_ops.write = penwell_otg_ulpi_write;
 
+	mutex_init(&pnw->iotg.otg.state_mutex);
+	mutex_init(&pnw->iotg.otg.mhl_mutex);
+
 	init_timer(&pnw->hsm_timer);
 	init_timer(&pnw->hnp_poll_timer);
 	init_completion(&pnw->adp.adp_comp);
@@ -2695,6 +2806,13 @@ static int penwell_otg_suspend(struct pci_dev *pdev, pm_message_t message)
 	struct intel_mid_otg_xceiv	*iotg = &pnw->iotg;
 	int				ret = 0;
 
+	mutex_lock(&iotg->otg.state_mutex);
+	if (iotg->otg.state == OTG_STATE_MHL) {
+		mutex_unlock(&iotg->otg.state_mutex);
+		return -EBUSY;
+	}
+	mutex_unlock(&iotg->otg.state_mutex);
+
 	/* Disbale OTG interrupts */
 	penwell_otg_intr(0);
 
@@ -2706,6 +2824,7 @@ static int penwell_otg_suspend(struct pci_dev *pdev, pm_message_t message)
 	destroy_workqueue(pnw->qwork);
 	pnw->qwork = NULL;
 
+	mutex_lock(&iotg->otg.state_mutex);
 	/* start actions */
 	switch (iotg->otg.state) {
 	case OTG_STATE_A_WAIT_VFALL:
@@ -2814,6 +2933,7 @@ static int penwell_otg_suspend(struct pci_dev *pdev, pm_message_t message)
 		dev_dbg(pnw->dev, "error state before suspend\n");
 		break;
 	}
+	mutex_unlock(&iotg->otg.state_mutex);
 
 	return ret;
 }
@@ -2874,6 +2994,7 @@ static int penwell_otg_runtime_suspend(struct device *dev)
 	switch (pnw->iotg.otg.state) {
 	case OTG_STATE_A_IDLE:
 	case OTG_STATE_B_IDLE:
+	case OTG_STATE_MHL:
 		/* Transceiver handle it itself */
 		penwell_otg_phy_low_power(1);
 		pci_save_state(pdev);
@@ -2909,13 +3030,14 @@ static int penwell_otg_runtime_resume(struct device *dev)
 
 	pdev = to_pci_dev(dev);
 
-	penwell_otg_intr(1);
 	penwell_otg_phy_low_power(0);
 
 	switch (pnw->iotg.otg.state) {
 	case OTG_STATE_A_IDLE:
 	case OTG_STATE_B_IDLE:
 		/* Transceiver handle it itself */
+		penwell_otg_intr(1);
+	case OTG_STATE_MHL:
 		pci_set_power_state(pdev, PCI_D0);
 		pci_restore_state(pdev);
 		ret = pci_enable_device(pdev);
@@ -2926,11 +3048,13 @@ static int penwell_otg_runtime_resume(struct device *dev)
 	case OTG_STATE_A_WAIT_BCON:
 	case OTG_STATE_A_HOST:
 	case OTG_STATE_A_SUSPEND:
+		penwell_otg_intr(1);
 		if (pnw->iotg.runtime_resume_host)
 			ret = pnw->iotg.runtime_resume_host(&pnw->iotg);
 		break;
 	case OTG_STATE_A_PERIPHERAL:
 	case OTG_STATE_B_PERIPHERAL:
+		penwell_otg_intr(1);
 		if (pnw->iotg.runtime_resume_peripheral)
 			ret = pnw->iotg.runtime_resume_peripheral(&pnw->iotg);
 		break;
diff --git a/include/linux/usb/penwell_otg.h b/include/linux/usb/penwell_otg.h
index 77776ca..f4284d0 100644
--- a/include/linux/usb/penwell_otg.h
+++ b/include/linux/usb/penwell_otg.h
@@ -229,7 +229,7 @@ enum penwell_otg_timer_type {
 };
 
 #define TA_WAIT_VRISE		100
-#define TA_WAIT_BCON		30000
+#define TA_WAIT_BCON		15000
 #define TA_AIDL_BDIS		1500
 #define TA_BIDL_ADIS		300
 #define TA_WAIT_VFALL		950
-- 
1.6.0.6

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux