[PATCH 4/5] ASoC: add 88pm860x codec driver

Haojian Zhuang haojian.zhuang at gmail.com
Fri Aug 13 10:53:52 EDT 2010


On Fri, Aug 13, 2010 at 10:23 PM, Mark Brown
<broonie at opensource.wolfsonmicro.com> wrote:
> On Fri, Aug 13, 2010 at 09:55:44PM +0800, Haojian Zhuang wrote:
>
>> +static int snd_soc_put_equalizer(struct snd_kcontrol *kcontrol,
>> +                              struct snd_ctl_elem_value *ucontrol)
>> +{
>> +     struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
>> +     struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
>> +
>> +     if (ucontrol->value.integer.value[0] >= FILTER_MAX)
>> +             return -EINVAL;
>> +     pm860x->filter = ucontrol->value.integer.value[0];
>> +     switch (pm860x->filter) {
>> +     case FILTER_BYPASS:
>> +             snd_soc_update_bits(codec, PM860X_I2S_IFACE_4, I2S_EQU_BYP,
>> +                                 I2S_EQU_BYP);
>> +             snd_soc_write(codec, PM860X_EQUALIZER_N0_1, 0);
>> +             snd_soc_write(codec, PM860X_EQUALIZER_N0_2, 0);
>> +             snd_soc_write(codec, PM860X_EQUALIZER_N1_1, 0);
>> +             snd_soc_write(codec, PM860X_EQUALIZER_N1_2, 0);
>> +             snd_soc_write(codec, PM860X_EQUALIZER_D1_1, 0);
>> +             snd_soc_write(codec, PM860X_EQUALIZER_D1_2, 0);
>> +             snd_soc_update_bits(codec, PM860X_EAR_CTRL_2, RSYNC_CHANGE,
>> +                                 RSYNC_CHANGE);
>> +             return 0;
>
> Rather than hard coding every possible set of EQ configurations into the
> driver it would be much better to allow the user to supply these via
> platform data.  That way if users want to supply their own EQ
> coefficients they will be able to.  Several Wolfson CODEC drivers have
> examples of doing this.
>
> It's fine to provide a default set of configurations in case the user
> doesn't specify anything in platform data.
>
OK.

>> +     case FILTER_HIGH_PASS_3:
>> +             snd_soc_write(codec, PM860X_EQUALIZER_N0_1, 0x55);
>> +             snd_soc_write(codec, PM860X_EQUALIZER_N0_2, 0x39);
>> +             snd_soc_write(codec, PM860X_EQUALIZER_N1_1, 0x5a);
>> +             snd_soc_write(codec, PM860X_EQUALIZER_N1_2, 0xf3);
>> +             snd_soc_write(codec, PM860X_EQUALIZER_D1_1, 0x7e);
>> +             snd_soc_write(codec, PM860X_EQUALIZER_D1_2, 0x53);
>> +             break;
>> +     }
>
> You're also missing a default.
>
Bypass is default.  After audio part is reset, EQ is in bypass state.

>> +/* DAPM Widget Events */
>> +static int pm860x_rsync_event(struct snd_soc_dapm_widget *w,
>> +                           struct snd_kcontrol *kcontrol, int event)
>> +{
>> +     struct snd_soc_codec *codec = w->codec;
>> +     struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
>> +
>> +     /* unmute DAC */
>> +     if (pm860x->automute) {
>> +             snd_soc_update_bits(codec, PM860X_DAC_OFFSET, DAC_MUTE, 0);
>> +             pm860x->automute = 0;
>> +     }
>> +     snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
>> +                         RSYNC_CHANGE, RSYNC_CHANGE);
>> +     return 0;
>> +}
>
> What's this doing?  There's also some similar code in the DAC widget
> event - I think some comments explaining what's being done here are
> required at the very least.
>
A lot of registers belong to RSYNC domain. It means that those
changing won't be valid until RSYNC bit is set. So I'll set RSYNC
again in a lot of areas.

>> +static int pm860x_adc_event(struct snd_soc_dapm_widget *w,
>> +                         struct snd_kcontrol *kcontrol, int event)
>> +{
>> +     struct snd_soc_codec *codec = w->codec;
>> +     unsigned int en1 = 0, en2 = 0;
>> +
>> +     if (!strcmp(w->name, "Left ADC")) {
>> +             en1 = ADC_MOD_LEFT;
>> +             en2 = ADC_LEFT;
>> +     }
>> +     if (!strcmp(w->name, "Right ADC")) {
>> +             en1 = ADC_MOD_RIGHT;
>> +             en2 = ADC_RIGHT;
>> +     }
>> +     switch (event) {
>> +     case SND_SOC_DAPM_PRE_PMU:
>> +             snd_soc_update_bits(codec, PM860X_ADC_EN_1, en1, en1);
>> +             snd_soc_update_bits(codec, PM860X_ADC_EN_2, en2, en2);
>
> Can you do this more simply by using a supply widget for the en1
> register bits?
>
When I enable this, I need to touch two different registers at the
same time. So I have to implement the adc_event().

>> +static int pm860x_spk_event(struct snd_soc_dapm_widget *w,
>> +                         struct snd_kcontrol *kcontrol, int event)
>> +{
>> +     struct snd_soc_codec *codec = w->codec;
>> +
>> +     dev_dbg(codec->dev, "event:%x\n", event);
>> +     return 0;
>> +}
>
> Remove this, it's purely for debug.
>
OK.

>> +static const struct soc_enum pm860x_enum[] = {
>> +     SOC_ENUM_SINGLE(PM860X_HS1_CTRL, 5, 4, pm860x_opamp_texts),
>
> Don't put your enums in an array, it is error prone and hard to read.
> Just define variables for each enum.
>
OK.


>> +/* Headset 1 Mux / Mux15 */
>> +static const char *in_text[] = {
>> +     "DIGITAL", "ANALOG",
>
> Why ALL CAPS?
>
Sure. I'll change to lowcases.

>> +     data = snd_soc_read(codec, PM860X_PCM_IFACE_2) & ~mask;
>> +     data |= inf;
>> +     snd_soc_write(codec, PM860X_PCM_IFACE_2, data);
>
> snd_soc_update_bits().
>
>> +static int pm860x_pcm_hw_free(struct snd_pcm_substream *substream,
>> +                           struct snd_soc_dai *dai)
>> +{
>> +     struct snd_soc_codec *codec = dai->codec;
>> +
>> +     /* disable PCM interface */
>> +     snd_soc_update_bits(codec, PM860X_ADC_EN_2, 1, 0);
>> +     snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
>> +                         RSYNC_CHANGE, RSYNC_CHANGE);
>> +     return 0;
>> +}
>
> This looks like it should be done by DAPM.
>
But I don't want to export this to amixer.

>> +static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream,
>> +                             struct snd_pcm_hw_params *params,
>> +                             struct snd_soc_dai *dai)
>> +{
>> +     struct snd_soc_codec *codec = dai->codec;
>> +     unsigned char inf;
>> +     int data;
>> +
>> +     /*
>> +      * Enable LDO15 for VDD_CODEC, audio charger pump for VDDSTP/VDDSTN
>> +      * and reset audio module.
>> +      */
>> +     data = LDO15_EN | CPUMP_EN | AUDIO_EN;
>> +     snd_soc_update_bits(codec, PM860X_AUDIO_SUPPLIES_2, data, data);
>
> These look like they should all be handled via DAPM, probably supply
> widgets.
>
>> +static int pm860x_i2s_hw_free(struct snd_pcm_substream *substream,
>> +                           struct snd_soc_dai *dai)
>> +{
>
> This also looks like DAPM stuff.
>
>> +                     /* Enable Mic1 Bias & MICDET, HSDET */
>> +                     pm860x_set_bits(codec->control_data, REG_ADC_ANA_1,
>> +                                     MIC1BIAS_MASK, MIC1BIAS_MASK);
>> +                     pm860x_set_bits(codec->control_data, REG_MIC_DET,
>> +                                     MICDET_MASK, MICDET_MASK);
>> +                     pm860x_set_bits(codec->control_data, REG_HS_DET,
>> +                                     EN_HS_DET, EN_HS_DET);
>
> The microphone bias should be handled via DAPM.
>
This one is different. Without this, mic/headset detection can't be
finished. This bit have to be set in initialization.



More information about the linux-arm-kernel mailing list