alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/8] ALSA: Add DSP firmware loader (resub)
@ 2012-09-14  1:15 Ian Minett
  2012-09-14  1:15 ` [PATCH 1/8] ALSA: Make snd_sgbuf_get_{ptr|addr}() available for non-SG cases Ian Minett
                   ` (8 more replies)
  0 siblings, 9 replies; 16+ messages in thread
From: Ian Minett @ 2012-09-14  1:15 UTC (permalink / raw)
  To: patch; +Cc: alsa-devel, Ian Minett

From: Ian Minett <ian_minett@creativelabs.com>

>Thanks for the patches, and sorry for the late response, 
No problem, and sorry for ours too- we've been working on fixing the issues you
raised last time. Hopefully this submission will sort out many of those items.
Macros have been taken out, comments added, and firmware is now cached.

>Keep the git commit author as me.
Ok. I ensured you were set as the git commit author for the first two patchfiles. 
Is there anything else I need to do to properly retain your authorship on those 
commits?

>- Why hda_stream_format is defined?
hda_stream_format is defined to store the stream format information used for 
the DSP download.

Thanks,
- Ian

Signed-off-by: Ian Minett <ian_minett@creativelabs.com>

---
1:
- memalloc.h
- pcm.h
- pcm_memory.c
- sgbuf.c
Include Takashi's patch:
Make snd_sgbuf_get_{ptr|addr}() available for non-SG cases.
Passing struct snd_dma_buffer pointer instead, so that they work no
matter whether real SG buffer is used or not.

2:
- hda_intel.c
- hda_codec.h
Include Takashi's code:
Pass DMA buffer pointers in calls to setup_bdle().
Add DSP loader callback routines to controller.

Add new DSP loader switch to Kconfig to enable DSP firmware loading.

3:
- patch_ca0132.c
- ca0132_regs.h
Add DSP register definitions header file

4:
- patch_ca0132.c
Add DSP firmware enums and defs to CA0132 codec

5:
- patch_ca0132.c
Add calls to new DSP loader system to transfer firmware binary
to the hardware.
Add chip read/write routines, DSP I/O, SCP packet format
helper functions and transfer DMA management.

6:
- patch_ca0132.c
Add DSP firmware caching to CA0132 codec

7:
- patch_ca0132.c
Add comments and descriptions to functions.
Merge chipio write address functions and add fix to dsp_write_wait().

8:
- hda_codec.h
Change return value for load_dsp_prepare to -ENOSYS
in case where DSP loader routines are not available.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 1/8] ALSA: Make snd_sgbuf_get_{ptr|addr}() available for non-SG cases
  2012-09-14  1:15 [PATCH 0/8] ALSA: Add DSP firmware loader (resub) Ian Minett
@ 2012-09-14  1:15 ` Ian Minett
  2012-09-14  1:15 ` [PATCH 2/8] ALSA: Add new DSP loader callback routines Ian Minett
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Ian Minett @ 2012-09-14  1:15 UTC (permalink / raw)
  To: patch; +Cc: Takashi Iwai, alsa-devel, Ian Minett

From: Takashi Iwai <tiwai@suse.de>

Passing struct snd_dma_buffer pointer instead, so that they work no
matter whether real SG buffer is used or not.

This is a preliminary work for the HD-audio DSP loader code.

Signed-off-by: Ian Minett <ian_minett@creativelabs.com>

diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h
index c425062..324e19d 100644
--- a/include/sound/memalloc.h
+++ b/include/sound/memalloc.h
@@ -98,8 +98,10 @@ static inline unsigned int snd_sgbuf_aligned_pages(size_t size)
 /*
  * return the physical address at the corresponding offset
  */
-static inline dma_addr_t snd_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t offset)
+static inline dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab,
+					   size_t offset)
 {
+	struct snd_sg_buf *sgbuf = dmab->private_data;
 	dma_addr_t addr = sgbuf->table[offset >> PAGE_SHIFT].addr;
 	addr &= PAGE_MASK;
 	return addr + offset % PAGE_SIZE;
@@ -108,10 +110,31 @@ static inline dma_addr_t snd_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t off
 /*
  * return the virtual address at the corresponding offset
  */
-static inline void *snd_sgbuf_get_ptr(struct snd_sg_buf *sgbuf, size_t offset)
+static inline void *snd_sgbuf_get_ptr(struct snd_dma_buffer *dmab,
+				     size_t offset)
 {
+	struct snd_sg_buf *sgbuf = dmab->private_data;
 	return sgbuf->table[offset >> PAGE_SHIFT].buf + offset % PAGE_SIZE;
 }
+
+unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
+				      unsigned int ofs, unsigned int size);
+#else
+/* non-SG versions */
+static inline dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab,
+					    size_t offset)
+{
+	return dmab->addr + offset;
+}
+
+static inline void *snd_sgbuf_get_ptr(struct snd_dma_buffer *dmab,
+				      size_t offset)
+{
+	return dmab->area + offset;
+}
+
+#define snd_sgbuf_get_chunk_size(dmab, ofs, size)	(size)
+
 #endif /* CONFIG_SND_DMA_SGBUF */
 
 /* allocate/release a buffer */
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index cdca2ab..0c054b9 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -982,54 +982,43 @@ static int snd_pcm_lib_alloc_vmalloc_32_buffer
 	_snd_pcm_lib_alloc_vmalloc_buffer \
 			(subs, size, GFP_KERNEL | GFP_DMA32 | __GFP_ZERO)
 
+#define snd_pcm_get_dma_buf(substream) ((substream)->runtime->dma_buffer_p)
+
 #ifdef CONFIG_SND_DMA_SGBUF
 /*
  * SG-buffer handling
  */
 #define snd_pcm_substream_sgbuf(substream) \
-	((substream)->runtime->dma_buffer_p->private_data)
-
-static inline dma_addr_t
-snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs)
-{
-	struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
-	return snd_sgbuf_get_addr(sg, ofs);
-}
-
-static inline void *
-snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
-{
-	struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
-	return snd_sgbuf_get_ptr(sg, ofs);
-}
+	snd_pcm_get_dma_buf(substream)->private_data
 
 struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream,
 				    unsigned long offset);
-unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
-					  unsigned int ofs, unsigned int size);
-
 #else /* !SND_DMA_SGBUF */
 /*
  * fake using a continuous buffer
  */
-static inline dma_addr_t
-snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs)
-{
-	return substream->runtime->dma_addr + ofs;
-}
-
-static inline void *
-snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
-{
-	return substream->runtime->dma_area + ofs;
-}
-
 #define snd_pcm_sgbuf_ops_page	NULL
-
-#define snd_pcm_sgbuf_get_chunk_size(subs, ofs, size)	(size)
-
 #endif /* SND_DMA_SGBUF */
 
+static inline dma_addr_t
+snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs)
+{
+	return snd_sgbuf_get_addr(snd_pcm_get_dma_buf(substream), ofs);
+}
+
+static inline void *
+snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
+{
+	return snd_sgbuf_get_ptr(snd_pcm_get_dma_buf(substream), ofs);
+}
+
+static inline unsigned int
+snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
+			     unsigned int ofs, unsigned int size)
+{
+	return snd_sgbuf_get_chunk_size(snd_pcm_get_dma_buf(substream), ofs, size);
+}
+
 /* handle mmap counter - PCM mmap callback should handle this counter properly */
 static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area)
 {
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index 9571313..69e01c4 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -327,32 +327,6 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne
 }
 
 EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
-
-/*
- * compute the max chunk size with continuous pages on sg-buffer
- */
-unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
-					  unsigned int ofs, unsigned int size)
-{
-	struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
-	unsigned int start, end, pg;
-
-	start = ofs >> PAGE_SHIFT;
-	end = (ofs + size - 1) >> PAGE_SHIFT;
-	/* check page continuity */
-	pg = sg->table[start].addr >> PAGE_SHIFT;
-	for (;;) {
-		start++;
-		if (start > end)
-			break;
-		pg++;
-		if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
-			return (start << PAGE_SHIFT) - ofs;
-	}
-	/* ok, all on continuous pages */
-	return size;
-}
-EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
 #endif /* CONFIG_SND_DMA_SGBUF */
 
 /**
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
index d0f0035..13a6036 100644
--- a/sound/core/sgbuf.c
+++ b/sound/core/sgbuf.c
@@ -22,6 +22,7 @@
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
+#include <linux/export.h>
 #include <sound/memalloc.h>
 
 
@@ -136,3 +137,29 @@ void *snd_malloc_sgbuf_pages(struct device *device,
 	snd_free_sgbuf_pages(dmab); /* free the table */
 	return NULL;
 }
+
+/*
+ * compute the max chunk size with continuous pages on sg-buffer
+ */
+unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
+				      unsigned int ofs, unsigned int size)
+{
+	struct snd_sg_buf *sg = dmab->private_data;
+	unsigned int start, end, pg;
+
+	start = ofs >> PAGE_SHIFT;
+	end = (ofs + size - 1) >> PAGE_SHIFT;
+	/* check page continuity */
+	pg = sg->table[start].addr >> PAGE_SHIFT;
+	for (;;) {
+		start++;
+		if (start > end)
+			break;
+		pg++;
+		if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
+			return (start << PAGE_SHIFT) - ofs;
+	}
+	/* ok, all on continuous pages */
+	return size;
+}
+EXPORT_SYMBOL(snd_sgbuf_get_chunk_size);
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 2/8] ALSA: Add new DSP loader callback routines
  2012-09-14  1:15 [PATCH 0/8] ALSA: Add DSP firmware loader (resub) Ian Minett
  2012-09-14  1:15 ` [PATCH 1/8] ALSA: Make snd_sgbuf_get_{ptr|addr}() available for non-SG cases Ian Minett
@ 2012-09-14  1:15 ` Ian Minett
  2012-09-14  1:15 ` [PATCH 3/8] ALSA: Add CA0132 register definitions file Ian Minett
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Ian Minett @ 2012-09-14  1:15 UTC (permalink / raw)
  To: patch; +Cc: Takashi Iwai, alsa-devel, Ian Minett

From: Takashi Iwai <tiwai@suse.de>

Signed-off-by: Ian Minett <ian_minett@creativelabs.com>

diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 7105c3d..ba1dbd8 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -236,4 +236,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
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index ee4ae8e..159e07a 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -618,6 +618,17 @@ struct hda_bus_ops {
 	/* notify power-up/down from codec to controller */
 	void (*pm_notify)(struct hda_bus *bus, bool power_up);
 #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 */
@@ -1128,6 +1139,40 @@ static inline void snd_hda_power_sync(struct hda_codec *codec)
 int snd_hda_load_patch(struct hda_bus *bus, size_t size, const void *buf);
 #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
  */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index e1a12c7..f4ec65d 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -1037,6 +1037,15 @@ static unsigned int azx_get_response(struct hda_bus *bus,
 static void azx_power_notify(struct hda_bus *bus, bool power_up);
 #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)
 {
@@ -1358,7 +1367,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
  * set up a BDL entry
  */
 static int setup_bdle(struct azx *chip,
-		      struct snd_pcm_substream *substream,
+		      struct snd_dma_buffer *dmab,
 		      struct azx_dev *azx_dev, u32 **bdlp,
 		      int ofs, int size, int with_ioc)
 {
@@ -1371,12 +1380,12 @@ static int setup_bdle(struct azx *chip,
 		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);
 		/* one BDLE cannot cross 4K boundary on CTHDA chips */
 		if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) {
 			u32 remain = 0x1000 - (ofs & 0xfff);
@@ -1435,7 +1444,8 @@ static int azx_setup_periods(struct azx *chip,
 				   bdl_pos_adj[chip->dev_index]);
 			pos_adj = 0;
 		} else {
-			ofs = setup_bdle(chip, substream, azx_dev,
+			ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
+					 azx_dev,
 					 &bdl, ofs, pos_adj, true);
 			if (ofs < 0)
 				goto error;
@@ -1444,10 +1454,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(chip, substream, azx_dev, &bdl, ofs,
+			ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
+					 azx_dev, &bdl, ofs,
 					 period_bytes - pos_adj, 0);
 		else
-			ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
+			ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
+					 azx_dev, &bdl, ofs,
 					 period_bytes,
 					 !azx_dev->no_period_wakeup);
 		if (ofs < 0)
@@ -1609,6 +1621,11 @@ static int DELAYED_INIT_MARK azx_codec_create(struct azx *chip, const char *mode
 	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)
@@ -2406,6 +2423,93 @@ static void azx_stop_chip(struct azx *chip)
 	chip->initialized = 0;
 }
 
+#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;
+
+	if (snd_hda_lock_devices(bus))
+		return -EBUSY;
+
+	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(chip, 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:
+	snd_hda_unlock_devices(bus);
+	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);
+
+	snd_hda_unlock_devices(bus);
+}
+#endif /* CONFIG_SND_HDA_DSP_LOADER */
+
 #ifdef CONFIG_PM
 /* power-up/down the controller */
 static void azx_power_notify(struct hda_bus *bus, bool power_up)
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 3/8] ALSA: Add CA0132 register definitions file
  2012-09-14  1:15 [PATCH 0/8] ALSA: Add DSP firmware loader (resub) Ian Minett
  2012-09-14  1:15 ` [PATCH 1/8] ALSA: Make snd_sgbuf_get_{ptr|addr}() available for non-SG cases Ian Minett
  2012-09-14  1:15 ` [PATCH 2/8] ALSA: Add new DSP loader callback routines Ian Minett
@ 2012-09-14  1:15 ` Ian Minett
  2012-09-14  1:15 ` [PATCH 4/8] ALSA: Add DSP firmware enums and defs to CA0132 codec Ian Minett
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Ian Minett @ 2012-09-14  1:15 UTC (permalink / raw)
  To: patch; +Cc: alsa-devel, Ian Minett

From: Ian Minett <ian_minett@creativelabs.com>


Signed-off-by: Ian Minett <ian_minett@creativelabs.com>

diff --git a/sound/pci/hda/ca0132_regs.h b/sound/pci/hda/ca0132_regs.h
new file mode 100644
index 0000000..831ca9c
--- /dev/null
+++ b/sound/pci/hda/ca0132_regs.h
@@ -0,0 +1,409 @@
+/*
+ * HD audio interface patch for Creative CA0132 chip.
+ * CA0132 registers defines.
+ *
+ * Copyright (c) 2011, Creative Technology Ltd.
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef __CA0132_REGS_H
+#define __CA0312_REGS_H
+
+#define DSP_CHIP_OFFSET                0x100000
+#define DSP_DBGCNTL_MODULE_OFFSET      0xE30
+#define DSP_DBGCNTL_INST_OFFSET \
+	(DSP_CHIP_OFFSET + DSP_DBGCNTL_MODULE_OFFSET)
+
+#define DSP_DBGCNTL_EXEC_LOBIT         0x0
+#define DSP_DBGCNTL_EXEC_HIBIT         0x3
+#define DSP_DBGCNTL_EXEC_MASK          0xF
+
+#define DSP_DBGCNTL_SS_LOBIT           0x4
+#define DSP_DBGCNTL_SS_HIBIT           0x7
+#define DSP_DBGCNTL_SS_MASK            0xF0
+
+#define DSP_DBGCNTL_STATE_LOBIT        0xA
+#define DSP_DBGCNTL_STATE_HIBIT        0xD
+#define DSP_DBGCNTL_STATE_MASK         0x3C00
+
+#define XRAM_CHIP_OFFSET               0x0
+#define XRAM_XRAM_CHANNEL_COUNT        0xE000
+#define XRAM_XRAM_MODULE_OFFSET        0x0
+#define XRAM_XRAM_CHAN_INCR            4
+#define XRAM_XRAM_INST_OFFSET(_chan) \
+	(XRAM_CHIP_OFFSET + XRAM_XRAM_MODULE_OFFSET + \
+	(_chan * XRAM_XRAM_CHAN_INCR))
+
+#define YRAM_CHIP_OFFSET               0x40000
+#define YRAM_YRAM_CHANNEL_COUNT        0x8000
+#define YRAM_YRAM_MODULE_OFFSET        0x0
+#define YRAM_YRAM_CHAN_INCR            4
+#define YRAM_YRAM_INST_OFFSET(_chan) \
+	(YRAM_CHIP_OFFSET + YRAM_YRAM_MODULE_OFFSET + \
+	(_chan * YRAM_YRAM_CHAN_INCR))
+
+#define UC_CHIP_OFFSET                 0x80000
+#define UC_UC_CHANNEL_COUNT            0x10000
+#define UC_UC_MODULE_OFFSET            0x0
+#define UC_UC_CHAN_INCR                4
+#define UC_UC_INST_OFFSET(_chan) \
+	(UC_CHIP_OFFSET + UC_UC_MODULE_OFFSET + \
+	(_chan * UC_UC_CHAN_INCR))
+
+#define AXRAM_CHIP_OFFSET              0x3C000
+#define AXRAM_AXRAM_CHANNEL_COUNT      0x1000
+#define AXRAM_AXRAM_MODULE_OFFSET      0x0
+#define AXRAM_AXRAM_CHAN_INCR          4
+#define AXRAM_AXRAM_INST_OFFSET(_chan) \
+	(AXRAM_CHIP_OFFSET + AXRAM_AXRAM_MODULE_OFFSET + \
+	(_chan * AXRAM_AXRAM_CHAN_INCR))
+
+#define AYRAM_CHIP_OFFSET              0x78000
+#define AYRAM_AYRAM_CHANNEL_COUNT      0x1000
+#define AYRAM_AYRAM_MODULE_OFFSET      0x0
+#define AYRAM_AYRAM_CHAN_INCR          4
+#define AYRAM_AYRAM_INST_OFFSET(_chan) \
+	(AYRAM_CHIP_OFFSET + AYRAM_AYRAM_MODULE_OFFSET + \
+	(_chan * AYRAM_AYRAM_CHAN_INCR))
+
+#define DSPDMAC_CHIP_OFFSET            0x110000
+#define DSPDMAC_DMA_CFG_CHANNEL_COUNT  12
+#define DSPDMAC_DMACFG_MODULE_OFFSET   0xF00
+#define DSPDMAC_DMACFG_CHAN_INCR       0x10
+#define DSPDMAC_DMACFG_INST_OFFSET(_chan) \
+	(DSPDMAC_CHIP_OFFSET + DSPDMAC_DMACFG_MODULE_OFFSET + \
+	(_chan * DSPDMAC_DMACFG_CHAN_INCR))
+
+#define DSPDMAC_DMACFG_DBADR_LOBIT     0x0
+#define DSPDMAC_DMACFG_DBADR_HIBIT     0x10
+#define DSPDMAC_DMACFG_DBADR_MASK      0x1FFFF
+#define DSPDMAC_DMACFG_LP_LOBIT        0x11
+#define DSPDMAC_DMACFG_LP_HIBIT        0x11
+#define DSPDMAC_DMACFG_LP_MASK         0x20000
+
+#define DSPDMAC_DMACFG_AINCR_LOBIT     0x12
+#define DSPDMAC_DMACFG_AINCR_HIBIT     0x12
+#define DSPDMAC_DMACFG_AINCR_MASK      0x40000
+
+#define DSPDMAC_DMACFG_DWR_LOBIT       0x13
+#define DSPDMAC_DMACFG_DWR_HIBIT       0x13
+#define DSPDMAC_DMACFG_DWR_MASK        0x80000
+
+#define DSPDMAC_DMACFG_AJUMP_LOBIT     0x14
+#define DSPDMAC_DMACFG_AJUMP_HIBIT     0x17
+#define DSPDMAC_DMACFG_AJUMP_MASK      0xF00000
+
+#define DSPDMAC_DMACFG_AMODE_LOBIT     0x18
+#define DSPDMAC_DMACFG_AMODE_HIBIT     0x19
+#define DSPDMAC_DMACFG_AMODE_MASK      0x3000000
+
+#define DSPDMAC_DMACFG_LK_LOBIT        0x1A
+#define DSPDMAC_DMACFG_LK_HIBIT        0x1A
+#define DSPDMAC_DMACFG_LK_MASK         0x4000000
+
+#define DSPDMAC_DMACFG_AICS_LOBIT      0x1B
+#define DSPDMAC_DMACFG_AICS_HIBIT      0x1F
+#define DSPDMAC_DMACFG_AICS_MASK       0xF8000000
+
+#define DSPDMAC_DMACFG_LP_SINGLE                 0
+#define DSPDMAC_DMACFG_LP_LOOPING                1
+
+#define DSPDMAC_DMACFG_AINCR_XANDY               0
+#define DSPDMAC_DMACFG_AINCR_XORY                1
+
+#define DSPDMAC_DMACFG_DWR_DMA_RD                0
+#define DSPDMAC_DMACFG_DWR_DMA_WR                1
+
+#define DSPDMAC_DMACFG_AMODE_LINEAR              0
+#define DSPDMAC_DMACFG_AMODE_RSV1                1
+#define DSPDMAC_DMACFG_AMODE_WINTLV              2
+#define DSPDMAC_DMACFG_AMODE_GINTLV              3
+
+#define DSPDMAC_DSP_ADR_OFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADROFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADROFS_CHAN_INCR    0x10
+#define DSPDMAC_DSPADROFS_INST_OFFSET(_chan) \
+	(DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADROFS_MODULE_OFFSET + \
+	(_chan * DSPDMAC_DSPADROFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADROFS_COFS_LOBIT   0x0
+#define DSPDMAC_DSPADROFS_COFS_HIBIT   0xF
+#define DSPDMAC_DSPADROFS_COFS_MASK    0xFFFF
+
+#define DSPDMAC_DSPADROFS_BOFS_LOBIT   0x10
+#define DSPDMAC_DSPADROFS_BOFS_HIBIT   0x1F
+#define DSPDMAC_DSPADROFS_BOFS_MASK    0xFFFF0000
+
+#define DSPDMAC_DSP_ADR_WOFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADRWOFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADRWOFS_CHAN_INCR   0x10
+
+#define DSPDMAC_DSPADRWOFS_INST_OFFSET(_chan) \
+	(DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRWOFS_MODULE_OFFSET + \
+	(_chan * DSPDMAC_DSPADRWOFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADRWOFS_WCOFS_LOBIT 0x0
+#define DSPDMAC_DSPADRWOFS_WCOFS_HIBIT 0xA
+#define DSPDMAC_DSPADRWOFS_WCOFS_MASK  0x7FF
+
+#define DSPDMAC_DSPADRWOFS_WCBFR_LOBIT 0xB
+#define DSPDMAC_DSPADRWOFS_WCBFR_HIBIT 0xF
+#define DSPDMAC_DSPADRWOFS_WCBFR_MASK  0xF800
+
+#define DSPDMAC_DSPADRWOFS_WBOFS_LOBIT 0x10
+#define DSPDMAC_DSPADRWOFS_WBOFS_HIBIT 0x1A
+#define DSPDMAC_DSPADRWOFS_WBOFS_MASK  0x7FF0000
+
+#define DSPDMAC_DSPADRWOFS_WBBFR_LOBIT 0x1B
+#define DSPDMAC_DSPADRWOFS_WBBFR_HIBIT 0x1F
+#define DSPDMAC_DSPADRWOFS_WBBFR_MASK  0xF8000000
+
+#define DSPDMAC_DSP_ADR_GOFS_CHANNEL_COUNT 12
+#define DSPDMAC_DSPADRGOFS_MODULE_OFFSET 0xF04
+#define DSPDMAC_DSPADRGOFS_CHAN_INCR   0x10
+#define DSPDMAC_DSPADRGOFS_INST_OFFSET(_chan) \
+	(DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRGOFS_MODULE_OFFSET + \
+	(_chan * DSPDMAC_DSPADRGOFS_CHAN_INCR))
+
+#define DSPDMAC_DSPADRGOFS_GCOFS_LOBIT 0x0
+#define DSPDMAC_DSPADRGOFS_GCOFS_HIBIT 0x9
+#define DSPDMAC_DSPADRGOFS_GCOFS_MASK  0x3FF
+
+#define DSPDMAC_DSPADRGOFS_GCS_LOBIT   0xA
+#define DSPDMAC_DSPADRGOFS_GCS_HIBIT   0xC
+#define DSPDMAC_DSPADRGOFS_GCS_MASK    0x1C00
+
+#define DSPDMAC_DSPADRGOFS_GCBFR_LOBIT 0xD
+#define DSPDMAC_DSPADRGOFS_GCBFR_HIBIT 0xF
+#define DSPDMAC_DSPADRGOFS_GCBFR_MASK  0xE000
+
+#define DSPDMAC_DSPADRGOFS_GBOFS_LOBIT 0x10
+#define DSPDMAC_DSPADRGOFS_GBOFS_HIBIT 0x19
+#define DSPDMAC_DSPADRGOFS_GBOFS_MASK  0x3FF0000
+
+#define DSPDMAC_DSPADRGOFS_GBS_LOBIT   0x1A
+#define DSPDMAC_DSPADRGOFS_GBS_HIBIT   0x1C
+#define DSPDMAC_DSPADRGOFS_GBS_MASK    0x1C000000
+
+#define DSPDMAC_DSPADRGOFS_GBBFR_LOBIT 0x1D
+#define DSPDMAC_DSPADRGOFS_GBBFR_HIBIT 0x1F
+#define DSPDMAC_DSPADRGOFS_GBBFR_MASK  0xE0000000
+
+#define DSPDMAC_XFR_CNT_CHANNEL_COUNT  12
+#define DSPDMAC_XFRCNT_MODULE_OFFSET   0xF08
+#define DSPDMAC_XFRCNT_CHAN_INCR       0x10
+
+#define DSPDMAC_XFRCNT_INST_OFFSET(_chan) \
+	(DSPDMAC_CHIP_OFFSET + DSPDMAC_XFRCNT_MODULE_OFFSET + \
+	(_chan * DSPDMAC_XFRCNT_CHAN_INCR))
+
+#define DSPDMAC_XFRCNT_CCNT_LOBIT      0x0
+#define DSPDMAC_XFRCNT_CCNT_HIBIT      0xF
+#define DSPDMAC_XFRCNT_CCNT_MASK       0xFFFF
+
+#define DSPDMAC_XFRCNT_BCNT_LOBIT      0x10
+#define DSPDMAC_XFRCNT_BCNT_HIBIT      0x1F
+#define DSPDMAC_XFRCNT_BCNT_MASK       0xFFFF0000
+
+#define DSPDMAC_IRQ_CNT_CHANNEL_COUNT  12
+#define DSPDMAC_IRQCNT_MODULE_OFFSET   0xF0C
+#define DSPDMAC_IRQCNT_CHAN_INCR       0x10
+#define DSPDMAC_IRQCNT_INST_OFFSET(_chan) \
+	(DSPDMAC_CHIP_OFFSET + DSPDMAC_IRQCNT_MODULE_OFFSET + \
+	(_chan * DSPDMAC_IRQCNT_CHAN_INCR))
+
+#define DSPDMAC_IRQCNT_CICNT_LOBIT     0x0
+#define DSPDMAC_IRQCNT_CICNT_HIBIT     0xF
+#define DSPDMAC_IRQCNT_CICNT_MASK      0xFFFF
+
+#define DSPDMAC_IRQCNT_BICNT_LOBIT     0x10
+#define DSPDMAC_IRQCNT_BICNT_HIBIT     0x1F
+#define DSPDMAC_IRQCNT_BICNT_MASK      0xFFFF0000
+
+#define DSPDMAC_AUD_CHSEL_CHANNEL_COUNT 12
+#define DSPDMAC_AUDCHSEL_MODULE_OFFSET 0xFC0
+#define DSPDMAC_AUDCHSEL_CHAN_INCR     0x4
+#define DSPDMAC_AUDCHSEL_INST_OFFSET(_chan) \
+	(DSPDMAC_CHIP_OFFSET + DSPDMAC_AUDCHSEL_MODULE_OFFSET + \
+	(_chan * DSPDMAC_AUDCHSEL_CHAN_INCR))
+
+#define DSPDMAC_AUDCHSEL_ACS_LOBIT     0x0
+#define DSPDMAC_AUDCHSEL_ACS_HIBIT     0x1F
+#define DSPDMAC_AUDCHSEL_ACS_MASK      0xFFFFFFFF
+
+#define DSPDMAC_CHNLSTART_MODULE_OFFSET 0xFF0
+#define DSPDMAC_CHNLSTART_INST_OFFSET \
+	(DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTART_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLSTART_EN_LOBIT     0x0
+#define DSPDMAC_CHNLSTART_EN_HIBIT     0xB
+#define DSPDMAC_CHNLSTART_EN_MASK      0xFFF
+
+#define DSPDMAC_CHNLSTART_VAI1_LOBIT   0xC
+#define DSPDMAC_CHNLSTART_VAI1_HIBIT   0xF
+#define DSPDMAC_CHNLSTART_VAI1_MASK    0xF000
+
+#define DSPDMAC_CHNLSTART_DIS_LOBIT    0x10
+#define DSPDMAC_CHNLSTART_DIS_HIBIT    0x1B
+#define DSPDMAC_CHNLSTART_DIS_MASK     0xFFF0000
+
+#define DSPDMAC_CHNLSTART_VAI2_LOBIT   0x1C
+#define DSPDMAC_CHNLSTART_VAI2_HIBIT   0x1F
+#define DSPDMAC_CHNLSTART_VAI2_MASK    0xF0000000
+
+#define DSPDMAC_CHNLSTATUS_MODULE_OFFSET 0xFF4
+#define DSPDMAC_CHNLSTATUS_INST_OFFSET \
+	(DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTATUS_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLSTATUS_ISC_LOBIT   0x0
+#define DSPDMAC_CHNLSTATUS_ISC_HIBIT   0xB
+#define DSPDMAC_CHNLSTATUS_ISC_MASK    0xFFF
+
+#define DSPDMAC_CHNLSTATUS_AOO_LOBIT   0xC
+#define DSPDMAC_CHNLSTATUS_AOO_HIBIT   0xC
+#define DSPDMAC_CHNLSTATUS_AOO_MASK    0x1000
+
+#define DSPDMAC_CHNLSTATUS_AOU_LOBIT   0xD
+#define DSPDMAC_CHNLSTATUS_AOU_HIBIT   0xD
+#define DSPDMAC_CHNLSTATUS_AOU_MASK    0x2000
+
+#define DSPDMAC_CHNLSTATUS_AIO_LOBIT   0xE
+#define DSPDMAC_CHNLSTATUS_AIO_HIBIT   0xE
+#define DSPDMAC_CHNLSTATUS_AIO_MASK    0x4000
+
+#define DSPDMAC_CHNLSTATUS_AIU_LOBIT   0xF
+#define DSPDMAC_CHNLSTATUS_AIU_HIBIT   0xF
+#define DSPDMAC_CHNLSTATUS_AIU_MASK    0x8000
+
+#define DSPDMAC_CHNLSTATUS_IEN_LOBIT   0x10
+#define DSPDMAC_CHNLSTATUS_IEN_HIBIT   0x1B
+#define DSPDMAC_CHNLSTATUS_IEN_MASK    0xFFF0000
+
+#define DSPDMAC_CHNLSTATUS_VAI0_LOBIT  0x1C
+#define DSPDMAC_CHNLSTATUS_VAI0_HIBIT  0x1F
+#define DSPDMAC_CHNLSTATUS_VAI0_MASK   0xF0000000
+
+#define DSPDMAC_CHNLPROP_MODULE_OFFSET 0xFF8
+#define DSPDMAC_CHNLPROP_INST_OFFSET \
+	(DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLPROP_MODULE_OFFSET)
+
+#define DSPDMAC_CHNLPROP_DCON_LOBIT    0x0
+#define DSPDMAC_CHNLPROP_DCON_HIBIT    0xB
+#define DSPDMAC_CHNLPROP_DCON_MASK     0xFFF
+
+#define DSPDMAC_CHNLPROP_FFS_LOBIT     0xC
+#define DSPDMAC_CHNLPROP_FFS_HIBIT     0xC
+#define DSPDMAC_CHNLPROP_FFS_MASK      0x1000
+
+#define DSPDMAC_CHNLPROP_NAJ_LOBIT     0xD
+#define DSPDMAC_CHNLPROP_NAJ_HIBIT     0xD
+#define DSPDMAC_CHNLPROP_NAJ_MASK      0x2000
+
+#define DSPDMAC_CHNLPROP_ENH_LOBIT     0xE
+#define DSPDMAC_CHNLPROP_ENH_HIBIT     0xE
+#define DSPDMAC_CHNLPROP_ENH_MASK      0x4000
+
+#define DSPDMAC_CHNLPROP_MSPCE_LOBIT   0x10
+#define DSPDMAC_CHNLPROP_MSPCE_HIBIT   0x1B
+#define DSPDMAC_CHNLPROP_MSPCE_MASK    0xFFF0000
+
+#define DSPDMAC_CHNLPROP_AC_LOBIT      0x1C
+#define DSPDMAC_CHNLPROP_AC_HIBIT      0x1F
+#define DSPDMAC_CHNLPROP_AC_MASK       0xF0000000
+
+#define DSPDMAC_ACTIVE_MODULE_OFFSET   0xFFC
+#define DSPDMAC_ACTIVE_INST_OFFSET \
+	(DSPDMAC_CHIP_OFFSET + DSPDMAC_ACTIVE_MODULE_OFFSET)
+
+#define DSPDMAC_ACTIVE_AAR_LOBIT       0x0
+#define DSPDMAC_ACTIVE_AAR_HIBIT       0xB
+#define DSPDMAC_ACTIVE_AAR_MASK        0xFFF
+
+#define DSPDMAC_ACTIVE_WFR_LOBIT       0xC
+#define DSPDMAC_ACTIVE_WFR_HIBIT       0x17
+#define DSPDMAC_ACTIVE_WFR_MASK        0xFFF000
+
+#define DSP_AUX_MEM_BASE            0xE000
+#define INVALID_CHIP_ADDRESS        (~0UL)
+
+#define X_SIZE  (XRAM_XRAM_CHANNEL_COUNT   * XRAM_XRAM_CHAN_INCR)
+#define Y_SIZE  (YRAM_YRAM_CHANNEL_COUNT   * YRAM_YRAM_CHAN_INCR)
+#define AX_SIZE (AXRAM_AXRAM_CHANNEL_COUNT * AXRAM_AXRAM_CHAN_INCR)
+#define AY_SIZE (AYRAM_AYRAM_CHANNEL_COUNT * AYRAM_AYRAM_CHAN_INCR)
+#define UC_SIZE (UC_UC_CHANNEL_COUNT       * UC_UC_CHAN_INCR)
+
+#define XEXT_SIZE (X_SIZE + AX_SIZE)
+#define YEXT_SIZE (Y_SIZE + AY_SIZE)
+
+#define U64K 0x10000UL
+
+#define X_END  (XRAM_CHIP_OFFSET  + X_SIZE)
+#define X_EXT  (XRAM_CHIP_OFFSET  + XEXT_SIZE)
+#define AX_END (XRAM_CHIP_OFFSET  + U64K*4)
+
+#define Y_END  (YRAM_CHIP_OFFSET  + Y_SIZE)
+#define Y_EXT  (YRAM_CHIP_OFFSET  + YEXT_SIZE)
+#define AY_END (YRAM_CHIP_OFFSET  + U64K*4)
+
+#define UC_END (UC_CHIP_OFFSET    + UC_SIZE)
+
+#define X_RANGE_MAIN(a, s) \
+	(((a)+((s)-1)*XRAM_XRAM_CHAN_INCR <  X_END))
+#define X_RANGE_AUX(a, s)  \
+	(((a) >= X_END) && ((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END))
+#define X_RANGE_EXT(a, s)  \
+	(((a)+((s)-1)*XRAM_XRAM_CHAN_INCR <  X_EXT))
+#define X_RANGE_ALL(a, s)  \
+	(((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END))
+
+#define Y_RANGE_MAIN(a, s) \
+	(((a) >= YRAM_CHIP_OFFSET) && \
+	((a)+((s)-1)*YRAM_YRAM_CHAN_INCR <  Y_END))
+#define Y_RANGE_AUX(a, s)  \
+	(((a) >= Y_END) && \
+	((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END))
+#define Y_RANGE_EXT(a, s)  \
+	(((a) >= YRAM_CHIP_OFFSET) && \
+	((a)+((s)-1)*YRAM_YRAM_CHAN_INCR <  Y_EXT))
+#define Y_RANGE_ALL(a, s)  \
+	(((a) >= YRAM_CHIP_OFFSET) && \
+	((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END))
+
+#define UC_RANGE(a, s) \
+	(((a) >= UC_CHIP_OFFSET) && \
+	((a)+((s)-1)*UC_UC_CHAN_INCR     < UC_END))
+
+#define X_OFF(a) \
+	(((a) - XRAM_CHIP_OFFSET) / XRAM_XRAM_CHAN_INCR)
+#define AX_OFF(a) \
+	(((a) % (AXRAM_AXRAM_CHANNEL_COUNT * \
+	AXRAM_AXRAM_CHAN_INCR)) / AXRAM_AXRAM_CHAN_INCR)
+
+#define Y_OFF(a) \
+	(((a) - YRAM_CHIP_OFFSET) / YRAM_YRAM_CHAN_INCR)
+#define AY_OFF(a) \
+	(((a) % (AYRAM_AYRAM_CHANNEL_COUNT * \
+	AYRAM_AYRAM_CHAN_INCR)) / AYRAM_AYRAM_CHAN_INCR)
+
+#define UC_OFF(a)  (((a) - UC_CHIP_OFFSET) / UC_UC_CHAN_INCR)
+
+#define X_EXT_MAIN_SIZE(a)  (XRAM_XRAM_CHANNEL_COUNT - X_OFF(a))
+#define X_EXT_AUX_SIZE(a, s) ((s) - X_EXT_MAIN_SIZE(a))
+
+#define Y_EXT_MAIN_SIZE(a)  (YRAM_YRAM_CHANNEL_COUNT - Y_OFF(a))
+#define Y_EXT_AUX_SIZE(a, s) ((s) - Y_EXT_MAIN_SIZE(a))
+
+#endif
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 49750a9..da65535 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -32,6 +32,8 @@
 #include "hda_local.h"
 #include "hda_auto_parser.h"
 
+#include "ca0132_regs.h"
+
 #define WIDGET_CHIP_CTRL      0x15
 #define WIDGET_DSP_CTRL       0x16
 
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 4/8] ALSA: Add DSP firmware enums and defs to CA0132 codec
  2012-09-14  1:15 [PATCH 0/8] ALSA: Add DSP firmware loader (resub) Ian Minett
                   ` (2 preceding siblings ...)
  2012-09-14  1:15 ` [PATCH 3/8] ALSA: Add CA0132 register definitions file Ian Minett
@ 2012-09-14  1:15 ` Ian Minett
  2012-09-14  8:40   ` Takashi Iwai
  2012-09-14  1:15 ` [PATCH 5/8] ALSA: Update CA0132 codec to load DSP firmware binary Ian Minett
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 16+ messages in thread
From: Ian Minett @ 2012-09-14  1:15 UTC (permalink / raw)
  To: patch; +Cc: alsa-devel, Ian Minett

From: Ian Minett <ian_minett@creativelabs.com>


Signed-off-by: Ian Minett <ian_minett@creativelabs.com>

diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index da65535..8ea3348 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -27,6 +27,7 @@
 #include <linux/pci.h>
 #include <linux/mutex.h>
 #include <linux/module.h>
+#include <linux/firmware.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
@@ -34,12 +35,37 @@
 
 #include "ca0132_regs.h"
 
+#define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18)
+#define DSP_DMA_WRITE_BUFLEN_OVLY (1UL<<15)
+
+#define DMA_TRANSFER_FRAME_SIZE_NWORDS		8
+#define DMA_TRANSFER_MAX_FRAME_SIZE_NWORDS	32
+#define DMA_OVERLAY_FRAME_SIZE_NWORDS		2
+
+#define MASTERCONTROL				0x80
+#define MASTERCONTROL_ALLOC_DMA_CHAN		9
+
 #define WIDGET_CHIP_CTRL      0x15
 #define WIDGET_DSP_CTRL       0x16
 
 #define WUH_MEM_CONNID        10
 #define DSP_MEM_CONNID        16
 
+#define MEM_CONNID_MICIN1     3
+#define MEM_CONNID_MICIN2     5
+#define MEM_CONNID_MICOUT1    12
+#define MEM_CONNID_MICOUT2    14
+#define MEM_CONNID_WUH        10
+#define MEM_CONNID_DSP        16
+#define MEM_CONNID_DMIC       100
+
+#define SCP_SET    0
+#define SCP_GET    1
+
+#define EFX_FILE   "ctefx.bin"
+
+MODULE_FIRMWARE(EFX_FILE);
+
 enum hda_cmd_vendor_io {
 	/* for DspIO node */
 	VENDOR_DSPIO_SCP_WRITE_DATA_LOW      = 0x000,
@@ -64,7 +90,11 @@ enum hda_cmd_vendor_io {
 	VENDOR_CHIPIO_HIC_POST_READ          = 0x702,
 	VENDOR_CHIPIO_HIC_READ_DATA          = 0xF03,
 
+	VENDOR_CHIPIO_8051_DATA_WRITE        = 0x707,
+	VENDOR_CHIPIO_8051_DATA_READ         = 0xF07,
+
 	VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE   = 0x70A,
+	VENDOR_CHIPIO_CT_EXTENSIONS_GET      = 0xF0A,
 
 	VENDOR_CHIPIO_PLL_PMU_WRITE          = 0x70C,
 	VENDOR_CHIPIO_PLL_PMU_READ           = 0xF0C,
@@ -72,18 +102,27 @@ enum hda_cmd_vendor_io {
 	VENDOR_CHIPIO_8051_ADDRESS_HIGH      = 0x70E,
 	VENDOR_CHIPIO_FLAG_SET               = 0x70F,
 	VENDOR_CHIPIO_FLAGS_GET              = 0xF0F,
-	VENDOR_CHIPIO_PARAMETER_SET          = 0x710,
-	VENDOR_CHIPIO_PARAMETER_GET          = 0xF10,
+	VENDOR_CHIPIO_PARAM_SET              = 0x710,
+	VENDOR_CHIPIO_PARAM_GET              = 0xF10,
 
 	VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET  = 0x711,
 	VENDOR_CHIPIO_PORT_ALLOC_SET         = 0x712,
 	VENDOR_CHIPIO_PORT_ALLOC_GET         = 0xF12,
 	VENDOR_CHIPIO_PORT_FREE_SET          = 0x713,
 
-	VENDOR_CHIPIO_PARAMETER_EX_ID_GET    = 0xF17,
-	VENDOR_CHIPIO_PARAMETER_EX_ID_SET    = 0x717,
-	VENDOR_CHIPIO_PARAMETER_EX_VALUE_GET = 0xF18,
-	VENDOR_CHIPIO_PARAMETER_EX_VALUE_SET = 0x718
+	VENDOR_CHIPIO_PARAM_EX_ID_GET        = 0xF17,
+	VENDOR_CHIPIO_PARAM_EX_ID_SET        = 0x717,
+	VENDOR_CHIPIO_PARAM_EX_VALUE_GET     = 0xF18,
+	VENDOR_CHIPIO_PARAM_EX_VALUE_SET     = 0x718,
+
+	VENDOR_CHIPIO_DMIC_CTL_SET           = 0x788,
+	VENDOR_CHIPIO_DMIC_CTL_GET           = 0xF88,
+	VENDOR_CHIPIO_DMIC_PIN_SET           = 0x789,
+	VENDOR_CHIPIO_DMIC_PIN_GET           = 0xF89,
+	VENDOR_CHIPIO_DMIC_MCLK_SET          = 0x78A,
+	VENDOR_CHIPIO_DMIC_MCLK_GET          = 0xF8A,
+
+	VENDOR_CHIPIO_EAPD_SEL_SET           = 0x78D
 };
 
 /*
@@ -133,7 +172,7 @@ enum control_flag_id {
 	/* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */
 	CONTROL_FLAG_PORT_A_10KOHM_LOAD     = 20,
 	/* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */
-	CONTROL_FLAG_PORT_D_10K0HM_LOAD     = 21,
+	CONTROL_FLAG_PORT_D_10KOHM_LOAD     = 21,
 	/* ASI rate is 48kHz/96kHz */
 	CONTROL_FLAG_ASI_96KHZ              = 22,
 	/* DAC power settings able to control attached ports no/yes */
@@ -147,7 +186,7 @@ enum control_flag_id {
 /*
  * Control parameter IDs
  */
-enum control_parameter_id {
+enum control_param_id {
 	/* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */
 	CONTROL_PARAM_SPDIF1_SOURCE            = 2,
 
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 5/8] ALSA: Update CA0132 codec to load DSP firmware binary
  2012-09-14  1:15 [PATCH 0/8] ALSA: Add DSP firmware loader (resub) Ian Minett
                   ` (3 preceding siblings ...)
  2012-09-14  1:15 ` [PATCH 4/8] ALSA: Add DSP firmware enums and defs to CA0132 codec Ian Minett
@ 2012-09-14  1:15 ` Ian Minett
  2012-09-14  8:51   ` Takashi Iwai
  2012-09-14  8:52   ` Takashi Iwai
  2012-09-14  1:15 ` [PATCH 6/8] ALSA: Add firmware caching to CA0132 codec Ian Minett
                   ` (3 subsequent siblings)
  8 siblings, 2 replies; 16+ messages in thread
From: Ian Minett @ 2012-09-14  1:15 UTC (permalink / raw)
  To: patch; +Cc: alsa-devel, Ian Minett

From: Ian Minett <ian_minett@creativelabs.com>

Signed-off-by: Ian Minett <ian_minett@creativelabs.com>

diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 8ea3348..11f5910 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -48,9 +48,6 @@
 #define WIDGET_CHIP_CTRL      0x15
 #define WIDGET_DSP_CTRL       0x16
 
-#define WUH_MEM_CONNID        10
-#define DSP_MEM_CONNID        16
-
 #define MEM_CONNID_MICIN1     3
 #define MEM_CONNID_MICIN2     5
 #define MEM_CONNID_MICOUT1    12
@@ -352,6 +349,25 @@ static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
 #define add_in_mono_volume(codec, nid, pfx, chan) \
 	_add_volume(codec, nid, pfx, chan, 1)
 
+enum dsp_download_state {
+	DSP_DOWNLOAD_FAILED = -1,
+	DSP_DOWNLOAD_INIT   = 0,
+	DSP_DOWNLOADING     = 1,
+	DSP_DOWNLOADED      = 2
+};
+
+struct hda_stream_format {
+	unsigned int   sample_rate;
+	unsigned short valid_bits_per_sample;
+	unsigned short container_size;
+	unsigned short number_channels;
+};
+
+/* retrieve parameters from hda format */
+#define get_hdafmt_chs(fmt)	(fmt & 0xf)
+#define get_hdafmt_bits(fmt)	((fmt >> 4) & 0x7)
+#define get_hdafmt_rate(fmt)	((fmt >> 8) & 0x7f)
+#define get_hdafmt_type(fmt)	((fmt >> 15) & 0x1)
 
 /*
  * CA0132 specific
@@ -371,11 +387,55 @@ struct ca0132_spec {
 	long curr_hp_switch;
 	long curr_hp_volume[2];
 	long curr_speaker_switch;
-	struct mutex chipio_mutex;
 	const char *input_labels[AUTO_PIN_LAST];
 	struct hda_pcm pcm_rec[2]; /* PCM information */
+
+	/* chip access */
+	struct mutex chipio_mutex; /* chip access mutex */
+	u32 curr_chip_addx;
+
+	/* DSP download related */
+	enum dsp_download_state dsp_state;
+	unsigned int dsp_stream_id;
+	unsigned int wait_scp;
+	unsigned int wait_scp_header;
+	unsigned int wait_num_data;
+	unsigned int scp_resp_header;
+	unsigned int scp_resp_data[4];
+	unsigned int scp_resp_count;
 };
 
+/*
+ * CA0132 codec access
+ */
+unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
+		unsigned int verb, unsigned int parm, unsigned int *res)
+{
+	unsigned int response;
+	response = snd_hda_codec_read(codec, nid, 0, verb, parm);
+	*res = response;
+
+	return ((response == -1) ? -1 : 0);
+}
+
+static int codec_set_converter_format(struct hda_codec *codec, hda_nid_t nid,
+		unsigned short converter_format, unsigned int *res)
+{
+	return codec_send_command(codec, nid, VENDOR_CHIPIO_STREAM_FORMAT,
+				converter_format & 0xffff, res);
+}
+
+static int codec_set_converter_stream_channel(struct hda_codec *codec,
+				hda_nid_t nid, unsigned char stream,
+				unsigned char channel, unsigned int *res)
+{
+	unsigned char converter_stream_channel = 0;
+
+	converter_stream_channel = (stream << 4) | (channel & 0x0f);
+	return codec_send_command(codec, nid, AC_VERB_SET_CHANNEL_STREAMID,
+				converter_stream_channel, res);
+}
+
 /* Chip access helper function */
 static int chipio_send(struct hda_codec *codec,
 		       unsigned int reg,
@@ -415,6 +475,30 @@ static int chipio_write_address(struct hda_codec *codec,
 	return res;
 }
 
+static int chipio_write_addx(struct hda_codec *codec, u32 chip_addx)
+{
+	struct ca0132_spec *spec = codec->spec;
+	int status;
+
+	if (spec->curr_chip_addx == chip_addx)
+		return 0;
+
+	/* send low 16 bits of the address */
+	status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
+			  chip_addx & 0xffff);
+
+	if (status < 0)
+		return status;
+
+	/* send high 16 bits of the address */
+	status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
+			  chip_addx >> 16);
+
+	spec->curr_chip_addx = (status < 0) ? ~0UL : chip_addx;
+
+	return status;
+}
+
 /*
  * Write data through the vendor widget -- NOT protected by the Mutex!
  */
@@ -435,6 +519,24 @@ static int chipio_write_data(struct hda_codec *codec, unsigned int data)
 	return res;
 }
 
+static int chipio_write_data_multiple(struct hda_codec *codec,
+				      const u32 *data,
+				      unsigned int count)
+{
+	int status = 0;
+
+	if (data == NULL) {
+		snd_printdd(KERN_ERR "chipio_write_data null ptr");
+		return -EINVAL;
+	}
+
+	while ((count-- != 0) && (status == 0))
+		status = chipio_write_data(codec, *data++);
+
+	return status;
+}
+
+
 /*
  * Read data through the vendor widget -- NOT protected by the Mutex!
  */
@@ -486,6 +588,26 @@ exit:
 	return err;
 }
 
+static int chipio_write_multiple(struct hda_codec *codec,
+				 u32 chip_addx,
+				 const u32 *data,
+				 unsigned int count)
+{
+	struct ca0132_spec *spec = codec->spec;
+	int status;
+
+	mutex_lock(&spec->chipio_mutex);
+	status = chipio_write_addx(codec, chip_addx);
+	if (status < 0)
+		goto error;
+
+	status = chipio_write_data_multiple(codec, data, count);
+error:
+	mutex_unlock(&spec->chipio_mutex);
+
+	return status;
+}
+
 /*
  * Read the given address through the chip I/O widget
  * protected by the Mutex
@@ -512,6 +634,1425 @@ exit:
 	return err;
 }
 
+static void chipio_set_control_flag(struct hda_codec *codec,
+				    enum control_flag_id flag_id,
+				    bool flag_state)
+{
+	unsigned int val;
+	unsigned int flag_bit;
+
+	flag_bit = (flag_state ? 1 : 0);
+	val = (flag_bit << 7) | (flag_id);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_FLAG_SET, val);
+}
+
+static void chipio_set_control_param(struct hda_codec *codec,
+		enum control_param_id param_id, int param_val)
+{
+	struct ca0132_spec *spec = codec->spec;
+	int val;
+
+	if ((param_id < 32) && (param_val < 8)) {
+		val = (param_val << 5) | (param_id);
+		snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+				    VENDOR_CHIPIO_PARAM_SET, val);
+	} else {
+		mutex_lock(&spec->chipio_mutex);
+		if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
+			snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+					    VENDOR_CHIPIO_PARAM_EX_ID_SET,
+					    param_id);
+			snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+					    VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
+					    param_val);
+		}
+		mutex_unlock(&spec->chipio_mutex);
+	}
+}
+
+static void chipio_set_conn_rate(struct hda_codec *codec,
+				int connid, enum ca0132_sample_rate rate)
+{
+	chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_ID, connid);
+	chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE,
+				 rate);
+}
+
+static void chipio_enable_clocks(struct hda_codec *codec)
+{
+	struct ca0132_spec *spec = codec->spec;
+
+	mutex_lock(&spec->chipio_mutex);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_ADDRESS_LOW, 0);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_ADDRESS_LOW, 5);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_PLL_PMU_WRITE, 0x0b);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_8051_ADDRESS_LOW, 6);
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
+	mutex_unlock(&spec->chipio_mutex);
+}
+
+/*
+ * CA0132 DSP IO stuffs
+ */
+static int dspio_send(struct hda_codec *codec, unsigned int reg,
+		      unsigned int data)
+{
+	unsigned int res;
+	int retry = 50;
+
+	/* send bits of data specified by reg to dsp */
+	do {
+		res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data);
+		if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY))
+			return res;
+	} while (--retry);
+
+	return -EIO;
+}
+
+static void dspio_write_wait(struct hda_codec *codec)
+{
+	int cur_val, prv_val;
+	int retry = 50;
+
+	cur_val = 0;
+	do {
+		prv_val = cur_val;
+		msleep(20);
+		dspio_send(codec, VENDOR_DSPIO_SCP_POST_COUNT_QUERY, 1);
+		dspio_send(codec, VENDOR_DSPIO_STATUS, 0);
+		cur_val = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+					   VENDOR_DSPIO_SCP_READ_COUNT, 0);
+	} while (cur_val && (cur_val == prv_val) && --retry);
+}
+
+static int dspio_write(struct hda_codec *codec, unsigned int scp_data)
+{
+	struct ca0132_spec *spec = codec->spec;
+	int status;
+
+	dspio_write_wait(codec);
+
+	mutex_lock(&spec->chipio_mutex);
+	status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW,
+			    scp_data & 0xffff);
+	if (status < 0)
+		goto error;
+
+	status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH,
+				    scp_data >> 16);
+	if (status < 0)
+		goto error;
+
+	/* OK, now check if the write itself has executed*/
+	status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+				    VENDOR_DSPIO_STATUS, 0);
+error:
+	mutex_unlock(&spec->chipio_mutex);
+
+	return (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL) ?
+			-EIO : 0;
+}
+
+static int dspio_write_multiple(struct hda_codec *codec,
+				unsigned int *buffer, unsigned int size)
+{
+	int status = 0;
+	unsigned int count;
+
+	if ((buffer == NULL))
+		return -EINVAL;
+
+	count = 0;
+	while (count < size) {
+		status = dspio_write(codec, *buffer++);
+		if (status != 0)
+			break;
+		count++;
+	}
+
+	return status;
+}
+
+static inline unsigned int
+make_scp_header(unsigned int target_id, unsigned int source_id,
+		unsigned int get_flag, unsigned int req,
+		unsigned int device_flag, unsigned int resp_flag,
+		unsigned int error_flag, unsigned int data_size)
+{
+	unsigned int header = 0;
+
+	header = (data_size & 0x1f) << 27;
+	header |= (error_flag & 0x01) << 26;
+	header |= (resp_flag & 0x01) << 25;
+	header |= (device_flag & 0x01) << 24;
+	header |= (req & 0x7f) << 17;
+	header |= (get_flag & 0x01) << 16;
+	header |= (source_id & 0xff) << 8;
+	header |= target_id & 0xff;
+
+	return header;
+}
+
+static inline void
+extract_scp_header(unsigned int header,
+		   unsigned int *target_id, unsigned int *source_id,
+		   unsigned int *get_flag, unsigned int *req,
+		   unsigned int *device_flag, unsigned int *resp_flag,
+		   unsigned int *error_flag, unsigned int *data_size)
+{
+	if (data_size)
+		*data_size = (header >> 27) & 0x1f;
+	if (error_flag)
+		*error_flag = (header >> 26) & 0x01;
+	if (resp_flag)
+		*resp_flag = (header >> 25) & 0x01;
+	if (device_flag)
+		*device_flag = (header >> 24) & 0x01;
+	if (req)
+		*req = (header >> 17) & 0x7f;
+	if (get_flag)
+		*get_flag = (header >> 16) & 0x01;
+	if (source_id)
+		*source_id = (header >> 8) & 0xff;
+	if (target_id)
+		*target_id = header & 0xff;
+}
+
+#define SCP_MAX_DATA_WORDS  (16)
+
+/* Structure to contain any SCP message */
+struct scp_msg {
+	unsigned int hdr;
+	unsigned int data[SCP_MAX_DATA_WORDS];
+};
+
+static int dspio_send_scp_message(struct hda_codec *codec,
+				  unsigned char *send_buf,
+				  unsigned int send_buf_size,
+				  unsigned char *return_buf,
+				  unsigned int return_buf_size,
+				  unsigned int *bytes_returned)
+{
+	struct ca0132_spec *spec = codec->spec;
+	int retry;
+	int status = -1;
+	unsigned int scp_send_size = 0;
+	unsigned int total_size;
+	bool waiting_for_resp = false;
+	unsigned int header;
+	struct scp_msg *ret_msg;
+	unsigned int resp_src_id, resp_target_id;
+	unsigned int data_size, src_id, target_id, get_flag, device_flag;
+
+	if (bytes_returned)
+		*bytes_returned = 0;
+
+	/* get scp header from buffer */
+	header = *((unsigned int *)send_buf);
+	extract_scp_header(header, &target_id, &src_id, &get_flag, NULL,
+			   &device_flag, NULL, NULL, &data_size);
+	scp_send_size = data_size + 1;
+	total_size = (scp_send_size * 4);
+
+	if (send_buf_size < total_size)
+		return -EINVAL;
+
+	if (get_flag || device_flag) {
+		if (!return_buf || return_buf_size < 4 || !bytes_returned)
+			return -EINVAL;
+
+		spec->wait_scp_header = *((unsigned int *)send_buf);
+
+		/* swap source id with target id */
+		resp_target_id = src_id;
+		resp_src_id = target_id;
+		spec->wait_scp_header &= 0xffff0000;
+		spec->wait_scp_header |= (resp_src_id << 8) | (resp_target_id);
+		spec->wait_num_data = return_buf_size/sizeof(unsigned int) - 1;
+		spec->wait_scp = 1;
+		waiting_for_resp = true;
+	}
+
+	status = dspio_write_multiple(codec, (unsigned int *)send_buf,
+				      scp_send_size);
+	if (status < 0) {
+		spec->wait_scp = 0;
+		return status;
+	}
+
+	if (waiting_for_resp) {
+		memset(return_buf, 0, return_buf_size);
+		retry = 50;
+		do {
+			msleep(20);
+		} while (spec->wait_scp && (--retry != 0));
+		waiting_for_resp = false;
+		if (retry != 0) {
+			ret_msg = (struct scp_msg *)return_buf;
+			memcpy(&ret_msg->hdr, &spec->scp_resp_header, 4);
+			memcpy(&ret_msg->data, spec->scp_resp_data,
+			       spec->wait_num_data);
+			*bytes_returned = (spec->scp_resp_count + 1) * 4;
+			status = 0;
+		} else {
+			status = -EIO;
+		}
+		spec->wait_scp = 0;
+	}
+
+	return status;
+}
+
+static int dspio_scp(struct hda_codec *codec,
+		int mod_id, int req, int dir, void *data, unsigned int len,
+		void *reply, unsigned int *reply_len)
+{
+	int status = 0;
+	struct scp_msg scp_send, scp_reply;
+	unsigned int ret_bytes, send_size, ret_size;
+	unsigned int send_get_flag, reply_resp_flag, reply_error_flag;
+	unsigned int reply_data_size;
+
+	memset(&scp_send, 0, sizeof(scp_send));
+	memset(&scp_reply, 0, sizeof(scp_reply));
+
+	if ((len != 0 && data == NULL) || (len > SCP_MAX_DATA_WORDS))
+		return -EINVAL;
+
+	if (dir == SCP_GET && reply == NULL) {
+		snd_printdd(KERN_ERR "dspio_scp get but has no buffer");
+		return -EINVAL;
+	}
+
+	if (reply != NULL && (reply_len == NULL || (*reply_len == 0))) {
+		snd_printdd(KERN_ERR "dspio_scp bad resp buf len parms");
+		return -EINVAL;
+	}
+
+	scp_send.hdr = make_scp_header(mod_id, 0x20, (dir == SCP_GET), req,
+				       0, 0, 0, len/sizeof(unsigned int));
+	if (data != NULL && len > 0) {
+		len = min((unsigned int)(sizeof(scp_send.data)), len);
+		memcpy(scp_send.data, data, len);
+	}
+
+	ret_bytes = 0;
+	send_size = sizeof(unsigned int) + len;
+	status = dspio_send_scp_message(codec, (unsigned char *)&scp_send,
+					send_size, (unsigned char *)&scp_reply,
+					sizeof(scp_reply), &ret_bytes);
+
+	if (status < 0) {
+		snd_printdd(KERN_ERR "dspio_scp: send scp msg failed");
+		return status;
+	}
+
+	/* extract send and reply headers members */
+	extract_scp_header(scp_send.hdr, NULL, NULL, &send_get_flag,
+			   NULL, NULL, NULL, NULL, NULL);
+	extract_scp_header(scp_reply.hdr, NULL, NULL, NULL, NULL, NULL,
+			   &reply_resp_flag, &reply_error_flag,
+			   &reply_data_size);
+
+	if (!send_get_flag)
+		return 0;
+
+	if (reply_resp_flag && !reply_error_flag) {
+		ret_size = (ret_bytes - sizeof(scp_reply.hdr))
+					/ sizeof(unsigned int);
+
+		if (*reply_len < ret_size*sizeof(unsigned int)) {
+			snd_printdd(KERN_ERR "reply too long for buf");
+			return -EINVAL;
+		} else if (ret_size != reply_data_size) {
+			snd_printdd(KERN_ERR "RetLen and HdrLen .NE.");
+			return -EINVAL;
+		} else {
+			*reply_len = ret_size*sizeof(unsigned int);
+			memcpy(reply, scp_reply.data, *reply_len);
+		}
+	} else {
+		snd_printdd(KERN_ERR "reply ill-formed or errflag set");
+		return -EIO;
+	}
+
+	return status;
+}
+
+static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan)
+{
+	int status = 0;
+	unsigned int size = sizeof(dma_chan);
+
+	snd_printdd(KERN_INFO "     dspio_alloc_dma_chan() -- begin");
+	status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN,
+			SCP_GET, NULL, 0, dma_chan, &size);
+
+	if (status < 0) {
+		snd_printdd(KERN_INFO "dspio_alloc_dma_chan: SCP Failed");
+		return status;
+	}
+
+	if ((*dma_chan + 1) == 0) {
+		snd_printdd(KERN_INFO "no free dma channels to allocate");
+		return -EBUSY;
+	}
+
+	snd_printdd("dspio_alloc_dma_chan: chan=%d\n", *dma_chan);
+	snd_printdd(KERN_INFO "     dspio_alloc_dma_chan() -- complete");
+
+	return status;
+}
+
+static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan)
+{
+	int status = 0;
+	unsigned int dummy = 0;
+
+	snd_printdd(KERN_INFO "     dspio_free_dma_chan() -- begin");
+	snd_printdd("dspio_free_dma_chan: chan=%d\n", dma_chan);
+
+	status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN,
+			   SCP_SET, &dma_chan, sizeof(dma_chan), NULL, &dummy);
+
+	if (status < 0) {
+		snd_printdd(KERN_INFO "dspio_free_dma_chan: SCP Failed");
+		return status;
+	}
+
+	snd_printdd(KERN_INFO "     dspio_free_dma_chan() -- complete");
+
+	return status;
+}
+
+/*
+ * CA0132 DSP access stuffs
+ */
+static int dsp_set_run_state(struct hda_codec *codec)
+{
+	unsigned int dbg_ctrl_reg;
+	unsigned int halt_state;
+	int err;
+
+	err = chipio_read(codec, DSP_DBGCNTL_INST_OFFSET, &dbg_ctrl_reg);
+	if (err < 0)
+		return err;
+
+	halt_state = (dbg_ctrl_reg & DSP_DBGCNTL_STATE_MASK) >>
+		      DSP_DBGCNTL_STATE_LOBIT;
+
+	if (halt_state != 0) {
+		dbg_ctrl_reg &= ~((halt_state << DSP_DBGCNTL_SS_LOBIT) &
+				  DSP_DBGCNTL_SS_MASK);
+		err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET,
+				   dbg_ctrl_reg);
+		if (err < 0)
+			return err;
+
+		dbg_ctrl_reg |= (halt_state << DSP_DBGCNTL_EXEC_LOBIT) &
+				DSP_DBGCNTL_EXEC_MASK;
+		err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET,
+				   dbg_ctrl_reg);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int dsp_reset(struct hda_codec *codec)
+{
+	unsigned int res;
+	int retry = 20;
+
+	snd_printdd("dsp_reset\n");
+	do {
+		res = dspio_send(codec, VENDOR_DSPIO_DSP_INIT, 0);
+		retry--;
+	} while (res == -EIO && retry);
+
+	if (!retry) {
+		snd_printdd("dsp_reset timeout\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx,
+					bool *code, bool *yram)
+{
+	*code = *yram = false;
+
+	if (UC_RANGE(chip_addx, 1)) {
+		*code = true;
+		return UC_OFF(chip_addx);
+	} else if (X_RANGE_ALL(chip_addx, 1)) {
+		return X_OFF(chip_addx);
+	} else if (Y_RANGE_ALL(chip_addx, 1)) {
+		*yram = true;
+		return Y_OFF(chip_addx);
+	}
+
+	return (unsigned int)INVALID_CHIP_ADDRESS;
+}
+
+static bool dsp_is_dma_active(struct hda_codec *codec, unsigned int dma_chan)
+{
+	unsigned int dma_chnlstart_reg;
+
+	chipio_read(codec, DSPDMAC_CHNLSTART_INST_OFFSET, &dma_chnlstart_reg);
+
+	return ((dma_chnlstart_reg & (1 <<
+			(DSPDMAC_CHNLSTART_EN_LOBIT + dma_chan))) != 0);
+}
+
+static int dsp_dma_setup_common(struct hda_codec *codec,
+				unsigned int chip_addx,
+				unsigned int dma_chan,
+				unsigned int port_map_mask,
+				bool ovly)
+{
+	int status = 0;
+	unsigned int chnl_prop;
+	unsigned int dsp_addx;
+	unsigned int active;
+	bool code, yram;
+
+	snd_printdd(KERN_INFO "-- dsp_dma_setup_common() -- Begin ---------");
+
+	if (dma_chan >= DSPDMAC_DMA_CFG_CHANNEL_COUNT) {
+		snd_printdd(KERN_ERR "dma chan num invalid");
+		return -EINVAL;
+	}
+
+	if (dsp_is_dma_active(codec, dma_chan)) {
+		snd_printdd(KERN_ERR "dma already active");
+		return -EBUSY;
+	}
+
+	dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram);
+
+	if (dsp_addx == INVALID_CHIP_ADDRESS) {
+		snd_printdd(KERN_ERR "invalid chip addr");
+		return -ENXIO;
+	}
+
+	chnl_prop = DSPDMAC_CHNLPROP_AC_MASK;
+	active = 0;
+
+	snd_printdd(KERN_INFO "   dsp_dma_setup_common()    start reg pgm");
+
+	if (ovly) {
+		status = chipio_read(codec, DSPDMAC_CHNLPROP_INST_OFFSET,
+				     &chnl_prop);
+
+		if (status < 0) {
+			snd_printdd(KERN_ERR "read CHNLPROP Reg fail");
+			return status;
+		}
+		snd_printdd(KERN_INFO "dsp_dma_setup_common() Read CHNLPROP");
+	}
+
+	if (!code)
+		chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan));
+	else
+		chnl_prop |=  (1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan));
+
+	chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_DCON_LOBIT + dma_chan));
+
+	status = chipio_write(codec, DSPDMAC_CHNLPROP_INST_OFFSET, chnl_prop);
+	if (status < 0) {
+		snd_printdd(KERN_ERR "write CHNLPROP Reg fail");
+		return status;
+	}
+	snd_printdd(KERN_INFO "   dsp_dma_setup_common()    Write CHNLPROP");
+
+	if (ovly) {
+		status = chipio_read(codec, DSPDMAC_ACTIVE_INST_OFFSET,
+				     &active);
+
+		if (status < 0) {
+			snd_printdd(KERN_ERR "read ACTIVE Reg fail");
+			return status;
+		}
+		snd_printdd(KERN_INFO "dsp_dma_setup_common() Read ACTIVE");
+	}
+
+	active &= (~(1 << (DSPDMAC_ACTIVE_AAR_LOBIT + dma_chan))) &
+		DSPDMAC_ACTIVE_AAR_MASK;
+
+	status = chipio_write(codec, DSPDMAC_ACTIVE_INST_OFFSET, active);
+	if (status < 0) {
+		snd_printdd(KERN_ERR "write ACTIVE Reg fail");
+		return status;
+	}
+
+	snd_printdd(KERN_INFO "   dsp_dma_setup_common()    Write ACTIVE");
+
+	status = chipio_write(codec, DSPDMAC_AUDCHSEL_INST_OFFSET(dma_chan),
+			      port_map_mask);
+	if (status < 0) {
+		snd_printdd(KERN_ERR "write AUDCHSEL Reg fail");
+		return status;
+	}
+	snd_printdd(KERN_INFO "   dsp_dma_setup_common()    Write AUDCHSEL");
+
+	status = chipio_write(codec, DSPDMAC_IRQCNT_INST_OFFSET(dma_chan),
+			DSPDMAC_IRQCNT_BICNT_MASK | DSPDMAC_IRQCNT_CICNT_MASK);
+	if (status < 0) {
+		snd_printdd(KERN_ERR "write IRQCNT Reg fail");
+		return status;
+	}
+	snd_printdd(KERN_INFO "   dsp_dma_setup_common()    Write IRQCNT");
+
+	snd_printdd(
+		   "ChipA=0x%x,DspA=0x%x,dmaCh=%u, "
+		   "CHSEL=0x%x,CHPROP=0x%x,Active=0x%x\n",
+		   chip_addx, dsp_addx, dma_chan,
+		   port_map_mask, chnl_prop, active);
+
+	snd_printdd(KERN_INFO "-- dsp_dma_setup_common() -- Complete ------");
+
+	return 0;
+}
+
+static int dsp_dma_setup(struct hda_codec *codec,
+			unsigned int chip_addx,
+			unsigned int count,
+			unsigned int dma_chan)
+{
+	int status = 0;
+	bool code, yram;
+	unsigned int dsp_addx;
+	unsigned int addr_field;
+	unsigned int incr_field;
+	unsigned int base_cnt;
+	unsigned int cur_cnt;
+	unsigned int dma_cfg = 0;
+	unsigned int adr_ofs = 0;
+	unsigned int xfr_cnt = 0;
+	const unsigned int max_dma_count = 1 << (DSPDMAC_XFRCNT_BCNT_HIBIT -
+						DSPDMAC_XFRCNT_BCNT_LOBIT + 1);
+
+	snd_printdd(KERN_INFO "-- dsp_dma_setup() -- Begin ---------");
+
+	if (count > max_dma_count) {
+		snd_printdd(KERN_ERR "count too big");
+		return -EINVAL;
+	}
+
+	dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram);
+	if (dsp_addx == INVALID_CHIP_ADDRESS) {
+		snd_printdd(KERN_ERR "invalid chip addr");
+		return -ENXIO;
+	}
+
+	snd_printdd(KERN_INFO "   dsp_dma_setup()    start reg pgm");
+
+	addr_field = dsp_addx << DSPDMAC_DMACFG_DBADR_LOBIT;
+	incr_field   = 0;
+
+	if (!code) {
+		addr_field <<= 1;
+		if (yram)
+			addr_field |= (1 << DSPDMAC_DMACFG_DBADR_LOBIT);
+
+		incr_field  = (1 << DSPDMAC_DMACFG_AINCR_LOBIT);
+	}
+
+	dma_cfg = addr_field + incr_field;
+	status = chipio_write(codec, DSPDMAC_DMACFG_INST_OFFSET(dma_chan),
+				dma_cfg);
+	if (status < 0) {
+		snd_printdd(KERN_ERR "write DMACFG Reg fail");
+		return status;
+	}
+	snd_printdd(KERN_INFO "   dsp_dma_setup()    Write DMACFG");
+
+	adr_ofs = (count - 1) << (DSPDMAC_DSPADROFS_BOFS_LOBIT +
+							(code ? 0 : 1));
+
+	status = chipio_write(codec, DSPDMAC_DSPADROFS_INST_OFFSET(dma_chan),
+				adr_ofs);
+	if (status < 0) {
+		snd_printdd(KERN_ERR "write DSPADROFS Reg fail");
+		return status;
+	}
+	snd_printdd(KERN_INFO "   dsp_dma_setup()    Write DSPADROFS");
+
+	base_cnt = (count - 1) << DSPDMAC_XFRCNT_BCNT_LOBIT;
+
+	cur_cnt  = (count - 1) << DSPDMAC_XFRCNT_CCNT_LOBIT;
+
+	xfr_cnt = base_cnt | cur_cnt;
+
+	status = chipio_write(codec,
+				DSPDMAC_XFRCNT_INST_OFFSET(dma_chan), xfr_cnt);
+	if (status < 0) {
+		snd_printdd(KERN_ERR "write XFRCNT Reg fail");
+		return status;
+	}
+	snd_printdd(KERN_INFO "   dsp_dma_setup()    Write XFRCNT");
+
+	snd_printdd(
+		   "ChipA=0x%x, cnt=0x%x, DMACFG=0x%x, "
+		   "ADROFS=0x%x, XFRCNT=0x%x\n",
+		   chip_addx, count, dma_cfg, adr_ofs, xfr_cnt);
+
+	snd_printdd(KERN_INFO "-- dsp_dma_setup() -- Complete ---------");
+
+	return 0;
+}
+
+static int dsp_dma_start(struct hda_codec *codec,
+			 unsigned int dma_chan, bool ovly)
+{
+	unsigned int reg = 0;
+	int status = 0;
+
+	snd_printdd(KERN_INFO "-- dsp_dma_start() -- Begin ---------");
+
+	if (ovly) {
+		status = chipio_read(codec,
+				     DSPDMAC_CHNLSTART_INST_OFFSET, &reg);
+
+		if (status < 0) {
+			snd_printdd(KERN_ERR "read CHNLSTART reg fail");
+			return status;
+		}
+		snd_printdd(KERN_INFO "-- dsp_dma_start()    Read CHNLSTART");
+
+		reg &= ~(DSPDMAC_CHNLSTART_EN_MASK |
+				DSPDMAC_CHNLSTART_DIS_MASK);
+	}
+
+	status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET,
+			reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_EN_LOBIT)));
+	if (status < 0) {
+		snd_printdd(KERN_ERR "write CHNLSTART reg fail");
+		return status;
+	}
+	snd_printdd(KERN_INFO "-- dsp_dma_start() -- Complete ---------");
+
+	return status;
+}
+
+static int dsp_dma_stop(struct hda_codec *codec,
+			unsigned int dma_chan, bool ovly)
+{
+	unsigned int reg = 0;
+	int status = 0;
+
+	snd_printdd(KERN_INFO "-- dsp_dma_stop() -- Begin ---------");
+
+	if (ovly) {
+		status = chipio_read(codec,
+				     DSPDMAC_CHNLSTART_INST_OFFSET, &reg);
+
+		if (status < 0) {
+			snd_printdd(KERN_ERR "read CHNLSTART reg fail");
+			return status;
+		}
+		snd_printdd(KERN_INFO "-- dsp_dma_stop()    Read CHNLSTART");
+		reg &= ~(DSPDMAC_CHNLSTART_EN_MASK |
+				DSPDMAC_CHNLSTART_DIS_MASK);
+	}
+
+	status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET,
+			reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_DIS_LOBIT)));
+	if (status < 0) {
+		snd_printdd(KERN_ERR "write CHNLSTART reg fail");
+		return status;
+	}
+	snd_printdd(KERN_INFO "-- dsp_dma_stop() -- Complete ---------");
+
+	return status;
+}
+
+static int dsp_allocate_router_ports(struct hda_codec *codec,
+				     unsigned int num_chans,
+				     unsigned int ports_per_channel,
+				     unsigned int start_device,
+				     unsigned int *port_map)
+{
+	int status = 0;
+	int res;
+	u8 val;
+
+	status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+	if (status < 0)
+		return status;
+
+	val = start_device << 6;
+	val |= (ports_per_channel - 1) << 4;
+	val |= num_chans - 1;
+
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET,
+			    val);
+
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_PORT_ALLOC_SET,
+			    MEM_CONNID_DSP);
+
+	status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+	if (status < 0)
+		return status;
+
+	res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+				VENDOR_CHIPIO_PORT_ALLOC_GET, 0);
+
+	*port_map = res;
+
+	return (res < 0) ? res : 0;
+}
+
+static int dsp_free_router_ports(struct hda_codec *codec)
+{
+	int status = 0;
+
+	status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+	if (status < 0)
+		return status;
+
+	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+			    VENDOR_CHIPIO_PORT_FREE_SET,
+			    MEM_CONNID_DSP);
+
+	status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+
+	return status;
+}
+
+static int dsp_allocate_ports(struct hda_codec *codec,
+			unsigned int num_chans,
+			unsigned int rate_multi, unsigned int *port_map)
+{
+	int status;
+
+	snd_printdd(KERN_INFO "     dsp_allocate_ports() -- begin");
+
+	if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) {
+		snd_printdd(KERN_ERR "bad rate multiple");
+		return -EINVAL;
+	}
+
+	status = dsp_allocate_router_ports(codec, num_chans,
+					   rate_multi, 0, port_map);
+
+	snd_printdd(KERN_INFO "     dsp_allocate_ports() -- complete");
+
+	return status;
+}
+
+static int dsp_free_ports(struct hda_codec *codec)
+{
+	int status;
+
+	snd_printdd(KERN_INFO "     dsp_free_ports() -- begin");
+
+	status = dsp_free_router_ports(codec);
+	if (status < 0) {
+		snd_printdd(KERN_ERR "free router ports fail");
+		return status;
+	}
+	snd_printdd(KERN_INFO "     dsp_free_ports() -- complete");
+
+	return status;
+}
+
+static int dsp_allocate_ports_format(struct hda_codec *codec,
+			const unsigned short fmt,
+			unsigned int *port_map)
+{
+	int status;
+	unsigned int num_chans;
+
+	unsigned int sample_rate_div = ((get_hdafmt_rate(fmt) >> 0) & 3) + 1;
+	unsigned int sample_rate_mul = ((get_hdafmt_rate(fmt) >> 3) & 3) + 1;
+	unsigned int rate_multi = sample_rate_mul / sample_rate_div;
+
+	if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) {
+		snd_printdd(KERN_ERR "bad rate multiple");
+		return -EINVAL;
+	}
+
+	num_chans = get_hdafmt_chs(fmt) + 1;
+
+	status = dsp_allocate_ports(codec, num_chans, rate_multi, port_map);
+
+	return status;
+}
+
+/*
+ *  HDA DMA engine stuffs for DSP code download
+ */
+struct dma_engine {
+	struct hda_codec *codec;
+	unsigned short m_converter_format;
+	struct snd_dma_buffer *dmab;
+	unsigned int buf_size;
+};
+
+
+enum dma_state {
+	DMA_STATE_STOP  = 0,
+	DMA_STATE_RUN   = 1
+};
+
+static int dma_convert_to_hda_format(
+		struct hda_stream_format *stream_format,
+		unsigned short *hda_format)
+{
+	unsigned int format_val;
+
+	format_val = snd_hda_calc_stream_format(
+				stream_format->sample_rate,
+				stream_format->number_channels,
+				SNDRV_PCM_FORMAT_S32_LE,
+				stream_format->container_size, 0);
+
+	if (hda_format)
+		*hda_format = (unsigned short)format_val;
+
+	return 0;
+}
+
+static int dma_reset(struct dma_engine *dma)
+{
+	struct hda_codec *codec = dma->codec;
+	struct ca0132_spec *spec = codec->spec;
+	int status;
+
+	if (dma->dmab)
+		snd_hda_codec_load_dsp_cleanup(codec, dma->dmab);
+
+	status = snd_hda_codec_load_dsp_prepare(codec,
+			dma->m_converter_format,
+			dma->buf_size,
+			dma->dmab);
+	if (status < 0)
+		return status;
+	spec->dsp_stream_id = status;
+	return 0;
+}
+
+static int dma_set_state(struct dma_engine *dma, enum dma_state state)
+{
+	bool cmd;
+
+	snd_printdd("dma_set_state state=%d\n", state);
+
+	switch (state) {
+	case DMA_STATE_STOP:
+		cmd = false;
+		break;
+	case DMA_STATE_RUN:
+		cmd = true;
+		break;
+	default:
+		return 0;
+	}
+
+	snd_hda_codec_load_dsp_trigger(dma->codec, cmd);
+	return 0;
+}
+
+static unsigned int dma_get_buffer_size(struct dma_engine *dma)
+{
+	return dma->dmab->bytes;
+}
+
+static unsigned char *dma_get_buffer_addr(struct dma_engine *dma)
+{
+	return dma->dmab->area;
+}
+
+static int dma_xfer(struct dma_engine *dma,
+		const unsigned int *data,
+		unsigned int count)
+{
+	memcpy(dma->dmab->area, data, count);
+	return 0;
+}
+
+static void dma_get_converter_format(
+		struct dma_engine *dma,
+		unsigned short *format)
+{
+	if (format)
+		*format = dma->m_converter_format;
+}
+
+static unsigned int dma_get_stream_id(struct dma_engine *dma)
+{
+	struct ca0132_spec *spec = dma->codec->spec;
+
+	return spec->dsp_stream_id;
+}
+
+struct dsp_image_seg {
+	u32 magic;
+	u32 chip_addr;
+	u32 count;
+	u32 data[0];
+};
+
+static const u32 g_magic_value = 0x4c46584d;
+static const u32 g_chip_addr_magic_value = 0xFFFFFF01;
+
+static bool is_valid(const struct dsp_image_seg *p)
+{
+	return p->magic == g_magic_value;
+}
+
+static bool is_hci_prog_list_seg(const struct dsp_image_seg *p)
+{
+	return g_chip_addr_magic_value == p->chip_addr;
+}
+
+static bool is_last(const struct dsp_image_seg *p)
+{
+	return p->count == 0;
+}
+
+static size_t dsp_sizeof(const struct dsp_image_seg *p)
+{
+	return sizeof(*p) + p->count*sizeof(u32);
+}
+
+static const struct dsp_image_seg *get_next_seg_ptr(
+				const struct dsp_image_seg *p)
+{
+	return (struct dsp_image_seg *)((unsigned char *)(p) + dsp_sizeof(p));
+}
+
+/*
+ * CA0132 chip DSP transfer stuffs.  For DSP download.
+ */
+#define INVALID_DMA_CHANNEL (~0UL)
+
+static int dspxfr_hci_write(struct hda_codec *codec,
+			const struct dsp_image_seg *fls)
+{
+	int status;
+	const u32 *data;
+	unsigned int count;
+
+	if (fls == NULL || fls->chip_addr != g_chip_addr_magic_value) {
+		snd_printdd(KERN_ERR "hci_write invalid params");
+		return -EINVAL;
+	}
+
+	count = fls->count;
+	data = (u32 *)(fls->data);
+	while (count >= 2) {
+		status = chipio_write(codec, data[0], data[1]);
+		if (status < 0) {
+			snd_printdd(KERN_ERR "hci_write chipio failed");
+			return status;
+		}
+		count -= 2;
+		data  += 2;
+	}
+	return 0;
+}
+
+static int dspxfr_one_seg(struct hda_codec *codec,
+			const struct dsp_image_seg *fls,
+			unsigned int reloc,
+			struct dma_engine *dma_engine,
+			unsigned int dma_chan,
+			unsigned int port_map_mask,
+			bool ovly)
+{
+	int status;
+	bool comm_dma_setup_done = false;
+	const unsigned int *data;
+	unsigned int chip_addx;
+	unsigned int words_to_write;
+	unsigned int buffer_size_words;
+	unsigned char *buffer_addx;
+	unsigned short hda_format;
+	unsigned int sample_rate_div;
+	unsigned int sample_rate_mul;
+	unsigned int num_chans;
+	unsigned int hda_frame_size_words;
+	unsigned int remainder_words;
+	const u32 *data_remainder;
+	u32 chip_addx_remainder;
+	unsigned int run_size_words;
+	const struct dsp_image_seg *hci_write = NULL;
+	int retry;
+
+	if (fls == NULL)
+		return -EINVAL;
+	if (is_hci_prog_list_seg(fls)) {
+		hci_write = fls;
+		fls = get_next_seg_ptr(fls);
+	}
+
+	if (hci_write && (!fls || is_last(fls))) {
+		snd_printdd("hci_write\n");
+		return dspxfr_hci_write(codec, hci_write);
+	}
+
+	if (fls == NULL || dma_engine == NULL || port_map_mask == 0) {
+		snd_printdd("Invalid Params\n");
+		return -EINVAL;
+	}
+
+	data = fls->data;
+	chip_addx = fls->chip_addr,
+	words_to_write = fls->count;
+
+	if (!words_to_write)
+		return hci_write ? dspxfr_hci_write(codec, hci_write) : 0;
+	if (reloc)
+		chip_addx = (chip_addx & (0xFFFF0000 << 2)) + (reloc << 2);
+
+	if (!UC_RANGE(chip_addx, words_to_write) &&
+	    !X_RANGE_ALL(chip_addx, words_to_write) &&
+	    !Y_RANGE_ALL(chip_addx, words_to_write)) {
+		snd_printdd("Invalid chip_addx Params\n");
+		return -EINVAL;
+	}
+
+	buffer_size_words = (unsigned int)dma_get_buffer_size(dma_engine) /
+					sizeof(u32);
+
+	buffer_addx = dma_get_buffer_addr(dma_engine);
+
+	if (buffer_addx == NULL) {
+		snd_printdd(KERN_ERR "dma_engine buffer NULL\n");
+		return -EINVAL;
+	}
+
+	dma_get_converter_format(dma_engine, &hda_format);
+	sample_rate_div = ((get_hdafmt_rate(hda_format) >> 0) & 3) + 1;
+	sample_rate_mul = ((get_hdafmt_rate(hda_format) >> 3) & 3) + 1;
+	num_chans = get_hdafmt_chs(hda_format) + 1;
+
+	hda_frame_size_words = ((sample_rate_div == 0) ? 0 :
+			(num_chans * sample_rate_mul / sample_rate_div));
+
+	buffer_size_words = min(buffer_size_words,
+				(unsigned int)(UC_RANGE(chip_addx, 1) ?
+				65536 : 32768));
+	buffer_size_words -= buffer_size_words % hda_frame_size_words;
+	snd_printdd(
+		   "chpadr=0x%08x frmsz=%u nchan=%u "
+		   "rate_mul=%u div=%u bufsz=%u\n",
+		   chip_addx, hda_frame_size_words, num_chans,
+		   sample_rate_mul, sample_rate_div, buffer_size_words);
+
+	if ((buffer_addx == NULL) || (hda_frame_size_words == 0) ||
+	    (buffer_size_words < hda_frame_size_words)) {
+		snd_printdd(KERN_ERR "dspxfr_one_seg:failed\n");
+		return -EINVAL;
+	}
+
+	remainder_words = words_to_write % hda_frame_size_words;
+	data_remainder = data;
+	chip_addx_remainder = chip_addx;
+
+	data += remainder_words;
+	chip_addx += remainder_words*sizeof(u32);
+	words_to_write -= remainder_words;
+
+	while (words_to_write != 0) {
+		run_size_words = min(buffer_size_words, words_to_write);
+		snd_printdd("dspxfr (seg loop)cnt=%u rs=%u remainder=%u\n",
+			    words_to_write, run_size_words, remainder_words);
+		dma_xfer(dma_engine, data, run_size_words*sizeof(u32));
+		if (!comm_dma_setup_done) {
+			status = dsp_dma_stop(codec, dma_chan, ovly);
+			if (status < 0)
+				return -EIO;
+			status = dsp_dma_setup_common(codec, chip_addx,
+						dma_chan, port_map_mask, ovly);
+			if (status < 0)
+				return status;
+			comm_dma_setup_done = true;
+		}
+
+		status = dsp_dma_setup(codec, chip_addx,
+						run_size_words, dma_chan);
+		if (status < 0)
+			return status;
+		status = dsp_dma_start(codec, dma_chan, ovly);
+		if (status < 0)
+			return status;
+		if (!dsp_is_dma_active(codec, dma_chan)) {
+			snd_printdd(KERN_ERR "dspxfr:DMA did not start");
+			return -EIO;
+		}
+		status = dma_set_state(dma_engine, DMA_STATE_RUN);
+		if (status < 0)
+			return status;
+		if (remainder_words != 0) {
+			status = chipio_write_multiple(codec,
+						chip_addx_remainder,
+						data_remainder,
+						remainder_words);
+			remainder_words = 0;
+		}
+		if (hci_write) {
+			status = dspxfr_hci_write(codec, hci_write);
+			hci_write = NULL;
+		}
+		retry = 5000;
+		while (dsp_is_dma_active(codec, dma_chan)) {
+			if (--retry <= 0)
+				break;
+		}
+		snd_printdd(KERN_INFO "+++++ DMA complete");
+		dma_set_state(dma_engine, DMA_STATE_STOP);
+		dma_reset(dma_engine);
+
+		if (status < 0)
+			return status;
+
+		data += run_size_words;
+		chip_addx += run_size_words*sizeof(u32);
+		words_to_write -= run_size_words;
+	}
+
+	if (remainder_words != 0) {
+		status = chipio_write_multiple(codec, chip_addx_remainder,
+					data_remainder, remainder_words);
+	}
+
+	return status;
+}
+
+static int dspxfr_image(struct hda_codec *codec,
+			const struct dsp_image_seg *fls_data,
+			unsigned int reloc, struct hda_stream_format *format,
+			bool ovly)
+{
+	struct ca0132_spec *spec = codec->spec;
+	int status;
+	unsigned short hda_format = 0;
+	unsigned int response;
+	unsigned char stream_id = 0;
+	struct dma_engine *dma_engine;
+	unsigned int dma_chan;
+	unsigned int port_map_mask;
+
+	if (fls_data == NULL)
+		return -EINVAL;
+
+	dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL);
+	if (!dma_engine) {
+		status = -ENOMEM;
+		goto exit;
+	}
+	memset((void *)dma_engine, 0, sizeof(*dma_engine));
+
+	dma_engine->dmab = kzalloc(sizeof(*dma_engine->dmab), GFP_KERNEL);
+	if (!dma_engine->dmab) {
+		status = -ENOMEM;
+		goto exit;
+	}
+
+	dma_engine->codec = codec;
+	dma_convert_to_hda_format(format, &hda_format);
+	dma_engine->m_converter_format = hda_format;
+	dma_engine->buf_size = (ovly ? DSP_DMA_WRITE_BUFLEN_OVLY :
+			DSP_DMA_WRITE_BUFLEN_INIT) * 2;
+
+	dma_chan = 0;
+
+	status = codec_set_converter_format(codec, WIDGET_CHIP_CTRL,
+					hda_format, &response);
+
+	if (status < 0) {
+		snd_printdd(KERN_ERR "set converter format fail");
+		goto exit;
+	}
+
+	status = snd_hda_codec_load_dsp_prepare(codec,
+				dma_engine->m_converter_format,
+				dma_engine->buf_size,
+				dma_engine->dmab);
+	if (status < 0)
+		goto exit;
+	spec->dsp_stream_id = status;
+
+	if (ovly) {
+		status = dspio_alloc_dma_chan(codec, &dma_chan);
+		if (status < 0) {
+			snd_printdd(KERN_ERR "alloc dmachan fail");
+			dma_chan = (unsigned int)INVALID_DMA_CHANNEL;
+			goto exit;
+		}
+	}
+
+	port_map_mask = 0;
+	status = dsp_allocate_ports_format(codec, hda_format,
+					&port_map_mask);
+	if (status < 0) {
+		snd_printdd(KERN_ERR "alloc ports fail");
+		goto exit;
+	}
+
+	stream_id = dma_get_stream_id(dma_engine);
+	status = codec_set_converter_stream_channel(codec,
+			WIDGET_CHIP_CTRL, stream_id, 0, &response);
+	if (status < 0) {
+		snd_printdd(KERN_ERR "set stream chan fail");
+		goto exit;
+	}
+
+	while ((fls_data != NULL) && !is_last(fls_data)) {
+		if (!is_valid(fls_data)) {
+			snd_printdd(KERN_ERR "FLS check fail");
+			status = -EINVAL;
+			goto exit;
+		}
+		status = dspxfr_one_seg(codec, fls_data, reloc,
+					dma_engine, dma_chan,
+					port_map_mask, ovly);
+		if (status < 0)
+			break;
+
+		if (is_hci_prog_list_seg(fls_data))
+			fls_data = get_next_seg_ptr(fls_data);
+
+		if ((fls_data != NULL) && !is_last(fls_data))
+			fls_data = get_next_seg_ptr(fls_data);
+	}
+
+	if (port_map_mask != 0)
+		status = dsp_free_ports(codec);
+
+	if (status < 0)
+		goto exit;
+
+	status = codec_set_converter_stream_channel(codec,
+				WIDGET_CHIP_CTRL, 0, 0, &response);
+
+exit:
+	if (ovly && (dma_chan != INVALID_DMA_CHANNEL))
+		dspio_free_dma_chan(codec, dma_chan);
+
+	if (dma_engine->dmab)
+		snd_hda_codec_load_dsp_cleanup(codec, dma_engine->dmab);
+	kfree(dma_engine->dmab);
+	kfree(dma_engine);
+
+	return status;
+}
+
+/*
+ * CA0132 DSP download stuffs.
+ */
+static void dspload_post_setup(struct hda_codec *codec)
+{
+	snd_printdd(KERN_INFO "---- dspload_post_setup ------");
+
+	/*set DSP speaker to 2.0 configuration*/
+	chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080);
+	chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000);
+
+	/*update write pointer*/
+	chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002);
+}
+
+static int dspload_image(struct hda_codec *codec,
+			const struct dsp_image_seg *fls,
+			bool ovly,
+			unsigned int reloc,
+			bool autostart,
+			int router_chans)
+{
+	int status = 0;
+	struct hda_stream_format stream_format;
+
+	snd_printdd(KERN_INFO "---- dspload_image begin ------");
+	if (router_chans == 0) {
+		if (!ovly)
+			router_chans = DMA_TRANSFER_FRAME_SIZE_NWORDS;
+		else
+			router_chans = DMA_OVERLAY_FRAME_SIZE_NWORDS;
+	}
+
+	stream_format.sample_rate = 48000;
+	stream_format.number_channels = (unsigned short)router_chans;
+
+	while (stream_format.number_channels > 16) {
+		stream_format.sample_rate *= 2;
+		stream_format.number_channels /= 2;
+	}
+
+	stream_format.container_size = 32;
+	stream_format.valid_bits_per_sample = 32;
+
+	do {
+		snd_printdd(KERN_INFO "Ready to program DMA");
+		if (!ovly)
+			status = dsp_reset(codec);
+
+		if (status < 0)
+			break;
+
+		snd_printdd(KERN_INFO "dsp_reset() complete");
+		status = dspxfr_image(codec, fls, reloc, &stream_format, ovly);
+
+		if (status < 0)
+			break;
+
+		snd_printdd(KERN_INFO "dspxfr_image() complete");
+		if (autostart && !ovly) {
+			dspload_post_setup(codec);
+			status = dsp_set_run_state(codec);
+		}
+
+		snd_printdd(KERN_INFO "LOAD FINISHED");
+	} while (0);
+
+	return status;
+}
+
+static bool dspload_is_loaded(struct hda_codec *codec)
+{
+	unsigned int data = 0;
+	int status = 0;
+
+	status = chipio_read(codec, 0x40004, &data);
+	if ((status < 0) || (data != 1))
+		return false;
+
+	return true;
+}
+
+static bool dspload_wait_loaded(struct hda_codec *codec)
+{
+	int retry = 100;
+
+	do {
+		msleep(20);
+		if (dspload_is_loaded(codec)) {
+			pr_info("ca0132 DOWNLOAD OK :-) DSP IS RUNNING.\n");
+			return true;
+		}
+	} while (--retry);
+
+	pr_err("ca0132 DOWNLOAD FAILED!!! DSP IS NOT RUNNING.\n");
+	return false;
+}
+
 /*
  * PCM callbacks
  */
@@ -983,12 +2524,66 @@ static void ca0132_exit_chip(struct hda_codec *codec)
 	/* put any chip cleanup stuffs here. */
 }
 
+static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k)
+{
+	chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, is96k);
+	chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, is96k);
+	chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, is96k);
+	chipio_set_control_flag(codec, CONTROL_FLAG_SRC_CLOCK_196MHZ, is96k);
+	chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, is96k);
+	chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, is96k);
+
+	chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000);
+	chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000);
+	chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+}
+
+static bool ca0132_download_dsp_images(struct hda_codec *codec)
+{
+	bool dsp_loaded = false;
+	const struct dsp_image_seg *dsp_os_image;
+	const struct firmware *fw_entry;
+
+	if (request_firmware(&fw_entry, EFX_FILE, codec->bus->card->dev) != 0)
+		return false;
+
+	dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
+	dspload_image(codec, dsp_os_image, 0, 0, true, 0);
+	dsp_loaded = dspload_wait_loaded(codec);
+
+	release_firmware(fw_entry);
+
+
+	return dsp_loaded;
+}
+
+static void ca0132_download_dsp(struct hda_codec *codec)
+{
+	struct ca0132_spec *spec = codec->spec;
+
+	spec->dsp_state = DSP_DOWNLOAD_INIT;
+
+	if (spec->dsp_state == DSP_DOWNLOAD_INIT) {
+		chipio_enable_clocks(codec);
+		spec->dsp_state = DSP_DOWNLOADING;
+		if (!ca0132_download_dsp_images(codec))
+			spec->dsp_state = DSP_DOWNLOAD_FAILED;
+		else
+			spec->dsp_state = DSP_DOWNLOADED;
+	}
+
+	if (spec->dsp_state == DSP_DOWNLOADED)
+		ca0132_set_dsp_msr(codec, true);
+}
+
 static int ca0132_init(struct hda_codec *codec)
 {
 	struct ca0132_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 	int i;
 
+	ca0132_download_dsp(codec);
+
 	for (i = 0; i < spec->multiout.num_dacs; i++) {
 		init_output(codec, spec->out_pins[i],
 			    spec->multiout.dac_nids[i]);
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 6/8] ALSA: Add firmware caching to CA0132 codec
  2012-09-14  1:15 [PATCH 0/8] ALSA: Add DSP firmware loader (resub) Ian Minett
                   ` (4 preceding siblings ...)
  2012-09-14  1:15 ` [PATCH 5/8] ALSA: Update CA0132 codec to load DSP firmware binary Ian Minett
@ 2012-09-14  1:15 ` Ian Minett
  2012-09-14  8:57   ` Takashi Iwai
  2012-09-14  1:15 ` [PATCH 7/8] ALSA: Add comments and descriptions to functions Ian Minett
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 16+ messages in thread
From: Ian Minett @ 2012-09-14  1:15 UTC (permalink / raw)
  To: patch; +Cc: alsa-devel, Ian Minett

From: Ian Minett <ian_minett@creativelabs.com>


Signed-off-by: Ian Minett <ian_minett@creativelabs.com>

diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 11f5910..ef66817 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -2025,6 +2025,24 @@ static int dspload_image(struct hda_codec *codec,
 	return status;
 }
 
+static const struct firmware *fw_efx;
+
+static int request_firmware_cached(const struct firmware **firmware_p,
+	const char *name, struct device *device)
+{
+	if (*firmware_p)
+		return 0;  /* already loaded */
+	return request_firmware(firmware_p, name, device);
+}
+
+static void release_cached_firmware(void)
+{
+	if (fw_efx) {
+		release_firmware(fw_efx);
+		fw_efx = NULL;
+	}
+}
+
 static bool dspload_is_loaded(struct hda_codec *codec)
 {
 	unsigned int data = 0;
@@ -2542,18 +2560,15 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec)
 {
 	bool dsp_loaded = false;
 	const struct dsp_image_seg *dsp_os_image;
-	const struct firmware *fw_entry;
 
-	if (request_firmware(&fw_entry, EFX_FILE, codec->bus->card->dev) != 0)
+	if (request_firmware_cached(&fw_efx, EFX_FILE,
+				    codec->bus->card->dev) != 0)
 		return false;
 
-	dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
+	dsp_os_image = (struct dsp_image_seg *)(fw_efx->data);
 	dspload_image(codec, dsp_os_image, 0, 0, true, 0);
 	dsp_loaded = dspload_wait_loaded(codec);
 
-	release_firmware(fw_entry);
-
-
 	return dsp_loaded;
 }
 
@@ -2662,7 +2677,8 @@ static int __init patch_ca0132_init(void)
 }
 
 static void __exit patch_ca0132_exit(void)
-{
+{
+	release_cached_firmware();
 	snd_hda_delete_codec_preset(&ca0132_list);
 }
 
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 7/8] ALSA: Add comments and descriptions to functions
  2012-09-14  1:15 [PATCH 0/8] ALSA: Add DSP firmware loader (resub) Ian Minett
                   ` (5 preceding siblings ...)
  2012-09-14  1:15 ` [PATCH 6/8] ALSA: Add firmware caching to CA0132 codec Ian Minett
@ 2012-09-14  1:15 ` Ian Minett
  2012-09-14  8:59   ` Takashi Iwai
  2012-09-14  1:15 ` [PATCH 8/8] ALSA: Change return value for load_dsp_prepare() to -ENOSYS Ian Minett
  2012-09-14  8:32 ` [PATCH 0/8] ALSA: Add DSP firmware loader (resub) Takashi Iwai
  8 siblings, 1 reply; 16+ messages in thread
From: Ian Minett @ 2012-09-14  1:15 UTC (permalink / raw)
  To: patch; +Cc: alsa-devel, Ian Minett

From: Ian Minett <ian_minett@creativelabs.com>

Signed-off-by: Ian Minett <ian_minett@creativelabs.com>

diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index ef66817..936e161 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -460,8 +460,12 @@ static int chipio_send(struct hda_codec *codec,
 static int chipio_write_address(struct hda_codec *codec,
 				unsigned int chip_addx)
 {
+	struct ca0132_spec *spec = codec->spec;
 	int res;
 
+	if (spec->curr_chip_addx == chip_addx)
+			return 0;
+
 	/* send low 16 bits of the address */
 	res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
 			  chip_addx & 0xffff);
@@ -472,37 +476,14 @@ static int chipio_write_address(struct hda_codec *codec,
 				  chip_addx >> 16);
 	}
 
+	spec->curr_chip_addx = (res < 0) ? ~0UL : chip_addx;
+
 	return res;
 }
 
-static int chipio_write_addx(struct hda_codec *codec, u32 chip_addx)
-{
-	struct ca0132_spec *spec = codec->spec;
-	int status;
-
-	if (spec->curr_chip_addx == chip_addx)
-		return 0;
-
-	/* send low 16 bits of the address */
-	status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
-			  chip_addx & 0xffff);
-
-	if (status < 0)
-		return status;
-
-	/* send high 16 bits of the address */
-	status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
-			  chip_addx >> 16);
-
-	spec->curr_chip_addx = (status < 0) ? ~0UL : chip_addx;
-
-	return status;
-}
-
 /*
  * Write data through the vendor widget -- NOT protected by the Mutex!
  */
-
 static int chipio_write_data(struct hda_codec *codec, unsigned int data)
 {
 	int res;
@@ -519,6 +500,9 @@ static int chipio_write_data(struct hda_codec *codec, unsigned int data)
 	return res;
 }
 
+/*
+ * Write multiple data through the vendor widget -- NOT protected by the Mutex!
+ */
 static int chipio_write_data_multiple(struct hda_codec *codec,
 				      const u32 *data,
 				      unsigned int count)
@@ -588,6 +572,10 @@ exit:
 	return err;
 }
 
+/*
+ * Write multiple values to the given address through the chip I/O widget.
+ * protected by the Mutex
+ */
 static int chipio_write_multiple(struct hda_codec *codec,
 				 u32 chip_addx,
 				 const u32 *data,
@@ -597,7 +585,7 @@ static int chipio_write_multiple(struct hda_codec *codec,
 	int status;
 
 	mutex_lock(&spec->chipio_mutex);
-	status = chipio_write_addx(codec, chip_addx);
+	status = chipio_write_address(codec, chip_addx);
 	if (status < 0)
 		goto error;
 
@@ -634,6 +622,9 @@ exit:
 	return err;
 }
 
+/*
+ * Set chip control flags through the chip I/O widget.
+ */
 static void chipio_set_control_flag(struct hda_codec *codec,
 				    enum control_flag_id flag_id,
 				    bool flag_state)
@@ -647,6 +638,9 @@ static void chipio_set_control_flag(struct hda_codec *codec,
 			    VENDOR_CHIPIO_FLAG_SET, val);
 }
 
+/*
+ * Set chip parameters through the chip I/O widget.
+ */
 static void chipio_set_control_param(struct hda_codec *codec,
 		enum control_param_id param_id, int param_val)
 {
@@ -671,6 +665,9 @@ static void chipio_set_control_param(struct hda_codec *codec,
 	}
 }
 
+/*
+ * Set sampling rate of the connection point.
+ */
 static void chipio_set_conn_rate(struct hda_codec *codec,
 				int connid, enum ca0132_sample_rate rate)
 {
@@ -679,6 +676,9 @@ static void chipio_set_conn_rate(struct hda_codec *codec,
 				 rate);
 }
 
+/*
+ * Enable clocks.
+ */
 static void chipio_enable_clocks(struct hda_codec *codec)
 {
 	struct ca0132_spec *spec = codec->spec;
@@ -718,22 +718,27 @@ static int dspio_send(struct hda_codec *codec, unsigned int reg,
 	return -EIO;
 }
 
+/*
+ * Wait for DSP to be ready for commands
+ */
 static void dspio_write_wait(struct hda_codec *codec)
 {
-	int cur_val, prv_val;
-	int retry = 50;
+	int status;
+	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
 
-	cur_val = 0;
 	do {
-		prv_val = cur_val;
-		msleep(20);
-		dspio_send(codec, VENDOR_DSPIO_SCP_POST_COUNT_QUERY, 1);
-		dspio_send(codec, VENDOR_DSPIO_STATUS, 0);
-		cur_val = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
-					   VENDOR_DSPIO_SCP_READ_COUNT, 0);
-	} while (cur_val && (cur_val == prv_val) && --retry);
+		status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
+						VENDOR_DSPIO_STATUS, 0);
+		if ((status == VENDOR_STATUS_DSPIO_OK) ||
+		    (status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY))
+			break;
+		msleep(1);
+	} while (time_before(jiffies, timeout));
 }
 
+/*
+ * Write SCP data to DSP
+ */
 static int dspio_write(struct hda_codec *codec, unsigned int scp_data)
 {
 	struct ca0132_spec *spec = codec->spec;
@@ -762,6 +767,9 @@ error:
 			-EIO : 0;
 }
 
+/*
+ * Write multiple SCP data to DSP
+ */
 static int dspio_write_multiple(struct hda_codec *codec,
 				unsigned int *buffer, unsigned int size)
 {
@@ -782,6 +790,9 @@ static int dspio_write_multiple(struct hda_codec *codec,
 	return status;
 }
 
+/*
+ * Construct the SCP header using corresponding fields
+ */
 static inline unsigned int
 make_scp_header(unsigned int target_id, unsigned int source_id,
 		unsigned int get_flag, unsigned int req,
@@ -802,6 +813,9 @@ make_scp_header(unsigned int target_id, unsigned int source_id,
 	return header;
 }
 
+/*
+ * Extract corresponding fields from SCP header
+ */
 static inline void
 extract_scp_header(unsigned int header,
 		   unsigned int *target_id, unsigned int *source_id,
@@ -835,6 +849,9 @@ struct scp_msg {
 	unsigned int data[SCP_MAX_DATA_WORDS];
 };
 
+/*
+ * Send SCP message to DSP
+ */
 static int dspio_send_scp_message(struct hda_codec *codec,
 				  unsigned char *send_buf,
 				  unsigned int send_buf_size,
@@ -912,6 +929,19 @@ static int dspio_send_scp_message(struct hda_codec *codec,
 	return status;
 }
 
+/**
+ * Prepare and send the SCP message to DSP
+ * @codec: the HDA codec
+ * @mod_id: ID of the DSP module to send the command
+ * @req: ID of request to send to the DSP module
+ * @dir: SET or GET
+ * @data: pointer to the data to send with the request, request specific
+ * @len: length of the data, in bytes
+ * @reply: point to the buffer to hold data returned for a reply
+ * @reply_len: length of the reply buffer returned from GET
+ *
+ * Returns zero or a negative error code.
+ */
 static int dspio_scp(struct hda_codec *codec,
 		int mod_id, int req, int dir, void *data, unsigned int len,
 		void *reply, unsigned int *reply_len)
@@ -988,6 +1018,9 @@ static int dspio_scp(struct hda_codec *codec,
 	return status;
 }
 
+/*
+ * Allocate a DSP DMA channel via an SCP message
+ */
 static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan)
 {
 	int status = 0;
@@ -1013,6 +1046,9 @@ static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan)
 	return status;
 }
 
+/*
+ * Free a DSP DMA via an SCP message
+ */
 static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan)
 {
 	int status = 0;
@@ -1035,7 +1071,7 @@ static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan)
 }
 
 /*
- * CA0132 DSP access stuffs
+ * (Re)start the DSP
  */
 static int dsp_set_run_state(struct hda_codec *codec)
 {
@@ -1069,6 +1105,9 @@ static int dsp_set_run_state(struct hda_codec *codec)
 	return 0;
 }
 
+/*
+ * Reset the DSP
+ */
 static int dsp_reset(struct hda_codec *codec)
 {
 	unsigned int res;
@@ -1088,6 +1127,9 @@ static int dsp_reset(struct hda_codec *codec)
 	return 0;
 }
 
+/*
+ * Convert chip address to DSP address
+ */
 static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx,
 					bool *code, bool *yram)
 {
@@ -1106,6 +1148,9 @@ static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx,
 	return (unsigned int)INVALID_CHIP_ADDRESS;
 }
 
+/*
+ * Check if the DSP DMA is active
+ */
 static bool dsp_is_dma_active(struct hda_codec *codec, unsigned int dma_chan)
 {
 	unsigned int dma_chnlstart_reg;
@@ -1226,6 +1271,9 @@ static int dsp_dma_setup_common(struct hda_codec *codec,
 	return 0;
 }
 
+/*
+ * Setup the DSP DMA per-transfer-specific registers
+ */
 static int dsp_dma_setup(struct hda_codec *codec,
 			unsigned int chip_addx,
 			unsigned int count,
@@ -1314,6 +1362,9 @@ static int dsp_dma_setup(struct hda_codec *codec,
 	return 0;
 }
 
+/*
+ * Start the DSP DMA
+ */
 static int dsp_dma_start(struct hda_codec *codec,
 			 unsigned int dma_chan, bool ovly)
 {
@@ -1347,6 +1398,9 @@ static int dsp_dma_start(struct hda_codec *codec,
 	return status;
 }
 
+/*
+ * Stop the DSP DMA
+ */
 static int dsp_dma_stop(struct hda_codec *codec,
 			unsigned int dma_chan, bool ovly)
 {
@@ -1379,6 +1433,17 @@ static int dsp_dma_stop(struct hda_codec *codec,
 	return status;
 }
 
+/**
+ * Allocate router ports
+ *
+ * @codec: the HDA codec
+ * @num_chans: number of channels in the stream
+ * @ports_per_channel: number of ports per channel
+ * @start_device: start device
+ * @port_map: pointer to the port list to hold the allocated ports
+ *
+ * Returns zero or a negative error code.
+ */
 static int dsp_allocate_router_ports(struct hda_codec *codec,
 				     unsigned int num_chans,
 				     unsigned int ports_per_channel,
@@ -1417,6 +1482,9 @@ static int dsp_allocate_router_ports(struct hda_codec *codec,
 	return (res < 0) ? res : 0;
 }
 
+/*
+ * Free router ports
+ */
 static int dsp_free_router_ports(struct hda_codec *codec)
 {
 	int status = 0;
@@ -1434,6 +1502,9 @@ static int dsp_free_router_ports(struct hda_codec *codec)
 	return status;
 }
 
+/*
+ * Allocate DSP ports for the download stream
+ */
 static int dsp_allocate_ports(struct hda_codec *codec,
 			unsigned int num_chans,
 			unsigned int rate_multi, unsigned int *port_map)
@@ -1455,22 +1526,6 @@ static int dsp_allocate_ports(struct hda_codec *codec,
 	return status;
 }
 
-static int dsp_free_ports(struct hda_codec *codec)
-{
-	int status;
-
-	snd_printdd(KERN_INFO "     dsp_free_ports() -- begin");
-
-	status = dsp_free_router_ports(codec);
-	if (status < 0) {
-		snd_printdd(KERN_ERR "free router ports fail");
-		return status;
-	}
-	snd_printdd(KERN_INFO "     dsp_free_ports() -- complete");
-
-	return status;
-}
-
 static int dsp_allocate_ports_format(struct hda_codec *codec,
 			const unsigned short fmt,
 			unsigned int *port_map)
@@ -1495,6 +1550,25 @@ static int dsp_allocate_ports_format(struct hda_codec *codec,
 }
 
 /*
+ * free DSP ports
+ */
+static int dsp_free_ports(struct hda_codec *codec)
+{
+	int status;
+
+	snd_printdd(KERN_INFO "     dsp_free_ports() -- begin");
+
+	status = dsp_free_router_ports(codec);
+	if (status < 0) {
+		snd_printdd(KERN_ERR "free router ports fail");
+		return status;
+	}
+	snd_printdd(KERN_INFO "     dsp_free_ports() -- complete");
+
+	return status;
+}
+
+/*
  *  HDA DMA engine stuffs for DSP code download
  */
 struct dma_engine {
@@ -1528,6 +1602,9 @@ static int dma_convert_to_hda_format(
 	return 0;
 }
 
+/*
+ *  Reset DMA for DSP download
+ */
 static int dma_reset(struct dma_engine *dma)
 {
 	struct hda_codec *codec = dma->codec;
@@ -1642,6 +1719,11 @@ static const struct dsp_image_seg *get_next_seg_ptr(
  */
 #define INVALID_DMA_CHANNEL (~0UL)
 
+/*
+ * Program a list of address/data pairs via the ChipIO widget.
+ * The segment data is in the format of successive pairs of words.
+ * These are repeated as indicated by the segment's cound field.
+ */
 static int dspxfr_hci_write(struct hda_codec *codec,
 			const struct dsp_image_seg *fls)
 {
@@ -1668,6 +1750,21 @@ static int dspxfr_hci_write(struct hda_codec *codec,
 	return 0;
 }
 
+/**
+ * Write a block of data into DSP code or data RAM using pre-allocated
+ * DMA engine.
+ *
+ * @codec: the HDA codec
+ * @fls: pointer to a fast load image
+ * @reloc: Relocation address for loading single-segment overlays, or 0 for
+ *	   no relocation
+ * @dma_engine: pointer to DMA engine to be used for DSP download
+ * @dma_chan: The number of DMA channels used for DSP download
+ * @port_map_mask: port mapping
+ * @ovly: TRUE if overlay format is required
+ *
+ * Returns zero or a negative error code.
+ */
 static int dspxfr_one_seg(struct hda_codec *codec,
 			const struct dsp_image_seg *fls,
 			unsigned int reloc,
@@ -1836,6 +1933,18 @@ static int dspxfr_one_seg(struct hda_codec *codec,
 	return status;
 }
 
+/**
+ * Write the entire DSP image of a DSP code/data overlay to DSP memories
+ *
+ * @codec: the HDA codec
+ * @fls_data: pointer to a fast load image
+ * @reloc: Relocation address for loading single-segment overlays, or 0 for
+ *	   no relocation
+ * @format: format of the stream used for DSP download
+ * @ovly: TRUE if overlay format is required
+ *
+ * Returns zero or a negative error code.
+ */
 static int dspxfr_image(struct hda_codec *codec,
 			const struct dsp_image_seg *fls_data,
 			unsigned int reloc, struct hda_stream_format *format,
@@ -1970,6 +2079,23 @@ static void dspload_post_setup(struct hda_codec *codec)
 	chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002);
 }
 
+/**
+ * Download DSP from a DSP Image Fast Load structure. This structure is a
+ * linear, non-constant sized element array of structures, each of which
+ * contain the count of the data to be loaded, the data itself, and the
+ * corresponding starting chip address of the starting data location.
+ *
+ * @codec: the HDA codec
+ * @fls: pointer to a fast load image
+ * @ovly: TRUE if overlay format is required
+ * @reloc: Relocation address for loading single-segment overlays, or 0 for
+ *	   no relocation
+ * @autostart: TRUE if DSP starts after loading; ignored if ovly is TRUE
+ * @router_chans: number of audio router channels to be allocated (0 means use
+ *		  internal defaults; max is 32)
+ *
+ * Returns zero or a negative error code.
+ */
 static int dspload_image(struct hda_codec *codec,
 			const struct dsp_image_seg *fls,
 			bool ovly,
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 8/8] ALSA: Change return value for load_dsp_prepare() to -ENOSYS
  2012-09-14  1:15 [PATCH 0/8] ALSA: Add DSP firmware loader (resub) Ian Minett
                   ` (6 preceding siblings ...)
  2012-09-14  1:15 ` [PATCH 7/8] ALSA: Add comments and descriptions to functions Ian Minett
@ 2012-09-14  1:15 ` Ian Minett
  2012-09-14  9:01   ` Takashi Iwai
  2012-09-14  8:32 ` [PATCH 0/8] ALSA: Add DSP firmware loader (resub) Takashi Iwai
  8 siblings, 1 reply; 16+ messages in thread
From: Ian Minett @ 2012-09-14  1:15 UTC (permalink / raw)
  To: patch; +Cc: alsa-devel, Ian Minett

From: Ian Minett <ian_minett@creativelabs.com>

Signed-off-by: Ian Minett <ian_minett@creativelabs.com>

diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 159e07a..ac60300 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -1164,7 +1164,7 @@ snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
 				unsigned int size,
 				struct snd_dma_buffer *bufp)
 {
-	return 0;
+	return -ENOSYS;
 }
 static inline void
 snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start) {}
-- 
1.7.4.1

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 0/8] ALSA: Add DSP firmware loader (resub)
  2012-09-14  1:15 [PATCH 0/8] ALSA: Add DSP firmware loader (resub) Ian Minett
                   ` (7 preceding siblings ...)
  2012-09-14  1:15 ` [PATCH 8/8] ALSA: Change return value for load_dsp_prepare() to -ENOSYS Ian Minett
@ 2012-09-14  8:32 ` Takashi Iwai
  8 siblings, 0 replies; 16+ messages in thread
From: Takashi Iwai @ 2012-09-14  8:32 UTC (permalink / raw)
  To: Ian Minett; +Cc: alsa-devel

At Thu, 13 Sep 2012 18:15:50 -0700,
Ian Minett wrote:
> 
> From: Ian Minett <ian_minett@creativelabs.com>
> 
> >Thanks for the patches, and sorry for the late response, 
> No problem, and sorry for ours too- we've been working on fixing the issues you
> raised last time. Hopefully this submission will sort out many of those items.
> Macros have been taken out, comments added, and firmware is now cached.

Thanks, now it looks _much_ better.

> >Keep the git commit author as me.
> Ok. I ensured you were set as the git commit author for the first two patchfiles. 
> Is there anything else I need to do to properly retain your authorship on those 
> commits?

That should suffice.

> >- Why hda_stream_format is defined?
> hda_stream_format is defined to store the stream format information used for 
> the DSP download.

But it's just passed to dma_convert_to_hda_format(), and the rest uses
only hda_format.  Then just passing rate and channels should suffice.

Or will the later patch use this new struct?

In anyway, I'll review and send a few nitpicking things.


thanks,

Takashi

> 
> Thanks,
> - Ian
> 
> Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
> 
> ---
> 1:
> - memalloc.h
> - pcm.h
> - pcm_memory.c
> - sgbuf.c
> Include Takashi's patch:
> Make snd_sgbuf_get_{ptr|addr}() available for non-SG cases.
> Passing struct snd_dma_buffer pointer instead, so that they work no
> matter whether real SG buffer is used or not.
> 
> 2:
> - hda_intel.c
> - hda_codec.h
> Include Takashi's code:
> Pass DMA buffer pointers in calls to setup_bdle().
> Add DSP loader callback routines to controller.
> 
> Add new DSP loader switch to Kconfig to enable DSP firmware loading.
> 
> 3:
> - patch_ca0132.c
> - ca0132_regs.h
> Add DSP register definitions header file
> 
> 4:
> - patch_ca0132.c
> Add DSP firmware enums and defs to CA0132 codec
> 
> 5:
> - patch_ca0132.c
> Add calls to new DSP loader system to transfer firmware binary
> to the hardware.
> Add chip read/write routines, DSP I/O, SCP packet format
> helper functions and transfer DMA management.
> 
> 6:
> - patch_ca0132.c
> Add DSP firmware caching to CA0132 codec
> 
> 7:
> - patch_ca0132.c
> Add comments and descriptions to functions.
> Merge chipio write address functions and add fix to dsp_write_wait().
> 
> 8:
> - hda_codec.h
> Change return value for load_dsp_prepare to -ENOSYS
> in case where DSP loader routines are not available.
> 
> 
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/8] ALSA: Add DSP firmware enums and defs to CA0132 codec
  2012-09-14  1:15 ` [PATCH 4/8] ALSA: Add DSP firmware enums and defs to CA0132 codec Ian Minett
@ 2012-09-14  8:40   ` Takashi Iwai
  0 siblings, 0 replies; 16+ messages in thread
From: Takashi Iwai @ 2012-09-14  8:40 UTC (permalink / raw)
  To: Ian Minett; +Cc: alsa-devel

At Thu, 13 Sep 2012 18:15:54 -0700,
Ian Minett wrote:
> 
> From: Ian Minett <ian_minett@creativelabs.com>
> 
> 
> Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
> 
> diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
> index da65535..8ea3348 100644
> --- a/sound/pci/hda/patch_ca0132.c
> +++ b/sound/pci/hda/patch_ca0132.c
> @@ -27,6 +27,7 @@
>  #include <linux/pci.h>
>  #include <linux/mutex.h>
>  #include <linux/module.h>
> +#include <linux/firmware.h>
>  #include <sound/core.h>
>  #include "hda_codec.h"
>  #include "hda_local.h"
> @@ -34,12 +35,37 @@
>  
>  #include "ca0132_regs.h"
>  
> +#define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18)
> +#define DSP_DMA_WRITE_BUFLEN_OVLY (1UL<<15)
> +
> +#define DMA_TRANSFER_FRAME_SIZE_NWORDS		8
> +#define DMA_TRANSFER_MAX_FRAME_SIZE_NWORDS	32
> +#define DMA_OVERLAY_FRAME_SIZE_NWORDS		2
> +
> +#define MASTERCONTROL				0x80
> +#define MASTERCONTROL_ALLOC_DMA_CHAN		9
> +
>  #define WIDGET_CHIP_CTRL      0x15
>  #define WIDGET_DSP_CTRL       0x16
>  
>  #define WUH_MEM_CONNID        10
>  #define DSP_MEM_CONNID        16
>  
> +#define MEM_CONNID_MICIN1     3
> +#define MEM_CONNID_MICIN2     5
> +#define MEM_CONNID_MICOUT1    12
> +#define MEM_CONNID_MICOUT2    14
> +#define MEM_CONNID_WUH        10
> +#define MEM_CONNID_DSP        16
> +#define MEM_CONNID_DMIC       100
> +
> +#define SCP_SET    0
> +#define SCP_GET    1
> +
> +#define EFX_FILE   "ctefx.bin"
> +
> +MODULE_FIRMWARE(EFX_FILE);

MODULE_FIRMWARE() line should be added only when the module really
calls request_firmware().  In this patch it's not implemented yet.
That is, this line should be moved to [PATCH 5/8] ALSA: Update CA0132
codec to load DSP firmware binary.


Takashi

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 5/8] ALSA: Update CA0132 codec to load DSP firmware binary
  2012-09-14  1:15 ` [PATCH 5/8] ALSA: Update CA0132 codec to load DSP firmware binary Ian Minett
@ 2012-09-14  8:51   ` Takashi Iwai
  2012-09-14  8:52   ` Takashi Iwai
  1 sibling, 0 replies; 16+ messages in thread
From: Takashi Iwai @ 2012-09-14  8:51 UTC (permalink / raw)
  To: Ian Minett; +Cc: alsa-devel

At Thu, 13 Sep 2012 18:15:55 -0700,
Ian Minett wrote:
> 
> From: Ian Minett <ian_minett@creativelabs.com>
> 

Give a bit more description what this patch really does and also what
it doesn't.  This just implements the firmware loading, but the new
DSP functionality itself isn't used yet, right?


> Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
> 
> diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
> index 8ea3348..11f5910 100644
> --- a/sound/pci/hda/patch_ca0132.c
> +++ b/sound/pci/hda/patch_ca0132.c
> @@ -48,9 +48,6 @@
>  #define WIDGET_CHIP_CTRL      0x15
>  #define WIDGET_DSP_CTRL       0x16
>  
> -#define WUH_MEM_CONNID        10
> -#define DSP_MEM_CONNID        16
> -
>  #define MEM_CONNID_MICIN1     3
>  #define MEM_CONNID_MICIN2     5
>  #define MEM_CONNID_MICOUT1    12
> @@ -352,6 +349,25 @@ static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
>  #define add_in_mono_volume(codec, nid, pfx, chan) \
>  	_add_volume(codec, nid, pfx, chan, 1)
>  
> +enum dsp_download_state {
> +	DSP_DOWNLOAD_FAILED = -1,
> +	DSP_DOWNLOAD_INIT   = 0,
> +	DSP_DOWNLOADING     = 1,
> +	DSP_DOWNLOADED      = 2
> +};
> +
> +struct hda_stream_format {
> +	unsigned int   sample_rate;
> +	unsigned short valid_bits_per_sample;
> +	unsigned short container_size;
> +	unsigned short number_channels;
> +};
> +
> +/* retrieve parameters from hda format */
> +#define get_hdafmt_chs(fmt)	(fmt & 0xf)
> +#define get_hdafmt_bits(fmt)	((fmt >> 4) & 0x7)
> +#define get_hdafmt_rate(fmt)	((fmt >> 8) & 0x7f)
> +#define get_hdafmt_type(fmt)	((fmt >> 15) & 0x1)

This can be well in hda_codec.h.  But it's fine to define here for
now...


>  /*
>   * CA0132 specific
> @@ -371,11 +387,55 @@ struct ca0132_spec {
>  	long curr_hp_switch;
>  	long curr_hp_volume[2];
>  	long curr_speaker_switch;
> -	struct mutex chipio_mutex;
>  	const char *input_labels[AUTO_PIN_LAST];
>  	struct hda_pcm pcm_rec[2]; /* PCM information */
> +
> +	/* chip access */
> +	struct mutex chipio_mutex; /* chip access mutex */
> +	u32 curr_chip_addx;
> +
> +	/* DSP download related */
> +	enum dsp_download_state dsp_state;
> +	unsigned int dsp_stream_id;
> +	unsigned int wait_scp;
> +	unsigned int wait_scp_header;
> +	unsigned int wait_num_data;
> +	unsigned int scp_resp_header;
> +	unsigned int scp_resp_data[4];
> +	unsigned int scp_resp_count;
>  };
>  
> +/*
> + * CA0132 codec access
> + */
> +unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
> +		unsigned int verb, unsigned int parm, unsigned int *res)
> +{
> +	unsigned int response;
> +	response = snd_hda_codec_read(codec, nid, 0, verb, parm);
> +	*res = response;
> +
> +	return ((response == -1) ? -1 : 0);
> +}
> +
> +static int codec_set_converter_format(struct hda_codec *codec, hda_nid_t nid,
> +		unsigned short converter_format, unsigned int *res)
> +{
> +	return codec_send_command(codec, nid, VENDOR_CHIPIO_STREAM_FORMAT,
> +				converter_format & 0xffff, res);
> +}
> +
> +static int codec_set_converter_stream_channel(struct hda_codec *codec,
> +				hda_nid_t nid, unsigned char stream,
> +				unsigned char channel, unsigned int *res)
> +{
> +	unsigned char converter_stream_channel = 0;
> +
> +	converter_stream_channel = (stream << 4) | (channel & 0x0f);
> +	return codec_send_command(codec, nid, AC_VERB_SET_CHANNEL_STREAMID,
> +				converter_stream_channel, res);
> +}
> +
>  /* Chip access helper function */
>  static int chipio_send(struct hda_codec *codec,
>  		       unsigned int reg,
> @@ -415,6 +475,30 @@ static int chipio_write_address(struct hda_codec *codec,
>  	return res;
>  }
>  
> +static int chipio_write_addx(struct hda_codec *codec, u32 chip_addx)
> +{
> +	struct ca0132_spec *spec = codec->spec;
> +	int status;
> +
> +	if (spec->curr_chip_addx == chip_addx)
> +		return 0;
> +
> +	/* send low 16 bits of the address */
> +	status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
> +			  chip_addx & 0xffff);
> +
> +	if (status < 0)
> +		return status;
> +
> +	/* send high 16 bits of the address */
> +	status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
> +			  chip_addx >> 16);
> +
> +	spec->curr_chip_addx = (status < 0) ? ~0UL : chip_addx;
> +
> +	return status;
> +}
> +
>  /*
>   * Write data through the vendor widget -- NOT protected by the Mutex!
>   */
> @@ -435,6 +519,24 @@ static int chipio_write_data(struct hda_codec *codec, unsigned int data)
>  	return res;
>  }
>  
> +static int chipio_write_data_multiple(struct hda_codec *codec,
> +				      const u32 *data,
> +				      unsigned int count)
> +{
> +	int status = 0;
> +
> +	if (data == NULL) {
> +		snd_printdd(KERN_ERR "chipio_write_data null ptr");
> +		return -EINVAL;
> +	}
> +
> +	while ((count-- != 0) && (status == 0))
> +		status = chipio_write_data(codec, *data++);
> +
> +	return status;
> +}
> +
> +
>  /*
>   * Read data through the vendor widget -- NOT protected by the Mutex!
>   */
> @@ -486,6 +588,26 @@ exit:
>  	return err;
>  }
>  
> +static int chipio_write_multiple(struct hda_codec *codec,
> +				 u32 chip_addx,
> +				 const u32 *data,
> +				 unsigned int count)
> +{
> +	struct ca0132_spec *spec = codec->spec;
> +	int status;
> +
> +	mutex_lock(&spec->chipio_mutex);
> +	status = chipio_write_addx(codec, chip_addx);
> +	if (status < 0)
> +		goto error;
> +
> +	status = chipio_write_data_multiple(codec, data, count);
> +error:
> +	mutex_unlock(&spec->chipio_mutex);
> +
> +	return status;
> +}
> +
>  /*
>   * Read the given address through the chip I/O widget
>   * protected by the Mutex
> @@ -512,6 +634,1425 @@ exit:
>  	return err;
>  }
>  
> +static void chipio_set_control_flag(struct hda_codec *codec,
> +				    enum control_flag_id flag_id,
> +				    bool flag_state)
> +{
> +	unsigned int val;
> +	unsigned int flag_bit;
> +
> +	flag_bit = (flag_state ? 1 : 0);
> +	val = (flag_bit << 7) | (flag_id);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_FLAG_SET, val);
> +}
> +
> +static void chipio_set_control_param(struct hda_codec *codec,
> +		enum control_param_id param_id, int param_val)
> +{
> +	struct ca0132_spec *spec = codec->spec;
> +	int val;
> +
> +	if ((param_id < 32) && (param_val < 8)) {
> +		val = (param_val << 5) | (param_id);
> +		snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +				    VENDOR_CHIPIO_PARAM_SET, val);
> +	} else {
> +		mutex_lock(&spec->chipio_mutex);
> +		if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
> +			snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +					    VENDOR_CHIPIO_PARAM_EX_ID_SET,
> +					    param_id);
> +			snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +					    VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
> +					    param_val);
> +		}
> +		mutex_unlock(&spec->chipio_mutex);
> +	}
> +}
> +
> +static void chipio_set_conn_rate(struct hda_codec *codec,
> +				int connid, enum ca0132_sample_rate rate)
> +{
> +	chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_ID, connid);
> +	chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE,
> +				 rate);
> +}
> +
> +static void chipio_enable_clocks(struct hda_codec *codec)
> +{
> +	struct ca0132_spec *spec = codec->spec;
> +
> +	mutex_lock(&spec->chipio_mutex);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_8051_ADDRESS_LOW, 0);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_8051_ADDRESS_LOW, 5);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_PLL_PMU_WRITE, 0x0b);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_8051_ADDRESS_LOW, 6);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
> +	mutex_unlock(&spec->chipio_mutex);
> +}
> +
> +/*
> + * CA0132 DSP IO stuffs
> + */
> +static int dspio_send(struct hda_codec *codec, unsigned int reg,
> +		      unsigned int data)
> +{
> +	unsigned int res;
> +	int retry = 50;
> +
> +	/* send bits of data specified by reg to dsp */
> +	do {
> +		res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data);
> +		if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY))
> +			return res;
> +	} while (--retry);
> +
> +	return -EIO;
> +}
> +
> +static void dspio_write_wait(struct hda_codec *codec)
> +{
> +	int cur_val, prv_val;
> +	int retry = 50;
> +
> +	cur_val = 0;
> +	do {
> +		prv_val = cur_val;
> +		msleep(20);
> +		dspio_send(codec, VENDOR_DSPIO_SCP_POST_COUNT_QUERY, 1);
> +		dspio_send(codec, VENDOR_DSPIO_STATUS, 0);
> +		cur_val = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
> +					   VENDOR_DSPIO_SCP_READ_COUNT, 0);
> +	} while (cur_val && (cur_val == prv_val) && --retry);
> +}
> +
> +static int dspio_write(struct hda_codec *codec, unsigned int scp_data)
> +{
> +	struct ca0132_spec *spec = codec->spec;
> +	int status;
> +
> +	dspio_write_wait(codec);
> +
> +	mutex_lock(&spec->chipio_mutex);
> +	status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW,
> +			    scp_data & 0xffff);
> +	if (status < 0)
> +		goto error;
> +
> +	status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH,
> +				    scp_data >> 16);
> +	if (status < 0)
> +		goto error;
> +
> +	/* OK, now check if the write itself has executed*/
> +	status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
> +				    VENDOR_DSPIO_STATUS, 0);
> +error:
> +	mutex_unlock(&spec->chipio_mutex);
> +
> +	return (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL) ?
> +			-EIO : 0;
> +}
> +
> +static int dspio_write_multiple(struct hda_codec *codec,
> +				unsigned int *buffer, unsigned int size)
> +{
> +	int status = 0;
> +	unsigned int count;
> +
> +	if ((buffer == NULL))
> +		return -EINVAL;
> +
> +	count = 0;
> +	while (count < size) {
> +		status = dspio_write(codec, *buffer++);
> +		if (status != 0)
> +			break;
> +		count++;
> +	}
> +
> +	return status;
> +}
> +
> +static inline unsigned int
> +make_scp_header(unsigned int target_id, unsigned int source_id,
> +		unsigned int get_flag, unsigned int req,
> +		unsigned int device_flag, unsigned int resp_flag,
> +		unsigned int error_flag, unsigned int data_size)
> +{
> +	unsigned int header = 0;
> +
> +	header = (data_size & 0x1f) << 27;
> +	header |= (error_flag & 0x01) << 26;
> +	header |= (resp_flag & 0x01) << 25;
> +	header |= (device_flag & 0x01) << 24;
> +	header |= (req & 0x7f) << 17;
> +	header |= (get_flag & 0x01) << 16;
> +	header |= (source_id & 0xff) << 8;
> +	header |= target_id & 0xff;
> +
> +	return header;
> +}
> +
> +static inline void
> +extract_scp_header(unsigned int header,
> +		   unsigned int *target_id, unsigned int *source_id,
> +		   unsigned int *get_flag, unsigned int *req,
> +		   unsigned int *device_flag, unsigned int *resp_flag,
> +		   unsigned int *error_flag, unsigned int *data_size)
> +{
> +	if (data_size)
> +		*data_size = (header >> 27) & 0x1f;
> +	if (error_flag)
> +		*error_flag = (header >> 26) & 0x01;
> +	if (resp_flag)
> +		*resp_flag = (header >> 25) & 0x01;
> +	if (device_flag)
> +		*device_flag = (header >> 24) & 0x01;
> +	if (req)
> +		*req = (header >> 17) & 0x7f;
> +	if (get_flag)
> +		*get_flag = (header >> 16) & 0x01;
> +	if (source_id)
> +		*source_id = (header >> 8) & 0xff;
> +	if (target_id)
> +		*target_id = header & 0xff;
> +}
> +
> +#define SCP_MAX_DATA_WORDS  (16)
> +
> +/* Structure to contain any SCP message */
> +struct scp_msg {
> +	unsigned int hdr;
> +	unsigned int data[SCP_MAX_DATA_WORDS];
> +};
> +
> +static int dspio_send_scp_message(struct hda_codec *codec,
> +				  unsigned char *send_buf,
> +				  unsigned int send_buf_size,
> +				  unsigned char *return_buf,
> +				  unsigned int return_buf_size,
> +				  unsigned int *bytes_returned)
> +{
> +	struct ca0132_spec *spec = codec->spec;
> +	int retry;
> +	int status = -1;
> +	unsigned int scp_send_size = 0;
> +	unsigned int total_size;
> +	bool waiting_for_resp = false;
> +	unsigned int header;
> +	struct scp_msg *ret_msg;
> +	unsigned int resp_src_id, resp_target_id;
> +	unsigned int data_size, src_id, target_id, get_flag, device_flag;
> +
> +	if (bytes_returned)
> +		*bytes_returned = 0;
> +
> +	/* get scp header from buffer */
> +	header = *((unsigned int *)send_buf);

This is not quite portable.  The HD-audio itself might be implemented
in big-endian machines, too.

Or, this might be more about struct scp_msg definition.
What is the expectation from the hardware?  Does the codec expect
32bit little endian values?  Then you'd need to convert data
explicitly when the architecture is big-endian.

(snip)
>  static int ca0132_init(struct hda_codec *codec)
>  {
>  	struct ca0132_spec *spec = codec->spec;
>  	struct auto_pin_cfg *cfg = &spec->autocfg;
>  	int i;
>  
> +	ca0132_download_dsp(codec);
> +

As I mentioned earlier, you cannot implement the DSP loading code
unconditionally.  User might not have a DSP firmware but only updated
the kernel.  Then it gets broken.

The DSP loading code (and the relevant stuff) must be protected via
ifdef CONFIG_SND_HDA_DSP_LOADER.  And yet, it wouldn't be bad to have
a module option to control the firmware loading as wel.


Takashi

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 5/8] ALSA: Update CA0132 codec to load DSP firmware binary
  2012-09-14  1:15 ` [PATCH 5/8] ALSA: Update CA0132 codec to load DSP firmware binary Ian Minett
  2012-09-14  8:51   ` Takashi Iwai
@ 2012-09-14  8:52   ` Takashi Iwai
  1 sibling, 0 replies; 16+ messages in thread
From: Takashi Iwai @ 2012-09-14  8:52 UTC (permalink / raw)
  To: Ian Minett; +Cc: alsa-devel

At Thu, 13 Sep 2012 18:15:55 -0700,
Ian Minett wrote:
> 
> From: Ian Minett <ian_minett@creativelabs.com>
> 

Give a bit more description what this patch really does and also what
it doesn't.  This just implements the firmware loading, but the new
DSP functionality itself isn't used yet, right?


> Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
> 
> diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
> index 8ea3348..11f5910 100644
> --- a/sound/pci/hda/patch_ca0132.c
> +++ b/sound/pci/hda/patch_ca0132.c
> @@ -48,9 +48,6 @@
>  #define WIDGET_CHIP_CTRL      0x15
>  #define WIDGET_DSP_CTRL       0x16
>  
> -#define WUH_MEM_CONNID        10
> -#define DSP_MEM_CONNID        16
> -
>  #define MEM_CONNID_MICIN1     3
>  #define MEM_CONNID_MICIN2     5
>  #define MEM_CONNID_MICOUT1    12
> @@ -352,6 +349,25 @@ static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
>  #define add_in_mono_volume(codec, nid, pfx, chan) \
>  	_add_volume(codec, nid, pfx, chan, 1)
>  
> +enum dsp_download_state {
> +	DSP_DOWNLOAD_FAILED = -1,
> +	DSP_DOWNLOAD_INIT   = 0,
> +	DSP_DOWNLOADING     = 1,
> +	DSP_DOWNLOADED      = 2
> +};
> +
> +struct hda_stream_format {
> +	unsigned int   sample_rate;
> +	unsigned short valid_bits_per_sample;
> +	unsigned short container_size;
> +	unsigned short number_channels;
> +};
> +
> +/* retrieve parameters from hda format */
> +#define get_hdafmt_chs(fmt)	(fmt & 0xf)
> +#define get_hdafmt_bits(fmt)	((fmt >> 4) & 0x7)
> +#define get_hdafmt_rate(fmt)	((fmt >> 8) & 0x7f)
> +#define get_hdafmt_type(fmt)	((fmt >> 15) & 0x1)

This can be well in hda_codec.h.  But it's fine to define here for
now...


>  /*
>   * CA0132 specific
> @@ -371,11 +387,55 @@ struct ca0132_spec {
>  	long curr_hp_switch;
>  	long curr_hp_volume[2];
>  	long curr_speaker_switch;
> -	struct mutex chipio_mutex;
>  	const char *input_labels[AUTO_PIN_LAST];
>  	struct hda_pcm pcm_rec[2]; /* PCM information */
> +
> +	/* chip access */
> +	struct mutex chipio_mutex; /* chip access mutex */
> +	u32 curr_chip_addx;
> +
> +	/* DSP download related */
> +	enum dsp_download_state dsp_state;
> +	unsigned int dsp_stream_id;
> +	unsigned int wait_scp;
> +	unsigned int wait_scp_header;
> +	unsigned int wait_num_data;
> +	unsigned int scp_resp_header;
> +	unsigned int scp_resp_data[4];
> +	unsigned int scp_resp_count;
>  };
>  
> +/*
> + * CA0132 codec access
> + */
> +unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
> +		unsigned int verb, unsigned int parm, unsigned int *res)
> +{
> +	unsigned int response;
> +	response = snd_hda_codec_read(codec, nid, 0, verb, parm);
> +	*res = response;
> +
> +	return ((response == -1) ? -1 : 0);
> +}
> +
> +static int codec_set_converter_format(struct hda_codec *codec, hda_nid_t nid,
> +		unsigned short converter_format, unsigned int *res)
> +{
> +	return codec_send_command(codec, nid, VENDOR_CHIPIO_STREAM_FORMAT,
> +				converter_format & 0xffff, res);
> +}
> +
> +static int codec_set_converter_stream_channel(struct hda_codec *codec,
> +				hda_nid_t nid, unsigned char stream,
> +				unsigned char channel, unsigned int *res)
> +{
> +	unsigned char converter_stream_channel = 0;
> +
> +	converter_stream_channel = (stream << 4) | (channel & 0x0f);
> +	return codec_send_command(codec, nid, AC_VERB_SET_CHANNEL_STREAMID,
> +				converter_stream_channel, res);
> +}
> +
>  /* Chip access helper function */
>  static int chipio_send(struct hda_codec *codec,
>  		       unsigned int reg,
> @@ -415,6 +475,30 @@ static int chipio_write_address(struct hda_codec *codec,
>  	return res;
>  }
>  
> +static int chipio_write_addx(struct hda_codec *codec, u32 chip_addx)
> +{
> +	struct ca0132_spec *spec = codec->spec;
> +	int status;
> +
> +	if (spec->curr_chip_addx == chip_addx)
> +		return 0;
> +
> +	/* send low 16 bits of the address */
> +	status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
> +			  chip_addx & 0xffff);
> +
> +	if (status < 0)
> +		return status;
> +
> +	/* send high 16 bits of the address */
> +	status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
> +			  chip_addx >> 16);
> +
> +	spec->curr_chip_addx = (status < 0) ? ~0UL : chip_addx;
> +
> +	return status;
> +}
> +
>  /*
>   * Write data through the vendor widget -- NOT protected by the Mutex!
>   */
> @@ -435,6 +519,24 @@ static int chipio_write_data(struct hda_codec *codec, unsigned int data)
>  	return res;
>  }
>  
> +static int chipio_write_data_multiple(struct hda_codec *codec,
> +				      const u32 *data,
> +				      unsigned int count)
> +{
> +	int status = 0;
> +
> +	if (data == NULL) {
> +		snd_printdd(KERN_ERR "chipio_write_data null ptr");
> +		return -EINVAL;
> +	}
> +
> +	while ((count-- != 0) && (status == 0))
> +		status = chipio_write_data(codec, *data++);
> +
> +	return status;
> +}
> +
> +
>  /*
>   * Read data through the vendor widget -- NOT protected by the Mutex!
>   */
> @@ -486,6 +588,26 @@ exit:
>  	return err;
>  }
>  
> +static int chipio_write_multiple(struct hda_codec *codec,
> +				 u32 chip_addx,
> +				 const u32 *data,
> +				 unsigned int count)
> +{
> +	struct ca0132_spec *spec = codec->spec;
> +	int status;
> +
> +	mutex_lock(&spec->chipio_mutex);
> +	status = chipio_write_addx(codec, chip_addx);
> +	if (status < 0)
> +		goto error;
> +
> +	status = chipio_write_data_multiple(codec, data, count);
> +error:
> +	mutex_unlock(&spec->chipio_mutex);
> +
> +	return status;
> +}
> +
>  /*
>   * Read the given address through the chip I/O widget
>   * protected by the Mutex
> @@ -512,6 +634,1425 @@ exit:
>  	return err;
>  }
>  
> +static void chipio_set_control_flag(struct hda_codec *codec,
> +				    enum control_flag_id flag_id,
> +				    bool flag_state)
> +{
> +	unsigned int val;
> +	unsigned int flag_bit;
> +
> +	flag_bit = (flag_state ? 1 : 0);
> +	val = (flag_bit << 7) | (flag_id);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_FLAG_SET, val);
> +}
> +
> +static void chipio_set_control_param(struct hda_codec *codec,
> +		enum control_param_id param_id, int param_val)
> +{
> +	struct ca0132_spec *spec = codec->spec;
> +	int val;
> +
> +	if ((param_id < 32) && (param_val < 8)) {
> +		val = (param_val << 5) | (param_id);
> +		snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +				    VENDOR_CHIPIO_PARAM_SET, val);
> +	} else {
> +		mutex_lock(&spec->chipio_mutex);
> +		if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
> +			snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +					    VENDOR_CHIPIO_PARAM_EX_ID_SET,
> +					    param_id);
> +			snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +					    VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
> +					    param_val);
> +		}
> +		mutex_unlock(&spec->chipio_mutex);
> +	}
> +}
> +
> +static void chipio_set_conn_rate(struct hda_codec *codec,
> +				int connid, enum ca0132_sample_rate rate)
> +{
> +	chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_ID, connid);
> +	chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE,
> +				 rate);
> +}
> +
> +static void chipio_enable_clocks(struct hda_codec *codec)
> +{
> +	struct ca0132_spec *spec = codec->spec;
> +
> +	mutex_lock(&spec->chipio_mutex);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_8051_ADDRESS_LOW, 0);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_8051_ADDRESS_LOW, 5);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_PLL_PMU_WRITE, 0x0b);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_8051_ADDRESS_LOW, 6);
> +	snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
> +			    VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
> +	mutex_unlock(&spec->chipio_mutex);
> +}
> +
> +/*
> + * CA0132 DSP IO stuffs
> + */
> +static int dspio_send(struct hda_codec *codec, unsigned int reg,
> +		      unsigned int data)
> +{
> +	unsigned int res;
> +	int retry = 50;
> +
> +	/* send bits of data specified by reg to dsp */
> +	do {
> +		res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data);
> +		if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY))
> +			return res;
> +	} while (--retry);
> +
> +	return -EIO;
> +}
> +
> +static void dspio_write_wait(struct hda_codec *codec)
> +{
> +	int cur_val, prv_val;
> +	int retry = 50;
> +
> +	cur_val = 0;
> +	do {
> +		prv_val = cur_val;
> +		msleep(20);
> +		dspio_send(codec, VENDOR_DSPIO_SCP_POST_COUNT_QUERY, 1);
> +		dspio_send(codec, VENDOR_DSPIO_STATUS, 0);
> +		cur_val = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
> +					   VENDOR_DSPIO_SCP_READ_COUNT, 0);
> +	} while (cur_val && (cur_val == prv_val) && --retry);
> +}
> +
> +static int dspio_write(struct hda_codec *codec, unsigned int scp_data)
> +{
> +	struct ca0132_spec *spec = codec->spec;
> +	int status;
> +
> +	dspio_write_wait(codec);
> +
> +	mutex_lock(&spec->chipio_mutex);
> +	status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW,
> +			    scp_data & 0xffff);
> +	if (status < 0)
> +		goto error;
> +
> +	status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH,
> +				    scp_data >> 16);
> +	if (status < 0)
> +		goto error;
> +
> +	/* OK, now check if the write itself has executed*/
> +	status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
> +				    VENDOR_DSPIO_STATUS, 0);
> +error:
> +	mutex_unlock(&spec->chipio_mutex);
> +
> +	return (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL) ?
> +			-EIO : 0;
> +}
> +
> +static int dspio_write_multiple(struct hda_codec *codec,
> +				unsigned int *buffer, unsigned int size)
> +{
> +	int status = 0;
> +	unsigned int count;
> +
> +	if ((buffer == NULL))
> +		return -EINVAL;
> +
> +	count = 0;
> +	while (count < size) {
> +		status = dspio_write(codec, *buffer++);
> +		if (status != 0)
> +			break;
> +		count++;
> +	}
> +
> +	return status;
> +}
> +
> +static inline unsigned int
> +make_scp_header(unsigned int target_id, unsigned int source_id,
> +		unsigned int get_flag, unsigned int req,
> +		unsigned int device_flag, unsigned int resp_flag,
> +		unsigned int error_flag, unsigned int data_size)
> +{
> +	unsigned int header = 0;
> +
> +	header = (data_size & 0x1f) << 27;
> +	header |= (error_flag & 0x01) << 26;
> +	header |= (resp_flag & 0x01) << 25;
> +	header |= (device_flag & 0x01) << 24;
> +	header |= (req & 0x7f) << 17;
> +	header |= (get_flag & 0x01) << 16;
> +	header |= (source_id & 0xff) << 8;
> +	header |= target_id & 0xff;
> +
> +	return header;
> +}
> +
> +static inline void
> +extract_scp_header(unsigned int header,
> +		   unsigned int *target_id, unsigned int *source_id,
> +		   unsigned int *get_flag, unsigned int *req,
> +		   unsigned int *device_flag, unsigned int *resp_flag,
> +		   unsigned int *error_flag, unsigned int *data_size)
> +{
> +	if (data_size)
> +		*data_size = (header >> 27) & 0x1f;
> +	if (error_flag)
> +		*error_flag = (header >> 26) & 0x01;
> +	if (resp_flag)
> +		*resp_flag = (header >> 25) & 0x01;
> +	if (device_flag)
> +		*device_flag = (header >> 24) & 0x01;
> +	if (req)
> +		*req = (header >> 17) & 0x7f;
> +	if (get_flag)
> +		*get_flag = (header >> 16) & 0x01;
> +	if (source_id)
> +		*source_id = (header >> 8) & 0xff;
> +	if (target_id)
> +		*target_id = header & 0xff;
> +}
> +
> +#define SCP_MAX_DATA_WORDS  (16)
> +
> +/* Structure to contain any SCP message */
> +struct scp_msg {
> +	unsigned int hdr;
> +	unsigned int data[SCP_MAX_DATA_WORDS];
> +};
> +
> +static int dspio_send_scp_message(struct hda_codec *codec,
> +				  unsigned char *send_buf,
> +				  unsigned int send_buf_size,
> +				  unsigned char *return_buf,
> +				  unsigned int return_buf_size,
> +				  unsigned int *bytes_returned)
> +{
> +	struct ca0132_spec *spec = codec->spec;
> +	int retry;
> +	int status = -1;
> +	unsigned int scp_send_size = 0;
> +	unsigned int total_size;
> +	bool waiting_for_resp = false;
> +	unsigned int header;
> +	struct scp_msg *ret_msg;
> +	unsigned int resp_src_id, resp_target_id;
> +	unsigned int data_size, src_id, target_id, get_flag, device_flag;
> +
> +	if (bytes_returned)
> +		*bytes_returned = 0;
> +
> +	/* get scp header from buffer */
> +	header = *((unsigned int *)send_buf);

This is not quite portable.  The HD-audio itself might be implemented
in big-endian machines, too.

Or, this might be more about struct scp_msg definition.
What is the expectation from the hardware?  Does the codec expect
32bit little endian values?  Then you'd need to convert data
explicitly when the architecture is big-endian.

(snip)
>  static int ca0132_init(struct hda_codec *codec)
>  {
>  	struct ca0132_spec *spec = codec->spec;
>  	struct auto_pin_cfg *cfg = &spec->autocfg;
>  	int i;
>  
> +	ca0132_download_dsp(codec);
> +

As I mentioned earlier, you cannot implement the DSP loading code
unconditionally.  User might not have a DSP firmware but only updated
the kernel.  Then it gets broken.

The DSP loading code (and the relevant stuff) must be protected via
ifdef CONFIG_SND_HDA_DSP_LOADER.  And yet, it wouldn't be bad to have
a module option to control the firmware loading as wel.


Takashi

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 6/8] ALSA: Add firmware caching to CA0132 codec
  2012-09-14  1:15 ` [PATCH 6/8] ALSA: Add firmware caching to CA0132 codec Ian Minett
@ 2012-09-14  8:57   ` Takashi Iwai
  0 siblings, 0 replies; 16+ messages in thread
From: Takashi Iwai @ 2012-09-14  8:57 UTC (permalink / raw)
  To: Ian Minett; +Cc: alsa-devel

At Thu, 13 Sep 2012 18:15:56 -0700,
Ian Minett wrote:
> 
> From: Ian Minett <ian_minett@creativelabs.com>

FWIW, the 3.7 kernel will have a firmware cache infrastructure, so
this patch can be converted with it later.

BTW, what about the firmware loading upon PM resume?
I guess at least S4 requires the firmware loading.  Does the firmware
on chip survive after S3?


Takashi

> 
> 
> Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
> 
> diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
> index 11f5910..ef66817 100644
> --- a/sound/pci/hda/patch_ca0132.c
> +++ b/sound/pci/hda/patch_ca0132.c
> @@ -2025,6 +2025,24 @@ static int dspload_image(struct hda_codec *codec,
>  	return status;
>  }
>  
> +static const struct firmware *fw_efx;
> +
> +static int request_firmware_cached(const struct firmware **firmware_p,
> +	const char *name, struct device *device)
> +{
> +	if (*firmware_p)
> +		return 0;  /* already loaded */
> +	return request_firmware(firmware_p, name, device);
> +}
> +
> +static void release_cached_firmware(void)
> +{
> +	if (fw_efx) {
> +		release_firmware(fw_efx);
> +		fw_efx = NULL;
> +	}
> +}
> +
>  static bool dspload_is_loaded(struct hda_codec *codec)
>  {
>  	unsigned int data = 0;
> @@ -2542,18 +2560,15 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec)
>  {
>  	bool dsp_loaded = false;
>  	const struct dsp_image_seg *dsp_os_image;
> -	const struct firmware *fw_entry;
>  
> -	if (request_firmware(&fw_entry, EFX_FILE, codec->bus->card->dev) != 0)
> +	if (request_firmware_cached(&fw_efx, EFX_FILE,
> +				    codec->bus->card->dev) != 0)
>  		return false;
>  
> -	dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
> +	dsp_os_image = (struct dsp_image_seg *)(fw_efx->data);
>  	dspload_image(codec, dsp_os_image, 0, 0, true, 0);
>  	dsp_loaded = dspload_wait_loaded(codec);
>  
> -	release_firmware(fw_entry);
> -
> -
>  	return dsp_loaded;
>  }
>  
> @@ -2662,7 +2677,8 @@ static int __init patch_ca0132_init(void)
>  }
>  
>  static void __exit patch_ca0132_exit(void)
> -{
> +{
> +	release_cached_firmware();
>  	snd_hda_delete_codec_preset(&ca0132_list);
>  }
>  
> -- 
> 1.7.4.1
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 7/8] ALSA: Add comments and descriptions to functions
  2012-09-14  1:15 ` [PATCH 7/8] ALSA: Add comments and descriptions to functions Ian Minett
@ 2012-09-14  8:59   ` Takashi Iwai
  0 siblings, 0 replies; 16+ messages in thread
From: Takashi Iwai @ 2012-09-14  8:59 UTC (permalink / raw)
  To: Ian Minett; +Cc: alsa-devel

At Thu, 13 Sep 2012 18:15:57 -0700,
Ian Minett wrote:
> 
> From: Ian Minett <ian_minett@creativelabs.com>
> 
> Signed-off-by: Ian Minett <ian_minett@creativelabs.com>
> 
> diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
> index ef66817..936e161 100644
> --- a/sound/pci/hda/patch_ca0132.c
> +++ b/sound/pci/hda/patch_ca0132.c
> @@ -460,8 +460,12 @@ static int chipio_send(struct hda_codec *codec,
>  static int chipio_write_address(struct hda_codec *codec,
>  				unsigned int chip_addx)
>  {
> +	struct ca0132_spec *spec = codec->spec;
>  	int res;
>  
> +	if (spec->curr_chip_addx == chip_addx)
> +			return 0;
> +

So, here you do more than what the patch subject says.
It shouldn't be mixed up.  If any real code changes are needed,
implement in another patch and describe properly what the patch does
and why needed.


>  	/* send low 16 bits of the address */
>  	res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
>  			  chip_addx & 0xffff);
> @@ -472,37 +476,14 @@ static int chipio_write_address(struct hda_codec *codec,
>  				  chip_addx >> 16);
>  	}
>  
> +	spec->curr_chip_addx = (res < 0) ? ~0UL : chip_addx;
> +
>  	return res;
>  }
>  
> -static int chipio_write_addx(struct hda_codec *codec, u32 chip_addx)
> -{
> -	struct ca0132_spec *spec = codec->spec;
> -	int status;
> -
> -	if (spec->curr_chip_addx == chip_addx)
> -		return 0;
> -
> -	/* send low 16 bits of the address */
> -	status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
> -			  chip_addx & 0xffff);
> -
> -	if (status < 0)
> -		return status;
> -
> -	/* send high 16 bits of the address */
> -	status = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
> -			  chip_addx >> 16);
> -
> -	spec->curr_chip_addx = (status < 0) ? ~0UL : chip_addx;
> -
> -	return status;
> -}
> -

Ditto.

So in this patch, there should be only additions and corrections of
comments.  Nothing more than that.


Takashi

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 8/8] ALSA: Change return value for load_dsp_prepare() to -ENOSYS
  2012-09-14  1:15 ` [PATCH 8/8] ALSA: Change return value for load_dsp_prepare() to -ENOSYS Ian Minett
@ 2012-09-14  9:01   ` Takashi Iwai
  0 siblings, 0 replies; 16+ messages in thread
From: Takashi Iwai @ 2012-09-14  9:01 UTC (permalink / raw)
  To: Ian Minett; +Cc: alsa-devel

At Thu, 13 Sep 2012 18:15:58 -0700,
Ian Minett wrote:
> 
> From: Ian Minett <ian_minett@creativelabs.com>
> 
> Signed-off-by: Ian Minett <ian_minett@creativelabs.com>

This depends on what is the expected.  If the function call is
mandatory for both cases with and without DSP loader implementation,
just returning zero is more practical.

In this case... well, maybe ca0132 is the only user, so I think it's
OK to return an error like this.


Takashi


> diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
> index 159e07a..ac60300 100644
> --- a/sound/pci/hda/hda_codec.h
> +++ b/sound/pci/hda/hda_codec.h
> @@ -1164,7 +1164,7 @@ snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
>  				unsigned int size,
>  				struct snd_dma_buffer *bufp)
>  {
> -	return 0;
> +	return -ENOSYS;
>  }
>  static inline void
>  snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start) {}
> -- 
> 1.7.4.1
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2012-09-14  9:01 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-09-14  1:15 [PATCH 0/8] ALSA: Add DSP firmware loader (resub) Ian Minett
2012-09-14  1:15 ` [PATCH 1/8] ALSA: Make snd_sgbuf_get_{ptr|addr}() available for non-SG cases Ian Minett
2012-09-14  1:15 ` [PATCH 2/8] ALSA: Add new DSP loader callback routines Ian Minett
2012-09-14  1:15 ` [PATCH 3/8] ALSA: Add CA0132 register definitions file Ian Minett
2012-09-14  1:15 ` [PATCH 4/8] ALSA: Add DSP firmware enums and defs to CA0132 codec Ian Minett
2012-09-14  8:40   ` Takashi Iwai
2012-09-14  1:15 ` [PATCH 5/8] ALSA: Update CA0132 codec to load DSP firmware binary Ian Minett
2012-09-14  8:51   ` Takashi Iwai
2012-09-14  8:52   ` Takashi Iwai
2012-09-14  1:15 ` [PATCH 6/8] ALSA: Add firmware caching to CA0132 codec Ian Minett
2012-09-14  8:57   ` Takashi Iwai
2012-09-14  1:15 ` [PATCH 7/8] ALSA: Add comments and descriptions to functions Ian Minett
2012-09-14  8:59   ` Takashi Iwai
2012-09-14  1:15 ` [PATCH 8/8] ALSA: Change return value for load_dsp_prepare() to -ENOSYS Ian Minett
2012-09-14  9:01   ` Takashi Iwai
2012-09-14  8:32 ` [PATCH 0/8] ALSA: Add DSP firmware loader (resub) Takashi Iwai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).