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; }
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; }
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; }
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; }