aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/twl6040.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/twl6040.c')
-rw-r--r--sound/soc/codecs/twl6040.c158
1 files changed, 97 insertions, 61 deletions
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 2dcc8fe..f217421 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -164,59 +164,59 @@ static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = {
0x00, /* TWL6040_STATUS (ro) 0x2E */
};
-/*
- * twl6040 vio/gnd registers:
- * registers under vio/gnd supply can be accessed
- * before the power-up sequence, after NRESPWRON goes high
- */
-static const int twl6040_vio_reg[TWL6040_VIOREGNUM] = {
- TWL6040_REG_ASICID,
- TWL6040_REG_ASICREV,
- TWL6040_REG_INTID,
- TWL6040_REG_INTMR,
- TWL6040_REG_NCPCTL,
- TWL6040_REG_LDOCTL,
- TWL6040_REG_AMICBCTL,
- TWL6040_REG_DMICBCTL,
- TWL6040_REG_HKCTL1,
- TWL6040_REG_HKCTL2,
- TWL6040_REG_GPOCTL,
- TWL6040_REG_TRIM1,
- TWL6040_REG_TRIM2,
- TWL6040_REG_TRIM3,
- TWL6040_REG_HSOTRIM,
- TWL6040_REG_HFOTRIM,
- TWL6040_REG_ACCCTL,
- TWL6040_REG_STATUS,
-};
-/*
- * twl6040 vdd/vss registers:
- * registers under vdd/vss supplies can only be accessed
- * after the power-up sequence
- */
-static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = {
- TWL6040_REG_HPPLLCTL,
- TWL6040_REG_LPPLLCTL,
- TWL6040_REG_LPPLLDIV,
- TWL6040_REG_MICLCTL,
- TWL6040_REG_MICRCTL,
- TWL6040_REG_MICGAIN,
- TWL6040_REG_LINEGAIN,
- TWL6040_REG_HSLCTL,
- TWL6040_REG_HSRCTL,
- TWL6040_REG_HSGAIN,
- TWL6040_REG_EARCTL,
- TWL6040_REG_HFLCTL,
- TWL6040_REG_HFLGAIN,
- TWL6040_REG_HFRCTL,
- TWL6040_REG_HFRGAIN,
- TWL6040_REG_VIBCTLL,
- TWL6040_REG_VIBDATL,
- TWL6040_REG_VIBCTLR,
- TWL6040_REG_VIBDATR,
- TWL6040_REG_ALB,
- TWL6040_REG_DLB,
+/* twl6040 vio/gnd registers: registers under vio/gnd supply can be accessed
+ * twl6040 vdd/vss registers: registers under vdd/vss supplies can only be
+ * accessed after the power-up sequence */
+
+static const u8 twl6040_reg_supply[TWL6040_CACHEREGNUM] = {
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_VIO_SUPPLY, /* TWL6040_ASICID (ro) */
+ TWL6040_VIO_SUPPLY, /* TWL6040_ASICREV (ro) */
+ TWL6040_VIO_SUPPLY, /* TWL6040_INTID */
+ TWL6040_VIO_SUPPLY, /* TWL6040_INTMR */
+ TWL6040_VIO_SUPPLY, /* TWL6040_NCPCTRL */
+ TWL6040_VIO_SUPPLY, /* TWL6040_LDOCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HPPLLCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_LPPLLCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_LPPLLDIV */
+ TWL6040_VIO_SUPPLY, /* TWL6040_AMICBCTL */
+ TWL6040_VIO_SUPPLY, /* TWL6040_DMICBCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_MICLCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_MICRCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_MICGAIN */
+ TWL6040_VDD_SUPPLY, /* TWL6040_LINEGAIN */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HSLCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HSRCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HSGAIN */
+ TWL6040_VDD_SUPPLY, /* TWL6040_EARCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HFLCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HFLGAIN */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HFRCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_HFRGAIN */
+ TWL6040_VDD_SUPPLY, /* TWL6040_VIBCTLL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_VIBDATL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_VIBCTLR */
+ TWL6040_VDD_SUPPLY, /* TWL6040_VIBDATR */
+ TWL6040_VIO_SUPPLY, /* TWL6040_HKCTL1 */
+ TWL6040_VIO_SUPPLY, /* TWL6040_HKCTL2 */
+ TWL6040_VIO_SUPPLY, /* TWL6040_GPOCTL */
+ TWL6040_VDD_SUPPLY, /* TWL6040_ALB */
+ TWL6040_VDD_SUPPLY, /* TWL6040_DLB */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_NO_SUPPLY, /* not used */
+ TWL6040_VIO_SUPPLY, /* TWL6040_TRIM1 */
+ TWL6040_VIO_SUPPLY, /* TWL6040_TRIM2 */
+ TWL6040_VIO_SUPPLY, /* TWL6040_TRIM3 */
+ TWL6040_VIO_SUPPLY, /* TWL6040_HSOTRIM */
+ TWL6040_VIO_SUPPLY, /* TWL6040_HFOTRIM */
+ TWL6040_VIO_SUPPLY, /* TWL6040_ACCCTL */
+ TWL6040_VIO_SUPPLY, /* TWL6040_STATUS (ro) */
};
/*
@@ -250,14 +250,20 @@ static inline void twl6040_write_reg_cache(struct snd_soc_codec *codec,
* read from twl6040 hardware register
*/
static int twl6040_read_reg_volatile(struct snd_soc_codec *codec,
- unsigned int reg)
+ unsigned int reg)
{
struct twl6040 *twl6040 = codec->control_data;
- u8 value;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ u8 value = 0;
if (reg >= TWL6040_CACHEREGNUM)
return -EIO;
+ /* read access not supported while in sleep state */
+ if ((twl6040_reg_supply[reg] == TWL6040_VDD_SUPPLY) &&
+ !priv->codec_powered)
+ return -EINVAL;
+
value = twl6040_reg_read(twl6040, reg);
twl6040_write_reg_cache(codec, reg, value);
@@ -271,21 +277,32 @@ static int twl6040_write(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value)
{
struct twl6040 *twl6040 = codec->control_data;
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
if (reg >= TWL6040_CACHEREGNUM)
return -EIO;
twl6040_write_reg_cache(codec, reg, value);
- return twl6040_reg_write(twl6040, reg, value);
+
+ if ((twl6040_reg_supply[reg] == TWL6040_VIO_SUPPLY) ||
+ priv->codec_powered)
+ ret = twl6040_reg_write(twl6040, reg, value);
+ else
+ dev_dbg(codec->dev, "deferring register 0x%02x write: %02x\n",
+ reg, value);
+
+ return ret;
}
static void twl6040_init_vio_regs(struct snd_soc_codec *codec)
{
u8 *cache = codec->reg_cache;
- int reg, i;
+ int reg;
- for (i = 0; i < TWL6040_VIOREGNUM; i++) {
- reg = twl6040_vio_reg[i];
+ for (reg = 0; reg < TWL6040_CACHEREGNUM; reg++) {
+ if (twl6040_reg_supply[reg] != TWL6040_VIO_SUPPLY)
+ continue;
/*
* skip read-only registers (ASICID, ASICREV, STATUS)
* and registers shared among MFD children
@@ -311,10 +328,11 @@ static void twl6040_init_vio_regs(struct snd_soc_codec *codec)
static void twl6040_init_vdd_regs(struct snd_soc_codec *codec)
{
u8 *cache = codec->reg_cache;
- int reg, i;
+ int reg;
- for (i = 0; i < TWL6040_VDDREGNUM; i++) {
- reg = twl6040_vdd_reg[i];
+ for (reg = 0; reg < TWL6040_CACHEREGNUM; reg++) {
+ if (twl6040_reg_supply[reg] != TWL6040_VDD_SUPPLY)
+ continue;
/* skip vibra and pll registers */
switch (reg) {
case TWL6040_REG_VIBCTLL:
@@ -1510,6 +1528,8 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
int ret;
+ priv->sysclk = twl6040_get_sysclk(twl6040);
+
switch (clk_id) {
case TWL6040_SYSCLK_SEL_LPPLL:
ret = twl6040_set_pll(twl6040, TWL6040_LPPLL_ID,
@@ -1539,11 +1559,26 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
+static int twl6040_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ /*
+ * pop-noise reduction sequence requires to shutdown
+ * analog side before CPU DAI
+ */
+ if (mute)
+ snd_soc_dapm_codec_stream_event(dai->codec,
+ dai->driver->playback.stream_name,
+ SND_SOC_DAPM_STREAM_STOP);
+
+ return 0;
+}
+
static struct snd_soc_dai_ops twl6040_dai_ops = {
.startup = twl6040_startup,
.hw_params = twl6040_hw_params,
.prepare = twl6040_prepare,
.set_sysclk = twl6040_set_dai_sysclk,
+ .digital_mute = twl6040_digital_mute,
};
static struct snd_soc_dai_driver twl6040_dai[] = {
@@ -1627,6 +1662,7 @@ static int twl6040_probe(struct snd_soc_codec *codec)
priv->codec = codec;
codec->control_data = dev_get_drvdata(codec->dev->parent);
+ codec->dapm.idle_bias_off = 1;
if (pdata && pdata->hs_left_step && pdata->hs_right_step) {
priv->hs_left_step = pdata->hs_left_step;