Пример #1
0
static int twl6040_power(struct twl6040_codec *twl6040, int enable)
{
	int audpwron = twl6040->audpwron;
	int naudint = twl6040->irq;
	int ret = 0;

	if (enable) {
		if (gpio_is_valid(audpwron)) {
			/* use AUDPWRON line */
			gpio_set_value(audpwron, 1);
			/* wait for power-up completion */
			ret = twl6040_power_up_completion(twl6040, naudint);
			if (ret) {
				dev_err(&twl6040_codec_dev->dev,
					"automatic power-down failed\n");
				return ret;
			}
		} else {
			/* use manual power-up sequence */
			ret = twl6040_power_up(twl6040);
			if (ret) {
				dev_err(&twl6040_codec_dev->dev,
					"manual power-up failed\n");
				return ret;
			}
		}
		twl6040->pll = TWL6040_LPPLL_ID;
		twl6040->sysclk = 19200000;
	} else {
		if (gpio_is_valid(audpwron)) {
			/* use AUDPWRON line */
			gpio_set_value(audpwron, 0);

			/* power-down sequence latency */
			udelay(500);
		} else {
			/* use manual power-down sequence */
			ret = twl6040_power_down(twl6040);
			if (ret) {
				dev_err(&twl6040_codec_dev->dev,
					"manual power-down failed\n");
				return ret;
			}
		}
		twl6040->pll = TWL6040_NOPLL_ID;
		twl6040->sysclk = 0;
	}

	twl6040->powered = enable;

	return ret;
}
Пример #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;
}
Пример #3
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;
}
static int twl6040_power(struct twl6040 *twl6040, int enable)
{
	struct twl4030_codec_data *pdata = dev_get_platdata(twl6040->dev);
	int audpwron = twl6040->audpwron;
	int naudint = twl6040->irq;
	int ret = 0;

	if (enable) {
		/* enable 32kHz external clock */
		if (pdata->set_ext_clk32k) {
			ret = pdata->set_ext_clk32k(true);
			if (ret) {
				dev_err(twl6040->dev,
					"failed to enable CLK32K %d\n", ret);
				return ret;
			}
		}

		/* disable internal 32kHz oscillator */
		twl6040_clear_bits(twl6040, TWL6040_REG_ACCCTL,
				TWL6040_CLK32KSEL);

		if (gpio_is_valid(audpwron)) {
			/* wait for power-up completion */
			ret = twl6040_power_up_completion(twl6040, naudint);
			if (ret) {
				dev_err(twl6040->dev,
					"automatic power-down failed\n");
				return ret;
			}
		} else {
			/* use manual power-up sequence */
			ret = twl6040_power_up(twl6040);
			if (ret) {
				dev_err(twl6040->dev,
					"manual power-up failed\n");
				return ret;
			}
		}

		/* Errata: PDMCLK can fail to generate at cold temperatures
		 * The workaround consists of resetting HPPLL and LPPLL
		 * after Sleep/Deep-Sleep mode and before application mode.
		 */
		twl6040_set_bits(twl6040, TWL6040_REG_HPPLLCTL,
				TWL6040_HPLLRST);
		twl6040_clear_bits(twl6040, TWL6040_REG_HPPLLCTL,
				TWL6040_HPLLRST);
		twl6040_set_bits(twl6040, TWL6040_REG_LPPLLCTL,
				TWL6040_LPLLRST);
		twl6040_clear_bits(twl6040, TWL6040_REG_LPPLLCTL,
				TWL6040_LPLLRST);

		twl6040->pll = TWL6040_LPPLL_ID;
		twl6040->sysclk = 19200000;
	} else {
		if (gpio_is_valid(audpwron)) {
			/* use AUDPWRON line */
			gpio_set_value(audpwron, 0);

			/* power-down sequence latency */
			udelay(500);
		} else {
			/* use manual power-down sequence */
			ret = twl6040_power_down(twl6040);
			if (ret) {
				dev_err(twl6040->dev,
					"manual power-down failed\n");
				return ret;
			}
		}

		/* enable internal 32kHz oscillator */
		twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL,
				TWL6040_CLK32KSEL);

		/* disable 32kHz external clock */
		if (pdata->set_ext_clk32k) {
			ret = pdata->set_ext_clk32k(false);
			if (ret)
				dev_err(twl6040->dev,
					"failed to disable CLK32K %d\n", ret);
		}

		twl6040->pll = TWL6040_NOPLL_ID;
		twl6040->sysclk = 0;
	}

	twl6040->powered = enable;

	return ret;
}
static int twl6040_power_up_completion(struct twl6040 *twl6040,
				       int naudint)
{
	int time_left;
	int round = 0;
	int ret = 0;
	int retry = 0;
	u8 intid;
	u8 ncpctl;
	u8 ldoctl;
	u8 lppllctl;
	u8 ncpctl_exp;
	u8 ldoctl_exp;
	u8 lppllctl_exp;

	/* NCPCTL expected value: NCP enabled */
	ncpctl_exp = (TWL6040_TSHUTENA | TWL6040_NCPENA);

	/* LDOCTL expected value: HS/LS LDOs and Reference enabled */
	ldoctl_exp = (TWL6040_REFENA | TWL6040_HSLDOENA | TWL6040_LSLDOENA);

	/* LPPLLCTL expected value: Low-Power PLL enabled */
	lppllctl_exp = TWL6040_LPLLENA;

	do {
		gpio_set_value(twl6040->audpwron, 1);
		time_left = wait_for_completion_timeout(&twl6040->ready,
							msecs_to_jiffies(700));
		if (!time_left) {
			intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
			if (!(intid & TWL6040_READYINT)) {
				dev_err(twl6040->dev,
					"timeout waiting for READYINT\n");
				return -ETIMEDOUT;
			}
		}
		/*
		 * Power on seemingly completed.
		 * Look for clues that the twl6040 might be still booting.
		 */

		retry = 0;
		ncpctl = twl6040_reg_read(twl6040, TWL6040_REG_NCPCTL);
		if (ncpctl != ncpctl_exp)
			retry++;

		ldoctl = twl6040_reg_read(twl6040, TWL6040_REG_LDOCTL);
		if (ldoctl != ldoctl_exp)
			retry++;

		lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL);
		if (lppllctl != lppllctl_exp)
			retry++;

		if (retry) {
			dev_err(twl6040->dev,
				"NCPCTL: 0x%02x (should be 0x%02x)\n"
				"LDOCTL: 0x%02x (should be 0x%02x)\n"
				"LPLLCTL: 0x%02x (should be 0x%02x)\n",
				ncpctl, ncpctl_exp,
				ldoctl, ldoctl_exp,
				lppllctl, lppllctl_exp);
			round++;
			gpio_set_value(twl6040->audpwron, 0);
			usleep_range(1000, 1500);
			continue;
		}
	} while (round && (round < 3));

	if (round >= 3) {
		dev_err(twl6040->dev,
			"Automatic power on failed, reverting to manual\n");
		twl6040->audpwron = -EINVAL;
		ret = twl6040_power_up(twl6040);
		if (ret)
			dev_err(twl6040->dev, "Manual power-up failed\n");
	}

	return ret;
}
Пример #6
0
int twl6040_power(struct twl6040 *twl6040, int on)
{
	int audpwron = twl6040->audpwron;
	int naudint = twl6040->irq;
	int ret = 0;

	mutex_lock(&twl6040->mutex);

	if (on) {
		/* already powered-up */
		if (twl6040->power_count++)
			goto out;

		if (gpio_is_valid(audpwron)) {
			/* use AUDPWRON line */
			gpio_set_value(audpwron, 1);
			/* wait for power-up completion */
			ret = twl6040_power_up_completion(twl6040, naudint);
			if (ret) {
				dev_err(&twl6040_dev->dev,
					"automatic power-down failed\n");
				twl6040->power_count = 0;
				goto out;
			}
		} else {
			/* use manual power-up sequence */
			ret = twl6040_power_up(twl6040);
			if (ret) {
				dev_err(&twl6040_dev->dev,
					"manual power-up failed\n");
				twl6040->power_count = 0;
				goto out;
			}
		}
		/* Default PLL configuration after power up */
		twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;
		twl6040->sysclk = 19200000;
	} else {
		/* already powered-down */
		if (!twl6040->power_count) {
			dev_err(&twl6040_dev->dev,
				"device is already powered-off\n");
			ret = -EPERM;
			goto out;
		}

		if (--twl6040->power_count)
			goto out;

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

			/* power-down sequence latency */
			usleep_range(500, 700);
		} else {
			/* use manual power-down sequence */
			twl6040_power_down(twl6040);
		}
		twl6040->sysclk = 0;
	}

out:
	mutex_unlock(&twl6040->mutex);
	return ret;
}