Beispiel #1
0
static void twl6040_hs_jack_report(struct snd_soc_codec *codec,
				struct snd_soc_jack *jack, int report)
{
	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
	int status, state;

	mutex_lock(&priv->mutex);

	/* Sync status */
	status = twl6040_read_reg_volatile(codec, TWL6040_REG_STATUS);
	if (status & TWL6040_PLUGCOMP)
		state = report;
	else
		state = 0;

	mutex_unlock(&priv->mutex);

	snd_soc_jack_report(jack, state, report);
	switch_set_state(&priv->hs_jack.sdev, !!state);
}
Beispiel #2
0
static int twl6040_set_bias_level(struct snd_soc_codec *codec,
				enum snd_soc_bias_level level)
{
	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
	int audpwron = priv->audpwron;
	int naudint = priv->naudint;
	int ret;

	switch (level) {
	case SND_SOC_BIAS_ON:
		break;
	case SND_SOC_BIAS_PREPARE:
		break;
	case SND_SOC_BIAS_STANDBY:
		if (priv->codec_powered)
			break;

		if (gpio_is_valid(audpwron)) {
			/* use AUDPWRON line */
			gpio_set_value(audpwron, 1);

			/* wait for power-up completion */
			ret = twl6040_power_up_completion(codec, naudint);
			if (ret)
				return ret;

			/* sync registers updated during power-up sequence */
			twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL);
			twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL);
			twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL);
		} else {
			/* use manual power-up sequence */
			twl6040_power_up(codec);
			priv->codec_powered = 1;
		}

		/* initialize vdd/vss registers with reg_cache */
		twl6040_init_vdd_regs(codec);
		break;
	case SND_SOC_BIAS_OFF:
		if (!priv->codec_powered)
			break;

		if (gpio_is_valid(audpwron)) {
			/* use AUDPWRON line */
			gpio_set_value(audpwron, 0);

			/* power-down sequence latency */
			udelay(500);

			/* sync registers updated during power-down sequence */
			twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL);
			twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL);
			twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL,
						0x00);
		} else {
			/* use manual power-down sequence */
			twl6040_power_down(codec);
		}

		priv->codec_powered = 0;
		break;
	}

	codec->bias_level = level;

	return 0;
}
Beispiel #3
0
static int twl6040_probe(struct snd_soc_codec *codec)
{
	struct twl4030_codec_audio_data *twl_codec = codec->dev->platform_data;
	struct twl6040_data *priv;
	struct twl6040_jack_data *jack;
	int audpwron, naudint;
	struct input_dev *ip_dev;

#if defined(CONFIG_MACH_LGE_COSMO_REV_A)
	unsigned hsjack_gpio, hsjack_irq;
	int err;
#endif
	int ret = 0;
	u8 icrev = 0, intmr = TWL6040_ALLINT_MSK;
	int idx;

	for( idx = 0 ; idx < TWL6040_MUTE_DATA_MAX ; idx++ )
	{
		s_mute_data[idx].dai = 0;
		s_mute_data[idx].mute = 0;
	}
	
	priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL);
	if (priv == NULL)
		return -ENOMEM;
	snd_soc_codec_set_drvdata(codec, priv);

	priv->codec = codec;
	priv->dl_active = 0;
	priv->ul_active = 0;

	twl6040_i2c_read(TWL6040_REG_ASICREV, &icrev);

	if (twl_codec && (icrev > 0))
		audpwron = twl_codec->audpwron_gpio;
	else
		audpwron = -EINVAL;

#if defined(CONFIG_MACH_LGE_COSMO_REV_A)
	if (twl_codec){
		naudint = twl_codec->naudint_irq;
		hsjack_gpio = twl_codec->hsjack_gpio;
		hsjack_irq = twl_codec->hsjack_irq;
	}
	else {
		naudint = 0;
		hsjack_gpio = 0;
		hsjack_irq = 0;
	}
#else
	if (twl_codec)
		naudint = twl_codec->naudint_irq;
	else
		naudint = 0;
#endif

	priv->audpwron = audpwron;
	priv->naudint = naudint;
	
#if defined(CONFIG_MACH_LGE_COSMO_REV_A)
	priv->hsjack_gpio = hsjack_gpio;
	priv->hsjack_irq = hsjack_irq;
#endif
	init_completion(&priv->ready);

	/* Disable safe mode in SYS_NIRQ PAD */
//	omap_writew(0x0118, 0x4A1001A0);

	INIT_DELAYED_WORK(&priv->hsdet_dwork, twl6040_hs_jack_detect_dwork);
	INIT_DELAYED_WORK(&priv->hook_work, twl6040_hs_hook_detect_work);

#ifndef CONFIG_MACH_LGE_COSMOPOLITAN
	INIT_WORK(&priv->audint_work, twl6040_audint_work);
#endif

	ip_dev	= input_allocate_device();
	if(!ip_dev){
		dev_err(codec->dev, "failed to allocation hook input device");
		goto switch_err;
	}
	__set_bit(EV_KEY, ip_dev->evbit);
	__set_bit(EV_SYN, ip_dev->evbit);
	__set_bit(KEY_HOOK, ip_dev->keybit);
	ip_dev->name = "headset_hook";
	ip_dev->phys = "headset_hook/input0";
	priv->hs_jack.headset_input = ip_dev;
	input_register_device(priv->hs_jack.headset_input);

	/* switch-class based headset detection */
	jack = &priv->hs_jack;
	jack->sdev.name = "h2w";
	ret = switch_dev_register(&jack->sdev);
	if (ret) {
		dev_err(codec->dev, "error registering switch device %d\n", ret);
		goto switch_err;
	}
#if defined(CONFIG_MACH_LGE_COSMO_REV_A)
	/* GPIO request and direction set */
	if(gpio_is_valid(hsjack_gpio)) {
		err = gpio_request(hsjack_gpio, "ear_sense");
		if (err) {
			printk(KERN_ERR "%s: failed to request GPIO_%d\n",
				   __func__, hsjack_gpio);
			goto err_hs_gpio_request;
		}
		err = gpio_direction_input(hsjack_gpio);
		if (err) {
			printk(KERN_ERR "%s: failed to set direction GPIO_%d\n",
				   __func__, hsjack_gpio);
			goto err_hs_gpio_direction;
		}
	}

	/* IRQ request */
	err = request_irq(hsjack_irq,
					  hsjack_irq_handler,
					  IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
					  "headset_detect",
					  codec);
	if (err) {
		printk(KERN_ERR "%s: failed to request irq (%d)\n",
			   __func__, hsjack_irq);
		goto err_hs_request_irq;
	}
#endif

	if (gpio_is_valid(audpwron)) {
		ret = gpio_request(audpwron, "audpwron");
		if (ret)
			goto gpio1_err;

		ret = gpio_direction_output(audpwron, 0);
		if (ret)
			goto gpio2_err;

		priv->codec_powered = 0;

		/* enable only codec ready interrupt */
		intmr &= ~(TWL6040_READYMSK | TWL6040_PLUGMSK );
		priv->intmask = intmr; 

		/* reset interrupt status to allow correct power up sequence */
		twl6040_read_reg_volatile(codec, TWL6040_REG_INTID);
	}
	twl6040_write(codec, TWL6040_REG_INTMR, intmr);

	if (naudint) {
		/* audio interrupt */
		ret = request_threaded_irq(naudint, NULL,
				twl6040_naudint_handler,
				IRQF_TRIGGER_LOW | IRQF_ONESHOT,
				"twl6040_codec", codec);
		if (ret)
			goto gpio2_err;
	}

	/* init vio registers */
	twl6040_init_vio_regs(codec);

	/* power on device */
	ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
	if (ret)
		goto bias_err;

	snd_soc_add_controls(codec, twl6040_snd_controls,
				ARRAY_SIZE(twl6040_snd_controls));
	twl6040_add_widgets(codec);

	wake_lock_init(&priv->wake_lock, WAKE_LOCK_SUSPEND, "twl6040");

	cdc_tcxo_set_req_int(CDC_TCXO_CLK3, 0);

	/* TODO: read HS jack insertion status */

	return 0;

bias_err:
	if (naudint)
		free_irq(naudint, codec);
#if defined(CONFIG_MACH_LGE_COSMO_REV_A)
err_hs_request_irq:
err_hs_gpio_direction:
	if (gpio_is_valid(hsjack_gpio))
		gpio_free(hsjack_gpio);
err_hs_gpio_request:
#endif
gpio2_err:
	if (gpio_is_valid(audpwron))
		gpio_free(audpwron);
gpio1_err:
	switch_dev_unregister(&jack->sdev);
switch_err:
	kfree(priv);
	return ret;
}
Beispiel #4
0
void twl6040_hs_jack_detect(struct snd_soc_codec *codec,
			    struct snd_soc_jack *jack, int report)
{
	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
	int status, state;

	priv->hs_jack.jack = jack;
	priv->hs_jack.report = report;

#if !defined(CONFIG_MACH_LGE_COSMO_REV_A)
	/* Sync status */
	status = twl6040_read_reg_volatile(codec, TWL6040_REG_STATUS);

#if defined(CONFIG_MACH_LGE_COSMOPOLITAN)
	if(status & TWL6040_PLUGCOMP)
		priv->hs_jack.state = HEADSET_NONE;		
	else
		priv->hs_jack.state = WIRED_HEADSET;
#else
	if(status & TWL6040_PLUGCOMP)
		state = report;
	else
		state = 0;
#endif

#endif// !CONFIG_MACH_LGE_COSMO_REV_A


#if defined(CONFIG_MACH_LGE_COSMO_REV_A)
	if(gpio_get_value(priv->hsjack_gpio))
#else
	if(priv->hs_jack.state)
#endif
	{
		hs_set_bias(codec, 1);
		set_hook_enable(codec, 1);
		mdelay(200);

		if(is_without_mic()){
			set_hook_enable(codec, 0);
			hs_set_bias(codec, 0);
			state = WIRED_HEADPHONE;//wired headset without MIC
			status = SND_JACK_HEADPHONE;
		}
		else{
			 /* for wired_headset */
			if(priv->intmask & TWL6040_HOOKMSK){
				priv->intmask &= ~TWL6040_HOOKMSK;
				twl6040_write(codec, TWL6040_REG_INTMR, priv->intmask);
			}
			state = WIRED_HEADSET;//wired headset with MIC
			status = priv->hs_jack.report;//SND_JACK_HEADSET
		}
	}
	else{
		 /* when headset removed */
		if(!(priv->intmask & TWL6040_HOOKMSK)) {
			priv->intmask |= TWL6040_HOOKMSK;
			twl6040_write(codec, TWL6040_REG_INTMR, priv->intmask);
		}
		set_hook_enable(codec, 0);
		hs_set_bias(codec, 0);
		state = HEADSET_NONE;
		status = 0;
	}

	switch_set_state(&priv->hs_jack.sdev, state);
	snd_soc_jack_report(jack, status, report);
}
Beispiel #5
0
static int twl6040_set_bias_level(struct snd_soc_codec *codec,
				enum snd_soc_bias_level level)
{
	struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
	int audpwron = priv->audpwron;
	int naudint = priv->naudint;
	int ret;

	switch (level) {
	case SND_SOC_BIAS_ON:
		break;
	case SND_SOC_BIAS_PREPARE:
		break;
	case SND_SOC_BIAS_STANDBY:
		if (priv->codec_powered)
			break;

		if (gpio_is_valid(audpwron)) {
			/* use AUDPWRON line */
			gpio_set_value(audpwron, 1);

#if defined(CONFIG_MACH_LGE_COSMO_REV_C)
			mdelay(50);
#endif

			/* wait for power-up completion */
			ret = twl6040_power_up_completion(codec, naudint);
			if (ret)
				return ret;

			/* sync registers updated during power-up sequence */
			twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL);
			twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL);
			twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL);
		} else {
			/* use manual power-up sequence */
			twl6040_power_up(codec);
			priv->codec_powered = 1;
		}

		/* initialize vdd/vss registers with reg_cache */
		twl6040_init_vdd_regs(codec);
		break;
	case SND_SOC_BIAS_OFF:
		if (!priv->codec_powered)
			break;

		if (gpio_is_valid(audpwron)) {			
			// Headset Left Driver make pop-noise when twl6040 enter into sleep mode.
			u8 val;
			val = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL);
			if( val & 0x04 )	// temp disableed. It will be recovered when power-on.
			{
				twl6040_i2c_write(TWL6040_REG_HSLCTL, (val & (~0x04)));
				msleep(1);	// delay;;
			}

			/* use AUDPWRON line */
			gpio_set_value(audpwron, 0);

			/* power-down sequence latency */
			mdelay(1);	// more more...

			/* sync registers updated during power-down sequence */
			twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL);
			twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL);
			twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL,
						0x00);
		} else {
			/* use manual power-down sequence */
			twl6040_power_down(codec);
		}

		priv->codec_powered = 0;
		break;
	}

	codec->dapm->bias_level = level;

	return 0;
}