summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManoj Rao <manojraj@codeaurora.org>2012-11-27 21:27:38 -0800
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 20:13:45 -0700
commit660b1cc176fcbc67b76d4ba2616b45ec34a2e7d3 (patch)
tree83a3d8e8058bd337ea80301577b86c870c90a28c
parent5989eb689e86158c2eb75832c017afca0aaa611b (diff)
msm: 8974: MHL-USB handshake API implementation
This change adds the implementation of mhl discovery detection procedure, USB hand-off function. Three APIs are exposed from MHL driver to USB driver: for register/unregistering callbacks from USB and triggering device discovery detection in MHL hardware. By default, all the USB interrupts are directed to the USB hardware which is detected by USB driver and at appropriate points the USB driver triggers the MHL device discovery and uses the results to either hand control to MHL driver or continue with it's own detection mechanism. Change-Id: Ic1e22bc4c446de209f2e72d5dda8b823bfd398c8 Signed-off-by: Manoj Rao <manojraj@codeaurora.org>
-rw-r--r--drivers/video/fbdev/msm/mhl_sii8334.c79
1 files changed, 77 insertions, 2 deletions
diff --git a/drivers/video/fbdev/msm/mhl_sii8334.c b/drivers/video/fbdev/msm/mhl_sii8334.c
index 6a63964ee036..2982aaa3ae42 100644
--- a/drivers/video/fbdev/msm/mhl_sii8334.c
+++ b/drivers/video/fbdev/msm/mhl_sii8334.c
@@ -18,6 +18,7 @@
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/types.h>
+#include <linux/usb/msm_hsusb.h>
#include <linux/mhl_8334.h>
#include "mdss_fb.h"
@@ -58,6 +59,9 @@ struct mhl_tx_ctrl {
uint8_t cur_state;
uint8_t chip_rev_id;
int mhl_mode;
+ struct completion rgnd_done;
+ void (*notify_usb_online)(int online);
+ struct usb_ext_notification *mhl_info;
};
@@ -196,6 +200,52 @@ static int mhl_sii_reset_pin(struct mhl_tx_ctrl *mhl_ctrl, int on)
return 0;
}
+/* USB_HANDSHAKING FUNCTIONS */
+static int mhl_sii_device_discovery(void *data, int id,
+ void (*usb_notify_cb)(int online))
+{
+ int timeout, rc;
+ struct mhl_tx_ctrl *mhl_ctrl = data;
+ struct i2c_client *client = mhl_ctrl->i2c_handle;
+
+ if (id) {
+ /* When MHL cable is disconnected we get a sii8334
+ * mhl_disconnect interrupt which is handled separately.
+ */
+ pr_debug("%s: USB ID pin high\n", __func__);
+ return id;
+ }
+
+ if (!mhl_ctrl || !usb_notify_cb) {
+ pr_warn("%s: cb || ctrl is NULL\n", __func__);
+ /* return "USB" so caller can proceed */
+ return -EINVAL;
+ }
+
+ if (!mhl_ctrl->notify_usb_online)
+ mhl_ctrl->notify_usb_online = usb_notify_cb;
+
+ MHL_SII_REG_NAME_WR(REG_DISC_CTRL1, 0x27);
+ msleep(50);
+ if (mhl_ctrl->cur_state == POWER_STATE_D3) {
+ /* give MHL driver chance to handle RGND interrupt */
+ INIT_COMPLETION(mhl_ctrl->rgnd_done);
+ timeout = wait_for_completion_interruptible_timeout
+ (&mhl_ctrl->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");
+ return 0;
+ }
+ rc = mhl_ctrl->mhl_mode ? 0 : 1;
+ } else {
+ /* not in D3. already in MHL mode */
+ rc = 0;
+ }
+ return rc;
+}
+
static void cbus_reset(struct i2c_client *client)
{
uint8_t i;
@@ -545,14 +595,15 @@ static int mhl_msm_read_rgnd_int(struct mhl_tx_ctrl *mhl_ctrl)
if (0x02 == rgnd_imp) {
pr_debug("%s: mhl sink\n", __func__);
- MHL_SII_REG_NAME_MOD(REG_DISC_CTRL9, BIT0, BIT0);
mhl_ctrl->mhl_mode = 1;
+ if (mhl_ctrl->notify_usb_online)
+ mhl_ctrl->notify_usb_online(1);
} else {
pr_debug("%s: non-mhl sink\n", __func__);
mhl_ctrl->mhl_mode = 0;
- MHL_SII_REG_NAME_MOD(REG_DISC_CTRL9, BIT3, BIT3);
switch_mode(mhl_ctrl, POWER_STATE_D3);
}
+ complete(&mhl_ctrl->rgnd_done);
return mhl_ctrl->mhl_mode ?
MHL_DISCOVERY_RESULT_MHL : MHL_DISCOVERY_RESULT_USB;
}
@@ -651,6 +702,8 @@ static void dev_detect_isr(struct mhl_tx_ctrl *mhl_ctrl)
reg = MHL_SII_REG_NAME_RD(REG_INTR4);
MHL_SII_REG_NAME_WR(REG_INTR4, reg);
mhl_msm_disconnection(mhl_ctrl);
+ if (mhl_ctrl->notify_usb_online)
+ mhl_ctrl->notify_usb_online(0);
}
if ((mhl_ctrl->cur_state != POWER_STATE_D0_MHL) &&\
@@ -1048,6 +1101,7 @@ static int mhl_i2c_probe(struct i2c_client *client,
int rc = 0;
struct mhl_tx_platform_data *pdata = NULL;
struct mhl_tx_ctrl *mhl_ctrl;
+ struct usb_ext_notification *mhl_info = NULL;
mhl_ctrl = devm_kzalloc(&client->dev, sizeof(*mhl_ctrl), GFP_KERNEL);
if (!mhl_ctrl) {
@@ -1107,6 +1161,8 @@ static int mhl_i2c_probe(struct i2c_client *client,
goto failed_probe;
}
+ init_completion(&mhl_ctrl->rgnd_done);
+
pr_debug("%s: IRQ from GPIO INTR = %d\n",
__func__, mhl_ctrl->i2c_handle->irq);
pr_debug("%s: Driver name = [%s]\n", __func__,
@@ -1123,8 +1179,25 @@ static int mhl_i2c_probe(struct i2c_client *client,
pr_debug("request_threaded_irq succeeded\n");
}
pr_debug("%s: i2c client addr is [%x]\n", __func__, client->addr);
+
+ mhl_info = devm_kzalloc(&client->dev, sizeof(*mhl_info), GFP_KERNEL);
+ if (!mhl_info) {
+ pr_err("%s: alloc mhl info failed\n", __func__);
+ goto failed_probe;
+ }
+
+ mhl_info->ctxt = mhl_ctrl;
+ mhl_info->notify = mhl_sii_device_discovery;
+ if (msm_register_usb_ext_notification(mhl_info)) {
+ pr_err("%s: register for usb notifcn failed\n", __func__);
+ goto failed_probe;
+ }
+ mhl_ctrl->mhl_info = mhl_info;
return 0;
failed_probe:
+ /* do not deep-free */
+ if (mhl_info)
+ devm_kfree(&client->dev, mhl_info);
failed_dt_data:
if (pdata)
devm_kfree(&client->dev, pdata);
@@ -1148,6 +1221,8 @@ static int mhl_i2c_remove(struct i2c_client *client)
free_irq(mhl_ctrl->i2c_handle->irq, mhl_ctrl);
mhl_gpio_config(mhl_ctrl, 0);
mhl_vreg_config(mhl_ctrl, 0);
+ if (mhl_ctrl->mhl_info)
+ devm_kfree(&client->dev, mhl_ctrl->mhl_info);
if (mhl_ctrl->pdata)
devm_kfree(&client->dev, mhl_ctrl->pdata);
devm_kfree(&client->dev, mhl_ctrl);