From 33f92ed4b3b9bef2080032b2b5d5dfba189eabeb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 26 May 2010 11:38:14 +0300 Subject: ASoC: TWL4030: Revisit codec defaults Reset most of the codec registers to their chip reset value. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/twl4030.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 6a34f562b563..9a3e999b595c 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 */ - 0x91, /* REG_CODEC_MODE (0x1) */ + 0x00, /* REG_CODEC_MODE (0x1) */ 0xc3, /* REG_OPTION (0x2) */ 0x00, /* REG_UNKNOWN (0x3) */ 0x00, /* REG_MICBIAS_CTL (0x4) */ @@ -51,28 +51,28 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { 0x00, /* REG_AVADC_CTL (0x7) */ 0x00, /* REG_ADCMICSEL (0x8) */ 0x00, /* REG_DIGMIXING (0x9) */ - 0x0c, /* REG_ATXL1PGA (0xA) */ - 0x0c, /* REG_ATXR1PGA (0xB) */ - 0x00, /* REG_AVTXL2PGA (0xC) */ - 0x00, /* REG_AVTXR2PGA (0xD) */ + 0x0f, /* REG_ATXL1PGA (0xA) */ + 0x0f, /* REG_ATXR1PGA (0xB) */ + 0x0f, /* REG_AVTXL2PGA (0xC) */ + 0x0f, /* REG_AVTXR2PGA (0xD) */ 0x00, /* REG_AUDIO_IF (0xE) */ 0x00, /* REG_VOICE_IF (0xF) */ - 0x00, /* REG_ARXR1PGA (0x10) */ - 0x00, /* REG_ARXL1PGA (0x11) */ - 0x6c, /* REG_ARXR2PGA (0x12) */ - 0x6c, /* REG_ARXL2PGA (0x13) */ - 0x00, /* REG_VRXPGA (0x14) */ + 0x3f, /* REG_ARXR1PGA (0x10) */ + 0x3f, /* REG_ARXL1PGA (0x11) */ + 0x3f, /* REG_ARXR2PGA (0x12) */ + 0x3f, /* REG_ARXL2PGA (0x13) */ + 0x25, /* REG_VRXPGA (0x14) */ 0x00, /* REG_VSTPGA (0x15) */ 0x00, /* REG_VRX2ARXPGA (0x16) */ 0x00, /* REG_AVDAC_CTL (0x17) */ 0x00, /* REG_ARX2VTXPGA (0x18) */ - 0x00, /* REG_ARXL1_APGA_CTL (0x19) */ - 0x00, /* REG_ARXR1_APGA_CTL (0x1A) */ - 0x4a, /* REG_ARXL2_APGA_CTL (0x1B) */ - 0x4a, /* REG_ARXR2_APGA_CTL (0x1C) */ + 0x32, /* REG_ARXL1_APGA_CTL (0x19) */ + 0x32, /* REG_ARXR1_APGA_CTL (0x1A) */ + 0x32, /* REG_ARXL2_APGA_CTL (0x1B) */ + 0x32, /* REG_ARXR2_APGA_CTL (0x1C) */ 0x00, /* REG_ATX2ARXPGA (0x1D) */ 0x00, /* REG_BT_IF (0x1E) */ - 0x00, /* REG_BTPGA (0x1F) */ + 0x55, /* REG_BTPGA (0x1F) */ 0x00, /* REG_BTSTPGA (0x20) */ 0x00, /* REG_EAR_CTL (0x21) */ 0x00, /* REG_HS_SEL (0x22) */ @@ -84,32 +84,32 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { 0x00, /* REG_PRECKR_CTL (0x28) */ 0x00, /* REG_HFL_CTL (0x29) */ 0x00, /* REG_HFR_CTL (0x2A) */ - 0x00, /* REG_ALC_CTL (0x2B) */ + 0x05, /* REG_ALC_CTL (0x2B) */ 0x00, /* REG_ALC_SET1 (0x2C) */ 0x00, /* REG_ALC_SET2 (0x2D) */ 0x00, /* REG_BOOST_CTL (0x2E) */ 0x00, /* REG_SOFTVOL_CTL (0x2F) */ - 0x00, /* REG_DTMF_FREQSEL (0x30) */ + 0x13, /* REG_DTMF_FREQSEL (0x30) */ 0x00, /* REG_DTMF_TONEXT1H (0x31) */ 0x00, /* REG_DTMF_TONEXT1L (0x32) */ 0x00, /* REG_DTMF_TONEXT2H (0x33) */ 0x00, /* REG_DTMF_TONEXT2L (0x34) */ - 0x00, /* REG_DTMF_TONOFF (0x35) */ - 0x00, /* REG_DTMF_WANONOFF (0x36) */ + 0x79, /* REG_DTMF_TONOFF (0x35) */ + 0x11, /* REG_DTMF_WANONOFF (0x36) */ 0x00, /* REG_I2S_RX_SCRAMBLE_H (0x37) */ 0x00, /* REG_I2S_RX_SCRAMBLE_M (0x38) */ 0x00, /* REG_I2S_RX_SCRAMBLE_L (0x39) */ 0x06, /* REG_APLL_CTL (0x3A) */ 0x00, /* REG_DTMF_CTL (0x3B) */ - 0x00, /* REG_DTMF_PGA_CTL2 (0x3C) */ - 0x00, /* REG_DTMF_PGA_CTL1 (0x3D) */ + 0x44, /* REG_DTMF_PGA_CTL2 (0x3C) */ + 0x69, /* REG_DTMF_PGA_CTL1 (0x3D) */ 0x00, /* REG_MISC_SET_1 (0x3E) */ 0x00, /* REG_PCMBTMUX (0x3F) */ 0x00, /* not used (0x40) */ 0x00, /* not used (0x41) */ 0x00, /* not used (0x42) */ 0x00, /* REG_RX_PATH_SEL (0x43) */ - 0x00, /* REG_VDL_APGA_CTL (0x44) */ + 0x32, /* REG_VDL_APGA_CTL (0x44) */ 0x00, /* REG_VIBRA_CTL (0x45) */ 0x00, /* REG_VIBRA_SET (0x46) */ 0x00, /* REG_VIBRA_PWM_SET (0x47) */ -- cgit v1.2.3 From cbd2db128f2cbec1a70aa6897cc4cddbbadecbf6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 26 May 2010 11:38:15 +0300 Subject: ASoC: TWL4030: Remove wrapper for power down There is no need for the power down wrapper. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/twl4030.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 9a3e999b595c..1e0aba5b2c5d 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -319,15 +319,6 @@ 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 */ - twl4030_codec_enable(codec, 0); -} - /* Earpiece */ static const struct snd_kcontrol_new twl4030_dapm_earpiece_controls[] = { SOC_DAPM_SINGLE("Voice", TWL4030_REG_EAR_CTL, 0, 1, 0), @@ -1607,7 +1598,7 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec, twl4030_power_up(codec); break; case SND_SOC_BIAS_OFF: - twl4030_power_down(codec); + twl4030_codec_enable(codec, 0); break; } codec->bias_level = level; @@ -2321,7 +2312,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) return 0; error_codec: - twl4030_power_down(codec); + twl4030_codec_enable(codec, 0); kfree(codec->reg_cache); error_cache: kfree(twl4030); -- cgit v1.2.3 From 979bb1f4b8b058e9fb23d6166807e30b507a1a6d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 26 May 2010 11:38:16 +0300 Subject: ASoC: TWL4030: Make offset cancellation path configurable Add means for machine drivers to select the path for offset cancellation. Reset the reg cache value to the chip reset value at the same time. Machine drivers can specify which path need to be used for offset cancellation via the twl4030_setup.offset_cncl_path. For paths use the defines from include/linux/mfd/twl4030-codec.h: TWL4030_OFFSET_CNCL_SEL_* Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/twl4030.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 1e0aba5b2c5d..a6cbaf3c51f2 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -46,7 +46,7 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { 0xc3, /* REG_OPTION (0x2) */ 0x00, /* REG_UNKNOWN (0x3) */ 0x00, /* REG_MICBIAS_CTL (0x4) */ - 0x20, /* REG_ANAMICL (0x5) */ + 0x00, /* REG_ANAMICL (0x5) */ 0x00, /* REG_ANAMICR (0x6) */ 0x00, /* REG_AVADC_CTL (0x7) */ 0x00, /* REG_ADCMICSEL (0x8) */ @@ -281,6 +281,8 @@ static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) static void twl4030_power_up(struct snd_soc_codec *codec) { + struct snd_soc_device *socdev = codec->socdev; + struct twl4030_setup_data *setup = socdev->codec_data; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); u8 anamicl, regmisc1, byte; int i = 0; @@ -293,6 +295,8 @@ static void twl4030_power_up(struct snd_soc_codec *codec) /* initiate offset cancellation */ anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); + anamicl &= ~TWL4030_OFFSET_CNCL_SEL; + anamicl |= setup->offset_cncl_path; twl4030_write(codec, TWL4030_REG_ANAMICL, anamicl | TWL4030_CNCL_OFFSET_START); -- cgit v1.2.3 From ee4ccac7cea0e4a4f44bbb109285129e1b293461 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 26 May 2010 11:38:17 +0300 Subject: ASoC: TWL4030: Optimize the power up sequence Since the reg cache now contains the chip default values for all registers (REG_OPTION is reset to it's default within this patch), there is no longer need to rewrite _all_ registers. Initialize only few selected registers. According to the latest information, the offset cancellation need to be done only once, when the codec is powered on, so there is no need for the power up wrapper. Move all chip initialization code under chip_init, and do it when the codec is initialized. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/twl4030.c | 133 ++++++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 73 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index a6cbaf3c51f2..08f24de406c2 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -43,7 +43,7 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { 0x00, /* this register not used */ 0x00, /* REG_CODEC_MODE (0x1) */ - 0xc3, /* REG_OPTION (0x2) */ + 0x00, /* REG_OPTION (0x2) */ 0x00, /* REG_UNKNOWN (0x3) */ 0x00, /* REG_MICBIAS_CTL (0x4) */ 0x00, /* REG_ANAMICL (0x5) */ @@ -243,62 +243,52 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) udelay(10); } -static void twl4030_init_chip(struct snd_soc_codec *codec) -{ - u8 *cache = codec->reg_cache; - int i; - - /* clear CODECPDZ prior to setting register defaults */ - 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++) - if (i != TWL4030_REG_APLL_CTL) - twl4030_write(codec, i, cache[i]); - -} - -static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) +static void twl4030_init_chip(struct platform_device *pdev) { + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct twl4030_setup_data *setup = socdev->codec_data; + struct snd_soc_codec *codec = socdev->card->codec; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); - int status = -1; + u8 reg, byte; + int i = 0; - if (enable) { - twl4030->apll_enabled++; - if (twl4030->apll_enabled == 1) - status = twl4030_codec_enable_resource( - TWL4030_CODEC_RES_APLL); - } else { - twl4030->apll_enabled--; - if (!twl4030->apll_enabled) - status = twl4030_codec_disable_resource( - TWL4030_CODEC_RES_APLL); - } + /* Refresh APLL_CTL register from HW */ + twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, + TWL4030_REG_APLL_CTL); + twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, byte); - if (status >= 0) - twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); -} + /* anti-pop when changing analog gain */ + reg = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1); + twl4030_write(codec, TWL4030_REG_MISC_SET_1, + reg | TWL4030_SMOOTH_ANAVOL_EN); -static void twl4030_power_up(struct snd_soc_codec *codec) -{ - struct snd_soc_device *socdev = codec->socdev; - struct twl4030_setup_data *setup = socdev->codec_data; - struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); - u8 anamicl, regmisc1, byte; - int i = 0; + twl4030_write(codec, TWL4030_REG_OPTION, + TWL4030_ATXL1_EN | TWL4030_ATXR1_EN | + TWL4030_ARXL2_EN | TWL4030_ARXR2_EN); - if (twl4030->codec_powered) + /* Machine dependent setup */ + if (!setup) return; - /* set CODECPDZ to turn on codec */ - twl4030_codec_enable(codec, 1); + /* Configuration for headset ramp delay from setup data */ + if (setup->sysclk != twl4030->sysclk) + dev_warn(codec->dev, + "Mismatch in APLL mclk: %u (configured: %u)\n", + setup->sysclk, twl4030->sysclk); + + reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); + reg &= ~TWL4030_RAMP_DELAY; + reg |= (setup->ramp_delay_value << 2); + twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, reg); /* initiate offset cancellation */ - anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); - anamicl &= ~TWL4030_OFFSET_CNCL_SEL; - anamicl |= setup->offset_cncl_path; + twl4030_codec_enable(codec, 1); + + reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); + reg &= ~TWL4030_OFFSET_CNCL_SEL; + reg |= setup->offset_cncl_path; twl4030_write(codec, TWL4030_REG_ANAMICL, - anamicl | TWL4030_CNCL_OFFSET_START); + reg | TWL4030_CNCL_OFFSET_START); /* wait for offset cancellation to complete */ do { @@ -313,14 +303,28 @@ static void twl4030_power_up(struct snd_soc_codec *codec) /* 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_apll_enable(struct snd_soc_codec *codec, int enable) +{ + struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); + int status = -1; + + if (enable) { + twl4030->apll_enabled++; + if (twl4030->apll_enabled == 1) + status = twl4030_codec_enable_resource( + TWL4030_CODEC_RES_APLL); + } else { + twl4030->apll_enabled--; + if (!twl4030->apll_enabled) + status = twl4030_codec_disable_resource( + TWL4030_CODEC_RES_APLL); + } + + if (status >= 0) + twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status); } /* Earpiece */ @@ -1599,7 +1603,7 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: if (codec->bias_level == SND_SOC_BIAS_OFF) - twl4030_power_up(codec); + twl4030_codec_enable(codec, 1); break; case SND_SOC_BIAS_OFF: twl4030_codec_enable(codec, 0); @@ -2196,31 +2200,16 @@ static struct snd_soc_codec *twl4030_codec; static int twl4030_soc_probe(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct twl4030_setup_data *setup = socdev->codec_data; struct snd_soc_codec *codec; - struct twl4030_priv *twl4030; int ret; BUG_ON(!twl4030_codec); codec = twl4030_codec; - twl4030 = snd_soc_codec_get_drvdata(codec); socdev->card->codec = codec; - /* Configuration for headset ramp delay from setup data */ - if (setup) { - unsigned char hs_pop; - - if (setup->sysclk != twl4030->sysclk) - dev_warn(&pdev->dev, - "Mismatch in APLL mclk: %u (configured: %u)\n", - setup->sysclk, twl4030->sysclk); - - hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); - hs_pop &= ~TWL4030_RAMP_DELAY; - hs_pop |= (setup->ramp_delay_value << 2); - twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop); - } + twl4030_init_chip(pdev); + twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); @@ -2296,9 +2285,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) /* Set the defaults, and power up the codec */ twl4030->sysclk = twl4030_codec_get_mclk() / 1000; - twl4030_init_chip(codec); codec->bias_level = SND_SOC_BIAS_OFF; - twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); ret = snd_soc_register_codec(codec); if (ret != 0) { -- cgit v1.2.3 From 9fdcc0f72af8801d8429a465a159d815774dbf6d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 26 May 2010 11:38:18 +0300 Subject: ASoC: TWL4030: Helper to check chip default registers Since the twl4030 codec driver supports different version of the PM chip, a helper function can come handy, which can check the driver's default versus the chip values. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/twl4030.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 08f24de406c2..30b7bbaf6aed 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -243,6 +243,25 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) udelay(10); } +static inline void twl4030_check_defaults(struct snd_soc_codec *codec) +{ + int i, difference = 0; + u8 val; + + dev_dbg(codec->dev, "Checking TWL audio default configuration\n"); + for (i = 1; i <= TWL4030_REG_MISC_SET_2; i++) { + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, i); + if (val != twl4030_reg[i]) { + difference++; + dev_dbg(codec->dev, + "Reg 0x%02x: chip: 0x%02x driver: 0x%02x\n", + i, val, twl4030_reg[i]); + } + } + dev_dbg(codec->dev, "Found %d non maching registers. %s\n", + difference, difference ? "Not OK" : "OK"); +} + static void twl4030_init_chip(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); @@ -252,8 +271,12 @@ static void twl4030_init_chip(struct platform_device *pdev) u8 reg, byte; int i = 0; + /* Check defaults, if instructed before anything else */ + if (setup && setup->check_defaults) + twl4030_check_defaults(codec); + /* Refresh APLL_CTL register from HW */ - twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, + twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, TWL4030_REG_APLL_CTL); twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, byte); -- cgit v1.2.3 From 3c36cc688e7ad4ab595a0ac59697e4e1d06338c5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 26 May 2010 11:38:19 +0300 Subject: ASoC: TWL4030: Correct the ARXR2_APGA_CTL chip default It seams at least on twl5031 that the ARXR2_APGA_CTL register does not have the same default value as it is written in the TRM. Since the codec part of the PM chip has not been actually changed according to TI, assuming, that all version has the same problem, so writing there the TRM value. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- 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 30b7bbaf6aed..c667ca5a8a9e 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -289,6 +289,9 @@ static void twl4030_init_chip(struct platform_device *pdev) TWL4030_ATXL1_EN | TWL4030_ATXR1_EN | TWL4030_ARXL2_EN | TWL4030_ARXR2_EN); + /* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */ + twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32); + /* Machine dependent setup */ if (!setup) return; -- cgit v1.2.3 From 2046f175bc7b4d37e33dbce6a867be3bacf685cc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 26 May 2010 11:38:20 +0300 Subject: ASoC: TWL4030: Use BIAS_OFF instead of BIAS_STANDBY, when not in use Restructure the codec power code in order to be able to hit off when the codec is not in use. Since the audio registers are accessible while the codec is powered down, there is no need for additional safety mechanism. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/twl4030.c | 86 +++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 35 deletions(-) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index c667ca5a8a9e..45de2aad283c 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -1818,13 +1818,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - 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); - } - /* sample size */ old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF); format = old_format; @@ -1842,16 +1835,20 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - if (format != old_format) { - - /* clear CODECPDZ before changing format (codec requirement) */ - twl4030_codec_enable(codec, 0); - - /* change format */ - twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); - - /* set CODECPDZ afterwards */ - twl4030_codec_enable(codec, 1); + if (format != old_format || mode != old_mode) { + if (twl4030->codec_powered) { + /* + * If the codec is powered, than we need to toggle the + * codec power. + */ + twl4030_codec_enable(codec, 0); + twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); + twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); + twl4030_codec_enable(codec, 1); + } else { + twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); + twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); + } } /* Store the important parameters for the DAI configuration and set @@ -1901,6 +1898,7 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; + struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); u8 old_format, format; /* get format */ @@ -1935,15 +1933,17 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai, } if (format != old_format) { - - /* clear CODECPDZ before changing format (codec requirement) */ - twl4030_codec_enable(codec, 0); - - /* change format */ - twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); - - /* set CODECPDZ afterwards */ - twl4030_codec_enable(codec, 1); + if (twl4030->codec_powered) { + /* + * If the codec is powered, than we need to toggle the + * codec power. + */ + twl4030_codec_enable(codec, 0); + twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); + twl4030_codec_enable(codec, 1); + } else { + twl4030_write(codec, TWL4030_REG_AUDIO_IF, format); + } } return 0; @@ -2035,6 +2035,7 @@ static int twl4030_voice_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->card->codec; + struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); u8 old_mode, mode; /* Enable voice digital filters */ @@ -2059,10 +2060,17 @@ static int twl4030_voice_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); + if (twl4030->codec_powered) { + /* + * If the codec is powered, than we need to toggle the + * codec power. + */ + twl4030_codec_enable(codec, 0); + twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); + twl4030_codec_enable(codec, 1); + } else { + twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); + } } return 0; @@ -2092,6 +2100,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; + struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); u8 old_format, format; /* get format */ @@ -2123,10 +2132,17 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, } if (format != old_format) { - /* change format and set CODECPDZ */ - twl4030_codec_enable(codec, 0); - twl4030_write(codec, TWL4030_REG_VOICE_IF, format); - twl4030_codec_enable(codec, 1); + if (twl4030->codec_powered) { + /* + * If the codec is powered, than we need to toggle the + * codec power. + */ + twl4030_codec_enable(codec, 0); + twl4030_write(codec, TWL4030_REG_VOICE_IF, format); + twl4030_codec_enable(codec, 1); + } else { + twl4030_write(codec, TWL4030_REG_VOICE_IF, format); + } } return 0; @@ -2235,7 +2251,6 @@ static int twl4030_soc_probe(struct platform_device *pdev) socdev->card->codec = codec; twl4030_init_chip(pdev); - twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); @@ -2296,6 +2311,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) codec->read = twl4030_read_reg_cache; codec->write = twl4030_write; codec->set_bias_level = twl4030_set_bias_level; + codec->idle_bias_off = 1; codec->dai = twl4030_dai; codec->num_dai = ARRAY_SIZE(twl4030_dai); codec->reg_cache_size = sizeof(twl4030_reg); -- cgit v1.2.3 From a3a29b55c70cefaac0d6fda170ccc85bd10e78bf Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 26 May 2010 11:38:21 +0300 Subject: ASoC: TWL4030: Add functionalty to reset the registers Machine driver can instruct the codec driver to reset the chip registers to their default values at probe time. If machine driver does not provide setup data, then the registers are going to be reseted to their defaults, to be safe. If the developer on the platform confirms that the register reset is not needed, than it can be skipped, saving ~20ms time in probe. As safety measure do the register reset at remove time also. Signed-off-by: Peter Ujfalusi Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- sound/soc/codecs/twl4030.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'sound/soc/codecs/twl4030.c') diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 45de2aad283c..b292c2d8f2a3 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -262,6 +262,17 @@ static inline void twl4030_check_defaults(struct snd_soc_codec *codec) difference, difference ? "Not OK" : "OK"); } +static inline void twl4030_reset_registers(struct snd_soc_codec *codec) +{ + int i; + + /* set all audio section registers to reasonable defaults */ + for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++) + if (i != TWL4030_REG_APLL_CTL) + twl4030_write(codec, i, twl4030_reg[i]); + +} + static void twl4030_init_chip(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); @@ -275,6 +286,10 @@ static void twl4030_init_chip(struct platform_device *pdev) if (setup && setup->check_defaults) twl4030_check_defaults(codec); + /* Reset registers, if no setup data or if instructed to do so */ + if (!setup || (setup && setup->reset_registers)) + twl4030_reset_registers(codec); + /* Refresh APLL_CTL register from HW */ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte, TWL4030_REG_APLL_CTL); @@ -2271,6 +2286,8 @@ static int twl4030_soc_remove(struct platform_device *pdev) struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->card->codec; + /* Reset registers to their chip default before leaving */ + twl4030_reset_registers(codec); twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); -- cgit v1.2.3