/*
 * Copyright (C) 2011 Samsung, Inc.
 * Copyright (C) 2011 Google Inc.
 *
 * Author: Adam Hampson <ahampson@sta.samsung.com>
 *         Dima Zavin <dima@android.com>
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#define pr_fmt(fmt) "%s: " fmt, __func__

#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/platform_data/fsa9480.h>
#include <linux/regulator/consumer.h>
#include <linux/usb/otg.h>
#include <linux/delay.h>
#include <linux/sii9234.h>
#include <linux/i2c/twl.h>
#include <linux/mutex.h>
#include <linux/switch.h>
#include <linux/wakelock.h>

#include <plat/usb.h>

#include "mux.h"
#include "board-tuna.h"

#define GPIO_JACK_INT_N		4
#define GPIO_CP_USB_ON		22
#define GPIO_USB_OTG_ID		24
#define GPIO_MHL_SEL		96
#define GPIO_AP_SEL		97
#define GPIO_MUX3_SEL0		139
#define GPIO_MUX3_SEL1		140
#define GPIO_USB_ID_SEL		191
#define GPIO_IF_UART_SEL	101

#define GPIO_MHL_RST		161
#define GPIO_MHL_WAKEUP		64
#define GPIO_MHL_INT		175
#define GPIO_HDMI_EN		100

#define MUX3_SEL0_AP		1
#define MUX3_SEL1_AP		1
#define MUX3_SEL0_MHL		1
#define MUX3_SEL1_MHL		0
#define MUX3_SEL0_FSA		0
#define MUX3_SEL1_FSA		1

#define FSA3200_AP_SEL_AP	0
#define FSA3200_MHL_SEL_AP	0
#define FSA3200_AP_SEL_FSA	1
#define FSA3200_MHL_SEL_FSA	0
#define FSA3200_AP_SEL_MHL	1
#define FSA3200_MHL_SEL_MHL	1

#define USB_ID_SEL_FSA		0
#define USB_ID_SEL_MHL		1

#define IF_UART_SEL_DEFAULT	1
#define IF_UART_SEL_AP		1
#define IF_UART_SEL_CP		0

#define TUNA_MANUAL_USB_NONE	0
#define TUNA_MANUAL_USB_MODEM	1
#define TUNA_MANUAL_USB_AP	2

#define TUNA_MANUAL_UART_NONE	0
#define TUNA_MANUAL_UART_MODEM	1
#define TUNA_MANUAL_UART_LTE	2
#define TUNA_MANUAL_UART_AP	3

#define CHARGERUSB_CTRL1	0x8
#define CHARGERUSB_CTRL3	0xA
#define CHARGERUSB_CINLIMIT	0xE

#define TWL6030_VBUS_IRQ	(TWL6030_IRQ_BASE + USB_PRES_INTR_OFFSET)
#define TWL6030_VBUS_FLAGS	(IRQF_TRIGGER_FALLING | IRQF_ONESHOT)

#define TWL_REG_CONTROLLER_INT_MASK	0x00
#define TWL_CONTROLLER_MVBUS_DET	BIT(1)
#define TWL_CONTROLLER_RSVD		BIT(5)

#define TWL_REG_CONTROLLER_STAT1	0x03
#define TWL_STAT1_VBUS_DET		BIT(2)

struct tuna_otg {
	struct otg_transceiver		otg;
	struct device			dev;

	struct regulator		*vusb;
	struct work_struct		set_vbus_work;
	struct mutex			lock;

	bool				reg_on;
	bool				need_vbus_drive;
	int				usb_manual_mode;
	int				uart_manual_mode;
	int				current_device;

	struct switch_dev		dock_switch;
};
static struct tuna_otg tuna_otg_xceiv;

enum {
	TUNA_USB_MUX_FSA = 0,
	TUNA_USB_MUX_MHL,
	TUNA_USB_MUX_AP,
	NUM_TUNA_USB_MUX,

	TUNA_USB_MUX_DEFAULT = TUNA_USB_MUX_FSA,
};

static struct {
	int mux3_sel0;
	int mux3_sel1;
} tuna_usb_mux_states[] = {
	[TUNA_USB_MUX_FSA] = { MUX3_SEL0_FSA, MUX3_SEL1_FSA },
	[TUNA_USB_MUX_MHL] = { MUX3_SEL0_MHL, MUX3_SEL1_MHL },
	[TUNA_USB_MUX_AP] = { MUX3_SEL0_AP, MUX3_SEL1_AP },
};

static struct {
	int ap_sel;
	int mhl_sel;
} tuna_fsa3200_mux_pair_states[] = {
	[TUNA_USB_MUX_FSA] = { FSA3200_AP_SEL_FSA, FSA3200_MHL_SEL_FSA },
	[TUNA_USB_MUX_MHL] = { FSA3200_AP_SEL_MHL, FSA3200_MHL_SEL_MHL },
	[TUNA_USB_MUX_AP] = { FSA3200_AP_SEL_AP, FSA3200_MHL_SEL_AP },
};

static int tuna_usb_id_mux_states[] = {
	[TUNA_USB_MUX_FSA] = USB_ID_SEL_FSA,
	[TUNA_USB_MUX_MHL] = USB_ID_SEL_MHL,
	[TUNA_USB_MUX_AP] = USB_ID_SEL_FSA,
};

static ssize_t tuna_otg_usb_sel_show(struct device *dev,
				     struct device_attribute *attr,
				     char *buf);
static ssize_t tuna_otg_usb_sel_store(struct device *dev,
				      struct device_attribute *attr,
				      const char *buf, size_t size);
static ssize_t tuna_otg_uart_switch_show(struct device *dev,
					 struct device_attribute *attr,
					 char *buf);
static ssize_t tuna_otg_uart_switch_store(struct device *dev,
					  struct device_attribute *attr,
					  const char *buf, size_t size);

static DEVICE_ATTR(usb_sel, S_IRUSR | S_IWUSR,
			tuna_otg_usb_sel_show, tuna_otg_usb_sel_store);
static DEVICE_ATTR(uart_sel, S_IRUSR | S_IWUSR,
			tuna_otg_uart_switch_show, tuna_otg_uart_switch_store);

static struct attribute *manual_mode_attributes[] = {
	&dev_attr_usb_sel.attr,
	&dev_attr_uart_sel.attr,
	NULL,
};

static const struct attribute_group manual_mode_group = {
	.attrs = manual_mode_attributes,
};

static bool tuna_twl_chgctrl_init;

static void tuna_mux_usb(int state)
{
	BUG_ON(state >= NUM_TUNA_USB_MUX);

	pr_debug("mux to %d\n", state);
	gpio_direction_output(GPIO_MUX3_SEL0,
			      tuna_usb_mux_states[state].mux3_sel0);
	gpio_direction_output(GPIO_MUX3_SEL1,
			      tuna_usb_mux_states[state].mux3_sel1);
}

static void tuna_mux_usb_id(int state)
{
	BUG_ON(state >= NUM_TUNA_USB_MUX);

	pr_debug("mux to %d\n", state);
	gpio_direction_output(GPIO_USB_ID_SEL, tuna_usb_id_mux_states[state]);
}

static void tuna_fsa3200_mux_pair(int state)
{
	BUG_ON(state >= NUM_TUNA_USB_MUX);

	pr_debug("mux to %d\n", state);
	gpio_direction_output(GPIO_AP_SEL,
			      tuna_fsa3200_mux_pair_states[state].ap_sel);
	gpio_direction_output(GPIO_MHL_SEL,
			      tuna_fsa3200_mux_pair_states[state].mhl_sel);
}

static void tuna_mux_usb_to_fsa(bool enable)
{
	if (omap4_tuna_get_revision() >= 3) {
		tuna_fsa3200_mux_pair(enable ? TUNA_USB_MUX_FSA :
				TUNA_USB_MUX_DEFAULT);
	} else {
		tuna_mux_usb(enable ? TUNA_USB_MUX_FSA : TUNA_USB_MUX_DEFAULT);

		/* When switching ID away from FSA, we want to ensure we switch
		 * it off FSA, and force it to MHL. Ideally, we'd just say mux
		 * to default, but FSA is likely the default mux position and
		 * there's no way to force the ID pin to float to the FSA.
		 */
		tuna_mux_usb_id(enable ? TUNA_USB_MUX_FSA : TUNA_USB_MUX_MHL);
	}
}

static void tuna_mux_usb_to_mhl(bool enable)
{
	if (omap4_tuna_get_revision() >= 3) {
		tuna_fsa3200_mux_pair(enable ? TUNA_USB_MUX_MHL :
				TUNA_USB_MUX_DEFAULT);
	} else {
		tuna_mux_usb(enable ? TUNA_USB_MUX_MHL : TUNA_USB_MUX_DEFAULT);
		tuna_mux_usb_id(enable ? TUNA_USB_MUX_MHL : TUNA_USB_MUX_DEFAULT);
	}
}

static void tuna_vusb_enable(struct tuna_otg *tuna_otg, bool enable)
{
	/* delay getting the regulator until later */
	if (IS_ERR_OR_NULL(tuna_otg->vusb)) {
		tuna_otg->vusb = regulator_get(&tuna_otg->dev, "vusb");
		if (IS_ERR(tuna_otg->vusb)) {
			dev_err(&tuna_otg->dev, "cannot get vusb regulator\n");
			return;
		}
	}

	if (enable) {
		regulator_enable(tuna_otg->vusb);
		tuna_otg->reg_on = true;
	} else if (tuna_otg->reg_on) {
		regulator_disable(tuna_otg->vusb);
		tuna_otg->reg_on = false;
	}
}

static void tuna_set_vbus_drive(bool enable)
{
	if (enable) {
		/* Set the VBUS current limit to 500mA */
		twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x09,
				CHARGERUSB_CINLIMIT);

		/* The TWL6030 has a feature to automatically turn on
		 * boost mode (VBUS Drive) when the ID signal is not
		 * grounded.  This feature needs to be disabled on Tuna
		 * as the ID signal is not hooked up to the TWL6030.
		 */
		twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x21,
				CHARGERUSB_CTRL3);
		twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x40,
				CHARGERUSB_CTRL1);
	} else {
		twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x01,
				CHARGERUSB_CTRL3);
		twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0, CHARGERUSB_CTRL1);
	}
}

static void tuna_ap_usb_attach(struct tuna_otg *tuna_otg)
{
	tuna_vusb_enable(tuna_otg, true);

	if (omap4_tuna_get_revision() >= 3) {
		tuna_fsa3200_mux_pair(TUNA_USB_MUX_AP);
	} else {
		tuna_mux_usb(TUNA_USB_MUX_AP);
		tuna_mux_usb_id(TUNA_USB_MUX_FSA);
	}

	tuna_otg->otg.state = OTG_STATE_B_IDLE;
	tuna_otg->otg.default_a = false;
	tuna_otg->otg.last_event = USB_EVENT_VBUS;
	atomic_notifier_call_chain(&tuna_otg->otg.notifier,
				   USB_EVENT_VBUS,
				   tuna_otg->otg.gadget);
}

static void tuna_ap_usb_detach(struct tuna_otg *tuna_otg)
{
	tuna_vusb_enable(tuna_otg, false);

	tuna_otg->otg.state = OTG_STATE_B_IDLE;
	tuna_otg->otg.default_a = false;
	tuna_otg->otg.last_event = USB_EVENT_NONE;
	atomic_notifier_call_chain(&tuna_otg->otg.notifier,
				   USB_EVENT_NONE,
				   tuna_otg->otg.gadget);
}

static void tuna_cp_usb_attach(struct tuna_otg *tuna_otg)
{
	if (omap4_tuna_get_type() == TUNA_TYPE_MAGURO)
		gpio_set_value(GPIO_CP_USB_ON, 1);

	tuna_mux_usb_to_fsa(true);
}

static void tuna_cp_usb_detach(struct tuna_otg *tuna_otg)
{
	if (omap4_tuna_get_type() == TUNA_TYPE_MAGURO)
		gpio_set_value(GPIO_CP_USB_ON, 0);
}

static void tuna_usb_host_detach(struct tuna_otg *tuna_otg)
{
	/* Make sure the VBUS drive is turned off */
	tuna_set_vbus_drive(false);

	tuna_vusb_enable(tuna_otg, false);

	tuna_otg->otg.state = OTG_STATE_B_IDLE;
	tuna_otg->otg.default_a = false;
	tuna_otg->otg.last_event = USB_EVENT_NONE;
	atomic_notifier_call_chain(&tuna_otg->otg.notifier,
				   USB_EVENT_NONE,
				   tuna_otg->otg.gadget);
}

static void tuna_ap_uart_actions(struct tuna_otg *tuna_otg)
{
	tuna_mux_usb_to_fsa(true);
	gpio_set_value(GPIO_IF_UART_SEL, IF_UART_SEL_AP);
}

static void tuna_cp_uart_actions(struct tuna_otg *tuna_otg)
{
	tuna_mux_usb_to_fsa(true);
	gpio_set_value(GPIO_IF_UART_SEL, IF_UART_SEL_CP);
}

static void tuna_lte_uart_actions(struct tuna_otg *tuna_otg)
{
	tuna_mux_usb_to_fsa(true);

	/* The LTE modem's UART lines are connected to the V_AUDIO_L and
	 * V_AUDIO_R pins on the FSA9480.  The RIL will configure the FSA9480
	 * separately to set manual routing.
	 */
}

static void tuna_otg_mask_vbus_irq(void)
{
	twl6030_interrupt_mask(TWL6030_CHARGER_CTRL_INT_MASK,
				REG_INT_MSK_LINE_C);
	twl6030_interrupt_mask(TWL6030_CHARGER_CTRL_INT_MASK,
				REG_INT_MSK_STS_C);
}

static void tuna_otg_unmask_vbus_irq(void)
{
	if (!tuna_twl_chgctrl_init) {
		int r;

		r = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
				     (u8) ~(TWL_CONTROLLER_RSVD |
					    TWL_CONTROLLER_MVBUS_DET),
				     TWL_REG_CONTROLLER_INT_MASK);

		if (r)
			pr_err_once("%s: Error writing twl charge ctrl int mask\n",
				    __func__);
		else
			tuna_twl_chgctrl_init = true;
	}

	twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
				REG_INT_MSK_LINE_C);
	twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
				REG_INT_MSK_STS_C);
}

static bool tuna_otg_vbus_present(void)
{
	u8 vbus_state;

	twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &vbus_state,
				TWL_REG_CONTROLLER_STAT1);

	return !!(vbus_state & TWL_STAT1_VBUS_DET);
}

static void tuna_fsa_usb_detected(int device)
{
	struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
	int old_device;

	mutex_lock(&tuna_otg->lock);

	old_device = tuna_otg->current_device;
	tuna_otg->current_device = device;

	pr_debug("detected %x\n", device);
	switch (device) {
	case FSA9480_DETECT_AV_365K_CHARGER:
		tuna_otg_set_dock_switch(1);
		/* intentional fall-through */
	case FSA9480_DETECT_USB:
		if (tuna_otg->usb_manual_mode == TUNA_MANUAL_USB_MODEM)
			tuna_cp_usb_attach(tuna_otg);
		else
			tuna_ap_usb_attach(tuna_otg);
		break;
	case FSA9480_DETECT_AV_POWERED:
		tuna_ap_usb_attach(tuna_otg);
		break;
	case FSA9480_DETECT_CHARGER:
		tuna_mux_usb_to_fsa(true);

		tuna_otg->otg.state = OTG_STATE_B_IDLE;
		tuna_otg->otg.default_a = false;
		tuna_otg->otg.last_event = USB_EVENT_CHARGER;
		atomic_notifier_call_chain(&tuna_otg->otg.notifier,
					   USB_EVENT_CHARGER,
					   tuna_otg->otg.gadget);
		break;
	case FSA9480_DETECT_USB_HOST:
		tuna_vusb_enable(tuna_otg, true);

		if (omap4_tuna_get_revision() >= 3) {
			tuna_fsa3200_mux_pair(TUNA_USB_MUX_AP);
		} else {
			tuna_mux_usb(TUNA_USB_MUX_AP);
			tuna_mux_usb_id(TUNA_USB_MUX_FSA);
		}

		tuna_otg->otg.state = OTG_STATE_A_IDLE;
		tuna_otg->otg.default_a = true;
		tuna_otg->otg.last_event = USB_EVENT_ID;
		atomic_notifier_call_chain(&tuna_otg->otg.notifier,
					   USB_EVENT_ID,
					   tuna_otg->otg.gadget);
		break;
	case FSA9480_DETECT_NONE:
		tuna_mux_usb_to_fsa(true);

		switch (old_device) {
		case FSA9480_DETECT_JIG:
			if (tuna_otg->uart_manual_mode == TUNA_MANUAL_UART_NONE)
				tuna_ap_uart_actions(tuna_otg);
			break;
		case FSA9480_DETECT_AV_365K_CHARGER:
			tuna_otg_set_dock_switch(0);
			/* intentional fall-through */
		case FSA9480_DETECT_USB:
			if (tuna_otg->usb_manual_mode == TUNA_MANUAL_USB_MODEM)
				tuna_cp_usb_detach(tuna_otg);
			else
				tuna_ap_usb_detach(tuna_otg);
			break;
		case FSA9480_DETECT_AV_POWERED:
			tuna_ap_usb_detach(tuna_otg);
			break;
		case FSA9480_DETECT_USB_HOST:
			tuna_usb_host_detach(tuna_otg);
			break;
		case FSA9480_DETECT_CHARGER:
			tuna_ap_usb_detach(tuna_otg);
			break;
		case FSA9480_DETECT_AV_365K:
			tuna_otg_set_dock_switch(0);
			break;
		case FSA9480_DETECT_UART:
		default:
			break;
		};
		break;
	case FSA9480_DETECT_JIG:
		switch (tuna_otg->uart_manual_mode) {
		case TUNA_MANUAL_UART_AP:
			tuna_ap_uart_actions(tuna_otg);
			break;
		case TUNA_MANUAL_UART_LTE:
			tuna_lte_uart_actions(tuna_otg);
			break;
		case TUNA_MANUAL_UART_MODEM:
		default:
			tuna_cp_uart_actions(tuna_otg);
			break;
		};
		break;
	case FSA9480_DETECT_AV_365K:
		tuna_otg_set_dock_switch(1);
		break;
	case FSA9480_DETECT_UART:
	default:
		break;
	}

	mutex_unlock(&tuna_otg->lock);
}

static struct fsa9480_detect_set fsa_detect_sets[] = {
	{
		.prio = TUNA_OTG_ID_FSA9480_PRIO,
		.mask = FSA9480_DETECT_ALL & ~FSA9480_DETECT_AV_POWERED,
	},
	{
		.prio = TUNA_OTG_ID_SII9234_FAILED_PRIO,
		.mask = FSA9480_DETECT_AV_POWERED,
	},
	{
		.prio = TUNA_OTG_ID_FSA9480_LAST_PRIO,
		.mask = 0,
		.fallback = true,
	},
};

static struct fsa9480_platform_data tuna_fsa9480_pdata = {
	.detect_time		= 500,
	.detect_sets		= fsa_detect_sets,
	.num_sets		= ARRAY_SIZE(fsa_detect_sets),

	.enable			= tuna_mux_usb_to_fsa,
	.detected		= tuna_fsa_usb_detected,
	.external_id		= GPIO_USB_OTG_ID,

	.external_vbus_irq	= TWL6030_VBUS_IRQ,
	.external_vbus_flags	= TWL6030_VBUS_FLAGS,
	.mask_vbus_irq		= tuna_otg_mask_vbus_irq,
	.unmask_vbus_irq	= tuna_otg_unmask_vbus_irq,
	.vbus_present		= tuna_otg_vbus_present,
};

static struct i2c_board_info __initdata tuna_connector_i2c4_boardinfo[] = {
	{
		I2C_BOARD_INFO("fsa9480", 0x4A >> 1),
		.irq = OMAP_GPIO_IRQ(GPIO_JACK_INT_N),
		.platform_data = &tuna_fsa9480_pdata,
	},
};

static int tuna_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host)
{
	otg->host = host;
	if (!host)
		otg->state = OTG_STATE_UNDEFINED;
	return 0;
}

static int tuna_otg_set_peripheral(struct otg_transceiver *otg,
				   struct usb_gadget *gadget)
{
	otg->gadget = gadget;
	if (!gadget)
		otg->state = OTG_STATE_UNDEFINED;
	return 0;
}

static void tuna_otg_work(struct work_struct *data)
{
	struct tuna_otg *tuna_otg = container_of(data, struct tuna_otg,
						set_vbus_work);

	mutex_lock(&tuna_otg->lock);

	/* Only allow VBUS drive when in host mode. */
	if (tuna_otg->current_device != FSA9480_DETECT_USB_HOST) {
		mutex_unlock(&tuna_otg->lock);
		return;
	}

	tuna_set_vbus_drive(tuna_otg->need_vbus_drive);

	mutex_unlock(&tuna_otg->lock);
}

static int tuna_otg_set_vbus(struct otg_transceiver *otg, bool enabled)
{
	struct tuna_otg *tuna_otg = container_of(otg, struct tuna_otg, otg);

	dev_dbg(otg->dev, "vbus %s\n", enabled ? "on" : "off");

	tuna_otg->need_vbus_drive = enabled;
	schedule_work(&tuna_otg->set_vbus_work);

	return 0;
}

static int tuna_otg_phy_init(struct otg_transceiver *otg)
{
	if (otg->last_event == USB_EVENT_ID)
		omap4430_phy_power(otg->dev, 1, 1);
	else
		omap4430_phy_power(otg->dev, 0, 1);
	return 0;
}

static void tuna_otg_phy_shutdown(struct otg_transceiver *otg)
{
	omap4430_phy_power(otg->dev, 0, 0);
}

static int tuna_otg_set_suspend(struct otg_transceiver *otg, int suspend)
{
	return omap4430_phy_suspend(otg->dev, suspend);
}

static ssize_t tuna_otg_usb_sel_show(struct device *dev,
				     struct device_attribute *attr,
				     char *buf)
{
	struct tuna_otg *tuna_otg = dev_get_drvdata(dev);
	const char* mode;

	switch (tuna_otg->usb_manual_mode) {
	case TUNA_MANUAL_USB_AP:
		mode = "PDA";
		break;
	case TUNA_MANUAL_USB_MODEM:
		mode = "MODEM";
		break;
	default:
		mode = "NONE";
	};

	return sprintf(buf, "%s\n", mode);
}

static ssize_t tuna_otg_usb_sel_store(struct device *dev,
				      struct device_attribute *attr,
				      const char *buf, size_t size)
{
	struct tuna_otg *tuna_otg = dev_get_drvdata(dev);
	int old_mode;

	mutex_lock(&tuna_otg->lock);

	old_mode = tuna_otg->usb_manual_mode;

	if (!strncasecmp(buf, "PDA", 3)) {
		tuna_otg->usb_manual_mode = TUNA_MANUAL_USB_AP;

		/* If we are transitioning from CP USB to AP USB then notify the
		 * USB stack that is now attached.
		 */
		if (tuna_otg->current_device == FSA9480_DETECT_USB &&
				old_mode == TUNA_MANUAL_USB_MODEM) {
			tuna_cp_usb_detach(tuna_otg);
			tuna_ap_usb_attach(tuna_otg);
		}
	} else if (!strncasecmp(buf, "MODEM", 5)) {
		tuna_otg->usb_manual_mode = TUNA_MANUAL_USB_MODEM;

		/* If we are transitioning from AP USB to CP USB then notify the
		 * USB stack that is has been detached.
		 */
		if (tuna_otg->current_device == FSA9480_DETECT_USB &&
				(old_mode == TUNA_MANUAL_USB_AP ||
				old_mode == TUNA_MANUAL_USB_NONE)) {
			tuna_ap_usb_detach(tuna_otg);
			tuna_cp_usb_attach(tuna_otg);
		}
	} else if (!strncasecmp(buf, "NONE", 4)) {
		tuna_otg->usb_manual_mode = TUNA_MANUAL_USB_NONE;

		/* If we are transitioning from CP USB to AP USB then notify the
		 * USB stack that it is now attached.
		 */
		if (tuna_otg->current_device == FSA9480_DETECT_USB &&
				old_mode == TUNA_MANUAL_USB_MODEM) {
			tuna_cp_usb_detach(tuna_otg);
			tuna_ap_usb_attach(tuna_otg);
		}
	}

	mutex_unlock(&tuna_otg->lock);

	return size;
}

static ssize_t tuna_otg_uart_switch_show(struct device *dev,
					 struct device_attribute *attr,
					 char *buf)
{
	struct tuna_otg *tuna_otg = dev_get_drvdata(dev);
	const char* mode;

	switch (tuna_otg->uart_manual_mode) {
	case TUNA_MANUAL_UART_AP:
		mode = "PDA";
		break;
	case TUNA_MANUAL_UART_MODEM:
		mode = "MODEM";
		break;
	case TUNA_MANUAL_UART_LTE:
		mode = "LTEMODEM";
		break;
	default:
		mode = "NONE";
	};

	return sprintf(buf, "%s\n", mode);
}

static ssize_t tuna_otg_uart_switch_store(struct device *dev,
					  struct device_attribute *attr,
					  const char *buf, size_t size)
{
	struct tuna_otg *tuna_otg = dev_get_drvdata(dev);

	mutex_lock(&tuna_otg->lock);

	if (!strncasecmp(buf, "PDA", 3)) {
		tuna_otg->uart_manual_mode = TUNA_MANUAL_UART_AP;

		if (tuna_otg->current_device == FSA9480_DETECT_JIG)
			tuna_ap_uart_actions(tuna_otg);
	} else if (!strncasecmp(buf, "MODEM", 5)) {
		tuna_otg->uart_manual_mode = TUNA_MANUAL_UART_MODEM;

		if (tuna_otg->current_device == FSA9480_DETECT_JIG)
			tuna_cp_uart_actions(tuna_otg);
	} else if (!strncasecmp(buf, "LTEMODEM", 8) &&
			omap4_tuna_get_type() == TUNA_TYPE_TORO) {
		tuna_otg->uart_manual_mode = TUNA_MANUAL_UART_LTE;

		if (tuna_otg->current_device == FSA9480_DETECT_JIG)
			tuna_lte_uart_actions(tuna_otg);
	} else if (!strncasecmp(buf, "NONE", 4)) {
		tuna_otg->uart_manual_mode = TUNA_MANUAL_UART_NONE;

		if (tuna_otg->current_device == FSA9480_DETECT_JIG)
			tuna_ap_uart_actions(tuna_otg);
	}

	mutex_unlock(&tuna_otg->lock);

	return size;
}

static struct wake_lock sii9234_wake_lock;

#define OMAP_HDMI_HPD_ADDR	0x4A100098
#define OMAP_HDMI_PULLTYPE_MASK	0x00000010
static void sii9234_power(int on)
{
	struct omap_mux_partition *p = omap_mux_get("core");

	u16 mux;

	mux = omap_mux_read(p, OMAP4_CTRL_MODULE_PAD_HDMI_HPD_OFFSET);

	if (on) {
		gpio_set_value(GPIO_HDMI_EN, 1);
		msleep(20);
		gpio_set_value(GPIO_MHL_RST, 1);

		omap_mux_write(p, mux | OMAP_PULL_UP,
			       OMAP4_CTRL_MODULE_PAD_HDMI_HPD_OFFSET);
	} else {
		omap_mux_write(p, mux & ~OMAP_PULL_UP,
			       OMAP4_CTRL_MODULE_PAD_HDMI_HPD_OFFSET);

		gpio_set_value(GPIO_HDMI_EN, 0);
		gpio_set_value(GPIO_MHL_RST, 0);

	}
}

static void sii9234_enable_vbus(bool enable)
{

}

static void sii9234_connect(bool on, u8 *devcap)
{
	struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
	unsigned long val;
	int dock = 0;

	if (on) {
		val = USB_EVENT_VBUS;
		if (devcap) {
			u16 adopter_id =
				(devcap[MHL_DEVCAP_ADOPTER_ID_H] << 8) |
				devcap[MHL_DEVCAP_ADOPTER_ID_L];
			u16 device_id =
				(devcap[MHL_DEVCAP_DEVICE_ID_H] << 8) |
				devcap[MHL_DEVCAP_DEVICE_ID_L];

			if (adopter_id == 0x3333 || adopter_id == 321) {
				if (devcap[MHL_DEVCAP_RESERVED] == 2)
					val = USB_EVENT_CHARGER;

				if (device_id == 0x1234)
					dock = 1;
			}
		}

		wake_lock(&sii9234_wake_lock);
	} else {
		wake_unlock(&sii9234_wake_lock);
		val = USB_EVENT_NONE;
	}

	tuna_otg->otg.state = OTG_STATE_B_IDLE;
	tuna_otg->otg.default_a = false;
	tuna_otg->otg.last_event = val;

	atomic_notifier_call_chain(&tuna_otg->otg.notifier,
				   val, tuna_otg->otg.gadget);
	tuna_otg_set_dock_switch(dock);

}

void tuna_otg_pogo_charger(enum pogo_power_state pogo_state)
{
	struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
	unsigned long power_state;

	switch (pogo_state) {
		case POGO_POWER_CHARGER:
			power_state = USB_EVENT_CHARGER;
			break;
		case POGO_POWER_HOST:
			power_state = USB_EVENT_VBUS;
			break;
		case POGO_POWER_DISCONNECTED:
		default:
			power_state = USB_EVENT_NONE;
			break;
	}

	tuna_otg->otg.state = OTG_STATE_B_IDLE;
	tuna_otg->otg.default_a = false;
	tuna_otg->otg.last_event = power_state;
	atomic_notifier_call_chain(&tuna_otg->otg.notifier, power_state,
				tuna_otg->otg.gadget);
}

void tuna_otg_set_dock_switch(int enable)
{
	struct tuna_otg *tuna_otg = &tuna_otg_xceiv;

	switch_set_state(&tuna_otg->dock_switch, enable);
}

static struct sii9234_platform_data sii9234_pdata = {
	.prio = TUNA_OTG_ID_SII9234_PRIO,
	.enable = tuna_mux_usb_to_mhl,
	.power = sii9234_power,
	.enable_vbus = sii9234_enable_vbus,
	.connect = sii9234_connect,
};

static struct i2c_board_info __initdata tuna_i2c5_boardinfo[] = {
	{
		I2C_BOARD_INFO("sii9234_mhl_tx", 0x72>>1),
		.irq = OMAP_GPIO_IRQ(GPIO_MHL_INT),
		.platform_data = &sii9234_pdata,
	},
	{
		I2C_BOARD_INFO("sii9234_tpi", 0x7A>>1),
		.platform_data = &sii9234_pdata,
	},
	{
		I2C_BOARD_INFO("sii9234_hdmi_rx", 0x92>>1),
		.platform_data = &sii9234_pdata,
	},
	{
		I2C_BOARD_INFO("sii9234_cbus", 0xC8>>1),
		.platform_data = &sii9234_pdata,
	},
};

int __init omap4_tuna_connector_init(void)
{
	struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
	int ret;

	if (omap4_tuna_get_revision() >= 3) {
		gpio_request(GPIO_MHL_SEL, "fsa3200_mhl_sel");
		gpio_request(GPIO_AP_SEL, "fsa3200_ap_sel");

		tuna_fsa3200_mux_pair(TUNA_USB_MUX_DEFAULT);

		omap_mux_init_gpio(GPIO_MHL_SEL, OMAP_PIN_OUTPUT);
		omap_mux_init_gpio(GPIO_AP_SEL, OMAP_PIN_OUTPUT);
	} else {
		gpio_request(GPIO_MUX3_SEL0, "usb_mux3_sel0");
		gpio_request(GPIO_MUX3_SEL1, "usb_mux3_sel1");
		gpio_request(GPIO_USB_ID_SEL, "usb_id_sel");

		tuna_mux_usb(TUNA_USB_MUX_DEFAULT);
		tuna_mux_usb_id(TUNA_USB_MUX_DEFAULT);

		omap_mux_init_gpio(GPIO_MUX3_SEL0, OMAP_PIN_OUTPUT);
		omap_mux_init_gpio(GPIO_MUX3_SEL1, OMAP_PIN_OUTPUT);
		omap_mux_init_gpio(GPIO_USB_ID_SEL, OMAP_PIN_OUTPUT);
	}

	if (omap4_tuna_get_type() == TUNA_TYPE_MAGURO) {
		gpio_request(GPIO_CP_USB_ON, "cp_usb_on");
		omap_mux_init_gpio(GPIO_CP_USB_ON, OMAP_PIN_OUTPUT);
		gpio_direction_output(GPIO_CP_USB_ON, 0);
	}

	omap_mux_init_gpio(GPIO_IF_UART_SEL, OMAP_PIN_OUTPUT);
	gpio_request(GPIO_IF_UART_SEL, "uart_sel");
	gpio_direction_output(GPIO_IF_UART_SEL, IF_UART_SEL_DEFAULT);

	omap_mux_init_gpio(GPIO_USB_OTG_ID, OMAP_PIN_INPUT |
			   OMAP_WAKEUP_EN);

	omap_mux_init_gpio(GPIO_JACK_INT_N,
			   OMAP_PIN_INPUT_PULLUP |
			   OMAP_PIN_OFF_INPUT_PULLUP);

	wake_lock_init(&sii9234_wake_lock, WAKE_LOCK_SUSPEND, "sii9234(mhl)");

	mutex_init(&tuna_otg->lock);

	INIT_WORK(&tuna_otg->set_vbus_work, tuna_otg_work);

	device_initialize(&tuna_otg->dev);
	dev_set_name(&tuna_otg->dev, "%s", "tuna_otg");
	ret = device_add(&tuna_otg->dev);
	if (ret) {
		pr_err("%s: cannot reg device '%s' (%d)\n", __func__,
		       dev_name(&tuna_otg->dev), ret);
		return ret;
	}

	dev_set_drvdata(&tuna_otg->dev, tuna_otg);

	tuna_otg->otg.dev		= &tuna_otg->dev;
	tuna_otg->otg.label		= "tuna_otg_xceiv";
	tuna_otg->otg.set_host		= tuna_otg_set_host;
	tuna_otg->otg.set_peripheral	= tuna_otg_set_peripheral;
	tuna_otg->otg.set_suspend	= tuna_otg_set_suspend;
	tuna_otg->otg.set_vbus		= tuna_otg_set_vbus;
	tuna_otg->otg.init		= tuna_otg_phy_init;
	tuna_otg->otg.shutdown		= tuna_otg_phy_shutdown;

	ATOMIC_INIT_NOTIFIER_HEAD(&tuna_otg->otg.notifier);

	ret = otg_set_transceiver(&tuna_otg->otg);
	if (ret)
		pr_err("tuna_otg: cannot set transceiver (%d)\n", ret);

	omap4430_phy_init(&tuna_otg->dev);
	tuna_otg_set_suspend(&tuna_otg->otg, 0);

	i2c_register_board_info(4, tuna_connector_i2c4_boardinfo,
				ARRAY_SIZE(tuna_connector_i2c4_boardinfo));

	ret = sysfs_create_group(&tuna_otg->dev.kobj, &manual_mode_group);
	if (ret)
		pr_err("tuna_otg: Unable to create manual mode sysfs group"
			"(%d)\n", ret);

	gpio_request(GPIO_HDMI_EN, NULL);
	omap_mux_init_gpio(GPIO_HDMI_EN, OMAP_PIN_OUTPUT);
	gpio_direction_output(GPIO_HDMI_EN, 0);

	gpio_request(GPIO_MHL_RST, NULL);
	omap_mux_init_gpio(GPIO_MHL_RST, OMAP_PIN_OUTPUT);
	gpio_direction_output(GPIO_MHL_RST, 0);

	gpio_request(GPIO_MHL_INT, NULL);
	omap_mux_init_gpio(GPIO_MHL_INT, OMAP_PIN_INPUT);
	gpio_direction_input(GPIO_MHL_INT);

	gpio_request(TUNA_GPIO_HDMI_HPD, NULL);
	omap_mux_init_gpio(TUNA_GPIO_HDMI_HPD, OMAP_PIN_INPUT | OMAP_PULL_ENA);
	gpio_direction_input(TUNA_GPIO_HDMI_HPD);

	i2c_register_board_info(5, tuna_i2c5_boardinfo,
			ARRAY_SIZE(tuna_i2c5_boardinfo));

	tuna_otg->dock_switch.name = "dock";
	switch_dev_register(&tuna_otg->dock_switch);

	return 0;
}
