static irqreturn_t hi6402_btnup_eco_handler(int irq, void *data)
{
	struct hi6402_mbhc_platform_data *pdata =
			(struct hi6402_mbhc_platform_data *)data;

	BUG_ON(NULL == pdata);

	pr_err("----cg---- %s\n", __FUNCTION__);

	mask_irq(pdata->irq[HI6402_IRQ_BTNUP_ECO], true);
	mask_irq(pdata->irq[HI6402_IRQ_BTNDOWN_ECO], false);

	if (!check_headset_pluged_in(pdata))
		return IRQ_HANDLED;

	wake_lock_timeout(&pdata->wake_lock, 100);

	if (HISI_JACK_INVERT == pdata->hs_status) {
		pr_err("%s: further detect\n", __FUNCTION__);
		/* further detect */
		queue_delayed_work(pdata->headset_plug_in_delay_wq,
				&pdata->headset_plug_in_delay_work,
				msecs_to_jiffies(50));
	} else if (0 == pdata->btn_report){
		return IRQ_HANDLED;
	} else {
		queue_delayed_work(pdata->headset_btn_up_delay_wq,
				&pdata->headset_btn_up_delay_work,
				msecs_to_jiffies(50));
	}

	return IRQ_HANDLED;
}
static irqreturn_t hi6402_btnup_eco_handler(int irq, void *data)
{
	struct hi6402_mbhc_platform_data *pdata =
			(struct hi6402_mbhc_platform_data *)data;

	BUG_ON(NULL == pdata);

	if (!check_headset_pluged_in(pdata))
		return IRQ_HANDLED;

	wake_lock_timeout(&pdata->wake_lock, 100);

	if (HISI_JACK_INVERT == pdata->hs_status) {
		pr_err("%s: further detect\n", __FUNCTION__);
		/* further detect */
		hi6402_plug_in_detect(pdata);
	} else if (0 == pdata->btn_report){
		if (HISI_JACK_HEADSET != pdata->hs_status) {
			/* further detect */
			hi6402_plug_in_detect(pdata);
		}
		return IRQ_HANDLED;
	} else {
		mutex_lock(&pdata->status_mutex);
		pdata->btn_report = 0;
		hi6402_soc_jack_report(pdata->btn_report, HI6402_BTN_MASK);
		mutex_unlock(&pdata->status_mutex);
		pr_info("%s(%u) : btn up !\n", __FUNCTION__, __LINE__);
	}

	return IRQ_HANDLED;
}
static irqreturn_t hi6402_btndown_handler(int irq, void *data)
{
	struct hi6402_mbhc_platform_data *pdata =
			(struct hi6402_mbhc_platform_data *)data;

	BUG_ON(NULL == pdata);

	pr_err("----cg---- %s\n", __FUNCTION__);

	if (!check_headset_pluged_in(pdata))
		return IRQ_HANDLED;

	/* mask btn down interrupt*/
	mask_irq(pdata->irq[HI6402_IRQ_BTNDOWN_COMP1], true);
	/* unmask btn up interrupt*/
	mask_irq(pdata->irq[HI6402_IRQ_BTNUP_COMP1], false);

	wake_lock_timeout(&pdata->wake_lock, 50);

	queue_delayed_work(pdata->headset_btn_down_delay_wq,
				&pdata->headset_btn_down_delay_work,
				msecs_to_jiffies(30));

	return IRQ_HANDLED;
}
void hi6402_plug_in_detect(struct hi6402_mbhc_platform_data *pdata)
{
	if (!check_headset_pluged_in(pdata))
		return;

	wake_lock(&pdata->wake_lock);
	mutex_lock(&pdata->plug_mutex);

	mutex_lock(&pdata->status_mutex);
	/* todo : btn_report 4-pole headset only now */
	pdata->hs_status = HISI_JACK_HEADSET;
	pdata->btn_report = SND_JACK_HEADSET;
	mutex_unlock(&pdata->status_mutex);

	hi6402_jack_report(pdata);

	/* todo */
	hi6402_reg_clr_bit(pdata->p_irq, HI6402_MBHC_VREF_REG, 7);
	hi6402_reg_set_bit(pdata->p_irq, HI6402_MICBIAS_ECO_REG, 0);

	/* unmask btn down irq */
	mask_irq(pdata->irq[HI6402_IRQ_BTNDOWN_COMP1], false);
	mask_irq(pdata->irq[HI6402_IRQ_BTNDOWN_ECO], false);

	mutex_unlock(&pdata->plug_mutex);
	wake_unlock(&pdata->wake_lock);
}
static irqreturn_t hi6402_plugout_handler(int irq, void *data)
{
	struct hi6402_mbhc_platform_data *pdata =
			(struct hi6402_mbhc_platform_data *)data;

	BUG_ON(NULL == pdata);

	if (check_headset_pluged_in(pdata)) {
		pr_info("%s : hs still plugin \n", __FUNCTION__);
		return IRQ_HANDLED;
	}

	mutex_lock(&pdata->plug_mutex);

	hi6402_irq_cancel_delay_work(pdata->p_irq);

	hi6402_irq_mask_btn_irqs(pdata->p_irq);
	//stop charge first
	anc_hs_stop_charge();

	/* eco off */
	hi6402_reg_clr_bit(pdata->p_irq, HI6402_MICBIAS_ECO_REG, HI6402_MICBIAS_ECO_ON_BIT);

	mutex_lock(&pdata->p_irq->hs_micbias_mutex);
	/* hs micbias off */
	pdata->p_irq->mbhc_micbias_work = false;
	pdata->p_irq->dapm_micbias_work = false;
	mutex_unlock(&pdata->p_irq->hs_micbias_mutex);
	hi6402_irq_hs_micbias_enable(pdata->p_irq, false);

	hi6402_irq_clr_btn_irqs(pdata->p_irq);

	hi6402_irq_mask_btn_irqs(pdata->p_irq);

	/* eco off */
	hi6402_reg_clr_bit(pdata->p_irq, HI6402_MICBIAS_ECO_REG, HI6402_MICBIAS_ECO_ON_BIT);
	pr_info("%s : eco disable \n", __FUNCTION__);
	/* vref off */
	hi6402_reg_set_bit(pdata->p_irq, HI6402_MBHC_VREF_REG, HI6402_MBHC_VREF_BIT);
	/* mbhc cmp off */
	hi6402_reg_set_bit(pdata->p_irq, HI6402_ANA_REG60, HI6402_MBHC_ON_BIT);

	mutex_lock(&pdata->status_mutex);
	pdata->hs_status = HISI_JACK_NONE;
	pdata->btn_report = 0;
	mutex_unlock(&pdata->status_mutex);

	hi6402_jack_report(pdata);

	hi6402_mbhc_enable_ldo8(pdata, false);

	mutex_unlock(&pdata->plug_mutex);

	return IRQ_HANDLED;
}
static int hi6402_mbhc_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct hi6402_mbhc_platform_data *pdata = NULL;
	const struct of_device_id *match = NULL;
	int ret = 0;

	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
	if (NULL == pdata) {
		dev_err(dev, "cannot allocate hisi 6421 spk platform data\n");
		return -ENOMEM;
	}

	match = of_match_device(hi6402_mbhc_of_match, dev);
	if (!match) {
		pr_err("get device info err\n");
		return -ENOENT;
	} else {
		struct device_node *node = dev->of_node;
		int temp;

		/* get board defination */
		if (!of_property_read_u32(node, "hisilicon,hi6402_hs_det_inv", &temp))
			pdata->hs_det_inv = temp;
		else
			pdata->hs_det_inv = 0;

		if (!of_property_read_u32(node, "hisilicon,hs_3_pole_min_voltage", &temp))
			pdata->hs_3_pole_min_voltage = temp;
		else
			pdata->hs_3_pole_min_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,hs_3_pole_max_voltage", &temp))
			pdata->hs_3_pole_max_voltage = temp;
		else
			pdata->hs_3_pole_max_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,hs_4_pole_min_voltage", &temp))
			pdata->hs_4_pole_min_voltage = temp;
		else
			pdata->hs_4_pole_min_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,hs_4_pole_max_voltage", &temp))
			pdata->hs_4_pole_max_voltage = temp;
		else
			pdata->hs_4_pole_max_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,btn_play_min_voltage", &temp))
			pdata->btn_play_min_voltage = temp;
		else
			pdata->btn_play_min_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,btn_play_max_voltage", &temp))
			pdata->btn_play_max_voltage = temp;
		else
			pdata->btn_play_max_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,btn_volume_up_min_voltage", &temp))
			pdata->btn_volume_up_min_voltage = temp;
		else
			pdata->btn_volume_up_min_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,btn_volume_up_max_voltage", &temp))
			pdata->btn_volume_up_max_voltage = temp;
		else
			pdata->btn_volume_up_max_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,btn_volume_down_min_voltage", &temp))
			pdata->btn_volume_down_min_voltage = temp;
		else
			pdata->btn_volume_down_min_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,btn_volume_down_max_voltage", &temp))
			pdata->btn_volume_down_max_voltage = temp;
		else
			pdata->btn_volume_down_max_voltage = 0;
	}

	pdata->p_irq = dev_get_drvdata(pdev->dev.parent);
	if (!pdata->p_irq) {
		dev_err(dev, "get parend device error\n");
		return -ENOENT;
	}

#ifdef CONFIG_SWITCH
	pdata->sdev.name = "h2w";
	ret = switch_dev_register(&pdata->sdev);
	if (ret) {
		pr_err("%s : error registering switch device %d\n", __FUNCTION__, ret);
		return ret;
	}
#endif

	/* get irqs */
	pdata->irq[HI6402_IRQ_PLL_UNLOCK] = platform_get_irq_byname(pdev, "pll_unlock");
	if (0 > pdata->irq[HI6402_IRQ_PLL_UNLOCK]) {
		pr_err("get pll unlock error");
		return -ENOENT;
	}

	pdata->irq[HI6402_IRQ_SOUND_TRIGER] = platform_get_irq_byname(pdev, "sound_triger");
	if (0 > pdata->irq[HI6402_IRQ_SOUND_TRIGER]) {
		pr_err("get sound triger error");
		return -ENOENT;
	}

	pdata->irq[HI6402_IRQ_PLUGOUT] = platform_get_irq_byname(pdev, "plugout");
	if (0 > pdata->irq[HI6402_IRQ_PLUGOUT]) {
		pr_err("get plug out irq num error");
		return -ENOENT;
	}

	pdata->irq[HI6402_IRQ_PLUGIN] = platform_get_irq_byname(pdev, "plugin");
	if (0 > pdata->irq[HI6402_IRQ_PLUGIN]) {
		pr_err("get plug in irq num error");
		return -ENOENT;
	}

	pdata->irq[HI6402_IRQ_BTNDOWN_ECO] = platform_get_irq_byname(pdev, "btndown_eco");
	if (0 > pdata->irq[HI6402_IRQ_BTNDOWN_ECO]) {
		pr_err("get btn down eco irq num error");
		return -ENOENT;
	}

	pdata->irq[HI6402_IRQ_BTNUP_ECO] = platform_get_irq_byname(pdev, "btnup_eco");
	if (0 > pdata->irq[HI6402_IRQ_BTNUP_ECO]) {
		pr_err("get btn up eco irq num error");
		return -ENOENT;
	}

	pdata->irq[HI6402_IRQ_BTNDOWN_COMP1] = platform_get_irq_byname(pdev, "btndown");
	if (0 > pdata->irq[HI6402_IRQ_BTNDOWN_COMP1]) {
		pr_err("get btn down irq num error");
		return -ENOENT;
	}

	pdata->irq[HI6402_IRQ_BTNUP_COMP1] = platform_get_irq_byname(pdev, "btnup");
	if (0 > pdata->irq[HI6402_IRQ_BTNUP_COMP1]) {
		pr_err("get btn up irq num error");
		return -ENOENT;
	}

	wake_lock_init(&pdata->wake_lock, WAKE_LOCK_SUSPEND, "hisi-6402-mbhc");
	wake_lock_init(&pdata->soundtrigger_wake_lock, WAKE_LOCK_SUSPEND, "hisi-6402-soundtrigger");
	mutex_init(&pdata->plug_mutex);
	mutex_init(&pdata->status_mutex);
	mutex_init(&pdata->saradc_mutex);

	/* irq request : pll unlock */
	ret = devm_request_threaded_irq(dev, pdata->irq[HI6402_IRQ_PLL_UNLOCK], NULL,
					hi6402_pll_unlock_handler,
					IRQF_NO_SUSPEND | IRQF_ONESHOT,
					"pll_unlock", pdata);
	if (0 > ret) {
		pr_err("request irq for pll unlock err\n");
		goto pll_unlock_err;
	}
	mutex_lock(&pdata->p_irq->irq_lock);
	hi6402_reg_set_bit(pdata->p_irq, HI6402_MASK_IRQ_REG_2, HI6402_MASK_PLL_UNLOCK_BIT);
	pdata->p_irq->mask2 |= 0x10;
	mutex_unlock(&pdata->p_irq->irq_lock);

	/* irq request : sound triger */
	ret = devm_request_threaded_irq(dev, pdata->irq[HI6402_IRQ_SOUND_TRIGER], NULL,
					hi6402_sound_triger_handler,
					IRQF_NO_SUSPEND | IRQF_ONESHOT,
					"sound_triger", pdata);
	if (0 > ret) {
		pr_err("request irq for sound triger err\n");
		goto sound_triger_err;
	}

	/* irq request : plugout */
	ret = devm_request_threaded_irq(dev, pdata->irq[HI6402_IRQ_PLUGOUT], NULL,
					hi6402_plugout_handler,
					IRQF_NO_SUSPEND | IRQF_ONESHOT,
					"plugout", pdata);
	if (0 > ret) {
		pr_err("request irq for plugout err\n");
		goto plugout_err;
	}

	/* irq request : plugin */
	ret = devm_request_threaded_irq(dev, pdata->irq[HI6402_IRQ_PLUGIN], NULL,
					hi6402_plugin_handler,
					IRQF_NO_SUSPEND | IRQF_ONESHOT,
					"plugin", pdata);
	if (0 > ret) {
		pr_err("request irq for plugin err\n");
		goto plugin_err;
	}

	/* irq request : button up(eco mode) */
	ret = devm_request_threaded_irq(dev, pdata->irq[HI6402_IRQ_BTNUP_ECO], NULL,
					hi6402_btnup_eco_handler,
					IRQF_NO_SUSPEND | IRQF_ONESHOT,
					"btnup_eco", pdata);
	if (0 > ret) {
		pr_err("request irq for btnup eco err\n");
		goto btnup_eco_err;
	}

	/* irq request : button down(eco mode) */
	ret = devm_request_threaded_irq(dev, pdata->irq[HI6402_IRQ_BTNDOWN_ECO], NULL,
					hi6402_btndown_eco_handler,
					IRQF_NO_SUSPEND | IRQF_ONESHOT,
					"btndown_eco", pdata);
	if (0 > ret) {
		pr_err("request irq for btndown eco err\n");
		goto btndown_eco_err;
	}

	/* irq request : button down */
	ret = devm_request_threaded_irq(dev, pdata->irq[HI6402_IRQ_BTNDOWN_COMP1], NULL,
					hi6402_btndown_handler,
					IRQF_NO_SUSPEND | IRQF_ONESHOT,
					"btndown_comp1", pdata);
	if (0 > ret) {
		pr_err("request irq for btndown comp1 err\n");
		goto btndown_comp1_err;
	}

	/* irq request : button up */
	ret = devm_request_threaded_irq(dev, pdata->irq[HI6402_IRQ_BTNUP_COMP1], NULL,
					hi6402_btnup_handler,
					IRQF_NO_SUSPEND | IRQF_ONESHOT,
					"btnup_comp1", pdata);
	if (0 > ret) {
		pr_err("request irq for btnup comp1 err\n");
		goto btnup_comp1_err;
	}

	hi6402_irq_mask_btn_irqs(pdata->p_irq);
	hi6402_irq_clr_btn_irqs(pdata->p_irq);
	
	/* enable hsdet */
	hi6402_irq_write(pdata->p_irq, HI6402_REG_HSDET_CTRL, 0x19);
	hi6402_irq_write(pdata->p_irq, HI6402_MBHC_VREF_REG, 0x8E);

	//register anc hs first
	anc_hs_dev_register(&anc_dev, pdata);

	//register soundtrigger input device.
	ret = soundtrigger_input_init(dev);
	if(ret)
		pr_err("input registor failed: %d\n", ret);

#if 0
	/* check jack at first time */
	if (check_headset_pluged_in(pdata))
		hi6402_plug_in_detect(pdata);
#endif

	pdata->miscdev.minor = MISC_DYNAMIC_MINOR;
	pdata->miscdev.name = "hi6402_mbhc";

	ret = misc_register(&pdata->miscdev);
	if (ret) {
		loge("%s : hisi 6421 spk_device register failed", __FUNCTION__);
		goto btnup_comp1_err;
	}

	if (!dsm_audio_client) {
		dsm_audio_client = dsm_register_client(&dsm_audio);
	}

	pr_info("%s : hi6402 mbhc probe ok \n", __FUNCTION__);

	return ret;

btnup_comp1_err:
	free_irq(pdata->irq[HI6402_IRQ_BTNDOWN_COMP1], pdata);
btndown_comp1_err:
	free_irq(pdata->irq[HI6402_IRQ_BTNDOWN_ECO], pdata);
btndown_eco_err:
	free_irq(pdata->irq[HI6402_IRQ_BTNUP_ECO], pdata);
btnup_eco_err:
	free_irq(pdata->irq[HI6402_IRQ_PLUGIN], pdata);
plugin_err:
	free_irq(pdata->irq[HI6402_IRQ_PLUGOUT], pdata);
plugout_err:
	free_irq(pdata->irq[HI6402_IRQ_SOUND_TRIGER], pdata);
sound_triger_err:
	free_irq(pdata->irq[HI6402_IRQ_PLL_UNLOCK], pdata);
pll_unlock_err:
	wake_lock_destroy(&pdata->soundtrigger_wake_lock);
	wake_lock_destroy(&pdata->wake_lock);
	mutex_destroy(&pdata->plug_mutex);
	mutex_destroy(&pdata->status_mutex);

	return ret;
}
static bool hi6402_check_headset_in(void *pdata)
{
	return check_headset_pluged_in((struct hi6402_mbhc_platform_data *)pdata);
}
void hi6402_btn_down(struct hi6402_mbhc_platform_data *pdata)
{
	int saradc_value = 0;

	if (!check_headset_pluged_in(pdata)) {
		pr_info("%s(%u) : hs pluged out \n", __FUNCTION__, __LINE__);
		return;
	}

	wake_lock(&pdata->wake_lock);

	if (HISI_JACK_HEADSET == pdata->hs_status) {
		/* micbias on */
		hi6402_irq_micbias_mbhc_enable(pdata->p_irq, true);

		/* auto read */
		saradc_value = hi6402_read_saradc_value_detect(pdata);

		/* micbias off */
		hi6402_irq_micbias_mbhc_enable(pdata->p_irq, false);

		msleep(30);

		if (!check_headset_pluged_in(pdata)) {
			pr_info("%s(%u) : hs pluged out \n", __FUNCTION__, __LINE__);
			goto end;
		}

		if ((saradc_value >= pdata->hs_4_pole_min_voltage) && (saradc_value <= pdata->hs_4_pole_max_voltage)) {
			pr_info("%s(%u) : process as btn up! \n", __FUNCTION__, __LINE__);
			mutex_lock(&pdata->status_mutex);
			pdata->btn_report = 0;
			mutex_unlock(&pdata->status_mutex);
		} else if ((saradc_value >= pdata->btn_play_min_voltage) && (saradc_value <= pdata->btn_play_max_voltage)) {
			mutex_lock(&pdata->status_mutex);
			pdata->btn_report = SND_JACK_BTN_0;
			mutex_unlock(&pdata->status_mutex);
		} else if (pdata->btn_volume_up_min_voltage < saradc_value && saradc_value <= pdata->btn_volume_up_max_voltage) {
			mutex_lock(&pdata->status_mutex);
			pdata->btn_report = SND_JACK_BTN_1;
			mutex_unlock(&pdata->status_mutex);
		} else if (pdata->btn_volume_down_min_voltage < saradc_value && saradc_value <= pdata->btn_volume_down_max_voltage) {
			mutex_lock(&pdata->status_mutex);
			pdata->btn_report = SND_JACK_BTN_2;
			mutex_unlock(&pdata->status_mutex);
		} else {
			msleep(30);
			hi6402_plug_in_detect(pdata);
			goto end;
		}

		if (!check_headset_pluged_in(pdata)) {
			pr_info("%s(%u) : hs pluged out \n", __FUNCTION__, __LINE__);
			goto end;
		}

		/*btn_report key event*/
		pr_info("%s(%u): btn_report type = 0x%x, status=0x%x\n",
				__FUNCTION__, __LINE__, pdata->btn_report, pdata->hs_status);
		hi6402_soc_jack_report(pdata->btn_report, HI6402_BTN_MASK);
	}

end:
	wake_unlock(&pdata->wake_lock);

	return;
}
void hi6402_plug_in_detect(struct hi6402_mbhc_platform_data *pdata)
{
	int saradc_value = 0;
	int anc_type = ANC_HS_REVERT_4POLE;

	if (!check_headset_pluged_in(pdata))
		return;

	wake_lock(&pdata->wake_lock);
	mutex_lock(&pdata->plug_mutex);

	if(check_anc_hs_support()) {
		//mask btn irqs while control boost
		hi6402_irq_mask_btn_irqs(pdata->p_irq);
		anc_hs_start_charge();
	}

	/* micbias on */
	hi6402_irq_micbias_mbhc_enable(pdata->p_irq, true);

	/* mbhc on (normal not auto) */
	hi6402_mbhc_on(pdata);
	/* read hs value */
	saradc_value = hi6402_read_saradc_value_detect(pdata);

	mutex_lock(&pdata->status_mutex);

	if (pdata->hs_3_pole_max_voltage >= saradc_value) {
		/* 3-pole headphone */
		pr_info("%s : 3 pole is pluged in\n", __FUNCTION__);
		pdata->hs_status = HISI_JACK_HEADPHONE;
		anc_type = ANC_HS_3POLE;
	} else if (pdata->hs_4_pole_min_voltage <= saradc_value &&
			pdata->hs_4_pole_max_voltage >= saradc_value) {
		/* 4-pole headset */
		pr_info("%s : 4 pole is pluged in\n", __FUNCTION__);
		pdata->hs_status = HISI_JACK_HEADSET;
		anc_type = ANC_HS_4POLE;
	} else {
		/* invert 4-pole headset */
		pr_info("%s : need further detect, report as 3-pole headphone\n", __FUNCTION__);
		pdata->hs_status = HISI_JACK_INVERT;
		anc_type = ANC_HS_REVERT_4POLE;
	}

	mutex_unlock(&pdata->status_mutex);

	if(check_anc_hs_support()) {
		//mask btn irqs while control boost
		hi6402_irq_mask_btn_irqs(pdata->p_irq);
		anc_hs_charge_detect(saradc_value, anc_type);
	}

	hi6402_jack_report(pdata);

	hi6402_mbhc_enable_ldo8(pdata, true);

	/* micbias off */
	hi6402_irq_micbias_mbhc_enable(pdata->p_irq, false);

	mutex_unlock(&pdata->plug_mutex);
	wake_unlock(&pdata->wake_lock);
}
static int hi6402_mbhc_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct hi6402_mbhc_platform_data *pdata = NULL;
	const struct of_device_id *match = NULL;
	int ret = 0;

	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
	if (NULL == pdata){
		dev_err(dev, "cannot allocate hisi 6421 spk platform data\n");
		return -ENOMEM;
	}

	match = of_match_device(hi6402_mbhc_of_match, dev);
	if (!match) {
		pr_err("get device info err\n");
		return -ENOENT;
	} else {
		struct device_node *node = dev->of_node;
		int temp;

		/* get board defination */
		if (!of_property_read_u32(node, "hisilicon,hi6402_hs_det_inv", &temp))
			pdata->hs_det_inv = temp;
		else
			pdata->hs_det_inv = 0;

		if (!of_property_read_u32(node, "hisilicon,hs_3_pole_min_voltage", &temp))
			pdata->hs_3_pole_min_voltage = temp;
		else
			pdata->hs_3_pole_min_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,hs_3_pole_max_voltage", &temp))
			pdata->hs_3_pole_max_voltage = temp;
		else
			pdata->hs_3_pole_max_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,hs_4_pole_min_voltage", &temp))
			pdata->hs_4_pole_min_voltage = temp;
		else
			pdata->hs_4_pole_min_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,hs_4_pole_max_voltage", &temp))
			pdata->hs_4_pole_max_voltage = temp;
		else
			pdata->hs_4_pole_max_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,btn_play_min_voltage", &temp))
			pdata->btn_play_min_voltage = temp;
		else
			pdata->btn_play_min_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,btn_play_max_voltage", &temp))
			pdata->btn_play_max_voltage = temp;
		else
			pdata->btn_play_max_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,btn_volume_up_min_voltage", &temp))
			pdata->btn_volume_up_min_voltage = temp;
		else
			pdata->btn_volume_up_min_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,btn_volume_up_max_voltage", &temp))
			pdata->btn_volume_up_max_voltage = temp;
		else
			pdata->btn_volume_up_max_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,btn_volume_down_min_voltage", &temp))
			pdata->btn_volume_down_min_voltage = temp;
		else
			pdata->btn_volume_down_min_voltage = 0;

		if (!of_property_read_u32(node, "hisilicon,btn_volume_down_max_voltage", &temp))
			pdata->btn_volume_down_max_voltage = temp;
		else
			pdata->btn_volume_down_max_voltage = 0;
	}

	pdata->p_irq = dev_get_drvdata(pdev->dev.parent);
	if (!pdata->p_irq) {
		dev_err(dev, "get parend device error\n");
		return -ENOENT;
	}

#ifdef CONFIG_SWITCH
	pdata->sdev.name = "h2w";
	ret = switch_dev_register(&pdata->sdev);
	if (ret) {
		pr_err("%s : error registering switch device %d\n", __FUNCTION__, ret);
		return ret;
	}
#endif

	wake_lock_init(&pdata->wake_lock, WAKE_LOCK_SUSPEND, "hisi-6402-mbhc");
	mutex_init(&pdata->plug_mutex);
	mutex_init(&pdata->status_mutex);

	/* get irqs */
	pdata->irq[HI6402_IRQ_PLUGOUT] = platform_get_irq_byname(pdev, "plugout");
	if (0 > pdata->irq[HI6402_IRQ_PLUGOUT])
		return -ENOENT;

	pdata->irq[HI6402_IRQ_PLUGIN] = platform_get_irq_byname(pdev, "plugin");
	if (0 > pdata->irq[HI6402_IRQ_PLUGIN])
		return -ENOENT;

	pdata->irq[HI6402_IRQ_BTNDOWN_ECO] = platform_get_irq_byname(pdev, "btndown_eco");
	if (0 > pdata->irq[HI6402_IRQ_BTNDOWN_ECO])
		return -ENOENT;

	pdata->irq[HI6402_IRQ_BTNUP_ECO] = platform_get_irq_byname(pdev, "btnup_eco");
	if (0 > pdata->irq[HI6402_IRQ_BTNUP_ECO])
		return -ENOENT;

	pdata->irq[HI6402_IRQ_BTNDOWN_COMP1] = platform_get_irq_byname(pdev, "btndown");
	if (0 > pdata->irq[HI6402_IRQ_BTNDOWN_COMP1])
		return -ENOENT;

	pdata->irq[HI6402_IRQ_BTNUP_COMP1] = platform_get_irq_byname(pdev, "btnup");
	if (0 > pdata->irq[HI6402_IRQ_BTNUP_COMP1])
		return -ENOENT;


	pdata->headset_plug_in_delay_wq =
			create_singlethread_workqueue("headset_plug_in_delay_wq");
	if (!(pdata->headset_plug_in_delay_wq)) {
		pr_err("%s : headset_plug_in_delay_wq create failed", __FUNCTION__);
		ret = -ENOMEM;
		goto headset_plug_in_wq_err;
	}
	INIT_DELAYED_WORK(&pdata->headset_plug_in_delay_work, hi6402_plug_in_workfunc);

	pdata->headset_plug_out_delay_wq =
			create_singlethread_workqueue("headset_plug_out_delay_wq");
	if (!(pdata->headset_plug_out_delay_wq)) {
		pr_err("%s : headset_plug_out_delay_wq create failed", __FUNCTION__);
		ret = -ENOMEM;
		goto headset_plug_out_wq_err;
	}
	INIT_DELAYED_WORK(&pdata->headset_plug_out_delay_work, hi6402_plug_out_workfunc);

	pdata->headset_btn_up_delay_wq =
			create_singlethread_workqueue("headset_btn_up_delay_wq");
	if (!(pdata->headset_btn_up_delay_wq)) {
		pr_err("%s : headset_btn_up_delay_wq create failed", __FUNCTION__);
		ret = -ENOMEM;
		goto headset_btn_up_wq_err;
	}
	INIT_DELAYED_WORK(&pdata->headset_btn_up_delay_work, hi6402_btnup_workfunc);

	pdata->headset_btn_down_delay_wq =
			create_singlethread_workqueue("headset_btn_down_delay_wq");
	if (!(pdata->headset_btn_down_delay_wq)) {
		pr_err("%s : headset_btn_down_delay_wq create failed", __FUNCTION__);
		ret = -ENOMEM;
		goto headset_btn_down_wq_err;
	}
	INIT_DELAYED_WORK(&pdata->headset_btn_down_delay_work, hi6402_btndown_workfunc);

	/* irq request : plugout */
	ret = request_irq(pdata->irq[HI6402_IRQ_PLUGOUT],
			  hi6402_plugout_handler,
			  IRQF_DISABLED | IRQF_NO_SUSPEND, "plugout", pdata);
	if (0 > ret)
		goto plugout_err;

	/* irq request : plugin */
	ret = request_irq(pdata->irq[HI6402_IRQ_PLUGIN],
			  hi6402_plugin_handler,
			  IRQF_DISABLED | IRQF_NO_SUSPEND, "plugin", pdata);
	if (0 > ret)
		goto plugin_err;

	/* irq request : button up(eco mode) */
	ret = request_irq(pdata->irq[HI6402_IRQ_BTNUP_ECO],
			  hi6402_btnup_eco_handler,
			  IRQF_DISABLED | IRQF_NO_SUSPEND, "btnup_eco", pdata);
	if (0 > ret)
		goto btnup_eco_err;

	/* irq request : button down(eco mode) */
	ret = request_irq(pdata->irq[HI6402_IRQ_BTNDOWN_ECO],
			  hi6402_btndown_eco_handler,
			  IRQF_DISABLED | IRQF_NO_SUSPEND, "btndown_eco", pdata);
	if (0 > ret)
		goto btndown_eco_err;

	/* irq request : button down */
	ret = request_irq(pdata->irq[HI6402_IRQ_BTNDOWN_COMP1],
			  hi6402_btndown_handler,
			  IRQF_DISABLED | IRQF_NO_SUSPEND, "btndown_comp1", pdata);
	if (0 > ret)
		goto btndown_comp1_err;

	/* irq request : button up */
	ret = request_irq(pdata->irq[HI6402_IRQ_BTNUP_COMP1],
			  hi6402_btnup_handler,
			  IRQF_DISABLED | IRQF_NO_SUSPEND, "btnup_comp1", pdata);
	if (0 > ret)
		goto btnup_comp1_err;

	/* mask all btn interrupt*/
	mask_irq(pdata->irq[HI6402_IRQ_BTNDOWN_ECO], true);
	mask_irq(pdata->irq[HI6402_IRQ_BTNUP_ECO], true);
	mask_irq(pdata->irq[HI6402_IRQ_BTNDOWN_COMP1], true);
	mask_irq(pdata->irq[HI6402_IRQ_BTNUP_COMP1], true);

#if 0 /* todo in cs version */
	/* check jack at first time */
	if (check_headset_pluged_in(pdata))
		hi6402_plug_in_detect(pdata);
#endif

	/* enable hsdet */
	hi6402_irq_write(pdata->p_irq, HI6402_REG_HSDET_CTRL, 0x1);
	hi6402_irq_write(pdata->p_irq, HI6402_MBHC_VREF_REG, 0x8c);

	pdata->miscdev.minor = MISC_DYNAMIC_MINOR;
	pdata->miscdev.name = "hi6402_mbhc";

	ret = misc_register(&pdata->miscdev);
	if (ret)
		loge("%s : hisi 6421 spk_device register failed", __FUNCTION__);

	return ret;

btnup_comp1_err:
	free_irq(pdata->irq[HI6402_IRQ_BTNDOWN_COMP1], pdata);
btndown_comp1_err:
	free_irq(pdata->irq[HI6402_IRQ_BTNDOWN_ECO], pdata);
btndown_eco_err:
	free_irq(pdata->irq[HI6402_IRQ_BTNUP_ECO], pdata);
btnup_eco_err:
	free_irq(pdata->irq[HI6402_IRQ_PLUGIN], pdata);
plugin_err:
	free_irq(pdata->irq[HI6402_IRQ_PLUGOUT], pdata);
plugout_err:
	if(pdata->headset_btn_down_delay_wq) {
		cancel_delayed_work(&pdata->headset_btn_down_delay_work);
		flush_workqueue(pdata->headset_btn_down_delay_wq);
		destroy_workqueue(pdata->headset_btn_down_delay_wq);
	}
headset_btn_down_wq_err:
	if(pdata->headset_btn_up_delay_wq) {
		cancel_delayed_work(&pdata->headset_btn_up_delay_work);
		flush_workqueue(pdata->headset_btn_up_delay_wq);
		destroy_workqueue(pdata->headset_btn_up_delay_wq);
	}
headset_btn_up_wq_err:
	if(pdata->headset_plug_out_delay_wq) {
		cancel_delayed_work(&pdata->headset_plug_out_delay_work);
		flush_workqueue(pdata->headset_plug_out_delay_wq);
		destroy_workqueue(pdata->headset_plug_out_delay_wq);
	}
headset_plug_out_wq_err:
	if(pdata->headset_plug_in_delay_wq) {
		cancel_delayed_work(&pdata->headset_plug_in_delay_work);
		flush_workqueue(pdata->headset_plug_in_delay_wq);
		destroy_workqueue(pdata->headset_plug_in_delay_wq);
	}
headset_plug_in_wq_err:
	return ret;
}
void hi6402_btndown_workfunc(struct work_struct *work)
{
	int saradc_value = 0;
	struct hi6402_mbhc_platform_data *pdata = container_of(work,
			struct hi6402_mbhc_platform_data,
			headset_btn_down_delay_work.work);

	BUG_ON(NULL == pdata);

	if (!check_headset_pluged_in(pdata)) {
		pr_info("%s(%u) : hs pluged out \n", __FUNCTION__, __LINE__);
		return;
	}

	pr_info("%s(%u) : btn down !\n", __FUNCTION__, __LINE__);

	wake_lock(&pdata->wake_lock);

	if (HISI_JACK_HEADSET == pdata->hs_status) {
		/* power on hs-micbias */
		//hi6401_hs_micbias_saradc_enable(codec, true);
		//saradc_value = hi6401_read_saradc_value(codec);
		//pr_info("%s(%u) :saradc_value: %d \n", __FUNCTION__, __LINE__, saradc_value);
		//hi6401_hs_micbias_saradc_enable(codec, false);

		if (!check_headset_pluged_in(pdata)) {
			pr_info("%s(%u) : hs pluged out \n", __FUNCTION__, __LINE__);

			/* mask all btn interrupt*/
			mask_irq(pdata->irq[HI6402_IRQ_BTNUP_COMP1], true);
			mask_irq(pdata->irq[HI6402_IRQ_BTNDOWN_COMP1], true);
			goto end;
		}

		if ((saradc_value >= pdata->hs_4_pole_min_voltage) && (saradc_value <= pdata->hs_4_pole_max_voltage)) {
			pr_info("%s(%u) : process as btn up! \n", __FUNCTION__, __LINE__);
			mutex_lock(&pdata->status_mutex);
			pdata->btn_report = 0;
			mutex_unlock(&pdata->status_mutex);
		} else if ((saradc_value >= pdata->btn_play_min_voltage) && (saradc_value <= pdata->btn_play_max_voltage)) {
			mutex_lock(&pdata->status_mutex);
			pdata->btn_report = SND_JACK_BTN_0;
			mutex_unlock(&pdata->status_mutex);
		} else if (pdata->btn_volume_up_min_voltage < saradc_value && saradc_value <= pdata->btn_volume_up_max_voltage) {
			mutex_lock(&pdata->status_mutex);
			pdata->btn_report = SND_JACK_BTN_1;
			mutex_unlock(&pdata->status_mutex);
		} else if (pdata->btn_volume_down_min_voltage < saradc_value && saradc_value <= pdata->btn_volume_down_max_voltage) {
			mutex_lock(&pdata->status_mutex);
			pdata->btn_report = SND_JACK_BTN_2;
			mutex_unlock(&pdata->status_mutex);
		} else {
			msleep(30);
			hi6402_plug_in_detect(pdata);
			goto end;
		}

		if (!check_headset_pluged_in(pdata)) {
			pr_info("%s(%u) : hs pluged out \n", __FUNCTION__, __LINE__);

			/* mask all btn interrupt*/
			mask_irq(pdata->irq[HI6402_IRQ_BTNUP_COMP1], true);
			mask_irq(pdata->irq[HI6402_IRQ_BTNDOWN_COMP1], true);
			goto end;
		}

		/*btn_report key event*/
		pr_info("%s(%u): btn_report type = 0x%x, status=0x%x\n",
				__FUNCTION__, __LINE__, pdata->btn_report, pdata->hs_status);
		hi6402_soc_jack_report(pdata->btn_report, HI6402_BTN_MASK);
	}

	/* mask btn down interrupt*/
	mask_irq(pdata->irq[HI6402_IRQ_BTNDOWN_COMP1], true);
	/* unmask btn up interrupt*/
	mask_irq(pdata->irq[HI6402_IRQ_BTNUP_COMP1], false);
end:
	wake_unlock(&pdata->wake_lock);

	return;
}