misc: cable_detect: Add HTC cable detect driver

HTC kernel version: villeu-jb-crc-3.4.10-ae8b65e

Change-Id: Ib9605aee2c2ed4f98db287816936bb221a651100
diff --git a/drivers/misc/cable_detect_8xxx.c b/drivers/misc/cable_detect_8xxx.c
new file mode 100644
index 0000000..00ed513
--- /dev/null
+++ b/drivers/misc/cable_detect_8xxx.c
@@ -0,0 +1,1105 @@
+/* drivers/misc/cable_detect.c - cable detect driver
+ *
+ * Copyright (C) 2009 HTC Corporation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/pmic8058-xoadc.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <mach/board.h>
+
+#ifdef CONFIG_RESET_BY_CABLE_IN
+#include <mach/board_htc.h>
+#endif
+
+#include <mach/cable_detect.h>
+#include <mach/mpp.h>
+#include <linux/switch.h>
+
+#ifdef CONFIG_HTC_HEADSET_MGR
+#include <mach/htc_headset_mgr.h>
+#ifdef CONFIG_HTC_HEADSET_MISC
+#include <mach/htc_headset_misc.h>
+#endif
+#endif
+
+#ifdef CONFIG_FB_MSM_HDMI_MHL_SII9234
+#include "../video/msm/sii9234/TPI.h"
+#endif
+
+#include "linux/mfd/pm8xxx/pm8921-charger.h"
+
+static int vbus;
+
+static struct switch_dev dock_switch = {
+	.name = "dock",
+};
+
+struct cable_detect_info {
+	spinlock_t lock;
+
+	int vbus_mpp_gpio;
+	int vbus_mpp_irq;
+
+	
+	int ad_en_active_state;
+	int ad_en_gpio;
+	int ad_en_irq;
+
+	enum usb_connect_type connect_type;
+	
+	int usb_id_pin_gpio;
+	__u8 detect_type;
+	__u8 accessory_type;
+	int idpin_irq;
+	u8 mfg_usb_carkit_enable;
+	u8 mhl_reset_gpio;
+	bool mhl_version_ctrl_flag;
+	struct workqueue_struct *cable_detect_wq;
+	struct delayed_work cable_detect_work;
+	struct delayed_work vbus_detect_work;
+	struct wake_lock vbus_wlock;
+	struct wake_lock cable_detect_wlock;
+	void (*usb_uart_switch)(int);
+	void (*usb_dpdn_switch)(int);
+	struct usb_id_mpp_config_data *mpp_data;
+	void (*config_usb_id_gpios)(bool enable);
+	void (*mhl_1v2_power)(bool enable);
+	int (*is_wireless_charger)(void);
+	u8 cable_redetect;
+	int64_t (*get_adc_cb)(void);
+
+	int ac_9v_gpio;
+	void (*configure_ac_9v_gpio) (int);
+	u8 mhl_internal_3v3;
+
+	int  audio_dock_lock;
+	int notify_init;
+} the_cable_info;
+
+
+#ifdef CONFIG_CABLE_DETECT_ACCESSORY
+static int cable_detect_get_adc(void);
+static int second_detect(struct cable_detect_info *pInfo);
+static void usb_id_detect_init(struct cable_detect_info *info);
+#endif
+
+static DEFINE_MUTEX(cable_notify_sem);
+static void send_cable_connect_notify(int cable_type)
+{
+	static struct t_cable_status_notifier *notifier;
+	struct cable_detect_info *pInfo = &the_cable_info;
+
+	mutex_lock(&cable_notify_sem);
+	CABLE_DEBUG("%s: cable_type = %d\n", __func__, cable_type);
+
+	if (cable_type == CONNECT_TYPE_UNKNOWN)
+		cable_type = CONNECT_TYPE_USB;
+
+	if (pInfo->ac_9v_gpio && (cable_type == CONNECT_TYPE_USB
+				|| cable_type == CONNECT_TYPE_AC
+				|| cable_type == CONNECT_TYPE_MHL_AC)) {
+		if (pInfo->configure_ac_9v_gpio)
+			pInfo->configure_ac_9v_gpio(1);
+
+		mdelay(5);
+		if (gpio_get_value(pInfo->ac_9v_gpio)) {
+			CABLE_INFO("%s detect 9v charger\n", __func__);
+			cable_type = CONNECT_TYPE_9V_AC;
+		}
+
+		if (pInfo->configure_ac_9v_gpio)
+			pInfo->configure_ac_9v_gpio(0);
+	}
+
+	if (cable_type > 0 && pInfo->accessory_type == DOCK_STATE_DMB) {
+		CABLE_INFO("%s: DMB presents. Disabling charge.\n", __func__);
+		cable_type = CONNECT_TYPE_CLEAR;
+	}
+
+	list_for_each_entry(notifier,
+		&g_lh_calbe_detect_notifier_list,
+		cable_notifier_link) {
+			if (notifier->func != NULL) {
+				CABLE_INFO("Send to: %s, type %d\n",
+						notifier->name, cable_type);
+				
+				
+				notifier->func(cable_type);
+			}
+		}
+	mutex_unlock(&cable_notify_sem);
+}
+
+int cable_detect_register_notifier(struct t_cable_status_notifier *notifier)
+{
+	if (!notifier || !notifier->name || !notifier->func)
+		return -EINVAL;
+
+	mutex_lock(&cable_notify_sem);
+	list_add(&notifier->cable_notifier_link,
+		&g_lh_calbe_detect_notifier_list);
+	if(the_cable_info.notify_init == 1)
+		notifier->func(cable_get_connect_type());
+	mutex_unlock(&cable_notify_sem);
+	return 0;
+}
+
+#if (defined(CONFIG_USB_OTG) && defined(CONFIG_USB_OTG_HOST))
+static DEFINE_MUTEX(usb_host_notify_sem);
+static void send_usb_host_connect_notify(int cable_in)
+{
+	struct t_usb_host_status_notifier *notifier;
+
+	mutex_lock(&usb_host_notify_sem);
+	list_for_each_entry(notifier,
+		&g_lh_usb_host_detect_notifier_list,
+		usb_host_notifier_link) {
+		if (notifier->func != NULL) {
+			CABLE_INFO("[HostNotify] Send to: %s: %d\n",
+					notifier->name, cable_in);
+			
+			
+			notifier->func(cable_in);
+		}
+	}
+	mutex_unlock(&usb_host_notify_sem);
+}
+
+int usb_host_detect_register_notifier(struct t_usb_host_status_notifier *notifier)
+{
+	if (!notifier || !notifier->name || !notifier->func)
+		return -EINVAL;
+
+	mutex_lock(&usb_host_notify_sem);
+	list_add(&notifier->usb_host_notifier_link,
+			&g_lh_usb_host_detect_notifier_list);
+	mutex_unlock(&usb_host_notify_sem);
+	return 0;
+}
+#endif
+
+static void check_vbus_in(struct work_struct *w)
+{
+	int vbus_in;
+	int level;
+	struct cable_detect_info *pInfo = container_of(
+			w, struct cable_detect_info, vbus_detect_work.work);
+
+	level = pm8921_is_pwr_src_plugged_in();
+	vbus_in = level;
+	CABLE_INFO("%s: vbus = %d, vbus_in = %d\n", __func__, vbus, vbus_in);
+
+#ifdef CONFIG_RESET_BY_CABLE_IN
+	reset_dflipflop();
+#endif
+
+#ifdef CONFIG_FB_MSM_HDMI_MHL_SII9234
+	if (pInfo->cable_redetect) {
+		CABLE_INFO("mhl re-detect\n");
+		disable_irq_nosync(pInfo->idpin_irq);
+		queue_delayed_work(pInfo->cable_detect_wq,
+			&pInfo->cable_detect_work, ADC_DELAY);
+	}
+#endif
+
+	if (pInfo->notify_init == 0 && vbus_in == 0 && vbus == 0)
+		send_cable_connect_notify(CONNECT_TYPE_NONE);
+	if (pInfo->notify_init == 0 && vbus == vbus_in)
+                msm_otg_set_vbus_state(vbus_in);
+
+	pInfo->notify_init = 1;
+
+	if (vbus != vbus_in) {
+		vbus = vbus_in;
+
+		if(pInfo->accessory_type == DOCK_STATE_MHL) {
+			CABLE_INFO("%s: usb_uart switch, MHL cable , Do nothing\n", __func__);
+		} else {
+			if (pInfo->usb_uart_switch)
+				pInfo->usb_uart_switch(!vbus);
+		}
+
+		msm_otg_set_vbus_state(vbus_in);
+
+		if (pInfo->ad_en_gpio) {
+			if (vbus) {
+				if (pInfo->ad_en_irq)
+					CABLE_INFO("%s: Enable ad_en_irq ++\n", __func__);
+					enable_irq(pInfo->ad_en_irq);
+			} else {
+					CABLE_INFO("%s: Disable ad_en_irq --\n", __func__);
+					disable_irq_nosync(pInfo->ad_en_irq);
+			}
+		}
+	}
+	wake_unlock(&pInfo->vbus_wlock);
+}
+
+#ifdef CONFIG_CABLE_DETECT_ACCESSORY
+void release_audio_dock_lock(void)
+{
+	int value;
+	struct cable_detect_info *pInfo = &the_cable_info;
+	if(pInfo->audio_dock_lock != 1) {
+		CABLE_INFO("audio_dock_removal fucntion should not be called when audio_dock_lock != 1\n");
+		return;
+	}
+	CABLE_INFO("unlock audio dock lock\n");
+
+	pInfo->audio_dock_lock = 0;
+	
+	value = gpio_get_value(pInfo->usb_id_pin_gpio);
+	irq_set_irq_type(pInfo->idpin_irq, value ? IRQF_TRIGGER_HIGH: IRQF_TRIGGER_LOW);	
+	enable_irq(pInfo->idpin_irq);
+}
+EXPORT_SYMBOL(release_audio_dock_lock);
+
+static int cable_detect_get_type(struct cable_detect_info *pInfo)
+{
+	int id_pin, adc, type;
+	static int prev_type, stable_count;
+
+	if (stable_count >= ADC_RETRY)
+		stable_count = 0;
+
+	id_pin = gpio_get_value_cansleep(pInfo->usb_id_pin_gpio);
+	if (id_pin == 0 || pInfo->cable_redetect) {
+		CABLE_INFO("%s: id pin low\n", __func__);
+
+
+		adc = cable_detect_get_adc();
+
+		if (adc > -100 && adc < 100)
+			type = second_detect(pInfo);
+		else {
+			if (adc > 150 && adc < 220)
+				type = DOCK_STATE_CAR;
+			else if (adc > 370 && adc < 440)
+				type = DOCK_STATE_USB_HEADSET;
+			else if (adc > 440 && adc < 550)
+				type = DOCK_STATE_DMB;
+			else if (adc > 550 && adc < 900)
+				type = DOCK_STATE_DESK;
+			else
+				type = DOCK_STATE_UNDEFINED;
+		}
+	} else {
+		CABLE_INFO("%s: id pin high\n", __func__);
+		type = DOCK_STATE_UNDOCKED;
+	}
+
+	if (prev_type == type)
+		stable_count++;
+	else
+		stable_count = 0;
+
+	CABLE_INFO("%s prev_type %d, type %d, stable_count %d\n",
+				__func__, prev_type, type, stable_count);
+
+	prev_type = type;
+	return (stable_count >= ADC_RETRY) ? type : -2;
+}
+
+static void cable_detect_handler(struct work_struct *w)
+{
+	struct cable_detect_info *pInfo = container_of(
+			w, struct cable_detect_info, cable_detect_work.work);
+	int value;
+	int accessory_type;
+
+	if (pInfo == NULL)
+		return;
+	if(pInfo->audio_dock_lock == 1) {
+		CABLE_INFO("audio dock lock! skip cable_detect_handler\n");
+		return;
+	}
+#ifdef CONFIG_FB_MSM_HDMI_MHL_SII9234
+	if (pInfo->mhl_reset_gpio != 0)
+		gpio_set_value_cansleep(pInfo->mhl_reset_gpio, 0); 
+#endif
+	if (pInfo->detect_type == CABLE_TYPE_PMIC_ADC) {
+		accessory_type = cable_detect_get_type(pInfo);
+		if (accessory_type == -2) {
+			queue_delayed_work(pInfo->cable_detect_wq,
+				&pInfo->cable_detect_work, ADC_DELAY);
+			return;
+		}
+	} else
+		accessory_type = DOCK_STATE_UNDOCKED;
+
+#ifdef CONFIG_FB_MSM_HDMI_MHL_SII9234
+	if (pInfo->mhl_reset_gpio != 0)
+		gpio_set_value_cansleep(pInfo->mhl_reset_gpio, 1); 
+	CABLE_INFO("[MHL] Enter D3 mode\n");
+	
+	if (accessory_type != DOCK_STATE_MHL)
+		D2ToD3();
+#endif
+
+	if (pInfo->accessory_type == DOCK_STATE_AUDIO_DOCK &&
+		accessory_type != DOCK_STATE_UNDEFINED &&
+		accessory_type != DOCK_STATE_UNDOCKED) {
+		CABLE_INFO("bad accessory state. from audio dock to state %d\n",accessory_type);
+		switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED);
+#ifdef CONFIG_HTC_HEADSET_MGR
+		headset_ext_detect(USB_NO_HEADSET);
+#endif
+		pInfo->accessory_type = DOCK_STATE_UNDOCKED;
+	}
+
+	switch (accessory_type) {
+	case DOCK_STATE_DESK:
+		CABLE_INFO("cradle inserted\n");
+		switch_set_state(&dock_switch, DOCK_STATE_DESK);
+		pInfo->accessory_type = DOCK_STATE_DESK;
+		break;
+	case DOCK_STATE_CAR:
+		CABLE_INFO("Car kit inserted\n");
+		switch_set_state(&dock_switch, DOCK_STATE_CAR);
+		pInfo->accessory_type = DOCK_STATE_CAR;
+		break;
+	case DOCK_STATE_USB_HEADSET:
+		CABLE_INFO("USB headset inserted\n");
+		pInfo->accessory_type = DOCK_STATE_USB_HEADSET;
+		if (pInfo->usb_dpdn_switch)
+			pInfo->usb_dpdn_switch(PATH_USB_AUD);
+#ifdef CONFIG_HTC_HEADSET_MGR
+		headset_ext_detect(USB_AUDIO_OUT);
+#endif
+		break;
+#ifdef CONFIG_FB_MSM_HDMI_MHL_SII9234
+	case DOCK_STATE_MHL:
+		CABLE_INFO("MHL inserted\n");
+		switch_set_state(&dock_switch, DOCK_STATE_MHL);
+		pInfo->accessory_type = DOCK_STATE_MHL;
+#ifdef CONFIG_INTERNAL_CHARGING_SUPPORT
+		if (!pInfo->mhl_internal_3v3 && !vbus)
+			send_cable_connect_notify(CONNECT_TYPE_INTERNAL);
+
+#endif
+		sii9234_mhl_device_wakeup();
+		break;
+#endif
+#if (defined(CONFIG_USB_OTG) && defined(CONFIG_USB_OTG_HOST))
+	case DOCK_STATE_USB_HOST:
+		CABLE_INFO("USB Host inserted\n");
+		send_usb_host_connect_notify(1);
+		pInfo->accessory_type = DOCK_STATE_USB_HOST;
+		switch_set_state(&dock_switch, DOCK_STATE_USB_HOST);
+		break;
+#endif
+	case DOCK_STATE_DMB:
+		CABLE_INFO("DMB inserted\n");
+		send_cable_connect_notify(CONNECT_TYPE_CLEAR);
+		switch_set_state(&dock_switch, DOCK_STATE_DMB);
+		pInfo->accessory_type = DOCK_STATE_DMB;
+		break;
+	case DOCK_STATE_AUDIO_DOCK:
+		CABLE_INFO("Audio Dock inserted\n");
+		switch_set_state(&dock_switch, DOCK_STATE_DESK);
+		pInfo->accessory_type = DOCK_STATE_AUDIO_DOCK;
+#if 0
+#ifdef CONFIG_HTC_HEADSET_MGR
+		cable_type_value = usb_get_connect_type();
+		if (cable_type_value == CONNECT_TYPE_UNKNOWN ||
+			cable_type_value == CONNECT_TYPE_USB ||
+			cable_type_value == CONNECT_TYPE_AC) {
+			CABLE_INFO("notify auido driver in cable_detect_handler, cable type %d\n",cable_type_value);
+			pInfo->audio_dock_lock = 1;
+			headset_ext_detect(USB_AUDIO_OUT);
+			return;	
+		}
+#endif
+#endif
+		break;
+	case DOCK_STATE_UNDEFINED:
+	case DOCK_STATE_UNDOCKED:
+		switch (pInfo->accessory_type) {
+		case DOCK_STATE_DESK:
+			CABLE_INFO("cradle removed\n");
+			switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED);
+			pInfo->accessory_type = DOCK_STATE_UNDOCKED;
+			break;
+		case DOCK_STATE_CAR:
+			CABLE_INFO("Car kit removed\n");
+			switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED);
+			pInfo->accessory_type = DOCK_STATE_UNDOCKED;
+			break;
+		case DOCK_STATE_USB_HEADSET:
+			CABLE_INFO("USB headset removed\n");
+#ifdef CONFIG_HTC_HEADSET_MGR
+			headset_ext_detect(USB_NO_HEADSET);
+#endif
+			if (pInfo->usb_dpdn_switch)
+				pInfo->usb_dpdn_switch(PATH_USB);
+			pInfo->accessory_type = DOCK_STATE_UNDOCKED;
+			break;
+#ifdef CONFIG_FB_MSM_HDMI_MHL_SII9234
+		case DOCK_STATE_MHL:
+			CABLE_INFO("MHL removed\n");
+			switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED);
+			sii9234_disableIRQ();
+			break;
+#endif
+#if (defined(CONFIG_USB_OTG) && defined(CONFIG_USB_OTG_HOST))
+		case DOCK_STATE_USB_HOST:
+			CABLE_INFO("USB host cable removed\n");
+			pInfo->accessory_type = DOCK_STATE_UNDOCKED;
+			send_usb_host_connect_notify(0);
+			switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED);
+			break;
+#endif
+		case DOCK_STATE_DMB:
+			CABLE_INFO("DMB removed\n");
+			switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED);
+			pInfo->accessory_type = DOCK_STATE_UNDOCKED;
+			break;
+		case DOCK_STATE_AUDIO_DOCK:
+			CABLE_INFO("Audio Dock removed\n");
+			switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED);
+#ifdef CONFIG_HTC_HEADSET_MGR
+			headset_ext_detect(USB_NO_HEADSET);
+#endif
+			pInfo->accessory_type = DOCK_STATE_UNDOCKED;
+			break;
+		}
+	default :
+		break;
+	}
+
+	value = gpio_get_value_cansleep(pInfo->usb_id_pin_gpio);
+	CABLE_INFO("%s ID pin %d, type %d\n", __func__,
+				value, pInfo->accessory_type);
+#ifdef CONFIG_FB_MSM_HDMI_MHL_SII9234
+	if (pInfo->accessory_type == DOCK_STATE_MHL)
+		return;
+#endif
+	if (pInfo->accessory_type == DOCK_STATE_UNDOCKED)
+		irq_set_irq_type(pInfo->idpin_irq,
+			value ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+	else
+		irq_set_irq_type(pInfo->idpin_irq, IRQF_TRIGGER_HIGH);
+
+	enable_irq(pInfo->idpin_irq);
+#if 0
+	wake_unlock(&pInfo->cable_detect_wlock);
+#endif
+}
+
+void set_mfg_usb_carkit_enable(int enable)
+{
+	the_cable_info.mfg_usb_carkit_enable = enable;
+}
+
+int cable_get_accessory_type(void)
+{
+	return the_cable_info.accessory_type;
+}
+
+static int cable_detect_get_adc(void)
+{
+	struct cable_detect_info *pInfo = &the_cable_info;
+
+	return pInfo->get_adc_cb();
+}
+
+int cable_get_usb_id_level(void)
+{
+	struct cable_detect_info *pInfo = &the_cable_info;
+
+	if (pInfo->usb_id_pin_gpio)
+		return gpio_get_value(pInfo->usb_id_pin_gpio);
+	else {
+		printk(KERN_INFO "usb id is not defined\n");
+		return 1;
+	}
+}
+
+static int second_detect(struct cable_detect_info *pInfo)
+{
+	uint32_t adc_value = 0xffffffff;
+	int type;
+
+	if (pInfo->config_usb_id_gpios)
+		pInfo->config_usb_id_gpios(1);
+
+	adc_value = cable_detect_get_adc();
+	CABLE_INFO("[2nd] accessory adc = %d\n", adc_value);
+
+	if ((pInfo->mhl_version_ctrl_flag) || (adc_value >= 776 && adc_value <= 1020))
+#ifdef CONFIG_FB_MSM_HDMI_MHL_SII9234
+		type = DOCK_STATE_MHL;
+#else
+		type = DOCK_STATE_UNDEFINED;
+#endif
+	else if(adc_value >= 1021 && adc_value <= 1224)
+		type = DOCK_STATE_AUDIO_DOCK;
+	else
+#if (defined(CONFIG_USB_OTG) && defined(CONFIG_USB_OTG_HOST))
+		type = DOCK_STATE_USB_HOST;
+#else
+		type = DOCK_STATE_UNDEFINED;
+#endif
+
+	if (pInfo->config_usb_id_gpios)
+		pInfo->config_usb_id_gpios(0);
+
+	return type;
+}
+
+static int get_usb_id_adc(char *buffer, struct kernel_param *kp)
+{
+	unsigned length = 0;
+	int adc;
+
+	adc = cable_detect_get_adc();
+
+	length += sprintf(buffer, "%d\n", adc);
+
+	return length;
+}
+module_param_call(usb_id_adc, NULL, get_usb_id_adc, NULL, 0664);
+
+static ssize_t dock_status_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct cable_detect_info *pInfo = &the_cable_info;
+
+	if (pInfo->accessory_type == DOCK_STATE_DESK || pInfo->accessory_type == DOCK_STATE_AUDIO_DOCK)
+		return sprintf(buf, "online\n");
+	else if (pInfo->accessory_type == 3) 
+		return sprintf(buf, "online\n");
+	else
+		return sprintf(buf, "offline\n");
+}
+static DEVICE_ATTR(status, S_IRUGO | S_IWUSR, dock_status_show, NULL);
+
+static irqreturn_t usbid_interrupt(int irq, void *data)
+{
+	struct cable_detect_info *pInfo = (struct cable_detect_info *)data;
+
+	disable_irq_nosync(pInfo->idpin_irq);
+
+	CABLE_INFO("usb: id interrupt\n");
+	pInfo->cable_redetect = 0;
+	queue_delayed_work(pInfo->cable_detect_wq,
+		&pInfo->cable_detect_work, ADC_DELAY);
+	wake_lock_timeout(&pInfo->cable_detect_wlock, HZ*2);
+	return IRQ_HANDLED;
+}
+
+static void usb_id_detect_init(struct cable_detect_info *pInfo)
+{
+	int ret;
+	CABLE_INFO("%s: id pin %d\n", __func__,
+		pInfo->usb_id_pin_gpio);
+
+	if (pInfo->usb_id_pin_gpio == 0)
+		return;
+	ret = gpio_request(pInfo->usb_id_pin_gpio, "USBID_GPIO");
+	if (ret) {
+		CABLE_ERR("%s: request id gpio failed\n", __func__);
+		return;
+	}
+
+	if (pInfo->config_usb_id_gpios)
+		pInfo->config_usb_id_gpios(0);
+
+	if (pInfo->idpin_irq == 0)
+		pInfo->idpin_irq = gpio_to_irq(pInfo->usb_id_pin_gpio);
+
+	set_irq_flags(pInfo->idpin_irq, IRQF_VALID | IRQF_NOAUTOEN);
+	ret = request_any_context_irq(pInfo->idpin_irq, usbid_interrupt,
+				IRQF_TRIGGER_LOW, "idpin_irq", pInfo);
+	if (ret < 0) {
+		CABLE_ERR("%s: request_irq failed\n", __func__);
+		return;
+	}
+
+	ret = enable_irq_wake(pInfo->idpin_irq);
+	if (ret < 0) {
+		CABLE_ERR("%s: set_irq_wake failed\n", __func__);
+		goto err;
+	}
+
+	enable_irq(pInfo->idpin_irq);
+	return;
+err:
+	free_irq(pInfo->idpin_irq, 0);
+}
+
+#ifdef CONFIG_FB_MSM_HDMI_MHL_SII9234
+static void mhl_status_notifier_func(bool isMHL, int charging_type)
+{
+	struct cable_detect_info *pInfo = &the_cable_info;
+	int id_pin = gpio_get_value_cansleep(pInfo->usb_id_pin_gpio);
+	static uint8_t mhl_connected;
+
+	CABLE_INFO("%s: isMHL %d, charging type %d, id_pin %d\n",
+				__func__, isMHL, charging_type, id_pin);
+	if (pInfo->accessory_type != DOCK_STATE_MHL) {
+		CABLE_INFO("%s: accessory is not MHL, type %d\n",
+					__func__, pInfo->accessory_type);
+		return;
+	}
+
+#ifdef CONFIG_HTC_HEADSET_MISC
+	headset_mhl_audio_jack_enable(isMHL);
+#endif
+
+	if (!isMHL) {
+		CABLE_INFO("MHL removed\n");
+		sii9234_disableIRQ();
+
+		if (pInfo->usb_dpdn_switch)
+			pInfo->usb_dpdn_switch(PATH_USB);
+
+		if (pInfo->mhl_1v2_power)
+			pInfo->mhl_1v2_power(0);
+#ifdef CONFIG_INTERNAL_CHARGING_SUPPORT
+		send_cable_connect_notify(CONNECT_TYPE_CLEAR);
+#endif
+#ifdef MHL_REDETECT
+		if (mhl_connected == 0) {
+			CABLE_INFO("MHL re-detect\n");
+			set_irq_type(pInfo->idpin_irq,
+				id_pin ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+			pInfo->cable_redetect = 1;
+		}
+#endif
+		mhl_connected = 0;
+
+		pInfo->accessory_type = DOCK_STATE_UNDOCKED;
+		sii9234_disableIRQ();
+		enable_irq(pInfo->idpin_irq);
+		return;
+	} else {
+		mhl_connected = 1;
+		set_irq_type(pInfo->idpin_irq,
+			id_pin ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+#ifdef CONFIG_INTERNAL_CHARGING_SUPPORT
+			if (charging_type == CONNECT_TYPE_INTERNAL)
+				charging_type = CONNECT_TYPE_NONE;
+			send_cable_connect_notify(charging_type);
+#else
+			send_cable_connect_notify(charging_type);
+#endif
+#if 0
+#ifdef CONFIG_INTERNAL_CHARGING_SUPPORT
+		else if (vbus)
+			send_cable_connect_notify(CONNECT_TYPE_USB);
+#endif
+#endif
+	}
+}
+
+static struct t_mhl_status_notifier mhl_status_notifier = {
+	.name = "mhl_detect",
+	.func = mhl_status_notifier_func,
+};
+#endif 
+#endif 
+
+
+static ssize_t vbus_status_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int level, vbus_in;
+
+	level = pm8921_is_usb_chg_plugged_in();
+	vbus_in = level;
+	CABLE_INFO("%s: vbus state = %d\n", __func__, vbus_in);
+	return sprintf(buf, "%d\n", vbus_in);
+}
+static DEVICE_ATTR(vbus, S_IRUGO | S_IWUSR, vbus_status_show, NULL);
+
+#ifdef CONFIG_CABLE_DETECT_ACCESSORY
+static ssize_t adc_status_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	int adc;
+
+	adc = cable_detect_get_adc();
+	CABLE_INFO("%s: ADC = %d\n", __func__, adc);
+	return sprintf(buf, "%d\n", adc);
+}
+static DEVICE_ATTR(adc, S_IRUGO | S_IWUSR, adc_status_show, NULL);
+
+static ssize_t dmb_wakeup_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct cable_detect_info *pInfo = &the_cable_info;
+	uint32_t wakeup;
+
+	if (pInfo->accessory_type != DOCK_STATE_DMB) {
+		CABLE_INFO("%s: DMB not exist. Do nothing.\n", __func__);
+		return count;
+	}
+
+	sscanf(buf, "%d", &wakeup);
+	CABLE_DEBUG("%s: wakeup = %d\n", __func__, wakeup);
+	if (!!wakeup) {
+		disable_irq_nosync(pInfo->idpin_irq);
+
+		gpio_direction_output(pInfo->usb_id_pin_gpio, 0);
+		msleep(1);
+		gpio_direction_output(pInfo->usb_id_pin_gpio, 1);
+		msleep(10);
+		gpio_direction_output(pInfo->usb_id_pin_gpio, 0);
+		msleep(1);
+
+		gpio_direction_input(pInfo->usb_id_pin_gpio);
+		enable_irq(pInfo->idpin_irq);
+	}
+	CABLE_INFO("%s(parent:%s): request DMB wakeup done.\n",
+			current->comm, current->parent->comm);
+
+	return count;
+}
+
+static DEVICE_ATTR(dmb_wakeup, S_IRUGO | S_IWUSR, NULL, dmb_wakeup_store);
+static ssize_t unlock_audio_dock_lock_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	uint32_t unlock;
+
+	sscanf(buf, "%d", &unlock);
+	CABLE_DEBUG("%s: unlock = %d\n", __func__, unlock);
+	if (!!unlock)
+		release_audio_dock_lock();
+
+	return count;
+}
+
+static DEVICE_ATTR(unlock_audio_dock_lock, S_IRUGO | S_IWUSR, NULL, unlock_audio_dock_lock_store);
+
+#endif
+
+int cable_get_connect_type(void)
+{
+	struct cable_detect_info *pInfo = &the_cable_info;
+
+	return pInfo->connect_type;
+}
+
+static irqreturn_t ad_en_irq_handler(int irq, void *data)
+{
+	unsigned long flags;
+	struct cable_detect_info *pInfo = &the_cable_info;
+
+	disable_irq_nosync(pInfo->ad_en_irq);
+	CABLE_INFO("%s: Disable ad_en_irq --\n", __func__);
+	spin_lock_irqsave(&pInfo->lock, flags);
+	queue_delayed_work(pInfo->cable_detect_wq,
+			&pInfo->vbus_detect_work, HZ/10);
+	spin_unlock_irqrestore(&pInfo->lock, flags);
+#if 1
+	wake_lock_timeout(&pInfo->vbus_wlock, HZ*2);
+#endif
+
+	return IRQ_HANDLED;
+}
+
+static int cd_pmic_request_irq(unsigned int gpio, unsigned int *irq,
+			       irq_handler_t handler, unsigned long flags,
+			       const char *name, unsigned int wake)
+{
+	int ret = 0;
+
+	ret = gpio_request(gpio, name);
+	if (ret < 0)
+		return ret;
+
+	ret = gpio_direction_input(gpio);
+	if (ret < 0) {
+		gpio_free(gpio);
+		return ret;
+	}
+
+	if (!(*irq)) {
+		ret = gpio_to_irq(gpio);
+		if (ret < 0) {
+			gpio_free(gpio);
+			return ret;
+		}
+		*irq = (unsigned int) ret;
+	}
+
+	ret = request_any_context_irq(*irq, handler, flags, name, NULL);
+	if (ret < 0) {
+		gpio_free(gpio);
+		return ret;
+	}
+
+	ret = irq_set_irq_wake(*irq, wake);
+	if (ret < 0) {
+		free_irq(*irq, 0);
+		gpio_free(gpio);
+		return ret;
+	}
+
+	return 1;
+}
+
+static int cable_detect_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct cable_detect_platform_data *pdata = pdev->dev.platform_data;
+	struct cable_detect_info *pInfo = &the_cable_info;
+
+	spin_lock_init(&the_cable_info.lock);
+
+	if (pdata) {
+		pInfo->vbus_mpp_gpio = pdata->vbus_mpp_gpio;
+		pInfo->vbus_mpp_irq = pdata->vbus_mpp_irq;
+		pInfo->ad_en_active_state = pdata->ad_en_active_state;
+		pInfo->ad_en_gpio = pdata->ad_en_gpio;
+		pInfo->ad_en_irq = pdata->ad_en_irq;
+		pInfo->usb_uart_switch = pdata->usb_uart_switch;
+		pInfo->usb_dpdn_switch = pdata->usb_dpdn_switch;
+		if (pInfo->usb_dpdn_switch)
+			pInfo->usb_dpdn_switch(PATH_USB);
+		pInfo->ac_9v_gpio = pdata->ac_9v_gpio;
+		pInfo->configure_ac_9v_gpio = pdata->configure_ac_9v_gpio;
+		pInfo->mhl_internal_3v3 = pdata->mhl_internal_3v3;
+
+#ifdef CONFIG_CABLE_DETECT_ACCESSORY
+		pInfo->detect_type = pdata->detect_type;
+		pInfo->usb_id_pin_gpio = pdata->usb_id_pin_gpio;
+		pInfo->mhl_reset_gpio = pdata->mhl_reset_gpio;
+		pInfo->mpp_data = &pdata->mpp_data;
+		pInfo->config_usb_id_gpios = pdata->config_usb_id_gpios;
+		pInfo->mhl_version_ctrl_flag = pdata->mhl_version_ctrl_flag;
+		pInfo->mhl_1v2_power = pdata->mhl_1v2_power;
+		pInfo->get_adc_cb = pdata->get_adc_cb;
+
+#endif
+
+		if (pdata->is_wireless_charger)
+			pInfo->is_wireless_charger = pdata->is_wireless_charger;
+#ifdef CONFIG_CABLE_DETECT_ACCESSORY
+		INIT_DELAYED_WORK(&pInfo->cable_detect_work, cable_detect_handler);
+#endif
+		INIT_DELAYED_WORK(&pInfo->vbus_detect_work, check_vbus_in);
+
+		pInfo->cable_detect_wq = create_singlethread_workqueue("cable_detect");
+		if (pInfo->cable_detect_wq == 0) {
+			CABLE_ERR("usb: fail to create workqueue\n");
+			return -ENOMEM;
+		}
+
+		if (pdata->vbus_mpp_config)
+			pdata->vbus_mpp_config();
+
+		wake_lock_init(&pInfo->vbus_wlock,
+			WAKE_LOCK_SUSPEND, "vbus_lock");
+
+		wake_lock_init(&pInfo->cable_detect_wlock,
+			WAKE_LOCK_SUSPEND, "cable_detect_lock");
+
+		if (pdata->ad_en_gpio) {
+			ret = cd_pmic_request_irq(pdata->ad_en_gpio,
+					&pdata->ad_en_irq, ad_en_irq_handler,
+					pdata->ad_en_active_state ?
+					IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING,
+					"ad_en_irq", 1);
+			if (ret < 0) {
+				printk("Failed to request PMIC AD_EN IRQ (0x%X)", ret);
+			} else
+				disable_irq(pdata->ad_en_irq);
+		}
+	}
+	if (switch_dev_register(&dock_switch) < 0) {
+		CABLE_ERR("fail to register dock switch!\n");
+		return 0;
+	}
+
+	ret = device_create_file(dock_switch.dev, &dev_attr_vbus);
+	if (ret != 0)
+		CABLE_ERR("dev_attr_vbus failed\n");
+
+#ifdef CONFIG_CABLE_DETECT_ACCESSORY
+	ret = device_create_file(dock_switch.dev, &dev_attr_status);
+	if (ret != 0)
+		CABLE_ERR("dev_attr_status failed\n");
+
+	ret = device_create_file(dock_switch.dev, &dev_attr_adc);
+	if (ret != 0)
+		CABLE_ERR("dev_attr_adc failed\n");
+
+	ret = device_create_file(dock_switch.dev, &dev_attr_dmb_wakeup);
+	if (ret != 0)
+		CABLE_ERR("dev_attr_dmb_wakeup failed\n");
+
+	ret = device_create_file(dock_switch.dev, &dev_attr_unlock_audio_dock_lock);
+	if (ret != 0)
+		CABLE_ERR("dev_attr_unlock_audio_dock_lock failed\n");
+
+	usb_id_detect_init(pInfo);
+#endif
+
+	return 0;
+}
+
+#ifdef CONFIG_KDDI_ADAPTER
+#define VBUS_DEBOUNCE_TIME	500
+#define MSPERIOD(end, start)  ktime_to_ms(ktime_sub(end, start))
+#endif
+irqreturn_t cable_detection_vbus_irq_handler(void)
+{
+	unsigned long flags;
+#ifdef CONFIG_KDDI_ADAPTER
+	static int previous_vbus = -1,current_vbus = -1;
+	static int firstdrop = 0;
+	static ktime_t end_ktime;
+	static ktime_t cable_remove_ktime;
+	s64 diff = 0;
+#endif
+	struct cable_detect_info *pInfo = &the_cable_info;
+
+	CABLE_INFO("%s\n", __func__);
+#ifdef CONFIG_KDDI_ADAPTER
+	if (previous_vbus  == -1)
+		previous_vbus = current_vbus = pm8921_is_pwr_src_plugged_in();
+	else {
+		current_vbus = pm8921_is_pwr_src_plugged_in();
+		if(previous_vbus == 1 && current_vbus  == 0) { 
+			cable_remove_ktime = ktime_get();
+			firstdrop  = 1;
+		} else if (previous_vbus == 0 && current_vbus  == 1 && firstdrop == 1) { 
+			end_ktime = ktime_get();
+			diff = MSPERIOD(end_ktime, cable_remove_ktime);
+			printk("=============diff %lld\n",diff);
+			if (diff < VBUS_DEBOUNCE_TIME) {
+				spin_lock_irqsave(&pInfo->lock, flags);
+				__cancel_delayed_work(&pInfo->vbus_detect_work); 
+				spin_unlock_irqrestore(&pInfo->lock, flags);
+				previous_vbus = current_vbus;
+				printk("==========cancel pending\n");
+				return IRQ_HANDLED;
+			}
+		}
+		previous_vbus = current_vbus;
+	}
+	CABLE_INFO("%s go\n", __func__);
+#endif
+
+	spin_lock_irqsave(&pInfo->lock, flags);
+#ifdef CONFIG_KDDI_ADAPTER
+	queue_delayed_work(pInfo->cable_detect_wq,
+			&pInfo->vbus_detect_work, HZ / 2);
+#else
+	queue_delayed_work(pInfo->cable_detect_wq,
+			&pInfo->vbus_detect_work, HZ/10);
+#endif
+	spin_unlock_irqrestore(&pInfo->lock, flags);
+#if 1
+	wake_lock_timeout(&pInfo->vbus_wlock, HZ*2);
+#endif
+
+	CABLE_INFO("%s --\n", __func__);
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(cable_detection_vbus_irq_handler);
+
+struct platform_driver cable_detect_driver = {
+	.probe = cable_detect_probe,
+	
+	.driver = {
+		.name	= "cable_detect",
+		.owner = THIS_MODULE,
+	},
+};
+
+static void usb_status_notifier_func(int cable_type)
+{
+	struct cable_detect_info*pInfo = &the_cable_info;
+
+	CABLE_INFO("%s: cable_type = %d\n", __func__, cable_type);
+
+	
+	if(pInfo->audio_dock_lock == 0 && (cable_type == CONNECT_TYPE_USB || cable_type == CONNECT_TYPE_AC || cable_type == CONNECT_TYPE_MHL_AC))
+		if(pInfo->accessory_type == DOCK_STATE_AUDIO_DOCK) {
+#ifdef CONFIG_HTC_HEADSET_MGR
+			CABLE_INFO("notify auido driver in usb_status_notifier_func\n");
+			pInfo->audio_dock_lock = 1;
+			
+			cancel_delayed_work_sync(&pInfo->cable_detect_work);
+			if (pInfo->accessory_type == DOCK_STATE_AUDIO_DOCK)
+				headset_ext_detect(USB_AUDIO_OUT);
+			else {
+				CABLE_INFO("latest accessory type is %d, stop to notify audio dock\n",pInfo->accessory_type);
+				pInfo->audio_dock_lock = 0;
+			}
+#endif
+		}
+
+	if (cable_type > CONNECT_TYPE_NONE) {
+		if (pInfo->ad_en_gpio) {
+			if (gpio_get_value(pInfo->ad_en_gpio) ==
+							pInfo->ad_en_active_state)
+				cable_type = CONNECT_TYPE_WIRELESS;
+		} else if (pInfo->is_wireless_charger) {
+			if (pInfo->is_wireless_charger())
+				cable_type = CONNECT_TYPE_WIRELESS;
+		}
+	}
+
+#ifdef CONFIG_FB_MSM_HDMI_MHL_SII9234
+#ifdef CONFIG_INTERNAL_CHARGING_SUPPORT
+	if (!pInfo->mhl_internal_3v3 &&
+			pInfo->accessory_type == DOCK_STATE_MHL) {
+		CABLE_INFO("%s: MHL detected. Do nothing\n", __func__);
+		return;
+	}
+#endif
+#endif
+	pInfo->connect_type = cable_type;
+	send_cable_connect_notify(cable_type);
+
+}
+
+static struct t_usb_status_notifier usb_status_notifier = {
+	.name = "cable_detect",
+	.func = usb_status_notifier_func,
+};
+
+static int __init cable_detect_init(void)
+{
+	vbus = 0;
+	the_cable_info.connect_type = CONNECT_TYPE_NONE;
+	htc_usb_register_notifier(&usb_status_notifier);
+#if (defined(CONFIG_CABLE_DETECT_ACCESSORY) && defined(CONFIG_FB_MSM_HDMI_MHL_SII9234))
+		mhl_detect_register_notifier(&mhl_status_notifier);
+#endif
+	return platform_driver_register(&cable_detect_driver);
+
+}
+
+static void __exit cable_detect_exit(void)
+{
+	platform_driver_unregister(&cable_detect_driver);
+}
+
+MODULE_DESCRIPTION("CABLE_DETECT");
+MODULE_LICENSE("GPL");
+
+module_init(cable_detect_init);
+module_exit(cable_detect_exit);