]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
Merge branch 'fix/sun4i-codec' of git://git.kernel.org/pub/scm/linux/kernel/git/broon...
authorMark Brown <broonie@kernel.org>
Wed, 2 Dec 2015 20:22:31 +0000 (20:22 +0000)
committerMark Brown <broonie@kernel.org>
Wed, 2 Dec 2015 20:22:31 +0000 (20:22 +0000)
1  2 
sound/soc/sunxi/sun4i-codec.c

index 0dc11f54793705f64453d381199f74f9239685df,1bb896d78d09817eff6fc38af7e2d1d1b3622ef3..7a3fe1dca17850ed4d5f6b00998e1e8a0ab1dad0
@@@ -2,6 -2,7 +2,7 @@@
   * Copyright 2014 Emilio López <emilio@elopez.com.ar>
   * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
   * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
+  * Copyright 2015 Adam Sampson <ats@offog.org>
   *
   * Based on the Allwinner SDK driver, released under the GPL.
   *
@@@ -69,7 -70,6 +70,7 @@@
  
  /* Codec ADC register offsets and bit fields */
  #define SUN4I_CODEC_ADC_FIFOC                 (0x1c)
 +#define SUN4I_CODEC_ADC_FIFOC_ADC_FS                  (29)
  #define SUN4I_CODEC_ADC_FIFOC_EN_AD                   (28)
  #define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE            (24)
  #define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL           (8)
@@@ -103,12 -103,16 +104,12 @@@ struct sun4i_codec 
        struct clk      *clk_apb;
        struct clk      *clk_module;
  
 +      struct snd_dmaengine_dai_dma_data       capture_dma_data;
        struct snd_dmaengine_dai_dma_data       playback_dma_data;
  };
  
  static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
  {
 -      /*
 -       * FIXME: according to the BSP, we might need to drive a PA
 -       *        GPIO high here on some boards
 -       */
 -
        /* Flush TX FIFO */
        regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
                           BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
  
  static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
  {
 -      /*
 -       * FIXME: according to the BSP, we might need to drive a PA
 -       *        GPIO low here on some boards
 -       */
 -
        /* Disable DAC DRQ */
        regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
                           BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
                           0);
  }
  
 +static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
 +{
 +      /* Enable ADC DRQ */
 +      regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
 +                         BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN),
 +                         BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
 +}
 +
 +static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
 +{
 +      /* Disable ADC DRQ */
 +      regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
 +                         BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0);
 +}
 +
  static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
                               struct snd_soc_dai *dai)
  {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
  
 -      if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
 -              return -ENOTSUPP;
 -
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 -              sun4i_codec_start_playback(scodec);
 +              if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 +                      sun4i_codec_start_playback(scodec);
 +              else
 +                      sun4i_codec_start_capture(scodec);
                break;
  
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 -              sun4i_codec_stop_playback(scodec);
 +              if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 +                      sun4i_codec_stop_playback(scodec);
 +              else
 +                      sun4i_codec_stop_capture(scodec);
                break;
  
        default:
        return 0;
  }
  
 -static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
 -                             struct snd_soc_dai *dai)
 +static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
 +                                     struct snd_soc_dai *dai)
  {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
 -      u32 val;
  
 -      if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
 -              return -ENOTSUPP;
 +
 +      /* Flush RX FIFO */
 +      regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
 +                         BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH),
 +                         BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));
 +
 +
 +      /* Set RX FIFO trigger level */
 +      regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
 +                         0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
 +                         0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
 +
 +      /*
 +       * FIXME: Undocumented in the datasheet, but
 +       *        Allwinner's code mentions that it is related
 +       *        related to microphone gain
 +       */
 +      regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
 +                         0x3 << 25,
 +                         0x1 << 25);
 +
 +      if (of_device_is_compatible(scodec->dev->of_node,
 +                                  "allwinner,sun7i-a20-codec"))
 +              /* FIXME: Undocumented bits */
 +              regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_TUNE,
 +                                 0x3 << 8,
 +                                 0x1 << 8);
 +
 +      /* Fill most significant bits with valid data MSB */
 +      regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
 +                         BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
 +                         BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
 +
 +      return 0;
 +}
 +
 +static int sun4i_codec_prepare_playback(struct snd_pcm_substream *substream,
 +                                      struct snd_soc_dai *dai)
 +{
 +      struct snd_soc_pcm_runtime *rtd = substream->private_data;
 +      struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
 +      u32 val;
  
        /* Flush the TX FIFO */
        regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
                           0);
  
        return 0;
 +};
 +
 +static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
 +                             struct snd_soc_dai *dai)
 +{
 +      if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 +              return sun4i_codec_prepare_playback(substream, dai);
 +
 +      return sun4i_codec_prepare_capture(substream, dai);
  }
  
  static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
@@@ -334,32 -277,30 +335,32 @@@ static int sun4i_codec_get_hw_rate(stru
        }
  }
  
 -static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
 -                               struct snd_pcm_hw_params *params,
 -                               struct snd_soc_dai *dai)
 +static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
 +                                       struct snd_pcm_hw_params *params,
 +                                       unsigned int hwrate)
  {
 -      struct snd_soc_pcm_runtime *rtd = substream->private_data;
 -      struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
 -      unsigned long clk_freq;
 -      int ret, hwrate;
 -      u32 val;
 +      /* Set ADC sample rate */
 +      regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
 +                         7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
 +                         hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
  
 -      if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
 -              return -ENOTSUPP;
 -
 -      clk_freq = sun4i_codec_get_mod_freq(params);
 -      if (!clk_freq)
 -              return -EINVAL;
 +      /* Set the number of channels we want to use */
 +      if (params_channels(params) == 1)
 +              regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
 +                                 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
 +                                 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
 +      else
 +              regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
 +                                 BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0);
  
 -      ret = clk_set_rate(scodec->clk_module, clk_freq);
 -      if (ret)
 -              return ret;
 +      return 0;
 +}
  
 -      hwrate = sun4i_codec_get_hw_rate(params);
 -      if (hwrate < 0)
 -              return hwrate;
 +static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
 +                                        struct snd_pcm_hw_params *params,
 +                                        unsigned int hwrate)
 +{
 +      u32 val;
  
        /* Set DAC sample rate */
        regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
        return 0;
  }
  
 +static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
 +                               struct snd_pcm_hw_params *params,
 +                               struct snd_soc_dai *dai)
 +{
 +      struct snd_soc_pcm_runtime *rtd = substream->private_data;
 +      struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
 +      unsigned long clk_freq;
 +      int ret, hwrate;
 +
 +      clk_freq = sun4i_codec_get_mod_freq(params);
 +      if (!clk_freq)
 +              return -EINVAL;
 +
 +      ret = clk_set_rate(scodec->clk_module, clk_freq);
 +      if (ret)
 +              return ret;
 +
 +      hwrate = sun4i_codec_get_hw_rate(params);
 +      if (hwrate < 0)
 +              return hwrate;
 +
 +      if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 +              return sun4i_codec_hw_params_playback(scodec, params,
 +                                                    hwrate);
 +
 +      return sun4i_codec_hw_params_capture(scodec, params,
 +                                           hwrate);
 +}
 +
  static int sun4i_codec_startup(struct snd_pcm_substream *substream,
                               struct snd_soc_dai *dai)
  {
@@@ -483,20 -395,6 +484,20 @@@ static struct snd_soc_dai_driver sun4i_
                                  SNDRV_PCM_FMTBIT_S32_LE,
                .sig_bits       = 24,
        },
 +      .capture = {
 +              .stream_name    = "Codec Capture",
 +              .channels_min   = 1,
 +              .channels_max   = 2,
 +              .rate_min       = 8000,
 +              .rate_max       = 192000,
 +              .rates          = SNDRV_PCM_RATE_8000_48000 |
 +                                SNDRV_PCM_RATE_96000 |
 +                                SNDRV_PCM_RATE_192000 |
 +                                SNDRV_PCM_RATE_KNOT,
 +              .formats        = SNDRV_PCM_FMTBIT_S16_LE |
 +                                SNDRV_PCM_FMTBIT_S32_LE,
 +              .sig_bits       = 24,
 +      },
  };
  
  /*** Codec ***/
@@@ -507,7 -405,7 +508,7 @@@ static const struct snd_kcontrol_new su
  static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);
  
  static const struct snd_kcontrol_new sun4i_codec_widgets[] = {
-       SOC_SINGLE_TLV("PA Volume", SUN4I_CODEC_DAC_ACTL,
+       SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,
                       SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
                       sun4i_codec_pa_volume_scale),
  };
@@@ -532,22 -430,11 +533,22 @@@ static const struct snd_kcontrol_new su
  };
  
  static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
 +      /* Digital parts of the ADCs */
 +      SND_SOC_DAPM_SUPPLY("ADC", SUN4I_CODEC_ADC_FIFOC,
 +                          SUN4I_CODEC_ADC_FIFOC_EN_AD, 0,
 +                          NULL, 0),
 +
        /* Digital parts of the DACs */
        SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
                            SUN4I_CODEC_DAC_DPC_EN_DA, 0,
                            NULL, 0),
  
 +      /* Analog parts of the ADCs */
 +      SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
 +                       SUN4I_CODEC_ADC_ACTL_ADC_L_EN, 0),
 +      SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN4I_CODEC_ADC_ACTL,
 +                       SUN4I_CODEC_ADC_ACTL_ADC_R_EN, 0),
 +
        /* Analog parts of the DACs */
        SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
                         SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
        SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
                            SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),
  
-       /* Pre-Amplifier */
-       SND_SOC_DAPM_MIXER("Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
 +      /* VMIC */
 +      SND_SOC_DAPM_SUPPLY("VMIC", SUN4I_CODEC_ADC_ACTL,
 +                          SUN4I_CODEC_ADC_ACTL_VMICEN, 0, NULL, 0),
 +
 +      /* Mic Pre-Amplifiers */
 +      SND_SOC_DAPM_PGA("MIC1 Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
 +                       SUN4I_CODEC_ADC_ACTL_PREG1EN, 0, NULL, 0),
 +
+       /* Power Amplifier */
+       SND_SOC_DAPM_MIXER("Power Amplifier", SUN4I_CODEC_ADC_ACTL,
                           SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
                           sun4i_codec_pa_mixer_controls,
                           ARRAY_SIZE(sun4i_codec_pa_mixer_controls)),
-       SND_SOC_DAPM_SWITCH("Pre-Amplifier Mute", SND_SOC_NOPM, 0, 0,
+       SND_SOC_DAPM_SWITCH("Power Amplifier Mute", SND_SOC_NOPM, 0, 0,
                            &sun4i_codec_pa_mute),
  
 +      SND_SOC_DAPM_INPUT("Mic1"),
 +
        SND_SOC_DAPM_OUTPUT("HP Right"),
        SND_SOC_DAPM_OUTPUT("HP Left"),
  };
  
  static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = {
 -      /* Left DAC Routes */
 +      /* Left ADC / DAC Routes */
 +      { "Left ADC", NULL, "ADC" },
        { "Left DAC", NULL, "DAC" },
  
 -      /* Right DAC Routes */
 +      /* Right ADC / DAC Routes */
 +      { "Right ADC", NULL, "ADC" },
        { "Right DAC", NULL, "DAC" },
  
        /* Right Mixer Routes */
        { "Left Mixer", NULL, "Mixer Enable" },
        { "Left Mixer", "Left DAC Playback Switch", "Left DAC" },
  
-       /* Pre-Amplifier Mixer Routes */
-       { "Pre-Amplifier", "Mixer Playback Switch", "Left Mixer" },
-       { "Pre-Amplifier", "Mixer Playback Switch", "Right Mixer" },
-       { "Pre-Amplifier", "DAC Playback Switch", "Left DAC" },
-       { "Pre-Amplifier", "DAC Playback Switch", "Right DAC" },
+       /* Power Amplifier Routes */
+       { "Power Amplifier", "Mixer Playback Switch", "Left Mixer" },
+       { "Power Amplifier", "Mixer Playback Switch", "Right Mixer" },
+       { "Power Amplifier", "DAC Playback Switch", "Left DAC" },
+       { "Power Amplifier", "DAC Playback Switch", "Right DAC" },
  
-       /* PA -> HP path */
-       { "Pre-Amplifier Mute", "Switch", "Pre-Amplifier" },
-       { "HP Right", NULL, "Pre-Amplifier Mute" },
-       { "HP Left", NULL, "Pre-Amplifier Mute" },
+       /* Headphone Output Routes */
+       { "Power Amplifier Mute", "Switch", "Power Amplifier" },
+       { "HP Right", NULL, "Power Amplifier Mute" },
+       { "HP Left", NULL, "Power Amplifier Mute" },
 +
 +      /* Mic1 Routes */
 +      { "Left ADC", NULL, "MIC1 Pre-Amplifier" },
 +      { "Right ADC", NULL, "MIC1 Pre-Amplifier" },
 +      { "MIC1 Pre-Amplifier", NULL, "Mic1"},
 +      { "Mic1", NULL, "VMIC" },
  };
  
  static struct snd_soc_codec_driver sun4i_codec_codec = {
@@@ -647,7 -516,7 +648,7 @@@ static int sun4i_codec_dai_probe(struc
        struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
  
        snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
 -                                NULL);
 +                                &scodec->capture_dma_data);
  
        return 0;
  }
@@@ -663,14 -532,6 +664,14 @@@ static struct snd_soc_dai_driver dummy_
                .formats        = SUN4I_CODEC_FORMATS,
                .sig_bits       = 24,
        },
 +      .capture = {
 +              .stream_name    = "Capture",
 +              .channels_min   = 1,
 +              .channels_max   = 2,
 +              .rates          = SUN4I_CODEC_RATES,
 +              .formats        = SUN4I_CODEC_FORMATS,
 +              .sig_bits       = 24,
 +       },
  };
  
  static const struct regmap_config sun4i_codec_regmap_config = {
@@@ -778,11 -639,6 +779,11 @@@ static int sun4i_codec_probe(struct pla
        scodec->playback_dma_data.maxburst = 4;
        scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
  
 +      /* DMA configuration for RX FIFO */
 +      scodec->capture_dma_data.addr = res->start + SUN4I_CODEC_ADC_RXDATA;
 +      scodec->capture_dma_data.maxburst = 4;
 +      scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
 +
        ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec,
                                     &sun4i_codec_dai, 1);
        if (ret) {