>From 1b24742dc9c1bc09a54f6de2fbc2b1c79b5fcae0 Mon Sep 17 00:00:00 2001
From: Hao Wu <[email protected]>
Date: Sun, 28 Nov 2010 15:08:02 +0800
Subject: [PATCH] usb: penwell_otg: add support for MHL-USB coexistence

Add MHL support in penwell_otg transceiver driver including
pm related support.

Signed-off-by: Hao Wu <[email protected]>
---
 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 724a516..d25959a 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);
@@ -2703,6 +2814,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);
 
@@ -2714,6 +2832,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:
@@ -2822,6 +2941,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;
 }
@@ -2882,6 +3002,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);
@@ -2917,13 +3038,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);
@@ -2934,11 +3056,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

Attachment: 0002-usb-penwell_otg-add-support-for-MHL-USB-coexistenc.patch
Description: 0002-usb-penwell_otg-add-support-for-MHL-USB-coexistenc.patch

_______________________________________________
MeeGo-kernel mailing list
[email protected]
http://lists.meego.com/listinfo/meego-kernel

Reply via email to