CHROMIUM: ALSA: Add new DSP loader callback routines
authorChih-Chung Chang <chihchung@chromium.org>
Mon, 3 Sep 2012 03:25:33 +0000 (11:25 +0800)
committerGerrit <chrome-bot@google.com>
Thu, 6 Sep 2012 07:47:03 +0000 (00:47 -0700)
BUG=chrome-os-partner:12327

Change-Id: I1dff2d4d672b0b03eb486fb97295367db4b07354
Signed-off-by: Chih-Chung Chang <chihchung@chromium.org>
Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
Reviewed-on: https://gerrit.chromium.org/gerrit/32126
Reviewed-by: Dylan Reid <dgreid@chromium.org>
sound/pci/hda/Kconfig
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_intel.c

index 163b6b5..3462d6a 100644 (file)
@@ -258,4 +258,12 @@ config SND_HDA_POWER_SAVE_DEFAULT
          The default time-out value in seconds for HD-audio automatic
          power-save mode.  0 means to disable the power-save mode.
 
+config SND_HDA_DSP_LOADER
+       bool "Enable DSP firmware loader"
+       depends on FW_LOADER
+       default y
+       help
+         Say Y here to enable the DSP firmware loader, used by certain
+         codecs (e.g. CA0132) to transfer their DSP binaries to the hardware.
+
 endif
index 29e0d08..b33091f 100644 (file)
@@ -334,15 +334,15 @@ enum {
 #define AC_KNBCAP_DELTA                        (1<<7)
 
 /* HDMI LPCM capabilities */
-#define AC_LPCMCAP_48K_CP_CHNS         (0x0f<<0) /* max channels w/ CP-on */   
+#define AC_LPCMCAP_48K_CP_CHNS         (0x0f<<0) /* max channels w/ CP-on */
 #define AC_LPCMCAP_48K_NO_CHNS         (0x0f<<4) /* max channels w/o CP-on */
 #define AC_LPCMCAP_48K_20BIT           (1<<8)  /* 20b bitrate supported */
 #define AC_LPCMCAP_48K_24BIT           (1<<9)  /* 24b bitrate supported */
-#define AC_LPCMCAP_96K_CP_CHNS         (0x0f<<10) /* max channels w/ CP-on */  
+#define AC_LPCMCAP_96K_CP_CHNS         (0x0f<<10) /* max channels w/ CP-on */
 #define AC_LPCMCAP_96K_NO_CHNS         (0x0f<<14) /* max channels w/o CP-on */
 #define AC_LPCMCAP_96K_20BIT           (1<<18) /* 20b bitrate supported */
 #define AC_LPCMCAP_96K_24BIT           (1<<19) /* 24b bitrate supported */
-#define AC_LPCMCAP_192K_CP_CHNS                (0x0f<<20) /* max channels w/ CP-on */  
+#define AC_LPCMCAP_192K_CP_CHNS                (0x0f<<20) /* max channels w/ CP-on */
 #define AC_LPCMCAP_192K_NO_CHNS                (0x0f<<24) /* max channels w/o CP-on */
 #define AC_LPCMCAP_192K_20BIT          (1<<28) /* 20b bitrate supported */
 #define AC_LPCMCAP_192K_24BIT          (1<<29) /* 24b bitrate supported */
@@ -611,6 +611,17 @@ struct hda_bus_ops {
        /* notify power-up/down from codec to controller */
        void (*pm_notify)(struct hda_bus *bus);
 #endif
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+       /* prepare DSP transfer */
+       int (*load_dsp_prepare)(struct hda_bus *bus, unsigned int format,
+                               unsigned int byte_size,
+                               struct snd_dma_buffer *bufp);
+       /* start/stop DSP transfer */
+       void (*load_dsp_trigger)(struct hda_bus *bus, bool start);
+       /* clean up DSP transfer */
+       void (*load_dsp_cleanup)(struct hda_bus *bus,
+                                struct snd_dma_buffer *dmab);
+#endif
 };
 
 /* template to pass to the bus constructor */
@@ -682,7 +693,7 @@ struct hda_codec_preset {
        const char *name;
        int (*patch)(struct hda_codec *codec);
 };
-       
+
 struct hda_codec_preset_list {
        const struct hda_codec_preset *preset;
        struct module *owner;
@@ -1067,6 +1078,40 @@ static inline void snd_hda_power_down(struct hda_codec *codec) {}
 int snd_hda_load_patch(struct hda_bus *bus, const char *patch);
 #endif
 
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+static inline int
+snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
+                               unsigned int size,
+                               struct snd_dma_buffer *bufp)
+{
+       return codec->bus->ops.load_dsp_prepare(codec->bus, format, size, bufp);
+}
+static inline void
+snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start)
+{
+       return codec->bus->ops.load_dsp_trigger(codec->bus, start);
+}
+static inline void
+snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
+                               struct snd_dma_buffer *dmab)
+{
+       return codec->bus->ops.load_dsp_cleanup(codec->bus, dmab);
+}
+#else
+static inline int
+snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
+                               unsigned int size,
+                               struct snd_dma_buffer *bufp)
+{
+       return 0;
+}
+static inline void
+snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start) {}
+static inline void
+snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
+                               struct snd_dma_buffer *dmab) {}
+#endif
+
 /*
  * Codec modularization
  */
index 7cf4806..a0e3516 100644 (file)
@@ -31,7 +31,7 @@
  *  CHANGES:
  *
  *  2004.12.01 Major rewrite by tiwai, merged the work of pshou
- * 
+ *
  */
 
 #include <linux/delay.h>
@@ -544,8 +544,8 @@ static char *driver_short_names[] __devinitdata = {
        [AZX_DRIVER_SIS] = "HDA SIS966",
        [AZX_DRIVER_ULI] = "HDA ULI M5461",
        [AZX_DRIVER_NVIDIA] = "HDA NVidia",
-       [AZX_DRIVER_TERA] = "HDA Teradici", 
-       [AZX_DRIVER_CTX] = "HDA Creative", 
+       [AZX_DRIVER_TERA] = "HDA Teradici",
+       [AZX_DRIVER_CTX] = "HDA Creative",
        [AZX_DRIVER_GENERIC] = "HD-Audio Generic",
 };
 
@@ -975,6 +975,15 @@ static unsigned int azx_get_response(struct hda_bus *bus,
 static void azx_power_notify(struct hda_bus *bus);
 #endif
 
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format,
+                               unsigned int byte_size,
+                               struct snd_dma_buffer *bufp);
+static void azx_load_dsp_trigger(struct hda_bus *bus, bool start);
+static void azx_load_dsp_cleanup(struct hda_bus *bus,
+                                struct snd_dma_buffer *dmab);
+#endif
+
 /* reset codec link */
 static int azx_reset(struct azx *chip, int full_reset)
 {
@@ -1032,7 +1041,7 @@ static int azx_reset(struct azx *chip, int full_reset)
 
 /*
  * Lowlevel interface
- */  
+ */
 
 /* enable interrupts */
 static void azx_int_enable(struct azx *chip)
@@ -1235,7 +1244,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
                spin_unlock(&chip->reg_lock);
                return IRQ_NONE;
        }
-       
+
        for (i = 0; i < chip->num_streams; i++) {
                azx_dev = &chip->azx_dev[i];
                if (status & azx_dev->sd_int_sta_mask) {
@@ -1277,7 +1286,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
                azx_writeb(chip, STATESTS, 0x04);
 #endif
        spin_unlock(&chip->reg_lock);
-       
+
        return IRQ_HANDLED;
 }
 
@@ -1285,7 +1294,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
 /*
  * set up a BDL entry
  */
-static int setup_bdle(struct snd_pcm_substream *substream,
+static int setup_bdle(struct snd_dma_buffer *dmab,
                      struct azx_dev *azx_dev, u32 **bdlp,
                      int ofs, int size, int with_ioc)
 {
@@ -1298,12 +1307,12 @@ static int setup_bdle(struct snd_pcm_substream *substream,
                if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
                        return -EINVAL;
 
-               addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+               addr = snd_sgbuf_get_addr(dmab, ofs);
                /* program the address field of the BDL entry */
                bdl[0] = cpu_to_le32((u32)addr);
                bdl[1] = cpu_to_le32(upper_32_bits(addr));
                /* program the size field of the BDL entry */
-               chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
+               chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size);
                bdl[2] = cpu_to_le32(chunk);
                /* program the IOC to enable interrupt
                 * only when the whole fragment is processed
@@ -1356,8 +1365,8 @@ static int azx_setup_periods(struct azx *chip,
                                   bdl_pos_adj[chip->dev_index]);
                        pos_adj = 0;
                } else {
-                       ofs = setup_bdle(substream, azx_dev,
-                                        &bdl, ofs, pos_adj,
+                       ofs = setup_bdle(snd_pcm_get_dma_buf(substream),
+                                        azx_dev, &bdl, ofs, pos_adj,
                                         !substream->runtime->no_period_wakeup);
                        if (ofs < 0)
                                goto error;
@@ -1366,11 +1375,12 @@ static int azx_setup_periods(struct azx *chip,
                pos_adj = 0;
        for (i = 0; i < periods; i++) {
                if (i == periods - 1 && pos_adj)
-                       ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+                       ofs = setup_bdle(snd_pcm_get_dma_buf(substream),
+                                        azx_dev, &bdl, ofs,
                                         period_bytes - pos_adj, 0);
                else
-                       ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
-                                        period_bytes,
+                       ofs = setup_bdle(snd_pcm_get_dma_buf(substream),
+                                        azx_dev, &bdl, ofs, period_bytes,
                                         !substream->runtime->no_period_wakeup);
                if (ofs < 0)
                        goto error;
@@ -1531,6 +1541,11 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
        bus_temp.power_save = &power_save;
        bus_temp.ops.pm_notify = azx_power_notify;
 #endif
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+       bus_temp.ops.load_dsp_prepare = azx_load_dsp_prepare;
+       bus_temp.ops.load_dsp_trigger = azx_load_dsp_trigger;
+       bus_temp.ops.load_dsp_cleanup = azx_load_dsp_cleanup;
+#endif
 
        err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus);
        if (err < 0)
@@ -2348,6 +2363,87 @@ static void azx_power_notify(struct hda_bus *bus)
 }
 #endif /* CONFIG_SND_HDA_POWER_SAVE */
 
+#ifdef CONFIG_SND_HDA_DSP_LOADER
+/*
+ * DSP loading code (e.g. for CA0132)
+ */
+
+/*use the first stream for loading DSP*/
+static struct azx_dev*
+azx_get_dsp_loader_dev(struct azx *chip)
+{
+       return &chip->azx_dev[chip->playback_index_offset];
+}
+
+static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format,
+                               unsigned int byte_size,
+                               struct snd_dma_buffer *bufp)
+{
+       u32 *bdl;
+       struct azx *chip = bus->private_data;
+       struct azx_dev *azx_dev;
+       int err;
+
+       err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG,
+                                 snd_dma_pci_data(chip->pci),
+                                 byte_size, bufp);
+       if (err < 0)
+               goto error;
+
+       azx_dev = azx_get_dsp_loader_dev(chip);
+       azx_dev->bufsize = byte_size;
+       azx_dev->period_bytes = byte_size;
+       azx_dev->format_val = format;
+
+       azx_stream_reset(chip, azx_dev);
+
+       /* reset BDL address */
+       azx_sd_writel(azx_dev, SD_BDLPL, 0);
+       azx_sd_writel(azx_dev, SD_BDLPU, 0);
+
+       azx_dev->frags = 0;
+       bdl = (u32 *)azx_dev->bdl.area;
+       err = setup_bdle(bufp, azx_dev, &bdl, 0, byte_size, 0);
+       if (err < 0)
+               goto error;
+
+       azx_setup_controller(chip, azx_dev);
+       return azx_dev->stream_tag;
+
+error:
+       return err;
+}
+
+static void azx_load_dsp_trigger(struct hda_bus *bus, bool start)
+{
+       struct azx *chip = bus->private_data;
+       struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
+
+       if (start)
+               azx_stream_start(chip, azx_dev);
+       else
+               azx_stream_stop(chip, azx_dev);
+       azx_dev->running = start;
+}
+
+static void azx_load_dsp_cleanup(struct hda_bus *bus,
+                                struct snd_dma_buffer *dmab)
+{
+       struct azx *chip = bus->private_data;
+       struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
+
+       /* reset BDL address */
+       azx_sd_writel(azx_dev, SD_BDLPL, 0);
+       azx_sd_writel(azx_dev, SD_BDLPU, 0);
+       azx_sd_writel(azx_dev, SD_CTL, 0);
+       azx_dev->bufsize = 0;
+       azx_dev->period_bytes = 0;
+       azx_dev->format_val = 0;
+
+       snd_dma_free_pages(dmab);
+}
+#endif /* CONFIG_SND_HDA_DSP_LOADER */
+
 #ifdef CONFIG_PM
 /*
  * power management