Merge branch 'topic/arizona' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorMark Brown <broonie@kernel.org>
Wed, 4 May 2016 16:51:26 +0000 (17:51 +0100)
committerMark Brown <broonie@kernel.org>
Wed, 4 May 2016 16:51:26 +0000 (17:51 +0100)
1  2 
sound/soc/codecs/cs47l24.c
sound/soc/codecs/wm_adsp.c

@@@ -807,9 -807,6 +807,9 @@@ static const struct snd_soc_dapm_route 
        { "IN2L PGA", NULL, "IN2L" },
        { "IN2R PGA", NULL, "IN2R" },
  
 +      { "Audio Trace DSP", NULL, "DSP2" },
 +      { "Audio Trace DSP", NULL, "SYSCLK" },
 +
        ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
        ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
  
@@@ -1019,27 -1016,6 +1019,27 @@@ static struct snd_soc_dai_driver cs47l2
                        .formats = CS47L24_FORMATS,
                },
        },
 +      {
 +              .name = "cs47l24-cpu-trace",
 +              .capture = {
 +                      .stream_name = "Audio Trace CPU",
 +                      .channels_min = 1,
 +                      .channels_max = 6,
 +                      .rates = CS47L24_RATES,
 +                      .formats = CS47L24_FORMATS,
 +              },
 +              .compress_new = snd_soc_new_compress,
 +      },
 +      {
 +              .name = "cs47l24-dsp-trace",
 +              .capture = {
 +                      .stream_name = "Audio Trace DSP",
 +                      .channels_min = 1,
 +                      .channels_max = 6,
 +                      .rates = CS47L24_RATES,
 +                      .formats = CS47L24_FORMATS,
 +              },
 +      },
  };
  
  static int cs47l24_open(struct snd_compr_stream *stream)
  
        if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) {
                n_adsp = 2;
 +      } else if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-trace") == 0) {
 +              n_adsp = 1;
        } else {
                dev_err(arizona->dev,
                        "No suitable compressed stream for DAI '%s'\n",
@@@ -1067,16 -1041,10 +1067,16 @@@ static irqreturn_t cs47l24_adsp2_irq(in
  {
        struct cs47l24_priv *priv = data;
        struct arizona *arizona = priv->core.arizona;
 -      int ret;
 +      int serviced = 0;
 +      int i, ret;
 +
 +      for (i = 1; i <= 2; ++i) {
 +              ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
 +              if (ret != -ENODEV)
 +                      serviced++;
 +      }
  
 -      ret = wm_adsp_compr_handle_irq(&priv->core.adsp[2]);
 -      if (ret == -ENODEV) {
 +      if (!serviced) {
                dev_err(arizona->dev, "Spurious compressed data IRQ\n");
                return IRQ_NONE;
        }
@@@ -1189,7 -1157,6 +1189,7 @@@ static struct snd_compr_ops cs47l24_com
  static struct snd_soc_platform_driver cs47l24_compr_platform = {
        .compr_ops = &cs47l24_compr_ops,
  };
 +
  static int cs47l24_probe(struct platform_device *pdev)
  {
        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
                dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
                return ret;
        }
 +
        ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
                                      cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
 -
        if (ret < 0) {
                dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
                snd_soc_unregister_platform(&pdev->dev);
  
  static int cs47l24_remove(struct platform_device *pdev)
  {
+       struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev);
        snd_soc_unregister_platform(&pdev->dev);
        snd_soc_unregister_codec(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
  
+       wm_adsp2_remove(&cs47l24->core.adsp[1]);
+       wm_adsp2_remove(&cs47l24->core.adsp[2]);
        return 0;
  }
  
  #define ADSP2_RAM_RDY_SHIFT                    0
  #define ADSP2_RAM_RDY_WIDTH                    1
  
 +#define ADSP_MAX_STD_CTRL_SIZE               512
 +
  struct wm_adsp_buf {
        struct list_head list;
        void *buf;
@@@ -437,7 -435,6 +437,7 @@@ struct wm_coeff_ctl 
        size_t len;
        unsigned int set:1;
        struct snd_kcontrol *kcontrol;
 +      struct soc_bytes_ext bytes_ext;
        unsigned int flags;
  };
  
@@@ -714,17 -711,10 +714,17 @@@ static void wm_adsp2_show_fw_status(str
                 be16_to_cpu(scratch[3]));
  }
  
 +static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
 +{
 +      return container_of(ext, struct wm_coeff_ctl, bytes_ext);
 +}
 +
  static int wm_coeff_info(struct snd_kcontrol *kctl,
                         struct snd_ctl_elem_info *uinfo)
  {
 -      struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
 +      struct soc_bytes_ext *bytes_ext =
 +              (struct soc_bytes_ext *)kctl->private_value;
 +      struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
  
        uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
        uinfo->count = ctl->len;
@@@ -773,9 -763,7 +773,9 @@@ static int wm_coeff_write_control(struc
  static int wm_coeff_put(struct snd_kcontrol *kctl,
                        struct snd_ctl_elem_value *ucontrol)
  {
 -      struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
 +      struct soc_bytes_ext *bytes_ext =
 +              (struct soc_bytes_ext *)kctl->private_value;
 +      struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
        char *p = ucontrol->value.bytes.data;
        int ret = 0;
  
        return ret;
  }
  
 +static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
 +                          const unsigned int __user *bytes, unsigned int size)
 +{
 +      struct soc_bytes_ext *bytes_ext =
 +              (struct soc_bytes_ext *)kctl->private_value;
 +      struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
 +      int ret = 0;
 +
 +      mutex_lock(&ctl->dsp->pwr_lock);
 +
 +      if (copy_from_user(ctl->cache, bytes, size)) {
 +              ret = -EFAULT;
 +      } else {
 +              ctl->set = 1;
 +              if (ctl->enabled)
 +                      ret = wm_coeff_write_control(ctl, ctl->cache, size);
 +      }
 +
 +      mutex_unlock(&ctl->dsp->pwr_lock);
 +
 +      return ret;
 +}
 +
  static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
                                 void *buf, size_t len)
  {
  static int wm_coeff_get(struct snd_kcontrol *kctl,
                        struct snd_ctl_elem_value *ucontrol)
  {
 -      struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
 +      struct soc_bytes_ext *bytes_ext =
 +              (struct soc_bytes_ext *)kctl->private_value;
 +      struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
        char *p = ucontrol->value.bytes.data;
        int ret = 0;
  
        return ret;
  }
  
 +static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
 +                          unsigned int __user *bytes, unsigned int size)
 +{
 +      struct soc_bytes_ext *bytes_ext =
 +              (struct soc_bytes_ext *)kctl->private_value;
 +      struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
 +      int ret = 0;
 +
 +      mutex_lock(&ctl->dsp->pwr_lock);
 +
 +      if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
 +              if (ctl->enabled)
 +                      ret = wm_coeff_read_control(ctl, ctl->cache, size);
 +              else
 +                      ret = -EPERM;
 +      } else {
 +              if (!ctl->flags && ctl->enabled)
 +                      ret = wm_coeff_read_control(ctl, ctl->cache, size);
 +      }
 +
 +      if (!ret && copy_to_user(bytes, ctl->cache, size))
 +              ret = -EFAULT;
 +
 +      mutex_unlock(&ctl->dsp->pwr_lock);
 +
 +      return ret;
 +}
 +
  struct wmfw_ctl_work {
        struct wm_adsp *dsp;
        struct wm_coeff_ctl *ctl;
        struct work_struct work;
  };
  
 +static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
 +{
 +      unsigned int out, rd, wr, vol;
 +
 +      if (len > ADSP_MAX_STD_CTRL_SIZE) {
 +              rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;
 +              wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;
 +              vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
 +
 +              out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
 +      } else {
 +              rd = SNDRV_CTL_ELEM_ACCESS_READ;
 +              wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
 +              vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
 +
 +              out = 0;
 +      }
 +
 +      if (in) {
 +              if (in & WMFW_CTL_FLAG_READABLE)
 +                      out |= rd;
 +              if (in & WMFW_CTL_FLAG_WRITEABLE)
 +                      out |= wr;
 +              if (in & WMFW_CTL_FLAG_VOLATILE)
 +                      out |= vol;
 +      } else {
 +              out |= rd | wr | vol;
 +      }
 +
 +      return out;
 +}
 +
  static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
  {
        struct snd_kcontrol_new *kcontrol;
        kcontrol->info = wm_coeff_info;
        kcontrol->get = wm_coeff_get;
        kcontrol->put = wm_coeff_put;
 -      kcontrol->private_value = (unsigned long)ctl;
 +      kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 +      kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
 +      kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
  
 -      if (ctl->flags) {
 -              if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE)
 -                      kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
 -              if (ctl->flags & WMFW_CTL_FLAG_READABLE)
 -                      kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
 -              if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
 -                      kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
 -      } else {
 -              kcontrol->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
 -              kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
 -      }
 +      ctl->bytes_ext.max = ctl->len;
 +      ctl->bytes_ext.get = wm_coeff_tlv_get;
 +      ctl->bytes_ext.put = wm_coeff_tlv_put;
 +
 +      kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
  
        ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
        if (ret < 0)
@@@ -1037,6 -944,13 +1037,13 @@@ static void wm_adsp_ctl_work(struct wor
        kfree(ctl_work);
  }
  
+ static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl)
+ {
+       kfree(ctl->cache);
+       kfree(ctl->name);
+       kfree(ctl);
+ }
  static int wm_adsp_create_control(struct wm_adsp *dsp,
                                  const struct wm_adsp_alg_region *alg_region,
                                  unsigned int offset, unsigned int len,
  
        ctl->flags = flags;
        ctl->offset = offset;
 -      if (len > 512) {
 -              adsp_warn(dsp, "Truncating control %s from %d\n",
 -                        ctl->name, len);
 -              len = 512;
 -      }
        ctl->len = len;
        ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
        if (!ctl->cache) {
@@@ -1652,6 -1571,19 +1659,19 @@@ static struct wm_adsp_alg_region *wm_ad
        return alg_region;
  }
  
+ static void wm_adsp_free_alg_regions(struct wm_adsp *dsp)
+ {
+       struct wm_adsp_alg_region *alg_region;
+       while (!list_empty(&dsp->alg_regions)) {
+               alg_region = list_first_entry(&dsp->alg_regions,
+                                             struct wm_adsp_alg_region,
+                                             list);
+               list_del(&alg_region->list);
+               kfree(alg_region);
+       }
+ }
  static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
  {
        struct wmfw_adsp1_id_hdr adsp1_id;
@@@ -2082,7 -2014,6 +2102,6 @@@ int wm_adsp1_event(struct snd_soc_dapm_
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
        struct wm_adsp *dsp = &dsps[w->shift];
-       struct wm_adsp_alg_region *alg_region;
        struct wm_coeff_ctl *ctl;
        int ret;
        unsigned int val;
                list_for_each_entry(ctl, &dsp->ctl_list, list)
                        ctl->enabled = 0;
  
-               while (!list_empty(&dsp->alg_regions)) {
-                       alg_region = list_first_entry(&dsp->alg_regions,
-                                                     struct wm_adsp_alg_region,
-                                                     list);
-                       list_del(&alg_region->list);
-                       kfree(alg_region);
-               }
+               wm_adsp_free_alg_regions(dsp);
                break;
  
        default:
@@@ -2310,7 -2236,6 +2324,6 @@@ int wm_adsp2_event(struct snd_soc_dapm_
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
        struct wm_adsp *dsp = &dsps[w->shift];
-       struct wm_adsp_alg_region *alg_region;
        struct wm_coeff_ctl *ctl;
        int ret;
  
                if (ret != 0)
                        goto err;
  
 +              mutex_lock(&dsp->pwr_lock);
 +
                if (wm_adsp_fw[dsp->fw].num_caps != 0)
                        ret = wm_adsp_buffer_init(dsp);
  
 +              mutex_unlock(&dsp->pwr_lock);
 +
                break;
  
        case SND_SOC_DAPM_PRE_PMD:
                list_for_each_entry(ctl, &dsp->ctl_list, list)
                        ctl->enabled = 0;
  
-               while (!list_empty(&dsp->alg_regions)) {
-                       alg_region = list_first_entry(&dsp->alg_regions,
-                                                     struct wm_adsp_alg_region,
-                                                     list);
-                       list_del(&alg_region->list);
-                       kfree(alg_region);
-               }
+               wm_adsp_free_alg_regions(dsp);
  
                if (wm_adsp_fw[dsp->fw].num_caps != 0)
                        wm_adsp_buffer_free(dsp);
@@@ -2432,6 -2347,19 +2439,19 @@@ int wm_adsp2_init(struct wm_adsp *dsp
  }
  EXPORT_SYMBOL_GPL(wm_adsp2_init);
  
+ void wm_adsp2_remove(struct wm_adsp *dsp)
+ {
+       struct wm_coeff_ctl *ctl;
+       while (!list_empty(&dsp->ctl_list)) {
+               ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl,
+                                       list);
+               list_del(&ctl->list);
+               wm_adsp_free_ctl_blk(ctl);
+       }
+ }
+ EXPORT_SYMBOL_GPL(wm_adsp2_remove);
  int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
  {
        struct wm_adsp_compr *compr;
@@@ -2897,41 -2825,21 +2917,41 @@@ static int wm_adsp_buffer_update_avail(
                avail += wm_adsp_buffer_size(buf);
  
        adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
 -               buf->read_index, write_index, avail);
 +               buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE);
  
        buf->avail = avail;
  
        return 0;
  }
  
 +static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)
 +{
 +      int ret;
 +
 +      ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
 +      if (ret < 0) {
 +              adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret);
 +              return ret;
 +      }
 +      if (buf->error != 0) {
 +              adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error);
 +              return -EIO;
 +      }
 +
 +      return 0;
 +}
 +
  int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
  {
 -      struct wm_adsp_compr_buf *buf = dsp->buffer;
 -      struct wm_adsp_compr *compr = dsp->compr;
 +      struct wm_adsp_compr_buf *buf;
 +      struct wm_adsp_compr *compr;
        int ret = 0;
  
        mutex_lock(&dsp->pwr_lock);
  
 +      buf = dsp->buffer;
 +      compr = dsp->compr;
 +
        if (!buf) {
                ret = -ENODEV;
                goto out;
  
        adsp_dbg(dsp, "Handling buffer IRQ\n");
  
 -      ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
 -      if (ret < 0) {
 -              adsp_err(dsp, "Failed to check buffer error: %d\n", ret);
 -              goto out;
 -      }
 -      if (buf->error != 0) {
 -              adsp_err(dsp, "Buffer error occurred: %d\n", buf->error);
 -              ret = -EIO;
 -              goto out;
 -      }
 +      ret = wm_adsp_buffer_get_error(buf);
 +      if (ret < 0)
 +              goto out_notify; /* Wake poll to report error */
  
        ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
                                  &buf->irq_count);
                goto out;
        }
  
 +out_notify:
        if (compr && compr->stream)
                snd_compr_fragment_elapsed(compr->stream);
  
@@@ -2985,16 -2899,14 +3005,16 @@@ int wm_adsp_compr_pointer(struct snd_co
                          struct snd_compr_tstamp *tstamp)
  {
        struct wm_adsp_compr *compr = stream->runtime->private_data;
 -      struct wm_adsp_compr_buf *buf = compr->buf;
        struct wm_adsp *dsp = compr->dsp;
 +      struct wm_adsp_compr_buf *buf;
        int ret = 0;
  
        adsp_dbg(dsp, "Pointer request\n");
  
        mutex_lock(&dsp->pwr_lock);
  
 +      buf = compr->buf;
 +
        if (!compr->buf) {
                ret = -ENXIO;
                goto out;
                 * DSP to inform us once a whole fragment is available.
                 */
                if (buf->avail < wm_adsp_compr_frag_words(compr)) {
 +                      ret = wm_adsp_buffer_get_error(buf);
 +                      if (ret < 0)
 +                              goto out;
 +
                        ret = wm_adsp_buffer_reenable_irq(buf);
                        if (ret < 0) {
                                adsp_err(dsp,