[alsa-devel] [PATCH 00/17] ASoC: twl6040: Cleanups and fixes
Hello,
The first four patch of this series (the MFD/Input patches) has been already sent to upstream to the relevant lists, but I wanted to include here as well, since I have dependency on that series later on. If it is OK with you, it would be best to merge these MFD/Input changes through the audio tree for 3.2.
Code cleanup, and fixes for the twl6040 codec driver. Main changes: - reg_cache has been rested to chip defaults - codec initialization changes - Correction for the Earphone path - AUX L/R output support - Headset/Handsfree control name changes - Headset DC offset workaround
Regards, Peter
I have excluded the linux-input list (but kept Dmitry) - to not spam them.
--- Peter Ujfalusi (17): MFD: twl6040: Remove global pointer for platform_device MFD: twl6040: Add accessor for revision ID Input: twl6040-vibra: Use accessor to get revision information MFD: twl6040: Fix power on GPIO handling ASoC: twl6040: Chip initialization cleanup ASoC: twl6040: Use chip defaults in the initial reg_cache ASoC: twl6040: Lower the initial gains ASoC: twl6040: Fix comments for register names ASoC: twl6040: Remove strings "NULL" from DAPM route ASoC: twl6040: Introduce SW only shadow register ASoC: twl6040: Earphone path correction ASoC: twl6040: Use consistent names for Handsfree path ASoC: twl6040: Use consistent names for Headset path ASoC: twl6040: Support for AUX L/R output ASoC: twl6040/sdp4430: Change legacy DAI name ASoC/MFD: twl6040: Combine bit definitions for Headset control registers ASoC: twl6040: Workaround for headset DC offset caused pop noise
drivers/input/misc/twl6040-vibra.c | 2 +- drivers/mfd/twl6040-core.c | 38 ++-- include/linux/mfd/twl6040.h | 22 +-- sound/soc/codecs/twl6040.c | 367 +++++++++++++++++++----------------- sound/soc/omap/sdp4430.c | 2 +- 5 files changed, 218 insertions(+), 213 deletions(-)
There is no need to keep global pointer for the platform device, since it is only used for dev_* prints, and the device pointer available within the twl6040 structure.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- drivers/mfd/twl6040-core.c | 27 +++++++++++---------------- 1 files changed, 11 insertions(+), 16 deletions(-)
diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 24d436c..b0519e6 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -34,8 +34,6 @@ #include <linux/mfd/core.h> #include <linux/mfd/twl6040.h>
-static struct platform_device *twl6040_dev; - int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg) { int ret; @@ -203,11 +201,11 @@ static irqreturn_t twl6040_naudint_handler(int irq, void *data) if (intid & TWL6040_THINT) { status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS); if (status & TWL6040_TSHUTDET) { - dev_warn(&twl6040_dev->dev, + dev_warn(twl6040->dev, "Thermal shutdown, powering-off"); twl6040_power(twl6040, 0); } else { - dev_warn(&twl6040_dev->dev, + dev_warn(twl6040->dev, "Leaving thermal shutdown, powering-on"); twl6040_power(twl6040, 1); } @@ -227,7 +225,7 @@ static int twl6040_power_up_completion(struct twl6040 *twl6040, if (!time_left) { intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); if (!(intid & TWL6040_READYINT)) { - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "timeout waiting for READYINT\n"); return -ETIMEDOUT; } @@ -255,7 +253,7 @@ int twl6040_power(struct twl6040 *twl6040, int on) /* wait for power-up completion */ ret = twl6040_power_up_completion(twl6040, naudint); if (ret) { - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "automatic power-down failed\n"); twl6040->power_count = 0; goto out; @@ -264,7 +262,7 @@ int twl6040_power(struct twl6040 *twl6040, int on) /* use manual power-up sequence */ ret = twl6040_power_up(twl6040); if (ret) { - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "manual power-up failed\n"); twl6040->power_count = 0; goto out; @@ -276,7 +274,7 @@ int twl6040_power(struct twl6040 *twl6040, int on) } else { /* already powered-down */ if (!twl6040->power_count) { - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "device is already powered-off\n"); ret = -EPERM; goto out; @@ -326,7 +324,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, lppllctl &= ~TWL6040_LPLLFIN; break; default: - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "freq_out %d not supported\n", freq_out); ret = -EINVAL; goto pll_out; @@ -347,7 +345,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, hppllctl); break; default: - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "freq_in %d not supported\n", freq_in); ret = -EINVAL; goto pll_out; @@ -356,7 +354,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, case TWL6040_SYSCLK_SEL_HPPLL: /* high-performance PLL can provide only 19.2 MHz */ if (freq_out != 19200000) { - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "freq_out %d not supported\n", freq_out); ret = -EINVAL; goto pll_out; @@ -389,7 +387,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, TWL6040_HPLLENA; break; default: - dev_err(&twl6040_dev->dev, + dev_err(twl6040->dev, "freq_in %d not supported\n", freq_in); ret = -EINVAL; goto pll_out; @@ -406,7 +404,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id, twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); break; default: - dev_err(&twl6040_dev->dev, "unknown pll id %d\n", pll_id); + dev_err(twl6040->dev, "unknown pll id %d\n", pll_id); ret = -EINVAL; goto pll_out; } @@ -471,7 +469,6 @@ static int __devinit twl6040_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, twl6040);
- twl6040_dev = pdev; twl6040->dev = &pdev->dev; twl6040->audpwron = pdata->audpwron_gpio; twl6040->irq = pdata->naudint_irq; @@ -566,7 +563,6 @@ gpio2_err: gpio1_err: platform_set_drvdata(pdev, NULL); kfree(twl6040); - twl6040_dev = NULL; return ret; }
@@ -586,7 +582,6 @@ static int __devexit twl6040_remove(struct platform_device *pdev) mfd_remove_devices(&pdev->dev); platform_set_drvdata(pdev, NULL); kfree(twl6040); - twl6040_dev = NULL;
return 0; }
On Thu, Sep 15, 2011 at 03:39:23PM +0300, Peter Ujfalusi wrote:
There is no need to keep global pointer for the platform device, since it is only used for dev_* prints, and the device pointer available within the twl6040 structure.
Applied, thanks.
For client driver to use, if they need chip resvision information.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- drivers/mfd/twl6040-core.c | 2 +- include/linux/mfd/twl6040.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletions(-)
diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index b0519e6..51c3b47 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -491,7 +491,7 @@ static int __devinit twl6040_probe(struct platform_device *pdev) }
/* ERRATA: Automatic power-up is not possible in ES1.0 */ - if (twl6040->rev == TWL6040_REV_ES1_0) + if (twl6040_get_revid(twl6040) == TWL6040_REV_ES1_0) twl6040->audpwron = -EINVAL;
/* codec interrupt */ diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index 4c806f6..cb3b822 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -225,4 +225,9 @@ unsigned int twl6040_get_sysclk(struct twl6040 *twl6040); int twl6040_irq_init(struct twl6040 *twl6040); void twl6040_irq_exit(struct twl6040 *twl6040);
+static inline int twl6040_get_revid(struct twl6040 *twl6040) +{ + return twl6040->rev; +} + #endif /* End of __TWL6040_CODEC_H__ */
On Thu, Sep 15, 2011 at 03:39:24PM +0300, Peter Ujfalusi wrote:
For client driver to use, if they need chip resvision information.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com
Applied, thanks.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com Acked-by: Dmitry Torokhov dtor@mail.ru --- drivers/input/misc/twl6040-vibra.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index c43002e..154b7a3 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -97,7 +97,7 @@ static void twl6040_vibra_enable(struct vibra_info *info) }
twl6040_power(info->twl6040, 1); - if (twl6040->rev <= TWL6040_REV_ES1_1) { + if (twl6040_get_revid(twl6040) <= TWL6040_REV_ES1_1) { /* * ERRATA: Disable overcurrent protection for at least * 3ms when enabling vibrator drivers to avoid false
On Thu, Sep 15, 2011 at 03:39:25PM +0300, Peter Ujfalusi wrote:
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com Acked-by: Dmitry Torokhov dtor@mail.ru
Applied, thanks.
Avoid requesting the audpwron gpio in case of ES1.0 revision. In the past we requested the gpio, but we did not free it up, since we made the check for the revision later. This results later checks for gpio validity to fail, leaving the gpio reserved (even after the driver has been removed).
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- drivers/mfd/twl6040-core.c | 11 ++++++----- 1 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index 51c3b47..7dc8c47 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -470,7 +470,6 @@ static int __devinit twl6040_probe(struct platform_device *pdev) platform_set_drvdata(pdev, twl6040);
twl6040->dev = &pdev->dev; - twl6040->audpwron = pdata->audpwron_gpio; twl6040->irq = pdata->naudint_irq; twl6040->irq_base = pdata->irq_base;
@@ -480,6 +479,12 @@ static int __devinit twl6040_probe(struct platform_device *pdev)
twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
+ /* ERRATA: Automatic power-up is not possible in ES1.0 */ + if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) + twl6040->audpwron = pdata->audpwron_gpio; + else + twl6040->audpwron = -EINVAL; + if (gpio_is_valid(twl6040->audpwron)) { ret = gpio_request(twl6040->audpwron, "audpwron"); if (ret) @@ -490,10 +495,6 @@ static int __devinit twl6040_probe(struct platform_device *pdev) goto gpio2_err; }
- /* ERRATA: Automatic power-up is not possible in ES1.0 */ - if (twl6040_get_revid(twl6040) == TWL6040_REV_ES1_0) - twl6040->audpwron = -EINVAL; - /* codec interrupt */ ret = twl6040_irq_init(twl6040); if (ret)
On Thu, Sep 15, 2011 at 03:39:26PM +0300, Peter Ujfalusi wrote:
Avoid requesting the audpwron gpio in case of ES1.0 revision. In the past we requested the gpio, but we did not free it up, since we made the check for the revision later. This results later checks for gpio validity to fail, leaving the gpio reserved (even after the driver has been removed).
Applied, thanks.
There is no need to write to the vio registers at probe time, since most them either read only, or shared with MFD or not used. On the other hand it is a good idea to updated the ASICREV register in the cache at this time.
After power up we need to restore some registers. Clean up the list to contain only the registers we are going to restore.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- include/linux/mfd/twl6040.h | 3 - sound/soc/codecs/twl6040.c | 100 ++++++------------------------------------- 2 files changed, 13 insertions(+), 90 deletions(-)
diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index cb3b822..ec1ec79 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -70,9 +70,6 @@
#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1)
-#define TWL6040_VIOREGNUM 18 -#define TWL6040_VDDREGNUM 21 - /* INTID (0x03) fields */
#define TWL6040_THINT 0x01 diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 443032b..8bbd46a 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -155,41 +155,8 @@ 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, +/* List of registers to be restored after power up */ +static const int twl6040_restore_list[] = { TWL6040_REG_MICLCTL, TWL6040_REG_MICRCTL, TWL6040_REG_MICGAIN, @@ -202,12 +169,6 @@ static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = { 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, };
/* set of rates for each pll: low-power and high-performance */ @@ -296,56 +257,23 @@ static int twl6040_write(struct snd_soc_codec *codec, return twl6040_reg_write(twl6040, reg, value); }
-static void twl6040_init_vio_regs(struct snd_soc_codec *codec) +static void twl6040_init_chip(struct snd_soc_codec *codec) { - u8 *cache = codec->reg_cache; - int reg, i; + struct twl6040 *twl6040 = codec->control_data; + u8 val; + + val = twl6040_get_revid(twl6040); + twl6040_write_reg_cache(codec, TWL6040_REG_ASICREV, val);
- for (i = 0; i < TWL6040_VIOREGNUM; i++) { - reg = twl6040_vio_reg[i]; - /* - * skip read-only registers (ASICID, ASICREV, STATUS) - * and registers shared among MFD children - */ - switch (reg) { - case TWL6040_REG_ASICID: - case TWL6040_REG_ASICREV: - case TWL6040_REG_INTID: - case TWL6040_REG_INTMR: - case TWL6040_REG_NCPCTL: - case TWL6040_REG_LDOCTL: - case TWL6040_REG_GPOCTL: - case TWL6040_REG_ACCCTL: - case TWL6040_REG_STATUS: - continue; - default: - break; - } - twl6040_write(codec, reg, cache[reg]); - } }
-static void twl6040_init_vdd_regs(struct snd_soc_codec *codec) +static void twl6040_restore_regs(struct snd_soc_codec *codec) { u8 *cache = codec->reg_cache; int reg, i;
- for (i = 0; i < TWL6040_VDDREGNUM; i++) { - reg = twl6040_vdd_reg[i]; - /* skip vibra and PLL registers */ - switch (reg) { - case TWL6040_REG_VIBCTLL: - case TWL6040_REG_VIBDATL: - case TWL6040_REG_VIBCTLR: - case TWL6040_REG_VIBDATR: - case TWL6040_REG_HPPLLCTL: - case TWL6040_REG_LPPLLCTL: - case TWL6040_REG_LPPLLDIV: - continue; - default: - break; - } - + for (i = 0; i < ARRAY_SIZE(twl6040_restore_list); i++) { + reg = twl6040_restore_list[i]; twl6040_write(codec, reg, cache[reg]); } } @@ -1325,8 +1253,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
priv->codec_powered = 1;
- /* initialize vdd/vss registers with reg_cache */ - twl6040_init_vdd_regs(codec); + twl6040_restore_regs(codec);
/* Set external boost GPO */ twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02); @@ -1620,8 +1547,7 @@ static int twl6040_probe(struct snd_soc_codec *codec) goto plugirq_err; }
- /* init vio registers */ - twl6040_init_vio_regs(codec); + twl6040_init_chip(codec);
/* power on device */ ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
On Thu, Sep 15, 2011 at 03:39:27PM +0300, Peter Ujfalusi wrote:
There is no need to write to the vio registers at probe time, since most them either read only, or shared with MFD or not used. On the other hand it is a good idea to updated the ASICREV register in the cache at this time.
Applied, thanks.
Reset the twl6040_reg array to hold the chip default values. The only changed values were for the microphone input selection. Select no input for the microphones in the twl6040_init_chip function.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 8 ++++++-- 1 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 8bbd46a..987d9c9 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -118,8 +118,8 @@ static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = { 0x4A, /* TWL6040_LPPLLDIV 0x09 */ 0x00, /* TWL6040_AMICBCTL 0x0A */ 0x00, /* TWL6040_DMICBCTL 0x0B */ - 0x18, /* TWL6040_MICLCTL 0x0C - No input selected on Left Mic */ - 0x18, /* TWL6040_MICRCTL 0x0D - No input selected on Right Mic */ + 0x00, /* TWL6040_MICLCTL 0x0C */ + 0x00, /* TWL6040_MICRCTL 0x0D */ 0x00, /* TWL6040_MICGAIN 0x0E */ 0x1B, /* TWL6040_LINEGAIN 0x0F */ 0x00, /* TWL6040_HSLCTL 0x10 */ @@ -265,6 +265,10 @@ static void twl6040_init_chip(struct snd_soc_codec *codec) val = twl6040_get_revid(twl6040); twl6040_write_reg_cache(codec, TWL6040_REG_ASICREV, val);
+ /* Change chip defaults */ + /* No imput selected for microphone amplifiers */ + twl6040_write_reg_cache(codec, TWL6040_REG_MICLCTL, 0x18); + twl6040_write_reg_cache(codec, TWL6040_REG_MICRCTL, 0x18); }
static void twl6040_restore_regs(struct snd_soc_codec *codec)
On Thu, Sep 15, 2011 at 03:39:28PM +0300, Peter Ujfalusi wrote:
Reset the twl6040_reg array to hold the chip default values. The only changed values were for the microphone input selection. Select no input for the microphones in the twl6040_init_chip function.
Applied, thanks.
The default gains on outputs/inputs are set to 0dB. To be able to handle pop noise better, we need to lower the gains on controls, where it is possible.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 987d9c9..1f6068b 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -269,6 +269,13 @@ static void twl6040_init_chip(struct snd_soc_codec *codec) /* No imput selected for microphone amplifiers */ twl6040_write_reg_cache(codec, TWL6040_REG_MICLCTL, 0x18); twl6040_write_reg_cache(codec, TWL6040_REG_MICRCTL, 0x18); + + /* Lower the gains */ + twl6040_write_reg_cache(codec, TWL6040_REG_HSGAIN, 0xff); + twl6040_write_reg_cache(codec, TWL6040_REG_EARCTL, 0x1e); + twl6040_write_reg_cache(codec, TWL6040_REG_HFLGAIN, 0x1d); + twl6040_write_reg_cache(codec, TWL6040_REG_HFRGAIN, 0x1d); + twl6040_write_reg_cache(codec, TWL6040_REG_LINEGAIN, 0); }
static void twl6040_restore_regs(struct snd_soc_codec *codec)
On Thu, Sep 15, 2011 at 03:39:29PM +0300, Peter Ujfalusi wrote:
The default gains on outputs/inputs are set to 0dB. To be able to handle pop noise better, we need to lower the gains on controls, where it is possible.
It looks like what you really want to do here is implement support having PGAs able to cache the gain the user has set but hold the gain at zero while the PGA is disabled. This would be generally useful, a lot of older parts liked to have the PGAs muted when powering them.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com
sound/soc/codecs/twl6040.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 987d9c9..1f6068b 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -269,6 +269,13 @@ static void twl6040_init_chip(struct snd_soc_codec *codec) /* No imput selected for microphone amplifiers */ twl6040_write_reg_cache(codec, TWL6040_REG_MICLCTL, 0x18); twl6040_write_reg_cache(codec, TWL6040_REG_MICRCTL, 0x18);
- /* Lower the gains */
- twl6040_write_reg_cache(codec, TWL6040_REG_HSGAIN, 0xff);
- twl6040_write_reg_cache(codec, TWL6040_REG_EARCTL, 0x1e);
- twl6040_write_reg_cache(codec, TWL6040_REG_HFLGAIN, 0x1d);
- twl6040_write_reg_cache(codec, TWL6040_REG_HFRGAIN, 0x1d);
- twl6040_write_reg_cache(codec, TWL6040_REG_LINEGAIN, 0);
}
static void twl6040_restore_regs(struct snd_soc_codec *codec)
1.7.6.1
On Monday 19 September 2011 12:00:06 Mark Brown wrote:
On Thu, Sep 15, 2011 at 03:39:29PM +0300, Peter Ujfalusi wrote:
The default gains on outputs/inputs are set to 0dB. To be able to handle pop noise better, we need to lower the gains on controls, where it is possible.
It looks like what you really want to do here is implement support having PGAs able to cache the gain the user has set but hold the gain at zero while the PGA is disabled.
The twl6040 had support for this, but we did not lowered the initial gain after cold power on. This resulted a pop noise at the first time the audio paths had been used.
This would be generally useful, a lot of older parts liked to have the PGAs muted when powering them.
I have similar implementation (caching of the output gain values) in the twl4030 - there the reason was to decrease the power consumption. We need to have some sort of caching for the gain values, and linking the volume controls to DAPM in order to restore, or turn them off at the correct time. In twl4030 case I have custom PGA event handlers for outputs, and I manage the individual output amplifiers through them. Here in twl6040 we also have event handlers to do the ramp up/down on the gains. AFAIK some wm* codec have similar code.
I can take a look at these to find a generic way to handle them.
-- Péter
On Monday 19 September 2011 12:40:32 Mark Brown wrote:
On Mon, Sep 19, 2011 at 02:26:31PM +0300, Péter Ujfalusi wrote:
Here in twl6040 we also have event handlers to do the ramp up/down on the gains. AFAIK some wm* codec have similar code.
WM8350.
The implementation in twl6040 looks similar to the one in the wm8350 in regards to the ramp code, and caching of the user configured gains.
If you take a look at the wm8350 driver it is also changing the L/R OUT 1/2 registers at probe time. I don't see any difference in the two. Also it seams that wm8350 has the same problem: The default after the reset is 0x39 (0 dB) In the wm8350 probe it is changed to 0x00 (-57dB)
The twl6040 already had the ramp up, and down code, but it was missing the initialization part (IOW configuring the lowest gain on the outputs after first power on).
We do have other scenarios covered, but this initialization was missing. This resulted that the _first_ playback has pop noise, but all subsequent playbacks were fine, since we had the runtime fix for this.
-- Péter
On Wed, Sep 21, 2011 at 12:22:26PM +0300, Péter Ujfalusi wrote:
The implementation in twl6040 looks similar to the one in the wm8350 in regards to the ramp code, and caching of the user configured gains.
Yes, it was copied from there.
If you take a look at the wm8350 driver it is also changing the L/R OUT 1/2 registers at probe time. I don't see any difference in the two.
The key problem here is that you sent a change saying "change the default volume" with no other context in either the changelog or the code. If you've got code that's actively managing the volume register while presenting a nice view to the application layer so that the register value isn't directly seen in the way it is for standard ASoC controls that's a very different thing to standard code. I'm not going to remember the details of everything in every CODEC driver.
We do have other scenarios covered, but this initialization was missing. This resulted that the _first_ playback has pop noise, but all subsequent playbacks were fine, since we had the runtime fix for this.
So what you're doing here is really a bug fix for the ramping code.
On Wednesday 21 September 2011 13:39:31 Mark Brown wrote:
The key problem here is that you sent a change saying "change the default volume" with no other context in either the changelog or the code. If you've got code that's actively managing the volume register while presenting a nice view to the application layer so that the register value isn't directly seen in the way it is for standard ASoC controls that's a very different thing to standard code. I'm not going to remember the details of everything in every CODEC driver.
We do have other scenarios covered, but this initialization was missing. This resulted that the _first_ playback has pop noise, but all subsequent playbacks were fine, since we had the runtime fix for this.
So what you're doing here is really a bug fix for the ramping code.
I will update the commit message, and I'll also put a comment to the code to explain it clearly.
-- Péter
Hi Mark,
On Wednesday 21 September 2011 16:03:49 Péter Ujfalusi wrote:
I will update the commit message, and I'll also put a comment to the code to explain it clearly.
Would you be able to take a look at the rest of the series? I would like to prepare a pull request for you for 3.2.
Thank you, Péter
On Wed, Sep 21, 2011 at 04:14:22PM +0300, Péter Ujfalusi wrote:
On Wednesday 21 September 2011 16:03:49 Péter Ujfalusi wrote:
I will update the commit message, and I'll also put a comment to the code to explain it clearly.
Would you be able to take a look at the rest of the series? I would like to prepare a pull request for you for 3.2.
If I haven't commented on patches they're probably fine - normally I'm waiting for review from Liam when this happens. Note that as you're changing stuff you should repost for review anyway.
On Wednesday 21 September 2011 14:17:44 Mark Brown wrote:
If I haven't commented on patches they're probably fine - normally I'm waiting for review from Liam when this happens.
He did acked the whole series, I'll add his acked-by to the patches.
Note that as you're changing stuff you should repost for review anyway.
Yes, I know. I'll only repost the changed patch (07/17), when I'm ready to not spam the list with the rest.
-- Péter
On Wed, Sep 21, 2011 at 04:25:57PM +0300, Péter Ujfalusi wrote:
On Wednesday 21 September 2011 14:17:44 Mark Brown wrote:
If I haven't commented on patches they're probably fine - normally I'm waiting for review from Liam when this happens.
He did acked the whole series, I'll add his acked-by to the patches.
Right for this particular series, but...
Yes, I know. I'll only repost the changed patch (07/17), when I'm ready to not spam the list with the rest.
...there's the obvious dependency from that patch in the rest of the series so I can't apply it anyway.
Change the register name strings in the comments for the twl6040_reg table, so it is easier to search for specific register.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 94 ++++++++++++++++++++++---------------------- 1 files changed, 47 insertions(+), 47 deletions(-)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 1f6068b..9d1f82d 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -106,53 +106,53 @@ struct twl6040_data { * twl6040 register cache & default register settings */ static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = { - 0x00, /* not used 0x00 */ - 0x4B, /* TWL6040_ASICID (ro) 0x01 */ - 0x00, /* TWL6040_ASICREV (ro) 0x02 */ - 0x00, /* TWL6040_INTID 0x03 */ - 0x00, /* TWL6040_INTMR 0x04 */ - 0x00, /* TWL6040_NCPCTRL 0x05 */ - 0x00, /* TWL6040_LDOCTL 0x06 */ - 0x60, /* TWL6040_HPPLLCTL 0x07 */ - 0x00, /* TWL6040_LPPLLCTL 0x08 */ - 0x4A, /* TWL6040_LPPLLDIV 0x09 */ - 0x00, /* TWL6040_AMICBCTL 0x0A */ - 0x00, /* TWL6040_DMICBCTL 0x0B */ - 0x00, /* TWL6040_MICLCTL 0x0C */ - 0x00, /* TWL6040_MICRCTL 0x0D */ - 0x00, /* TWL6040_MICGAIN 0x0E */ - 0x1B, /* TWL6040_LINEGAIN 0x0F */ - 0x00, /* TWL6040_HSLCTL 0x10 */ - 0x00, /* TWL6040_HSRCTL 0x11 */ - 0x00, /* TWL6040_HSGAIN 0x12 */ - 0x00, /* TWL6040_EARCTL 0x13 */ - 0x00, /* TWL6040_HFLCTL 0x14 */ - 0x00, /* TWL6040_HFLGAIN 0x15 */ - 0x00, /* TWL6040_HFRCTL 0x16 */ - 0x00, /* TWL6040_HFRGAIN 0x17 */ - 0x00, /* TWL6040_VIBCTLL 0x18 */ - 0x00, /* TWL6040_VIBDATL 0x19 */ - 0x00, /* TWL6040_VIBCTLR 0x1A */ - 0x00, /* TWL6040_VIBDATR 0x1B */ - 0x00, /* TWL6040_HKCTL1 0x1C */ - 0x00, /* TWL6040_HKCTL2 0x1D */ - 0x00, /* TWL6040_GPOCTL 0x1E */ - 0x00, /* TWL6040_ALB 0x1F */ - 0x00, /* TWL6040_DLB 0x20 */ - 0x00, /* not used 0x21 */ - 0x00, /* not used 0x22 */ - 0x00, /* not used 0x23 */ - 0x00, /* not used 0x24 */ - 0x00, /* not used 0x25 */ - 0x00, /* not used 0x26 */ - 0x00, /* not used 0x27 */ - 0x00, /* TWL6040_TRIM1 0x28 */ - 0x00, /* TWL6040_TRIM2 0x29 */ - 0x00, /* TWL6040_TRIM3 0x2A */ - 0x00, /* TWL6040_HSOTRIM 0x2B */ - 0x00, /* TWL6040_HFOTRIM 0x2C */ - 0x09, /* TWL6040_ACCCTL 0x2D */ - 0x00, /* TWL6040_STATUS (ro) 0x2E */ + 0x00, /* not used 0x00 */ + 0x4B, /* REG_ASICID 0x01 (ro) */ + 0x00, /* REG_ASICREV 0x02 (ro) */ + 0x00, /* REG_INTID 0x03 */ + 0x00, /* REG_INTMR 0x04 */ + 0x00, /* REG_NCPCTRL 0x05 */ + 0x00, /* REG_LDOCTL 0x06 */ + 0x60, /* REG_HPPLLCTL 0x07 */ + 0x00, /* REG_LPPLLCTL 0x08 */ + 0x4A, /* REG_LPPLLDIV 0x09 */ + 0x00, /* REG_AMICBCTL 0x0A */ + 0x00, /* REG_DMICBCTL 0x0B */ + 0x00, /* REG_MICLCTL 0x0C */ + 0x00, /* REG_MICRCTL 0x0D */ + 0x00, /* REG_MICGAIN 0x0E */ + 0x1B, /* REG_LINEGAIN 0x0F */ + 0x00, /* REG_HSLCTL 0x10 */ + 0x00, /* REG_HSRCTL 0x11 */ + 0x00, /* REG_HSGAIN 0x12 */ + 0x00, /* REG_EARCTL 0x13 */ + 0x00, /* REG_HFLCTL 0x14 */ + 0x00, /* REG_HFLGAIN 0x15 */ + 0x00, /* REG_HFRCTL 0x16 */ + 0x00, /* REG_HFRGAIN 0x17 */ + 0x00, /* REG_VIBCTLL 0x18 */ + 0x00, /* REG_VIBDATL 0x19 */ + 0x00, /* REG_VIBCTLR 0x1A */ + 0x00, /* REG_VIBDATR 0x1B */ + 0x00, /* REG_HKCTL1 0x1C */ + 0x00, /* REG_HKCTL2 0x1D */ + 0x00, /* REG_GPOCTL 0x1E */ + 0x00, /* REG_ALB 0x1F */ + 0x00, /* REG_DLB 0x20 */ + 0x00, /* not used 0x21 */ + 0x00, /* not used 0x22 */ + 0x00, /* not used 0x23 */ + 0x00, /* not used 0x24 */ + 0x00, /* not used 0x25 */ + 0x00, /* not used 0x26 */ + 0x00, /* not used 0x27 */ + 0x00, /* REG_TRIM1 0x28 */ + 0x00, /* REG_TRIM2 0x29 */ + 0x00, /* REG_TRIM3 0x2A */ + 0x00, /* REG_HSOTRIM 0x2B */ + 0x00, /* REG_HFOTRIM 0x2C */ + 0x09, /* REG_ACCCTL 0x2D */ + 0x00, /* REG_STATUS 0x2E (ro) */ };
/* List of registers to be restored after power up */
Replace the string with plain NULL.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 9d1f82d..689e88b 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1195,8 +1195,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"ADC Right", NULL, "MicAmpR"},
/* AFM path */ - {"AFMAmpL", "NULL", "AFML"}, - {"AFMAmpR", "NULL", "AFMR"}, + {"AFMAmpL", NULL, "AFML"}, + {"AFMAmpR", NULL, "AFMR"},
{"HS Left Playback", "HS DAC", "HSDAC Left"}, {"HS Left Playback", "Line-In amp", "AFMAmpL"}, @@ -1204,8 +1204,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"HS Right Playback", "HS DAC", "HSDAC Right"}, {"HS Right Playback", "Line-In amp", "AFMAmpR"},
- {"Headset Left Driver", "NULL", "HS Left Playback"}, - {"Headset Right Driver", "NULL", "HS Right Playback"}, + {"Headset Left Driver", NULL, "HS Left Playback"}, + {"Headset Right Driver", NULL, "HS Right Playback"},
{"HSOL", NULL, "Headset Left Driver"}, {"HSOR", NULL, "Headset Right Driver"},
Software only shadow register to be used by the driver. For example Earpiece path will need this shadow register.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- include/linux/mfd/twl6040.h | 2 -- sound/soc/codecs/twl6040.c | 19 ++++++++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index ec1ec79..47470ca 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -68,8 +68,6 @@ #define TWL6040_REG_ACCCTL 0x2D #define TWL6040_REG_STATUS 0x2E
-#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1) - /* INTID (0x03) fields */
#define TWL6040_THINT 0x01 diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 689e88b..ce9fb37 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -57,6 +57,10 @@ #define TWL6040_HF_VOL_MASK 0x1F #define TWL6040_HF_VOL_SHIFT 0
+/* Shadow register used by the driver */ +#define TWL6040_REG_SW_SHADOW 0x2F +#define TWL6040_CACHEREGNUM (TWL6040_REG_SW_SHADOW + 1) + struct twl6040_output { u16 active; u16 left_vol; @@ -153,6 +157,8 @@ static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = { 0x00, /* REG_HFOTRIM 0x2C */ 0x09, /* REG_ACCCTL 0x2D */ 0x00, /* REG_STATUS 0x2E (ro) */ + + 0x00, /* REG_SW_SHADOW 0x2F - Shadow, non HW register */ };
/* List of registers to be restored after power up */ @@ -236,8 +242,12 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, if (reg >= TWL6040_CACHEREGNUM) return -EIO;
- value = twl6040_reg_read(twl6040, reg); - twl6040_write_reg_cache(codec, reg, value); + if (likely(reg < TWL6040_REG_SW_SHADOW)) { + value = twl6040_reg_read(twl6040, reg); + twl6040_write_reg_cache(codec, reg, value); + } else { + value = twl6040_read_reg_cache(codec, reg); + }
return value; } @@ -254,7 +264,10 @@ static int twl6040_write(struct snd_soc_codec *codec, return -EIO;
twl6040_write_reg_cache(codec, reg, value); - return twl6040_reg_write(twl6040, reg, value); + if (likely(reg < TWL6040_REG_SW_SHADOW)) + return twl6040_reg_write(twl6040, reg, value); + else + return 0; }
static void twl6040_init_chip(struct snd_soc_codec *codec)
Fix the DAPM routing for the earphone path. Convert the DAPM_SWITCH_E to DAPM_OUT_DRV_E, so we can have correct power up, and down sequence for EP. Introduce mute control (Earphone Playback Switch) for users to enable/disable the EP path. Note: the EP does not have it's own dedicated DAC. EP is connected to HSL DAC.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 17 ++++++++++++----- 1 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index ce9fb37..8b908ab 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -61,6 +61,9 @@ #define TWL6040_REG_SW_SHADOW 0x2F #define TWL6040_CACHEREGNUM (TWL6040_REG_SW_SHADOW + 1)
+/* TWL6040_REG_SW_SHADOW (0x2F) fields */ +#define TWL6040_EAR_PATH_ENABLE 0x01 + struct twl6040_output { u16 active; u16 left_vol; @@ -987,8 +990,8 @@ static const struct snd_kcontrol_new hfl_mux_controls = static const struct snd_kcontrol_new hfr_mux_controls = SOC_DAPM_ENUM("Route", twl6040_hf_enum[1]);
-static const struct snd_kcontrol_new ep_driver_switch_controls = - SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0); +static const struct snd_kcontrol_new ep_path_enable_control = + SOC_DAPM_SINGLE("Switch", TWL6040_REG_SW_SHADOW, 0, 1, 0);
/* Headset power mode */ static const char *twl6040_power_mode_texts[] = { @@ -1161,6 +1164,9 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { SND_SOC_DAPM_MUX("HS Right Playback", SND_SOC_NOPM, 0, 0, &hsr_mux_controls),
+ SND_SOC_DAPM_SWITCH("Earphone Playback", SND_SOC_NOPM, 0, 0, + &ep_path_enable_control), + /* Analog playback drivers */ SND_SOC_DAPM_OUT_DRV_E("Handsfree Left Driver", TWL6040_REG_HFLCTL, 4, 0, NULL, 0, @@ -1178,8 +1184,8 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { TWL6040_REG_HSRCTL, 2, 0, NULL, 0, pga_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_SWITCH_E("Earphone Driver", - SND_SOC_NOPM, 0, 0, &ep_driver_switch_controls, + SND_SOC_DAPM_OUT_DRV_E("Earphone Driver", + TWL6040_REG_EARCTL, 0, 0, NULL, 0, twl6040_power_mode_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
@@ -1224,7 +1230,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"HSOR", NULL, "Headset Right Driver"},
/* Earphone playback path */ - {"Earphone Driver", "Switch", "HSDAC Left"}, + {"Earphone Playback", "Switch", "HSDAC Left"}, + {"Earphone Driver", NULL, "Earphone Playback"}, {"EP", NULL, "Earphone Driver"},
{"HF Left Playback", "HF DAC", "HFDAC Left"},
Use "Handsfree XYZ" for user visible controls, while the internal DAPM widgets can use "HF XYZ". In this way we can group the Handsfree related controls in UI (alsamixer for example).
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 32 ++++++++++++++++---------------- 1 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 8b908ab..858bfd4 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1154,9 +1154,9 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { twl6040_power_mode_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_MUX("HF Left Playback", + SND_SOC_DAPM_MUX("Handsfree Left Playback", SND_SOC_NOPM, 0, 0, &hfl_mux_controls), - SND_SOC_DAPM_MUX("HF Right Playback", + SND_SOC_DAPM_MUX("Handsfree Right Playback", SND_SOC_NOPM, 0, 0, &hfr_mux_controls), /* Analog playback Muxes */ SND_SOC_DAPM_MUX("HS Left Playback", @@ -1168,11 +1168,11 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { &ep_path_enable_control),
/* Analog playback drivers */ - SND_SOC_DAPM_OUT_DRV_E("Handsfree Left Driver", + SND_SOC_DAPM_OUT_DRV_E("HF Left Driver", TWL6040_REG_HFLCTL, 4, 0, NULL, 0, pga_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_OUT_DRV_E("Handsfree Right Driver", + SND_SOC_DAPM_OUT_DRV_E("HF Right Driver", TWL6040_REG_HFRCTL, 4, 0, NULL, 0, pga_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), @@ -1190,9 +1190,9 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* Analog playback PGAs */ - SND_SOC_DAPM_PGA("HFDAC Left PGA", + SND_SOC_DAPM_PGA("HF Left PGA", TWL6040_REG_HFLCTL, 1, 0, NULL, 0), - SND_SOC_DAPM_PGA("HFDAC Right PGA", + SND_SOC_DAPM_PGA("HF Right PGA", TWL6040_REG_HFRCTL, 1, 0, NULL, 0),
}; @@ -1234,20 +1234,20 @@ static const struct snd_soc_dapm_route intercon[] = { {"Earphone Driver", NULL, "Earphone Playback"}, {"EP", NULL, "Earphone Driver"},
- {"HF Left Playback", "HF DAC", "HFDAC Left"}, - {"HF Left Playback", "Line-In amp", "AFMAmpL"}, + {"Handsfree Left Playback", "HF DAC", "HFDAC Left"}, + {"Handsfree Left Playback", "Line-In amp", "AFMAmpL"},
- {"HF Right Playback", "HF DAC", "HFDAC Right"}, - {"HF Right Playback", "Line-In amp", "AFMAmpR"}, + {"Handsfree Right Playback", "HF DAC", "HFDAC Right"}, + {"Handsfree Right Playback", "Line-In amp", "AFMAmpR"},
- {"HFDAC Left PGA", NULL, "HF Left Playback"}, - {"HFDAC Right PGA", NULL, "HF Right Playback"}, + {"HF Left PGA", NULL, "Handsfree Left Playback"}, + {"HF Right PGA", NULL, "Handsfree Right Playback"},
- {"Handsfree Left Driver", "Switch", "HFDAC Left PGA"}, - {"Handsfree Right Driver", "Switch", "HFDAC Right PGA"}, + {"HF Left Driver", NULL, "HF Left PGA"}, + {"HF Right Driver", NULL, "HF Right PGA"},
- {"HFL", NULL, "Handsfree Left Driver"}, - {"HFR", NULL, "Handsfree Right Driver"}, + {"HFL", NULL, "HF Left Driver"}, + {"HFR", NULL, "HF Right Driver"}, };
static int twl6040_add_widgets(struct snd_soc_codec *codec)
Use "Headset XYZ" for user visible controls, while the internal DAPM widgets can use "HS XYZ". In this way we can group the Headset related controls in UI (alsamixer for example).
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 24 ++++++++++++------------ 1 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 858bfd4..035803c 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1159,9 +1159,9 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { SND_SOC_DAPM_MUX("Handsfree Right Playback", SND_SOC_NOPM, 0, 0, &hfr_mux_controls), /* Analog playback Muxes */ - SND_SOC_DAPM_MUX("HS Left Playback", + SND_SOC_DAPM_MUX("Headset Left Playback", SND_SOC_NOPM, 0, 0, &hsl_mux_controls), - SND_SOC_DAPM_MUX("HS Right Playback", + SND_SOC_DAPM_MUX("Headset Right Playback", SND_SOC_NOPM, 0, 0, &hsr_mux_controls),
SND_SOC_DAPM_SWITCH("Earphone Playback", SND_SOC_NOPM, 0, 0, @@ -1176,11 +1176,11 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { TWL6040_REG_HFRCTL, 4, 0, NULL, 0, pga_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_OUT_DRV_E("Headset Left Driver", + SND_SOC_DAPM_OUT_DRV_E("HS Left Driver", TWL6040_REG_HSLCTL, 2, 0, NULL, 0, pga_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_OUT_DRV_E("Headset Right Driver", + SND_SOC_DAPM_OUT_DRV_E("HS Right Driver", TWL6040_REG_HSRCTL, 2, 0, NULL, 0, pga_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), @@ -1217,17 +1217,17 @@ static const struct snd_soc_dapm_route intercon[] = { {"AFMAmpL", NULL, "AFML"}, {"AFMAmpR", NULL, "AFMR"},
- {"HS Left Playback", "HS DAC", "HSDAC Left"}, - {"HS Left Playback", "Line-In amp", "AFMAmpL"}, + {"Headset Left Playback", "HS DAC", "HSDAC Left"}, + {"Headset Left Playback", "Line-In amp", "AFMAmpL"},
- {"HS Right Playback", "HS DAC", "HSDAC Right"}, - {"HS Right Playback", "Line-In amp", "AFMAmpR"}, + {"Headset Right Playback", "HS DAC", "HSDAC Right"}, + {"Headset Right Playback", "Line-In amp", "AFMAmpR"},
- {"Headset Left Driver", NULL, "HS Left Playback"}, - {"Headset Right Driver", NULL, "HS Right Playback"}, + {"HS Left Driver", NULL, "Headset Left Playback"}, + {"HS Right Driver", NULL, "Headset Right Playback"},
- {"HSOL", NULL, "Headset Left Driver"}, - {"HSOR", NULL, "Headset Right Driver"}, + {"HSOL", NULL, "HS Left Driver"}, + {"HSOR", NULL, "HS Right Driver"},
/* Earphone playback path */ {"Earphone Playback", "Switch", "HSDAC Left"},
AUX L/R outputs can be driver from the Handsfree PGA output.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 035803c..891b9ec 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -993,6 +993,12 @@ static const struct snd_kcontrol_new hfr_mux_controls = static const struct snd_kcontrol_new ep_path_enable_control = SOC_DAPM_SINGLE("Switch", TWL6040_REG_SW_SHADOW, 0, 1, 0);
+static const struct snd_kcontrol_new auxl_switch_control = + SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFLCTL, 6, 1, 0); + +static const struct snd_kcontrol_new auxr_switch_control = + SOC_DAPM_SINGLE("Switch", TWL6040_REG_HFRCTL, 6, 1, 0); + /* Headset power mode */ static const char *twl6040_power_mode_texts[] = { "Low-Power", "High-Perfomance", @@ -1101,6 +1107,8 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("HFL"), SND_SOC_DAPM_OUTPUT("HFR"), SND_SOC_DAPM_OUTPUT("EP"), + SND_SOC_DAPM_OUTPUT("AUXL"), + SND_SOC_DAPM_OUTPUT("AUXR"),
/* Analog input muxes for the capture amplifiers */ SND_SOC_DAPM_MUX("Analog Left Capture Route", @@ -1166,6 +1174,10 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
SND_SOC_DAPM_SWITCH("Earphone Playback", SND_SOC_NOPM, 0, 0, &ep_path_enable_control), + SND_SOC_DAPM_SWITCH("AUXL Playback", SND_SOC_NOPM, 0, 0, + &auxl_switch_control), + SND_SOC_DAPM_SWITCH("AUXR Playback", SND_SOC_NOPM, 0, 0, + &auxr_switch_control),
/* Analog playback drivers */ SND_SOC_DAPM_OUT_DRV_E("HF Left Driver", @@ -1248,6 +1260,12 @@ static const struct snd_soc_dapm_route intercon[] = {
{"HFL", NULL, "HF Left Driver"}, {"HFR", NULL, "HF Right Driver"}, + + {"AUXL Playback", "Switch", "HF Left PGA"}, + {"AUXR Playback", "Switch", "HF Right PGA"}, + + {"AUXL", NULL, "AUXL Playback"}, + {"AUXR", NULL, "AUXR Playback"}, };
static int twl6040_add_widgets(struct snd_soc_codec *codec)
Change the legacy DAI name from "twl6040-hifi" to "twl6040-legacy" to be more intuitive.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/codecs/twl6040.c | 2 +- sound/soc/omap/sdp4430.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 891b9ec..1be8d1b 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -1440,7 +1440,7 @@ static struct snd_soc_dai_ops twl6040_dai_ops = {
static struct snd_soc_dai_driver twl6040_dai[] = { { - .name = "twl6040-hifi", + .name = "twl6040-legacy", .playback = { .stream_name = "Playback", .channels_min = 1, diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c index d462f1a..060066b 100644 --- a/sound/soc/omap/sdp4430.c +++ b/sound/soc/omap/sdp4430.c @@ -166,7 +166,7 @@ static struct snd_soc_dai_link sdp4430_dai = { .name = "TWL6040", .stream_name = "TWL6040", .cpu_dai_name = "omap-mcpdm", - .codec_dai_name = "twl6040-hifi", + .codec_dai_name = "twl6040-legacy", .platform_name = "omap-pcm-audio", .codec_name = "twl6040-codec", .init = sdp4430_twl6040_init,
Use one set of defines for the HS bits, since they are identical in both control register.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- include/linux/mfd/twl6040.h | 11 +++-------- sound/soc/codecs/twl6040.c | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-)
diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index 47470ca..d9e05ea 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -120,15 +120,10 @@ #define TWL6040_LPLLFIN 0x08 #define TWL6040_HPLLSEL 0x10
-/* HSLCTL (0x10) fields */ +/* HSLCTL/R (0x10/0x11) fields */
-#define TWL6040_HSDACMODEL 0x02 -#define TWL6040_HSDRVMODEL 0x08 - -/* HSRCTL (0x11) fields */ - -#define TWL6040_HSDACMODER 0x02 -#define TWL6040_HSDRVMODER 0x08 +#define TWL6040_HSDACMODE (1 << 1) +#define TWL6040_HSDRVMODE (1 << 3)
/* VIBCTLL (0x18) fields */
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 1be8d1b..abde625 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -638,7 +638,7 @@ static int pga_event(struct snd_soc_dapm_widget *w, static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) { int hslctl, hsrctl; - int mask = TWL6040_HSDRVMODEL | TWL6040_HSDACMODEL; + int mask = TWL6040_HSDRVMODE | TWL6040_HSDACMODE;
hslctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSLCTL); hsrctl = twl6040_read_reg_cache(codec, TWL6040_REG_HSRCTL);
Both Headset DAC need to be enabled at the same time, before any of the output drivers are enabled.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- include/linux/mfd/twl6040.h | 1 + sound/soc/codecs/twl6040.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index d9e05ea..0bbb1e7 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -122,6 +122,7 @@
/* HSLCTL/R (0x10/0x11) fields */
+#define TWL6040_HSDACENA (1 << 0) #define TWL6040_HSDACMODE (1 << 1) #define TWL6040_HSDRVMODE (1 << 3)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index abde625..063defc 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -90,6 +90,7 @@ struct twl6040_data { int pll_power_mode; int hs_power_mode; int hs_power_mode_locked; + int hs_dac_enabled; unsigned int clk_in; unsigned int sysclk; u16 hs_left_step; @@ -660,6 +661,43 @@ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) static int twl6040_hs_dac_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + struct snd_soc_codec *codec = w->codec; + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + u8 hslctl, hsrctl; + + /* + * Workaround for Headset DC offset caused pop noise: + * Both HS DAC need to be turned on (before the HS driver) and off at + * the same time. + */ + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (!priv->hs_dac_enabled++) { + hslctl = twl6040_read_reg_cache(codec, + TWL6040_REG_HSLCTL); + hslctl |= TWL6040_HSDACENA; + + hsrctl = twl6040_read_reg_cache(codec, + TWL6040_REG_HSRCTL); + hsrctl |= TWL6040_HSDACENA; + + twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl); + twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl); + } + } else { + if (!--priv->hs_dac_enabled) { + hslctl = twl6040_read_reg_cache(codec, + TWL6040_REG_HSLCTL); + hslctl &= ~TWL6040_HSDACENA; + + hsrctl = twl6040_read_reg_cache(codec, + TWL6040_REG_HSRCTL); + hsrctl &= ~TWL6040_HSDACENA; + + twl6040_write(codec, TWL6040_REG_HSLCTL, hslctl); + twl6040_write(codec, TWL6040_REG_HSRCTL, hsrctl); + } + } + msleep(1); return 0; } @@ -1146,11 +1184,11 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
/* DACs */ SND_SOC_DAPM_DAC_E("HSDAC Left", "Headset Playback", - TWL6040_REG_HSLCTL, 0, 0, + SND_SOC_NOPM, 0, 0, twl6040_hs_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_DAC_E("HSDAC Right", "Headset Playback", - TWL6040_REG_HSRCTL, 0, 0, + SND_SOC_NOPM, 0, 0, twl6040_hs_dac_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_DAC_E("HFDAC Left", "Handsfree Playback",
On Thu, 2011-09-15 at 15:39 +0300, Peter Ujfalusi wrote:
Hello,
The first four patch of this series (the MFD/Input patches) has been already sent to upstream to the relevant lists, but I wanted to include here as well, since I have dependency on that series later on. If it is OK with you, it would be best to merge these MFD/Input changes through the audio tree for 3.2.
Code cleanup, and fixes for the twl6040 codec driver. Main changes:
- reg_cache has been rested to chip defaults
- codec initialization changes
- Correction for the Earphone path
- AUX L/R output support
- Headset/Handsfree control name changes
- Headset DC offset workaround
All Acked-by: Liam Girdwood lrg@ti.com
Samuel, IIRC we have a dependency on MFD. Do you mind if this goes via ASoC ?
Thanks
Liam
Regards, Peter
I have excluded the linux-input list (but kept Dmitry) - to not spam them.
Peter Ujfalusi (17): MFD: twl6040: Remove global pointer for platform_device MFD: twl6040: Add accessor for revision ID Input: twl6040-vibra: Use accessor to get revision information MFD: twl6040: Fix power on GPIO handling ASoC: twl6040: Chip initialization cleanup ASoC: twl6040: Use chip defaults in the initial reg_cache ASoC: twl6040: Lower the initial gains ASoC: twl6040: Fix comments for register names ASoC: twl6040: Remove strings "NULL" from DAPM route ASoC: twl6040: Introduce SW only shadow register ASoC: twl6040: Earphone path correction ASoC: twl6040: Use consistent names for Handsfree path ASoC: twl6040: Use consistent names for Headset path ASoC: twl6040: Support for AUX L/R output ASoC: twl6040/sdp4430: Change legacy DAI name ASoC/MFD: twl6040: Combine bit definitions for Headset control registers ASoC: twl6040: Workaround for headset DC offset caused pop noise
drivers/input/misc/twl6040-vibra.c | 2 +- drivers/mfd/twl6040-core.c | 38 ++-- include/linux/mfd/twl6040.h | 22 +-- sound/soc/codecs/twl6040.c | 367 +++++++++++++++++++----------------- sound/soc/omap/sdp4430.c | 2 +- 5 files changed, 218 insertions(+), 213 deletions(-)
On Thu, Sep 15, 2011 at 03:39:22PM +0300, Peter Ujfalusi wrote:
The first four patch of this series (the MFD/Input patches) has been already sent to upstream to the relevant lists, but I wanted to include here as well, since I have dependency on that series later on. If it is OK with you, it would be best to merge these MFD/Input changes through the audio tree for 3.2.
It'd be really good if you could manage to split this stuff up - this series is enormous and it's getting posted rather a lot, much of this stuff seems like it could be split out and posted separately.
On Monday 19 September 2011 11:12:36 Mark Brown wrote:
It'd be really good if you could manage to split this stuff up - this series is enormous and it's getting posted rather a lot, much of this stuff seems like it could be split out and posted separately.
It used to be 29 patches ;) I would try to avoid resending this 17 patch in a smaller packs, unless it is really needed. I did separated the two bigger change from this update series (Offset cancellation, vibra). The changes within this series are quite straight forward, Also as they pretty much depend on each other I thought it is better to keep them together.
-- Péter
participants (3)
-
Liam Girdwood
-
Mark Brown
-
Peter Ujfalusi