From 3e8e1952e3a3dd59b11233a532ca68e6471742d9 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Fri, 9 Jan 2009 00:23:21 +0000 Subject: ASoC: cleanup duplicated code. Many codec drivers were implementing cookie-cutter copies of the function that adds kcontrols to the codec. This patch moves this code to a common function snd_soc_add_controls() in soc-core.c and updates all drivers using copies of this function to use the new common version. [Edited to raise priority of error log message and document parameters. -- broonie] Signed-off-by: Ian Molton Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index fd0f338..253063f 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -670,22 +670,6 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { 0, 3, 5, 0, input_gain_tlv), }; -/* add non dapm controls */ -static int twl4030_add_controls(struct snd_soc_codec *codec) -{ - int err, i; - - for (i = 0; i < ARRAY_SIZE(twl4030_snd_controls); i++) { - err = snd_ctl_add(codec->card, - snd_soc_cnew(&twl4030_snd_controls[i], - codec, NULL)); - if (err < 0) - return err; - } - - return 0; -} - static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { /* Left channel inputs */ SND_SOC_DAPM_INPUT("MAINMIC"), @@ -1233,7 +1217,8 @@ static int twl4030_init(struct snd_soc_device *socdev) /* power on device */ twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - twl4030_add_controls(codec); + snd_soc_add_controls(codec, twl4030_snd_controls, + ARRAY_SIZE(twl4030_snd_controls)); twl4030_add_widgets(codec); ret = snd_soc_init_card(socdev); -- cgit v1.1 From 91432e976ff1323e5dd6f52498969602953c6ee9 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Sat, 17 Jan 2009 17:44:23 +0000 Subject: ASoC: fixes to caching implementations This patch takes fixes a number of bugs in the caching code used by several ASoC codec drivers. Mostly off-by-one fixes. Signed-off-by: Ian Molton Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index ddc9f37..f530c1e 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -125,6 +125,9 @@ static inline unsigned int twl4030_read_reg_cache(struct snd_soc_codec *codec, { u8 *cache = codec->reg_cache; + if (reg >= TWL4030_CACHEREGNUM) + return -EIO; + return cache[reg]; } -- cgit v1.1 From 6627a653bceb3a54e55e5cdc478ec5b8d5c9cc44 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 23 Jan 2009 22:55:23 +0000 Subject: ASoC: Push the codec runtime storage into the card structure This is a further stage on the road to refactoring away the ASoC platform device. Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index f530c1e..796f34c 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -981,7 +981,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_codec *codec = socdev->card->codec; u8 mode, old_mode, format, old_format; @@ -1166,7 +1166,7 @@ EXPORT_SYMBOL_GPL(twl4030_dai); static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_codec *codec = socdev->card->codec; twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); @@ -1176,7 +1176,7 @@ static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) static int twl4030_resume(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_codec *codec = socdev->card->codec; twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); twl4030_set_bias_level(codec, codec->suspend_bias_level); @@ -1190,7 +1190,7 @@ static int twl4030_resume(struct platform_device *pdev) static int twl4030_init(struct snd_soc_device *socdev) { - struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_codec *codec = socdev->card->codec; int ret = 0; printk(KERN_INFO "TWL4030 Audio Codec init \n"); @@ -1251,7 +1251,7 @@ static int twl4030_probe(struct platform_device *pdev) if (codec == NULL) return -ENOMEM; - socdev->codec = codec; + socdev->card->codec = codec; mutex_init(&codec->mutex); INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); @@ -1265,7 +1265,7 @@ static int twl4030_probe(struct platform_device *pdev) static int twl4030_remove(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_codec *codec = socdev->card->codec; printk(KERN_INFO "TWL4030 Audio Codec remove\n"); snd_soc_free_pcms(socdev); -- cgit v1.1 From 3fc93030e5a792fdd0da3321487f5cbfd1143c2b Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Jan 2009 11:29:39 +0200 Subject: ASoC: TWL4030: Syncronize the reg_cache for ANAMICL after the offset cancelation The offset cancelation bit in ANAMICL register is self cleanig. Make sure that the reg_cache holds the same value as the HW register. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 796f34c..24419af 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -913,6 +913,9 @@ static void twl4030_power_up(struct snd_soc_codec *codec) ((byte & TWL4030_CNCL_OFFSET_START) == TWL4030_CNCL_OFFSET_START)); + /* Make sure that the reg_cache has the same value as the HW */ + twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); + /* anti-pop when changing analog gain */ regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1); twl4030_write(codec, TWL4030_REG_MISC_SET_1, -- cgit v1.1 From db04e2c58a65364218b89f1372b4b3b78d206423 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Jan 2009 11:29:40 +0200 Subject: ASoC: TWL4030: Code clean up for codec power up and down Merge the codec up and down functions to a simple one. Codec is powered down by default (reg_cache change). Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 24419af..af7b433 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -42,7 +42,7 @@ */ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { 0x00, /* this register not used */ - 0x93, /* REG_CODEC_MODE (0x1) */ + 0x91, /* REG_CODEC_MODE (0x1) */ 0xc3, /* REG_OPTION (0x2) */ 0x00, /* REG_UNKNOWN (0x3) */ 0x00, /* REG_MICBIAS_CTL (0x4) */ @@ -154,26 +154,17 @@ static int twl4030_write(struct snd_soc_codec *codec, return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg); } -static void twl4030_clear_codecpdz(struct snd_soc_codec *codec) +static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) { u8 mode; mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); - twl4030_write(codec, TWL4030_REG_CODEC_MODE, - mode & ~TWL4030_CODECPDZ); + if (enable) + mode |= TWL4030_CODECPDZ; + else + mode &= ~TWL4030_CODECPDZ; - /* REVISIT: this delay is present in TI sample drivers */ - /* but there seems to be no TRM requirement for it */ - udelay(10); -} - -static void twl4030_set_codecpdz(struct snd_soc_codec *codec) -{ - u8 mode; - - mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); - twl4030_write(codec, TWL4030_REG_CODEC_MODE, - mode | TWL4030_CODECPDZ); + twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); /* REVISIT: this delay is present in TI sample drivers */ /* but there seems to be no TRM requirement for it */ @@ -185,7 +176,7 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) int i; /* clear CODECPDZ prior to setting register defaults */ - twl4030_clear_codecpdz(codec); + twl4030_codec_enable(codec, 0); /* set all audio section registers to reasonable defaults */ for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) @@ -895,7 +886,7 @@ static void twl4030_power_up(struct snd_soc_codec *codec) int i = 0; /* set CODECPDZ to turn on codec */ - twl4030_set_codecpdz(codec); + twl4030_codec_enable(codec, 1); /* initiate offset cancellation */ anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); @@ -922,8 +913,8 @@ static void twl4030_power_up(struct snd_soc_codec *codec) regmisc1 | TWL4030_SMOOTH_ANAVOL_EN); /* toggle CODECPDZ as per TRM */ - twl4030_clear_codecpdz(codec); - twl4030_set_codecpdz(codec); + twl4030_codec_enable(codec, 0); + twl4030_codec_enable(codec, 1); /* program anti-pop with bias ramp delay */ popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); @@ -952,7 +943,7 @@ static void twl4030_power_down(struct snd_soc_codec *codec) twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn); /* power down */ - twl4030_clear_codecpdz(codec); + twl4030_codec_enable(codec, 0); } static int twl4030_set_bias_level(struct snd_soc_codec *codec, @@ -1030,7 +1021,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, if (mode != old_mode) { /* change rate and set CODECPDZ */ twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); - twl4030_set_codecpdz(codec); + twl4030_codec_enable(codec, 1); } /* sample size */ @@ -1053,13 +1044,13 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, if (format != old_format) { /* clear CODECPDZ before changing format (codec requirement) */ - twl4030_clear_codecpdz(codec); + twl4030_codec_enable(codec, 0); /* change format */ twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); /* set CODECPDZ afterwards */ - twl4030_set_codecpdz(codec); + twl4030_codec_enable(codec, 1); } return 0; } @@ -1129,13 +1120,13 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, if (format != old_format) { /* clear CODECPDZ before changing format (codec requirement) */ - twl4030_clear_codecpdz(codec); + twl4030_codec_enable(codec, 0); /* change format */ twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); /* set CODECPDZ afterwards */ - twl4030_set_codecpdz(codec); + twl4030_codec_enable(codec, 1); } return 0; -- cgit v1.1 From aad749e51a66d473f5cef4a050e3e36795261be3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Jan 2009 11:29:41 +0200 Subject: ASoC: TWL4030: Enable Headset Left anti-pop/bias ramp only if the Headset Left is in use The Headset Left anti-pop and bias ramp does not need to be enabled, if the headset is not in use. Move the code to DAPM event handler for HeadsetL. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 71 +++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 26 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index af7b433..900486e 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -414,6 +414,47 @@ static int handsfree_event(struct snd_soc_dapm_widget *w, return 0; } +static int headsetl_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + unsigned char hs_gain, hs_pop; + + /* Save the current volume */ + hs_gain = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_GAIN_SET); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Do the anti-pop/bias ramp enable according to the TRM */ + hs_pop = TWL4030_RAMP_DELAY_645MS; + twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); + hs_pop |= TWL4030_VMID_EN; + twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); + /* Is this needed? Can we just use whatever gain here? */ + twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET, + (hs_gain & (~0x0f)) | 0x0a); + hs_pop |= TWL4030_RAMP_EN; + twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); + + /* Restore the original volume */ + twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET, hs_gain); + break; + case SND_SOC_DAPM_POST_PMD: + /* Do the anti-pop/bias ramp disable according to the TRM */ + hs_pop = twl4030_read_reg_cache(w->codec, + TWL4030_REG_HS_POPN_SET); + hs_pop &= ~TWL4030_RAMP_EN; + twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); + /* Bypass the reg_cache to mute the headset */ + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + hs_gain & (~0x0f), + TWL4030_REG_HS_GAIN_SET); + hs_pop &= ~TWL4030_VMID_EN; + twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); + break; + } + return 0; +} + /* * Some of the gain controls in TWL (mostly those which are associated with * the outputs) are implemented in an interesting way: @@ -720,8 +761,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_VALUE_MUX("PredriveR Mux", SND_SOC_NOPM, 0, 0, &twl4030_dapm_predriver_control), /* HeadsetL/R */ - SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0, - &twl4030_dapm_hsol_control), + SND_SOC_DAPM_MUX_E("HeadsetL Mux", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_hsol_control, headsetl_event, + SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0, &twl4030_dapm_hsor_control), /* CarkitL/R */ @@ -882,7 +924,7 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec) static void twl4030_power_up(struct snd_soc_codec *codec) { - u8 anamicl, regmisc1, byte, popn; + u8 anamicl, regmisc1, byte; int i = 0; /* set CODECPDZ to turn on codec */ @@ -915,33 +957,10 @@ static void twl4030_power_up(struct snd_soc_codec *codec) /* toggle CODECPDZ as per TRM */ twl4030_codec_enable(codec, 0); twl4030_codec_enable(codec, 1); - - /* program anti-pop with bias ramp delay */ - popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); - popn &= TWL4030_RAMP_DELAY; - popn |= TWL4030_RAMP_DELAY_645MS; - twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn); - popn |= TWL4030_VMID_EN; - twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn); - - /* enable anti-pop ramp */ - popn |= TWL4030_RAMP_EN; - twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn); } static void twl4030_power_down(struct snd_soc_codec *codec) { - u8 popn; - - /* disable anti-pop ramp */ - popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); - popn &= ~TWL4030_RAMP_EN; - twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn); - - /* disable bias out */ - popn &= ~TWL4030_VMID_EN; - twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn); - /* power down */ twl4030_codec_enable(codec, 0); } -- cgit v1.1 From fb2a2f84908460c18c3894963990518b224dd9ff Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Jan 2009 11:29:42 +0200 Subject: ASoC: TWL4030: Physical ADC and amplifier power switch change Change the power switches for the physical ADC and for the amplifiers for the analog capture path to map more closely the actual path inside of the codec. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 900486e..8fe059e 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -802,16 +802,16 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD| SND_SOC_DAPM_POST_REG), - /* Analog input muxes with power switch for the physical ADCL/R */ + /* Analog input muxes with switch for the capture amplifiers */ SND_SOC_DAPM_VALUE_MUX("Analog Left Capture Route", - TWL4030_REG_AVADC_CTL, 3, 0, &twl4030_dapm_analoglmic_control), + TWL4030_REG_ANAMICL, 4, 0, &twl4030_dapm_analoglmic_control), SND_SOC_DAPM_VALUE_MUX("Analog Right Capture Route", - TWL4030_REG_AVADC_CTL, 1, 0, &twl4030_dapm_analogrmic_control), + TWL4030_REG_ANAMICR, 4, 0, &twl4030_dapm_analogrmic_control), - SND_SOC_DAPM_PGA("Analog Left Amplifier", - TWL4030_REG_ANAMICL, 4, 0, NULL, 0), - SND_SOC_DAPM_PGA("Analog Right Amplifier", - TWL4030_REG_ANAMICR, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("ADC Physical Left", + TWL4030_REG_AVADC_CTL, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("ADC Physical Right", + TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0), SND_SOC_DAPM_PGA("Digimic0 Enable", TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0), @@ -885,23 +885,23 @@ static const struct snd_soc_dapm_route intercon[] = { {"Analog Right Capture Route", "Sub mic", "SUBMIC"}, {"Analog Right Capture Route", "AUXR", "AUXR"}, - {"Analog Left Amplifier", NULL, "Analog Left Capture Route"}, - {"Analog Right Amplifier", NULL, "Analog Right Capture Route"}, + {"ADC Physical Left", NULL, "Analog Left Capture Route"}, + {"ADC Physical Right", NULL, "Analog Right Capture Route"}, {"Digimic0 Enable", NULL, "DIGIMIC0"}, {"Digimic1 Enable", NULL, "DIGIMIC1"}, /* TX1 Left capture path */ - {"TX1 Capture Route", "Analog", "Analog Left Amplifier"}, + {"TX1 Capture Route", "Analog", "ADC Physical Left"}, {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, /* TX1 Right capture path */ - {"TX1 Capture Route", "Analog", "Analog Right Amplifier"}, + {"TX1 Capture Route", "Analog", "ADC Physical Right"}, {"TX1 Capture Route", "Digimic0", "Digimic0 Enable"}, /* TX2 Left capture path */ - {"TX2 Capture Route", "Analog", "Analog Left Amplifier"}, + {"TX2 Capture Route", "Analog", "ADC Physical Left"}, {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"}, /* TX2 Right capture path */ - {"TX2 Capture Route", "Analog", "Analog Right Amplifier"}, + {"TX2 Capture Route", "Analog", "ADC Physical Right"}, {"TX2 Capture Route", "Digimic1", "Digimic1 Enable"}, {"ADC Virtual Left1", NULL, "TX1 Capture Route"}, -- cgit v1.1 From 006f367e38fb45e2f161c0f500c74449ae63e866 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Jan 2009 11:29:43 +0200 Subject: ASoC: TWL4030: Move the twl4030_power_up and _power_down function Move the twl4030_power_up and twl4030_power_down function earlier to facilitate the analog bypass implementation. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 85 +++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 43 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 8fe059e..f985bef 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -184,6 +184,48 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) } +static void twl4030_power_up(struct snd_soc_codec *codec) +{ + u8 anamicl, regmisc1, byte; + int i = 0; + + /* set CODECPDZ to turn on codec */ + twl4030_codec_enable(codec, 1); + + /* initiate offset cancellation */ + anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); + twl4030_write(codec, TWL4030_REG_ANAMICL, + anamicl | TWL4030_CNCL_OFFSET_START); + + /* wait for offset cancellation to complete */ + do { + /* this takes a little while, so don't slam i2c */ + udelay(2000); + twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, + TWL4030_REG_ANAMICL); + } while ((i++ < 100) && + ((byte & TWL4030_CNCL_OFFSET_START) == + TWL4030_CNCL_OFFSET_START)); + + /* Make sure that the reg_cache has the same value as the HW */ + twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); + + /* anti-pop when changing analog gain */ + regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1); + twl4030_write(codec, TWL4030_REG_MISC_SET_1, + regmisc1 | TWL4030_SMOOTH_ANAVOL_EN); + + /* toggle CODECPDZ as per TRM */ + twl4030_codec_enable(codec, 0); + twl4030_codec_enable(codec, 1); +} + +static void twl4030_power_down(struct snd_soc_codec *codec) +{ + /* power down */ + twl4030_codec_enable(codec, 0); +} + /* Earpiece */ static const char *twl4030_earpiece_texts[] = {"Off", "DACL1", "DACL2", "DACR1"}; @@ -922,49 +964,6 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec) return 0; } -static void twl4030_power_up(struct snd_soc_codec *codec) -{ - u8 anamicl, regmisc1, byte; - int i = 0; - - /* set CODECPDZ to turn on codec */ - twl4030_codec_enable(codec, 1); - - /* initiate offset cancellation */ - anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); - twl4030_write(codec, TWL4030_REG_ANAMICL, - anamicl | TWL4030_CNCL_OFFSET_START); - - - /* wait for offset cancellation to complete */ - do { - /* this takes a little while, so don't slam i2c */ - udelay(2000); - twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, - TWL4030_REG_ANAMICL); - } while ((i++ < 100) && - ((byte & TWL4030_CNCL_OFFSET_START) == - TWL4030_CNCL_OFFSET_START)); - - /* Make sure that the reg_cache has the same value as the HW */ - twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte); - - /* anti-pop when changing analog gain */ - regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1); - twl4030_write(codec, TWL4030_REG_MISC_SET_1, - regmisc1 | TWL4030_SMOOTH_ANAVOL_EN); - - /* toggle CODECPDZ as per TRM */ - twl4030_codec_enable(codec, 0); - twl4030_codec_enable(codec, 1); -} - -static void twl4030_power_down(struct snd_soc_codec *codec) -{ - /* power down */ - twl4030_codec_enable(codec, 0); -} - static int twl4030_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { -- cgit v1.1 From 7393958f630ac91e591e62058f2bdb61523ec60c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 29 Jan 2009 14:57:50 +0200 Subject: ASoC: TWL4030: Add analog loopback support This patch adds the analog loopback/bypass support for twl4030 codec. Details for the implementation: It seams that the analog loopback needs the DAC powered on on the channel, where the loopback is selected. The switch for the DACs has been moved from the DAPM_DAC to the "Analog XX Playback Mixer". In this way the DAC will be powered while the audio playback is used or/and the loopback is enabled for the channel. The twl4030 codec powering has been reworked. Now the codec will be powered as long as it does not receives the SND_SOC_BIAS_OFF event. The exceptions are when the given change in the registers needs the codec power down/up cycle in order to take effect. Otherwise the codec is on. When the codec enters to STANDBY state and none of the loopback paths are enabled, than the amplifiers, which are no in the DAPM path are forced to turn off and the PLL is disabled. When playback/capture starts the disabled gains are restored and the PLL is enabled. When one of the loopback enabled in STANDBY mode, the disabled gains are restored and the PLL is enabled also. In short: the codec always goes to the lowest power state based on the bias_level and the bypass_state. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 212 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 199 insertions(+), 13 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index f985bef..c26854b 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -117,6 +117,13 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { 0x00, /* REG_MISC_SET_2 (0x49) */ }; +/* codec private data */ +struct twl4030_priv { + unsigned int bypass_state; + unsigned int codec_powered; + unsigned int codec_muted; +}; + /* * read twl4030 register cache */ @@ -156,8 +163,12 @@ static int twl4030_write(struct snd_soc_codec *codec, static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) { + struct twl4030_priv *twl4030 = codec->private_data; u8 mode; + if (enable == twl4030->codec_powered) + return; + mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE); if (enable) mode |= TWL4030_CODECPDZ; @@ -165,6 +176,7 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) mode &= ~TWL4030_CODECPDZ; twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); + twl4030->codec_powered = enable; /* REVISIT: this delay is present in TI sample drivers */ /* but there seems to be no TRM requirement for it */ @@ -184,11 +196,82 @@ static void twl4030_init_chip(struct snd_soc_codec *codec) } +static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute) +{ + struct twl4030_priv *twl4030 = codec->private_data; + u8 reg_val; + + if (mute == twl4030->codec_muted) + return; + + if (mute) { + /* Bypass the reg_cache and mute the volumes + * Headset mute is done in it's own event handler + * Things to mute: Earpiece, PreDrivL/R, CarkitL/R + */ + reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL); + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + reg_val & (~TWL4030_EAR_GAIN), + TWL4030_REG_EAR_CTL); + + reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL); + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + reg_val & (~TWL4030_PREDL_GAIN), + TWL4030_REG_PREDL_CTL); + reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL); + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + reg_val & (~TWL4030_PREDR_GAIN), + TWL4030_REG_PREDL_CTL); + + reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL); + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + reg_val & (~TWL4030_PRECKL_GAIN), + TWL4030_REG_PRECKL_CTL); + reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL); + twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, + reg_val & (~TWL4030_PRECKL_GAIN), + TWL4030_REG_PRECKR_CTL); + + /* Disable PLL */ + reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); + reg_val &= ~TWL4030_APLL_EN; + twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); + } else { + /* Restore the volumes + * Headset mute is done in it's own event handler + * Things to restore: Earpiece, PreDrivL/R, CarkitL/R + */ + twl4030_write(codec, TWL4030_REG_EAR_CTL, + twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL)); + + twl4030_write(codec, TWL4030_REG_PREDL_CTL, + twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL)); + twl4030_write(codec, TWL4030_REG_PREDR_CTL, + twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL)); + + twl4030_write(codec, TWL4030_REG_PRECKL_CTL, + twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL)); + twl4030_write(codec, TWL4030_REG_PRECKR_CTL, + twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL)); + + /* Enable PLL */ + reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); + reg_val |= TWL4030_APLL_EN; + twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); + } + + twl4030->codec_muted = mute; +} + static void twl4030_power_up(struct snd_soc_codec *codec) { + struct twl4030_priv *twl4030 = codec->private_data; u8 anamicl, regmisc1, byte; int i = 0; + if (twl4030->codec_powered) + return; + /* set CODECPDZ to turn on codec */ twl4030_codec_enable(codec, 1); @@ -220,6 +303,9 @@ static void twl4030_power_up(struct snd_soc_codec *codec) twl4030_codec_enable(codec, 1); } +/* + * Unconditional power down + */ static void twl4030_power_down(struct snd_soc_codec *codec) { /* power down */ @@ -402,6 +488,22 @@ static const struct soc_enum twl4030_micpathtx2_enum = static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control = SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum); +/* Analog bypass for AudioR1 */ +static const struct snd_kcontrol_new twl4030_dapm_abypassr1_control = + SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR1_APGA_CTL, 2, 1, 0); + +/* Analog bypass for AudioL1 */ +static const struct snd_kcontrol_new twl4030_dapm_abypassl1_control = + SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL1_APGA_CTL, 2, 1, 0); + +/* Analog bypass for AudioR2 */ +static const struct snd_kcontrol_new twl4030_dapm_abypassr2_control = + SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR2_APGA_CTL, 2, 1, 0); + +/* Analog bypass for AudioL2 */ +static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control = + SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0); + static int micpath_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -497,6 +599,31 @@ static int headsetl_event(struct snd_soc_dapm_widget *w, return 0; } +static int bypass_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct soc_mixer_control *m = + (struct soc_mixer_control *)w->kcontrols->private_value; + struct twl4030_priv *twl4030 = w->codec->private_data; + unsigned char reg; + + reg = twl4030_read_reg_cache(w->codec, m->reg); + if (reg & (1 << m->shift)) + twl4030->bypass_state |= + (1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); + else + twl4030->bypass_state &= + ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); + + if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) { + if (twl4030->bypass_state) + twl4030_codec_mute(w->codec, 0); + else + twl4030_codec_mute(w->codec, 1); + } + return 0; +} + /* * Some of the gain controls in TWL (mostly those which are associated with * the outputs) are implemented in an interesting way: @@ -775,13 +902,13 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { /* DACs */ SND_SOC_DAPM_DAC("DAC Right1", "Right Front Playback", - TWL4030_REG_AVDAC_CTL, 0, 0), + SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC("DAC Left1", "Left Front Playback", - TWL4030_REG_AVDAC_CTL, 1, 0), + SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC("DAC Right2", "Right Rear Playback", - TWL4030_REG_AVDAC_CTL, 2, 0), + SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC("DAC Left2", "Left Rear Playback", - TWL4030_REG_AVDAC_CTL, 3, 0), + SND_SOC_NOPM, 0, 0), /* Analog PGAs */ SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL, @@ -793,6 +920,29 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_PGA("ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL, 0, 0, NULL, 0), + /* Analog bypasses */ + SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassr1_control, bypass_event, + SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_SWITCH_E("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassl1_control, + bypass_event, SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_SWITCH_E("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassr2_control, + bypass_event, SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_abypassl2_control, + bypass_event, SND_SOC_DAPM_POST_REG), + + SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer", TWL4030_REG_AVDAC_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer", TWL4030_REG_AVDAC_CTL, + 1, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer", TWL4030_REG_AVDAC_CTL, + 2, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer", TWL4030_REG_AVDAC_CTL, + 3, 0, NULL, 0), + /* Output MUX controls */ /* Earpiece */ SND_SOC_DAPM_VALUE_MUX("Earpiece Mux", SND_SOC_NOPM, 0, 0, @@ -863,13 +1013,19 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0), SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0), SND_SOC_DAPM_MICBIAS("Headset Mic Bias", TWL4030_REG_MICBIAS_CTL, 2, 0), + }; static const struct snd_soc_dapm_route intercon[] = { - {"ARXL1_APGA", NULL, "DAC Left1"}, - {"ARXR1_APGA", NULL, "DAC Right1"}, - {"ARXL2_APGA", NULL, "DAC Left2"}, - {"ARXR2_APGA", NULL, "DAC Right2"}, + {"Analog L1 Playback Mixer", NULL, "DAC Left1"}, + {"Analog R1 Playback Mixer", NULL, "DAC Right1"}, + {"Analog L2 Playback Mixer", NULL, "DAC Left2"}, + {"Analog R2 Playback Mixer", NULL, "DAC Right2"}, + + {"ARXL1_APGA", NULL, "Analog L1 Playback Mixer"}, + {"ARXR1_APGA", NULL, "Analog R1 Playback Mixer"}, + {"ARXL2_APGA", NULL, "Analog L2 Playback Mixer"}, + {"ARXR2_APGA", NULL, "Analog R2 Playback Mixer"}, /* Internal playback routings */ /* Earpiece */ @@ -951,6 +1107,17 @@ static const struct snd_soc_dapm_route intercon[] = { {"ADC Virtual Left2", NULL, "TX2 Capture Route"}, {"ADC Virtual Right2", NULL, "TX2 Capture Route"}, + /* Analog bypass routes */ + {"Right1 Analog Loopback", "Switch", "Analog Right Capture Route"}, + {"Left1 Analog Loopback", "Switch", "Analog Left Capture Route"}, + {"Right2 Analog Loopback", "Switch", "Analog Right Capture Route"}, + {"Left2 Analog Loopback", "Switch", "Analog Left Capture Route"}, + + {"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"}, + {"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"}, + {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"}, + {"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"}, + }; static int twl4030_add_widgets(struct snd_soc_codec *codec) @@ -967,16 +1134,25 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec) static int twl4030_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct twl4030_priv *twl4030 = codec->private_data; + switch (level) { case SND_SOC_BIAS_ON: - twl4030_power_up(codec); + twl4030_codec_mute(codec, 0); break; case SND_SOC_BIAS_PREPARE: - /* TODO: develop a twl4030_prepare function */ + twl4030_power_up(codec); + if (twl4030->bypass_state) + twl4030_codec_mute(codec, 0); + else + twl4030_codec_mute(codec, 1); break; case SND_SOC_BIAS_STANDBY: - /* TODO: develop a twl4030_standby function */ - twl4030_power_down(codec); + twl4030_power_up(codec); + if (twl4030->bypass_state) + twl4030_codec_mute(codec, 0); + else + twl4030_codec_mute(codec, 1); break; case SND_SOC_BIAS_OFF: twl4030_power_down(codec); @@ -996,7 +1172,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = socdev->card->codec; u8 mode, old_mode, format, old_format; - /* bit rate */ old_mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ; @@ -1038,6 +1213,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, if (mode != old_mode) { /* change rate and set CODECPDZ */ + twl4030_codec_enable(codec, 0); twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); twl4030_codec_enable(codec, 1); } @@ -1258,11 +1434,19 @@ static int twl4030_probe(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec; + struct twl4030_priv *twl4030; codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); if (codec == NULL) return -ENOMEM; + twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL); + if (twl4030 == NULL) { + kfree(codec); + return -ENOMEM; + } + + codec->private_data = twl4030; socdev->card->codec = codec; mutex_init(&codec->mutex); INIT_LIST_HEAD(&codec->dapm_widgets); @@ -1280,8 +1464,10 @@ static int twl4030_remove(struct platform_device *pdev) struct snd_soc_codec *codec = socdev->card->codec; printk(KERN_INFO "TWL4030 Audio Codec remove\n"); + twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); + kfree(codec->private_data); kfree(codec); return 0; -- cgit v1.1 From 6bab83fd886564e96abcff62862732159535f600 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 18 Feb 2009 14:39:05 +0200 Subject: ASoC: TWL4030: Add digital loopback support This patch adds the digital loopback/bypass support for twl4030 codec. The digital loopback will let the digimic0 (routed in the TX1 capture path inside of TWL4030) data to be routed back to the RX2 playback path (I2S stereo). It can also route the analog capture date routed through the TX1 back to RX2. Effectively the digital loopback is routing the audio from the TX1 capture path to the RX2 playback path. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 56 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 6 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index c26854b..535d8ce 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -504,6 +504,25 @@ static const struct snd_kcontrol_new twl4030_dapm_abypassr2_control = static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control = SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0); +/* Digital bypass gain, 0 mutes the bypass */ +static const unsigned int twl4030_dapm_dbypass_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 3, TLV_DB_SCALE_ITEM(-2400, 0, 1), + 4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0), +}; + +/* Digital bypass left (TX1L -> RX2L) */ +static const struct snd_kcontrol_new twl4030_dapm_dbypassl_control = + SOC_DAPM_SINGLE_TLV("Volume", + TWL4030_REG_ATX2ARXPGA, 3, 7, 0, + twl4030_dapm_dbypass_tlv); + +/* Digital bypass right (TX1R -> RX2R) */ +static const struct snd_kcontrol_new twl4030_dapm_dbypassr_control = + SOC_DAPM_SINGLE_TLV("Volume", + TWL4030_REG_ATX2ARXPGA, 0, 7, 0, + twl4030_dapm_dbypass_tlv); + static int micpath_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -608,12 +627,22 @@ static int bypass_event(struct snd_soc_dapm_widget *w, unsigned char reg; reg = twl4030_read_reg_cache(w->codec, m->reg); - if (reg & (1 << m->shift)) - twl4030->bypass_state |= - (1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); - else - twl4030->bypass_state &= - ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); + + if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) { + /* Analog bypass */ + if (reg & (1 << m->shift)) + twl4030->bypass_state |= + (1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); + else + twl4030->bypass_state &= + ~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL)); + } else { + /* Digital bypass */ + if (reg & (0x7 << m->shift)) + twl4030->bypass_state |= (1 << (m->shift ? 5 : 4)); + else + twl4030->bypass_state &= ~(1 << (m->shift ? 5 : 4)); + } if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) { if (twl4030->bypass_state) @@ -934,6 +963,14 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { &twl4030_dapm_abypassl2_control, bypass_event, SND_SOC_DAPM_POST_REG), + /* Digital bypasses */ + SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_dbypassl_control, bypass_event, + SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0, + &twl4030_dapm_dbypassr_control, bypass_event, + SND_SOC_DAPM_POST_REG), + SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer", TWL4030_REG_AVDAC_CTL, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer", TWL4030_REG_AVDAC_CTL, @@ -1118,6 +1155,13 @@ static const struct snd_soc_dapm_route intercon[] = { {"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"}, {"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"}, + /* Digital bypass routes */ + {"Right Digital Loopback", "Volume", "TX1 Capture Route"}, + {"Left Digital Loopback", "Volume", "TX1 Capture Route"}, + + {"Analog R2 Playback Mixer", NULL, "Right Digital Loopback"}, + {"Analog L2 Playback Mixer", NULL, "Left Digital Loopback"}, + }; static int twl4030_add_widgets(struct snd_soc_codec *codec) -- cgit v1.1 From 89492be88616aa20b3a6c3eb512f83c0c7d0c8a3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 5 Mar 2009 12:48:49 +0200 Subject: ASoC: TWL4030: Make the HS ramp delay configurable Enum type for selecting the desired ramp delay for the headset output. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 535d8ce..86bb15c 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -584,12 +584,11 @@ static int headsetl_event(struct snd_soc_dapm_widget *w, /* Save the current volume */ hs_gain = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_GAIN_SET); + hs_pop = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_POPN_SET); switch (event) { case SND_SOC_DAPM_POST_PMU: /* Do the anti-pop/bias ramp enable according to the TRM */ - hs_pop = TWL4030_RAMP_DELAY_645MS; - twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); hs_pop |= TWL4030_VMID_EN; twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); /* Is this needed? Can we just use whatever gain here? */ @@ -603,8 +602,6 @@ static int headsetl_event(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMD: /* Do the anti-pop/bias ramp disable according to the TRM */ - hs_pop = twl4030_read_reg_cache(w->codec, - TWL4030_REG_HS_POPN_SET); hs_pop &= ~TWL4030_RAMP_EN; twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop); /* Bypass the reg_cache to mute the headset */ @@ -847,6 +844,17 @@ static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0); */ static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0); +static const char *twl4030_rampdelay_texts[] = { + "27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms", + "437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms", + "3495/2581/1748 ms" +}; + +static const struct soc_enum twl4030_rampdelay_enum = + SOC_ENUM_SINGLE(TWL4030_REG_HS_POPN_SET, 2, + ARRAY_SIZE(twl4030_rampdelay_texts), + twl4030_rampdelay_texts); + static const struct snd_kcontrol_new twl4030_snd_controls[] = { /* Common playback gain controls */ SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume", @@ -901,6 +909,8 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = { SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN, 0, 3, 5, 0, input_gain_tlv), + + SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum), }; static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = { -- cgit v1.1 From 10d9e3d99ee8332bb73a3d7f12a8cd8ffab8b136 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Mon, 16 Mar 2009 21:23:35 +0900 Subject: ASoC: twl4030 - Fix build error CC sound/soc/codecs/twl4030.o sound/soc/codecs/twl4030.c:1400: warning: braces around scalar initializer sound/soc/codecs/twl4030.c:1400: warning: (near initialization for 'twl4030_dai.ops') sound/soc/codecs/twl4030.c:1401: error: field name not in record or union initializer sound/soc/codecs/twl4030.c:1401: error: (near initialization for 'twl4030_dai.ops') sound/soc/codecs/twl4030.c:1401: warning: initialization from incompatible pointer type sound/soc/codecs/twl4030.c:1402: error: field name not in record or union initializer sound/soc/codecs/twl4030.c:1402: error: (near initialization for 'twl4030_dai.ops') sound/soc/codecs/twl4030.c:1402: warning: excess elements in scalar initializer sound/soc/codecs/twl4030.c:1402: warning: (near initialization for 'twl4030_dai.ops') sound/soc/codecs/twl4030.c:1403: error: field name not in record or union initializer sound/soc/codecs/twl4030.c:1403: error: (near initialization for 'twl4030_dai.ops') sound/soc/codecs/twl4030.c:1403: warning: excess elements in scalar initializer sound/soc/codecs/twl4030.c:1403: warning: (near initialization for 'twl4030_dai.ops') Signed-off-by: Joonyoung Shim Signed-off-by: Mark Brown --- sound/soc/codecs/twl4030.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 86bb15c..97738e2 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -1383,6 +1383,12 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, #define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) #define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE) +static struct snd_soc_dai_ops twl4030_dai_ops = { + .hw_params = twl4030_hw_params, + .set_sysclk = twl4030_set_dai_sysclk, + .set_fmt = twl4030_set_dai_fmt, +}; + struct snd_soc_dai twl4030_dai = { .name = "twl4030", .playback = { @@ -1397,11 +1403,7 @@ struct snd_soc_dai twl4030_dai = { .channels_max = 2, .rates = TWL4030_RATES, .formats = TWL4030_FORMATS,}, - .ops = { - .hw_params = twl4030_hw_params, - .set_sysclk = twl4030_set_dai_sysclk, - .set_fmt = twl4030_set_dai_fmt, - } + .ops = &twl4030_dai_ops, }; EXPORT_SYMBOL_GPL(twl4030_dai); -- cgit v1.1