[alsa-devel] [PATCH v3 2/8] ASoC: da7210: Add support for DAPM
This patch adds support for DAPM covering all inputs and outputs as well as ADC and DAC.
Signed-off-by: Ashish Chavan ashish.chavan@kpitcummins.com Signed-off-by: David Dajun Chen dchen@diasemi.com --- Changes since v1: - used supply widget for mic bias - removed mic bias hookup - assigned dapm widgets and routes in card struct - removed explicit setting of default mixer settings - removed explicit setting of ios to standby mode --- sound/soc/codecs/da7210.c | 210 +++++++++++++++++++++++++++++++++++++++----- 1 files changed, 186 insertions(+), 24 deletions(-)
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index 0809f32..b6e5085 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -28,6 +28,8 @@ /* DA7210 register space */ #define DA7210_STATUS 0x02 #define DA7210_STARTUP1 0x03 +#define DA7210_STARTUP2 0x04 +#define DA7210_STARTUP3 0x05 #define DA7210_MIC_L 0x07 #define DA7210_MIC_R 0x08 #define DA7210_INMIX_L 0x0D @@ -40,6 +42,7 @@ #define DA7210_DAC_SEL 0x17 #define DA7210_OUTMIX_L 0x1C #define DA7210_OUTMIX_R 0x1D +#define DA7210_OUT2 0x20 #define DA7210_HP_L_VOL 0x21 #define DA7210_HP_R_VOL 0x22 #define DA7210_HP_CFG 0x23 @@ -165,6 +168,185 @@ static const struct snd_kcontrol_new da7210_snd_controls[] = { 0, 0x3F, 0, hp_out_tlv), };
+/* + * DAPM Controls + */ +/* In Mixer Left */ +static const struct snd_kcontrol_new da7210_dapm_inmixl_controls[] = { + SOC_DAPM_SINGLE("Mic Left Switch", DA7210_INMIX_L, 0, 1, 0), + SOC_DAPM_SINGLE("Mic Right Switch", DA7210_INMIX_L, 1, 1, 0), + SOC_DAPM_SINGLE("Aux1 Left Switch", DA7210_INMIX_L, 2, 1, 0), + SOC_DAPM_SINGLE("Aux2 Switch", DA7210_INMIX_L, 3, 1, 0), + SOC_DAPM_SINGLE("Outmix Left Switch", DA7210_INMIX_L, 4, 1, 0), +}; + +/* In Mixer Right */ +static const struct snd_kcontrol_new da7210_dapm_inmixr_controls[] = { + SOC_DAPM_SINGLE("Mic Right Switch", DA7210_INMIX_R, 0, 1, 0), + SOC_DAPM_SINGLE("Mic Left Switch", DA7210_INMIX_R, 1, 1, 0), + SOC_DAPM_SINGLE("Aux1 Right Switch", DA7210_INMIX_R, 2, 1, 0), + SOC_DAPM_SINGLE("Aux2 Switch", DA7210_INMIX_R, 3, 1, 0), + SOC_DAPM_SINGLE("Outmix Right Switch", DA7210_INMIX_R, 4, 1, 0), +}; + +/* Out Mixer Left */ +static const struct snd_kcontrol_new da7210_dapm_outmixl_controls[] = { + SOC_DAPM_SINGLE("Aux1 Left Switch", DA7210_OUTMIX_L, 0, 1, 0), + SOC_DAPM_SINGLE("Aux2 Switch", DA7210_OUTMIX_L, 1, 1, 0), + SOC_DAPM_SINGLE("INPGA Left Switch", DA7210_OUTMIX_L, 2, 1, 0), + SOC_DAPM_SINGLE("INPGA Right Switch", DA7210_OUTMIX_L, 3, 1, 0), + SOC_DAPM_SINGLE("DAC Left Switch", DA7210_OUTMIX_L, 4, 1, 0), +}; + +/* Out Mixer Right */ +static const struct snd_kcontrol_new da7210_dapm_outmixr_controls[] = { + SOC_DAPM_SINGLE("Aux1 Right Switch", DA7210_OUTMIX_R, 0, 1, 0), + SOC_DAPM_SINGLE("Aux2 Switch", DA7210_OUTMIX_R, 1, 1, 0), + SOC_DAPM_SINGLE("INPGA Left Switch", DA7210_OUTMIX_R, 2, 1, 0), + SOC_DAPM_SINGLE("INPGA Right Switch", DA7210_OUTMIX_R, 3, 1, 0), + SOC_DAPM_SINGLE("DAC Right Switch", DA7210_OUTMIX_R, 4, 1, 0), +}; + +/* Mono Mixer */ +static const struct snd_kcontrol_new da7210_dapm_monomix_controls[] = { + SOC_DAPM_SINGLE("INPGA Right Switch", DA7210_OUT2, 3, 1, 0), + SOC_DAPM_SINGLE("INPGA Left Switch", DA7210_OUT2, 4, 1, 0), + SOC_DAPM_SINGLE("Outmix Right Switch", DA7210_OUT2, 5, 1, 0), + SOC_DAPM_SINGLE("Outmix Left Switch", DA7210_OUT2, 6, 1, 0), +}; + +/* DAPM widgets */ +static const struct snd_soc_dapm_widget da7210_dapm_widgets[] = { + /* Input Side */ + /* Input Lines */ + SND_SOC_DAPM_INPUT("MICL"), + SND_SOC_DAPM_INPUT("MICR"), + SND_SOC_DAPM_INPUT("AUX1L"), + SND_SOC_DAPM_INPUT("AUX1R"), + SND_SOC_DAPM_INPUT("AUX2"), + + /* Input PGAs */ + SND_SOC_DAPM_PGA("Mic Left", DA7210_STARTUP3, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("Mic Right", DA7210_STARTUP3, 1, 1, NULL, 0), + SND_SOC_DAPM_PGA("Aux1 Left", DA7210_STARTUP3, 2, 1, NULL, 0), + SND_SOC_DAPM_PGA("Aux1 Right", DA7210_STARTUP3, 3, 1, NULL, 0), + SND_SOC_DAPM_PGA("Aux2 Mono", DA7210_STARTUP3, 4, 1, NULL, 0), + + SND_SOC_DAPM_PGA("INPGA Left", DA7210_INMIX_L, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("INPGA Right", DA7210_INMIX_R, 7, 0, NULL, 0), + + /* MICBIAS */ + SND_SOC_DAPM_SUPPLY("Mic Bias", DA7210_MIC_L, 6, 0, NULL, 0), + + /* Input Mixers */ + SND_SOC_DAPM_MIXER("In Mixer Left", SND_SOC_NOPM, 0, 0, + &da7210_dapm_inmixl_controls[0], + ARRAY_SIZE(da7210_dapm_inmixl_controls)), + + SND_SOC_DAPM_MIXER("In Mixer Right", SND_SOC_NOPM, 0, 0, + &da7210_dapm_inmixr_controls[0], + ARRAY_SIZE(da7210_dapm_inmixr_controls)), + + /* ADCs */ + SND_SOC_DAPM_ADC("ADC Left", "Capture", DA7210_STARTUP3, 5, 1), + SND_SOC_DAPM_ADC("ADC Right", "Capture", DA7210_STARTUP3, 6, 1), + + /* Output Side */ + /* DACs */ + SND_SOC_DAPM_DAC("DAC Left", "Playback", DA7210_STARTUP2, 5, 1), + SND_SOC_DAPM_DAC("DAC Right", "Playback", DA7210_STARTUP2, 6, 1), + + /* Output Mixers */ + SND_SOC_DAPM_MIXER("Out Mixer Left", SND_SOC_NOPM, 0, 0, + &da7210_dapm_outmixl_controls[0], + ARRAY_SIZE(da7210_dapm_outmixl_controls)), + + SND_SOC_DAPM_MIXER("Out Mixer Right", SND_SOC_NOPM, 0, 0, + &da7210_dapm_outmixr_controls[0], + ARRAY_SIZE(da7210_dapm_outmixr_controls)), + + SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, + &da7210_dapm_monomix_controls[0], + ARRAY_SIZE(da7210_dapm_monomix_controls)), + + /* Output PGAs */ + SND_SOC_DAPM_PGA("OUTPGA Left Enable", DA7210_OUTMIX_L, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("OUTPGA Right Enable", DA7210_OUTMIX_R, 7, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Out1 Left", DA7210_STARTUP2, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("Out1 Right", DA7210_STARTUP2, 1, 1, NULL, 0), + SND_SOC_DAPM_PGA("Out2 Mono", DA7210_STARTUP2, 2, 1, NULL, 0), + SND_SOC_DAPM_PGA("Headphone Left", DA7210_STARTUP2, 3, 1, NULL, 0), + SND_SOC_DAPM_PGA("Headphone Right", DA7210_STARTUP2, 4, 1, NULL, 0), + + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("OUT1L"), + SND_SOC_DAPM_OUTPUT("OUT1R"), + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("OUT2"), +}; + +/* DAPM audio route definition */ +static const struct snd_soc_dapm_route da7210_audio_map[] = { + /* Dest Connecting Widget source */ + /* Input path */ + {"Mic Left", NULL, "MICL"}, + {"Mic Right", NULL, "MICR"}, + {"Aux1 Left", NULL, "AUX1L"}, + {"Aux1 Right", NULL, "AUX1R"}, + {"Aux2 Mono", NULL, "AUX2"}, + + {"In Mixer Left", "Mic Left Switch", "Mic Left"}, + {"In Mixer Left", "Aux1 Left Switch", "Aux1 Left"}, + {"In Mixer Left", "Aux2 Switch", "Aux2 Mono"}, + {"In Mixer Right", "Mic Right Switch", "Mic Right"}, + {"In Mixer Right", "Aux1 Right Switch", "Aux1 Right"}, + {"In Mixer Right", "Aux2 Switch", "Aux2 Mono"}, + + {"INPGA Left", NULL, "In Mixer Left"}, + {"ADC Left", NULL, "INPGA Left"}, + + {"INPGA Right", NULL, "In Mixer Right"}, + {"ADC Right", NULL, "INPGA Right"}, + + /* Output path */ + {"Out Mixer Left", "Aux1 Left Switch", "Aux1 Left"}, + {"Out Mixer Left", "Aux2 Switch", "Aux2 Mono"}, + {"Out Mixer Left", "INPGA Left Switch", "INPGA Left"}, + {"Out Mixer Left", "INPGA Right Switch", "INPGA Right"}, + {"Out Mixer Left", "DAC Left Switch", "DAC Left"}, + + {"Out Mixer Right", "Aux1 Right Switch", "Aux1 Right"}, + {"Out Mixer Right", "Aux2 Switch", "Aux2 Mono"}, + {"Out Mixer Right", "INPGA Right Switch", "INPGA Right"}, + {"Out Mixer Right", "INPGA Left Switch", "INPGA Left"}, + {"Out Mixer Right", "DAC Right Switch", "DAC Right"}, + + {"Mono Mixer", "INPGA Right Switch", "INPGA Right"}, + {"Mono Mixer", "INPGA Left Switch", "INPGA Left"}, + {"Mono Mixer", "Outmix Right Switch", "Out Mixer Right"}, + {"Mono Mixer", "Outmix Left Switch", "Out Mixer Left"}, + + {"OUTPGA Left Enable", NULL, "Out Mixer Left"}, + {"OUTPGA Right Enable", NULL, "Out Mixer Right"}, + + {"Out1 Left", NULL, "OUTPGA Left Enable"}, + {"OUT1L", NULL, "Out1 Left"}, + + {"Out1 Right", NULL, "OUTPGA Right Enable"}, + {"OUT1R", NULL, "Out1 Right"}, + + {"Headphone Left", NULL, "OUTPGA Left Enable"}, + {"HPL", NULL, "Headphone Left"}, + + {"Headphone Right", NULL, "OUTPGA Right Enable"}, + {"HPR", NULL, "Headphone Right"}, + + {"Out2 Mono", NULL, "Mono Mixer"}, + {"OUT2", NULL, "Out2 Mono"}, +}; + /* Codec private data */ struct da7210_priv { enum snd_soc_control_type control_type; @@ -204,29 +386,6 @@ static int da7210_volatile_register(struct snd_soc_codec *codec, return 0; } } -static int da7210_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - struct snd_soc_codec *codec = dai->codec; - - if (is_play) { - /* Enable Out */ - snd_soc_update_bits(codec, DA7210_OUTMIX_L, 0x1F, 0x10); - snd_soc_update_bits(codec, DA7210_OUTMIX_R, 0x1F, 0x10); - - } else { - /* Volume 7 */ - snd_soc_update_bits(codec, DA7210_MIC_L, 0x7, 0x7); - snd_soc_update_bits(codec, DA7210_MIC_R, 0x7, 0x7); - - /* Enable Mic */ - snd_soc_update_bits(codec, DA7210_INMIX_L, 0x1F, 0x1); - snd_soc_update_bits(codec, DA7210_INMIX_R, 0x1F, 0x1); - } - - return 0; -}
/* * Set PCM DAI word length. @@ -409,7 +568,6 @@ static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
/* DAI operations */ static struct snd_soc_dai_ops da7210_dai_ops = { - .startup = da7210_startup, .hw_params = da7210_hw_params, .set_fmt = da7210_set_dai_fmt, }; @@ -544,6 +702,10 @@ static struct snd_soc_codec_driver soc_codec_dev_da7210 = { .reg_word_size = sizeof(u8), .reg_cache_default = da7210_reg, .volatile_register = da7210_volatile_register, + .dapm_widgets = da7210_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(da7210_dapm_widgets), + .dapm_routes = da7210_audio_map, + .num_dapm_routes = ARRAY_SIZE(da7210_audio_map), };
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
On Fri, Oct 14, 2011 at 04:31:38PM +0530, Ashish Chavan wrote:
- /* ADCs */
- SND_SOC_DAPM_ADC("ADC Left", "Capture", DA7210_STARTUP3, 5, 1),
- SND_SOC_DAPM_ADC("ADC Right", "Capture", DA7210_STARTUP3, 6, 1),
- /* Output Side */
- /* DACs */
- SND_SOC_DAPM_DAC("DAC Left", "Playback", DA7210_STARTUP2, 5, 1),
- SND_SOC_DAPM_DAC("DAC Right", "Playback", DA7210_STARTUP2, 6, 1),
These are also being enabled in the probe() function using completely different registers - there the registers used are _ADC and _DAC. What's going on there? It's good to see the stuff in the startup() function gone but almost all of the probe() just shouldn't be there in a properly written driver and much of it looks like stuff that DAPM should be taking care of.
On Fri, 2011-10-14 at 20:20 +0100, Mark Brown wrote:
On Fri, Oct 14, 2011 at 04:31:38PM +0530, Ashish Chavan wrote:
- /* ADCs */
- SND_SOC_DAPM_ADC("ADC Left", "Capture", DA7210_STARTUP3, 5, 1),
- SND_SOC_DAPM_ADC("ADC Right", "Capture", DA7210_STARTUP3, 6, 1),
- /* Output Side */
- /* DACs */
- SND_SOC_DAPM_DAC("DAC Left", "Playback", DA7210_STARTUP2, 5, 1),
- SND_SOC_DAPM_DAC("DAC Right", "Playback", DA7210_STARTUP2, 6, 1),
These are also being enabled in the probe() function using completely different registers - there the registers used are _ADC and _DAC. What's going on there? It's good to see the stuff in the startup() function gone but almost all of the probe() just shouldn't be there in a properly written driver and much of it looks like stuff that DAPM should be taking care of.
There are two type of registers for all IOs (HP, MIC, AUX) as well as DAC and ADC. One type of registers control individual enable and disable of IO, while other set of register controls the standby mode of that particular IO. e.g. For ADC,
- Enable/disable of ADC left and right channel is controlled by bit 3 and 7 of DA7210_ADC register.
- Standby mode of ADC left and right channel is controlled by bit 5 and 6 of DA7210_STARTUP3 register.
As per hardware designer's recommendation, standby mode is preferred for minimum power consumption. So, current DAPM patch only takes care of STANDBY bits.
On Sat, Oct 15, 2011 at 01:50:03PM +0530, Ashish Chavan wrote:
There are two type of registers for all IOs (HP, MIC, AUX) as well as DAC and ADC. One type of registers control individual enable and disable of IO, while other set of register controls the standby mode of that particular IO. e.g. For ADC,
What are "enable and disable" in this context? What do they do if not put the part of the chip into a low power mode? This is an intriguing hardware design and it really needs some documentation in the driver, right now it's just far too obscure and the driver looks buggy on code inspection.
On Mon, 2011-10-17 at 22:41 +0100, Mark Brown wrote:
On Sat, Oct 15, 2011 at 01:50:03PM +0530, Ashish Chavan wrote:
There are two type of registers for all IOs (HP, MIC, AUX) as well as DAC and ADC. One type of registers control individual enable and disable of IO, while other set of register controls the standby mode of that particular IO. e.g. For ADC,
What are "enable and disable" in this context? What do they do if not put the part of the chip into a low power mode? This is an intriguing hardware design and it really needs some documentation in the driver, right now it's just far too obscure and the driver looks buggy on code inspection.
I have queried hardware designers to get more insight in to this and will update the response here. Probably after that we will be confident about driver's handling of DAPM.
BTW if in case, hardware designers reiterate to use STANDBY registers, will it suffice to only document this somewhere is the source?
On Wed, Oct 19, 2011 at 12:51:35PM +0530, Ashish Chavan wrote:
BTW if in case, hardware designers reiterate to use STANDBY registers, will it suffice to only document this somewhere is the source?
The main thing is to make sure that the code is understandable and doesn't look buggy to visual inspection so yes, if there's adequate documentation that would be OK.
On Wed, 2011-10-19 at 13:13 +0100, Mark Brown wrote:
On Wed, Oct 19, 2011 at 12:51:35PM +0530, Ashish Chavan wrote:
BTW if in case, hardware designers reiterate to use STANDBY registers, will it suffice to only document this somewhere is the source?
The main thing is to make sure that the code is understandable and doesn't look buggy to visual inspection so yes, if there's adequate documentation that would be OK.
I see.
BTW today I received response from hardware designers and as expected, they are recommending usage of STANDBY registers instead of ENABLE/DISABLE registers. The main reason for this is that STANDBY registers are part of system controller which allows system power up/down in a controlled, pop free manner. Also, as per application note, STANDBY register bits are only effective if a particular IO (or ADC/DAC) is already enabled using enable/disable register bits. This is inline with my previous patch to support DAPM, i.e.
- All IOs and ADC/DAC are enabled using enable/disable register bits in probe() - STANDBY mode of all IOs and ADC/DAC is controlled by DAPM
I am thinking of re-posting the patch after adding above info somewhere.
Is there anything else that I need to take care of?
On Thu, Oct 20, 2011 at 05:17:45PM +0530, Ashish Chavan wrote:
Is there anything else that I need to take care of?
Not that I remember but often major issues obscure others so more may become apparent on more detailed review.
On Thu, 2011-10-20 at 13:36 +0100, Mark Brown wrote:
On Thu, Oct 20, 2011 at 05:17:45PM +0530, Ashish Chavan wrote:
Is there anything else that I need to take care of?
Not that I remember but often major issues obscure others so more may become apparent on more detailed review.
OK, thanks. Let me post the patch to allow you and others to review it in detail.
participants (2)
-
Ashish Chavan
-
Mark Brown