Merge tag 'asoc-v3.10-2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorTakashi Iwai <tiwai@suse.de>
Thu, 18 Apr 2013 14:24:31 +0000 (16:24 +0200)
committerTakashi Iwai <tiwai@suse.de>
Thu, 18 Apr 2013 14:24:31 +0000 (16:24 +0200)
ASoC: More updates for v3.10

The main additional change here is Lars-Peter's DMA work plus the
platform conversions which have been tested - getting this in mainline
will make life easier for development after the merge window.  These
factor a large chunk of code out of the drivers for the platforms using
dmaengine, greatly simplifying development.

1  2 
include/sound/soc.h
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/imx-ssi.c
sound/soc/soc-core.c
sound/soc/spear/spear_pcm.c
sound/usb/quirks.c

diff --combined include/sound/soc.h
@@@ -324,8 -324,6 +324,8 @@@ struct snd_soc_dai_link
  struct snd_soc_platform_driver;
  struct snd_soc_codec;
  struct snd_soc_codec_driver;
 +struct snd_soc_component;
 +struct snd_soc_component_driver;
  struct soc_enum;
  struct snd_soc_jack;
  struct snd_soc_jack_zone;
@@@ -375,14 -373,14 +375,18 @@@ int snd_soc_poweroff(struct device *dev
  int snd_soc_register_platform(struct device *dev,
                const struct snd_soc_platform_driver *platform_drv);
  void snd_soc_unregister_platform(struct device *dev);
+ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
+               const struct snd_soc_platform_driver *platform_drv);
+ void snd_soc_remove_platform(struct snd_soc_platform *platform);
+ struct snd_soc_platform *snd_soc_lookup_platform(struct device *dev);
  int snd_soc_register_codec(struct device *dev,
                const struct snd_soc_codec_driver *codec_drv,
                struct snd_soc_dai_driver *dai_drv, int num_dai);
  void snd_soc_unregister_codec(struct device *dev);
 +int snd_soc_register_component(struct device *dev,
 +                       const struct snd_soc_component_driver *cmpnt_drv,
 +                       struct snd_soc_dai_driver *dai_drv, int num_dai);
 +void snd_soc_unregister_component(struct device *dev);
  int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
                                    unsigned int reg);
  int snd_soc_codec_readable_register(struct snd_soc_codec *codec,
@@@ -847,20 -845,6 +851,20 @@@ struct snd_soc_platform 
  #endif
  };
  
 +struct snd_soc_component_driver {
 +      const char *name;
 +};
 +
 +struct snd_soc_component {
 +      const char *name;
 +      int id;
 +      int num_dai;
 +      struct device *dev;
 +      struct list_head list;
 +
 +      const struct snd_soc_component_driver *driver;
 +};
 +
  struct snd_soc_dai_link {
        /* config - must be set by machine driver */
        const char *name;                       /* Codec name */
diff --combined sound/soc/fsl/fsl_ssi.c
@@@ -425,12 -425,6 +425,6 @@@ static int fsl_ssi_startup(struct snd_p
                ssi_private->second_stream = substream;
        }
  
-       if (ssi_private->ssi_on_imx)
-               snd_soc_dai_set_dma_data(dai, substream,
-                       (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
-                               &ssi_private->dma_params_tx :
-                               &ssi_private->dma_params_rx);
        return 0;
  }
  
@@@ -552,6 -546,18 +546,18 @@@ static void fsl_ssi_shutdown(struct snd
        }
  }
  
+ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
+ {
+       struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai);
+       if (ssi_private->ssi_on_imx) {
+               dai->playback_dma_data = &ssi_private->dma_params_tx;
+               dai->capture_dma_data = &ssi_private->dma_params_rx;
+       }
+       return 0;
+ }
  static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
        .startup        = fsl_ssi_startup,
        .hw_params      = fsl_ssi_hw_params,
  
  /* Template for the CPU dai driver structure */
  static struct snd_soc_dai_driver fsl_ssi_dai_template = {
+       .probe = fsl_ssi_dai_probe,
        .playback = {
                /* The SSI does not support monaural audio. */
                .channels_min = 2,
        .ops = &fsl_ssi_dai_ops,
  };
  
 +static const struct snd_soc_component_driver fsl_ssi_component = {
 +      .name           = "fsl-ssi",
 +};
 +
  /* Show the statistics of a flag only if its interrupt is enabled.  The
   * compiler will optimze this code to a no-op if the interrupt is not
   * enabled.
@@@ -794,8 -797,7 +801,8 @@@ static int fsl_ssi_probe(struct platfor
        /* Register with ASoC */
        dev_set_drvdata(&pdev->dev, ssi_private);
  
 -      ret = snd_soc_register_dai(&pdev->dev, &ssi_private->cpu_dai_drv);
 +      ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
 +                                       &ssi_private->cpu_dai_drv, 1);
        if (ret) {
                dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
                goto error_dev;
@@@ -848,7 -850,7 +855,7 @@@ done
  error_dai:
        if (ssi_private->ssi_on_imx)
                platform_device_unregister(ssi_private->imx_pcm_pdev);
 -      snd_soc_unregister_dai(&pdev->dev);
 +      snd_soc_unregister_component(&pdev->dev);
  
  error_dev:
        dev_set_drvdata(&pdev->dev, NULL);
@@@ -886,7 -888,7 +893,7 @@@ static int fsl_ssi_remove(struct platfo
                clk_disable_unprepare(ssi_private->clk);
                clk_put(ssi_private->clk);
        }
 -      snd_soc_unregister_dai(&pdev->dev);
 +      snd_soc_unregister_component(&pdev->dev);
        device_remove_file(&pdev->dev, &ssi_private->dev_attr);
  
        free_irq(ssi_private->irq, ssi_private);
diff --combined sound/soc/fsl/imx-ssi.c
@@@ -232,23 -232,6 +232,6 @@@ static int imx_ssi_set_dai_clkdiv(struc
        return 0;
  }
  
- static int imx_ssi_startup(struct snd_pcm_substream *substream,
-                          struct snd_soc_dai *cpu_dai)
- {
-       struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai);
-       struct snd_dmaengine_dai_dma_data *dma_data;
-       /* Tx/Rx config */
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               dma_data = &ssi->dma_params_tx;
-       else
-               dma_data = &ssi->dma_params_rx;
-       snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
-       return 0;
- }
  /*
   * Should only be called when port is inactive (i.e. SSIEN = 0),
   * although can be called multiple times by upper layers.
@@@ -353,7 -336,6 +336,6 @@@ static int imx_ssi_trigger(struct snd_p
  }
  
  static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
-       .startup        = imx_ssi_startup,
        .hw_params      = imx_ssi_hw_params,
        .set_fmt        = imx_ssi_set_dai_fmt,
        .set_clkdiv     = imx_ssi_set_dai_clkdiv,
@@@ -373,6 -355,10 +355,10 @@@ static int imx_ssi_dai_probe(struct snd
                SSI_SFCSR_RFWM0(ssi->dma_params_rx.maxburst);
        writel(val, ssi->base + SSI_SFCSR);
  
+       /* Tx/Rx config */
+       dai->playback_dma_data = &ssi->dma_params_tx;
+       dai->capture_dma_data = &ssi->dma_params_rx;
        return 0;
  }
  
@@@ -413,10 -399,6 +399,10 @@@ static struct snd_soc_dai_driver imx_ac
        .ops = &imx_ssi_pcm_dai_ops,
  };
  
 +static const struct snd_soc_component_driver imx_component = {
 +      .name           = DRV_NAME,
 +};
 +
  static void setup_channel_to_ac97(struct imx_ssi *imx_ssi)
  {
        void __iomem *base = imx_ssi->base;
@@@ -602,8 -584,7 +588,8 @@@ static int imx_ssi_probe(struct platfor
  
        platform_set_drvdata(pdev, ssi);
  
 -      ret = snd_soc_register_dai(&pdev->dev, dai);
 +      ret = snd_soc_register_component(&pdev->dev, &imx_component,
 +                                       dai, 1);
        if (ret) {
                dev_err(&pdev->dev, "register DAI failed\n");
                goto failed_register;
@@@ -644,7 -625,7 +630,7 @@@ failed_pdev_alloc
  failed_pdev_fiq_add:
        platform_device_put(ssi->soc_platform_pdev_fiq);
  failed_pdev_fiq_alloc:
 -      snd_soc_unregister_dai(&pdev->dev);
 +      snd_soc_unregister_component(&pdev->dev);
  failed_register:
        release_mem_region(res->start, resource_size(res));
  failed_get_resource:
@@@ -662,7 -643,7 +648,7 @@@ static int imx_ssi_remove(struct platfo
        platform_device_unregister(ssi->soc_platform_pdev);
        platform_device_unregister(ssi->soc_platform_pdev_fiq);
  
 -      snd_soc_unregister_dai(&pdev->dev);
 +      snd_soc_unregister_component(&pdev->dev);
  
        if (ssi->flags & IMX_SSI_USE_AC97)
                ac97_ssi = NULL;
diff --combined sound/soc/soc-core.c
@@@ -58,7 -58,6 +58,7 @@@ static DEFINE_MUTEX(client_mutex)
  static LIST_HEAD(dai_list);
  static LIST_HEAD(platform_list);
  static LIST_HEAD(codec_list);
 +static LIST_HEAD(component_list);
  
  /*
   * This is a timeout to do a DAPM powerdown after a stream is closed().
@@@ -3741,7 -3740,7 +3741,7 @@@ static inline char *fmt_multiple_name(s
   *
   * @dai: DAI to register
   */
 -int snd_soc_register_dai(struct device *dev,
 +static int snd_soc_register_dai(struct device *dev,
                struct snd_soc_dai_driver *dai_drv)
  {
        struct snd_soc_codec *codec;
  
        return 0;
  }
 -EXPORT_SYMBOL_GPL(snd_soc_register_dai);
  
  /**
   * snd_soc_unregister_dai - Unregister a DAI from the ASoC core
   *
   * @dai: DAI to unregister
   */
 -void snd_soc_unregister_dai(struct device *dev)
 +static void snd_soc_unregister_dai(struct device *dev)
  {
        struct snd_soc_dai *dai;
  
@@@ -3813,6 -3813,7 +3813,6 @@@ found
        kfree(dai->name);
        kfree(dai);
  }
 -EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);
  
  /**
   * snd_soc_register_dais - Register multiple DAIs with the ASoC core
   * @dai: Array of DAIs to register
   * @count: Number of DAIs
   */
 -int snd_soc_register_dais(struct device *dev,
 +static int snd_soc_register_dais(struct device *dev,
                struct snd_soc_dai_driver *dai_drv, size_t count)
  {
        struct snd_soc_codec *codec;
@@@ -3884,6 -3885,7 +3884,6 @@@ err
  
        return ret;
  }
 -EXPORT_SYMBOL_GPL(snd_soc_register_dais);
  
  /**
   * snd_soc_unregister_dais - Unregister multiple DAIs from the ASoC core
   * @dai: Array of DAIs to unregister
   * @count: Number of DAIs
   */
 -void snd_soc_unregister_dais(struct device *dev, size_t count)
 +static void snd_soc_unregister_dais(struct device *dev, size_t count)
  {
        int i;
  
        for (i = 0; i < count; i++)
                snd_soc_unregister_dai(dev);
  }
 -EXPORT_SYMBOL_GPL(snd_soc_unregister_dais);
  
  /**
-  * snd_soc_register_platform - Register a platform with the ASoC core
-  *
-  * @platform: platform to register
+  * snd_soc_add_platform - Add a platform to the ASoC core
+  * @dev: The parent device for the platform
+  * @platform: The platform to add
+  * @platform_driver: The driver for the platform
   */
- int snd_soc_register_platform(struct device *dev,
+ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
                const struct snd_soc_platform_driver *platform_drv)
  {
-       struct snd_soc_platform *platform;
-       dev_dbg(dev, "ASoC: platform register %s\n", dev_name(dev));
-       platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);
-       if (platform == NULL)
-               return -ENOMEM;
        /* create platform component name */
        platform->name = fmt_single_name(dev, &platform->id);
        if (platform->name == NULL) {
  
        return 0;
  }
- EXPORT_SYMBOL_GPL(snd_soc_register_platform);
+ EXPORT_SYMBOL_GPL(snd_soc_add_platform);
  
  /**
-  * snd_soc_unregister_platform - Unregister a platform from the ASoC core
+  * snd_soc_register_platform - Register a platform with the ASoC core
   *
-  * @platform: platform to unregister
+  * @platform: platform to register
   */
- void snd_soc_unregister_platform(struct device *dev)
+ int snd_soc_register_platform(struct device *dev,
+               const struct snd_soc_platform_driver *platform_drv)
  {
        struct snd_soc_platform *platform;
+       int ret;
  
-       list_for_each_entry(platform, &platform_list, list) {
-               if (dev == platform->dev)
-                       goto found;
-       }
-       return;
+       dev_dbg(dev, "ASoC: platform register %s\n", dev_name(dev));
  
- found:
+       platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);
+       if (platform == NULL)
+               return -ENOMEM;
+       ret = snd_soc_add_platform(dev, platform, platform_drv);
+       if (ret)
+               kfree(platform);
+       return ret;
+ }
+ EXPORT_SYMBOL_GPL(snd_soc_register_platform);
+ /**
+  * snd_soc_remove_platform - Remove a platform from the ASoC core
+  * @platform: the platform to remove
+  */
+ void snd_soc_remove_platform(struct snd_soc_platform *platform)
+ {
        mutex_lock(&client_mutex);
        list_del(&platform->list);
        mutex_unlock(&client_mutex);
  
-       dev_dbg(dev, "ASoC: Unregistered platform '%s'\n", platform->name);
+       dev_dbg(platform->dev, "ASoC: Unregistered platform '%s'\n",
+               platform->name);
        kfree(platform->name);
+ }
+ EXPORT_SYMBOL_GPL(snd_soc_remove_platform);
+ struct snd_soc_platform *snd_soc_lookup_platform(struct device *dev)
+ {
+       struct snd_soc_platform *platform;
+       list_for_each_entry(platform, &platform_list, list) {
+               if (dev == platform->dev)
+                       return platform;
+       }
+       return NULL;
+ }
+ EXPORT_SYMBOL_GPL(snd_soc_lookup_platform);
+ /**
+  * snd_soc_unregister_platform - Unregister a platform from the ASoC core
+  *
+  * @platform: platform to unregister
+  */
+ void snd_soc_unregister_platform(struct device *dev)
+ {
+       struct snd_soc_platform *platform;
+       platform = snd_soc_lookup_platform(dev);
+       if (!platform)
+               return;
+       snd_soc_remove_platform(platform);
        kfree(platform);
  }
  EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
@@@ -4137,92 -4179,6 +4176,92 @@@ found
  }
  EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
  
 +
 +/**
 + * snd_soc_register_component - Register a component with the ASoC core
 + *
 + */
 +int snd_soc_register_component(struct device *dev,
 +                       const struct snd_soc_component_driver *cmpnt_drv,
 +                       struct snd_soc_dai_driver *dai_drv,
 +                       int num_dai)
 +{
 +      struct snd_soc_component *cmpnt;
 +      int ret;
 +
 +      dev_dbg(dev, "component register %s\n", dev_name(dev));
 +
 +      cmpnt = devm_kzalloc(dev, sizeof(*cmpnt), GFP_KERNEL);
 +      if (!cmpnt) {
 +              dev_err(dev, "ASoC: Failed to allocate memory\n");
 +              return -ENOMEM;
 +      }
 +
 +      cmpnt->name = fmt_single_name(dev, &cmpnt->id);
 +      if (!cmpnt->name) {
 +              dev_err(dev, "ASoC: Failed to simplifying name\n");
 +              return -ENOMEM;
 +      }
 +
 +      cmpnt->dev      = dev;
 +      cmpnt->driver   = cmpnt_drv;
 +      cmpnt->num_dai  = num_dai;
 +
 +      /*
 +       * snd_soc_register_dai()  uses fmt_single_name(), and
 +       * snd_soc_register_dais() uses fmt_multiple_name()
 +       * for dai->name which is used for name based matching
 +       */
 +      if (1 == num_dai)
 +              ret = snd_soc_register_dai(dev, dai_drv);
 +      else
 +              ret = snd_soc_register_dais(dev, dai_drv, num_dai);
 +      if (ret < 0) {
 +              dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
 +              goto error_component_name;
 +      }
 +
 +      mutex_lock(&client_mutex);
 +      list_add(&cmpnt->list, &component_list);
 +      mutex_unlock(&client_mutex);
 +
 +      dev_dbg(cmpnt->dev, "ASoC: Registered component '%s'\n", cmpnt->name);
 +
 +      return ret;
 +
 +error_component_name:
 +      kfree(cmpnt->name);
 +
 +      return ret;
 +}
 +EXPORT_SYMBOL_GPL(snd_soc_register_component);
 +
 +/**
 + * snd_soc_unregister_component - Unregister a component from the ASoC core
 + *
 + */
 +void snd_soc_unregister_component(struct device *dev)
 +{
 +      struct snd_soc_component *cmpnt;
 +
 +      list_for_each_entry(cmpnt, &component_list, list) {
 +              if (dev == cmpnt->dev)
 +                      goto found;
 +      }
 +      return;
 +
 +found:
 +      snd_soc_unregister_dais(dev, cmpnt->num_dai);
 +
 +      mutex_lock(&client_mutex);
 +      list_del(&cmpnt->list);
 +      mutex_unlock(&client_mutex);
 +
 +      dev_dbg(dev, "ASoC: Unregistered component '%s'\n", cmpnt->name);
 +      kfree(cmpnt->name);
 +}
 +EXPORT_SYMBOL_GPL(snd_soc_unregister_component);
 +
  /* Retrieve a card's name from device tree */
  int snd_soc_of_parse_card_name(struct snd_soc_card *card,
                               const char *propname)
@@@ -25,7 -25,7 +25,7 @@@
  #include <sound/soc.h>
  #include <sound/spear_dma.h>
  
 -struct snd_pcm_hardware spear_pcm_hardware = {
 +static struct snd_pcm_hardware spear_pcm_hardware = {
        .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
                 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
                 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
@@@ -64,7 -64,8 +64,8 @@@ static int spear_pcm_open(struct snd_pc
        if (ret)
                return ret;
  
-       return snd_dmaengine_pcm_open(substream, dma_data->filter, dma_data)
+       return snd_dmaengine_pcm_open_request_chan(substream, dma_data->filter,
+                               dma_data);
  }
  
  static int spear_pcm_mmap(struct snd_pcm_substream *substream,
@@@ -79,7 -80,7 +80,7 @@@
  
  static struct snd_pcm_ops spear_pcm_ops = {
        .open           = spear_pcm_open,
-       .close          = snd_dmaengine_pcm_close,
+       .close          = snd_dmaengine_pcm_close_release_chan,
        .ioctl          = snd_pcm_lib_ioctl,
        .hw_params      = spear_pcm_hw_params,
        .hw_free        = spear_pcm_hw_free,
@@@ -164,7 -165,7 +165,7 @@@ static int spear_pcm_new(struct snd_soc
        return 0;
  }
  
 -struct snd_soc_platform_driver spear_soc_platform = {
 +static struct snd_soc_platform_driver spear_soc_platform = {
        .ops            =       &spear_pcm_ops,
        .pcm_new        =       spear_pcm_new,
        .pcm_free       =       spear_pcm_free,
diff --combined sound/usb/quirks.c
@@@ -165,10 -165,8 +165,10 @@@ static int create_fixed_stream_quirk(st
                return -EINVAL;
        }
        alts = &iface->altsetting[fp->altset_idx];
 -      fp->datainterval = snd_usb_parse_datainterval(chip, alts);
 -      fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
 +      if (fp->datainterval == 0)
 +              fp->datainterval = snd_usb_parse_datainterval(chip, alts);
 +      if (fp->maxpacksize == 0)
 +              fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
        usb_set_interface(chip->dev, fp->iface, 0);
        snd_usb_init_pitch(chip, fp->iface, alts, fp);
        snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max);
@@@ -448,17 -446,6 +448,17 @@@ static int snd_usb_cm6206_boot_quirk(st
  }
  
  /*
 + * Novation Twitch DJ controller
 + */
 +static int snd_usb_twitch_boot_quirk(struct usb_device *dev)
 +{
 +      /* preemptively set up the device because otherwise the
 +       * raw MIDI endpoints are not active */
 +      usb_set_interface(dev, 0, 1);
 +      return 0;
 +}
 +
 +/*
   * This call will put the synth in "USB send" mode, i.e it will send MIDI
   * messages through USB (this is disabled at startup). The synth will
   * acknowledge by sending a sysex on endpoint 0x85 and by displaying a USB
@@@ -499,7 -486,7 +499,7 @@@ static int snd_usb_nativeinstruments_bo
  {
        int ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                                  0xaf, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                                 cpu_to_le16(1), 0, NULL, 0, 1000);
+                                 1, 0, NULL, 0, 1000);
  
        if (ret < 0)
                return ret;
@@@ -759,10 -746,6 +759,10 @@@ int snd_usb_apply_boot_quirk(struct usb
                /* Digidesign Mbox 2 */
                return snd_usb_mbox2_boot_quirk(dev);
  
 +      case USB_ID(0x1235, 0x0018):
 +              /* Focusrite Novation Twitch */
 +              return snd_usb_twitch_boot_quirk(dev);
 +
        case USB_ID(0x133e, 0x0815):
                /* Access Music VirusTI Desktop */
                return snd_usb_accessmusic_boot_quirk(dev);
@@@ -854,7 -837,6 +854,7 @@@ static void set_format_emu_quirk(struc
                break;
        }
        snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id);
 +      subs->pkt_offset_adj = (emu_samplerate_id >= EMU_QUIRK_SR_176400HZ) ? 4 : 0;
  }
  
  void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
@@@ -893,16 -875,6 +893,16 @@@ void snd_usb_endpoint_start_quirk(struc
                ep->skip_packets = 16;
  }
  
 +void snd_usb_set_interface_quirk(struct usb_device *dev)
 +{
 +      /*
 +       * "Playback Design" products need a 50ms delay after setting the
 +       * USB interface.
 +       */
 +      if (le16_to_cpu(dev->descriptor.idVendor) == 0x23ba)
 +              mdelay(50);
 +}
 +
  void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
                           __u8 request, __u8 requesttype, __u16 value,
                           __u16 index, void *data, __u16 size)
                mdelay(20);
  }
  
 +/*
 + * snd_usb_interface_dsd_format_quirks() is called from format.c to
 + * augment the PCM format bit-field for DSD types. The UAC standards
 + * don't have a designated bit field to denote DSD-capable interfaces,
 + * hence all hardware that is known to support this format has to be
 + * listed here.
 + */
 +u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
 +                                      struct audioformat *fp,
 +                                      unsigned int sample_bytes)
 +{
 +      /* Playback Designs */
 +      if (le16_to_cpu(chip->dev->descriptor.idVendor) == 0x23ba) {
 +              switch (fp->altsetting) {
 +              case 1:
 +                      fp->dsd_dop = true;
 +                      return SNDRV_PCM_FMTBIT_DSD_U16_LE;
 +              case 2:
 +                      fp->dsd_bitrev = true;
 +                      return SNDRV_PCM_FMTBIT_DSD_U8;
 +              case 3:
 +                      fp->dsd_bitrev = true;
 +                      return SNDRV_PCM_FMTBIT_DSD_U16_LE;
 +              }
 +      }
 +
 +      return 0;
 +}