alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/4] ASoC: adsp: Add ADSP base support
@ 2012-10-26 18:36 Mark Brown
  2012-10-26 18:36 ` [PATCH 2/4] ASoC: wm2200: Convert over to wm_adsp for ADSP1 support Mark Brown
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Mark Brown @ 2012-10-26 18:36 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, patches, Mark Brown

Many current Wolfson devices feature DSPs based around an architecture
known as ADSP.  Since there is a lot of commonality in the system
integration of these devices a common library will be used to provide
support for them.

This version provides equivalent support for ADSP1 to that currently
included in the WM2200 driver.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 sound/soc/codecs/Kconfig   |    5 +
 sound/soc/codecs/Makefile  |    1 +
 sound/soc/codecs/wm_adsp.c |  571 ++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/wm_adsp.h |   54 +++++
 sound/soc/codecs/wmfw.h    |   29 +++
 5 files changed, 660 insertions(+)
 create mode 100644 sound/soc/codecs/wm_adsp.c
 create mode 100644 sound/soc/codecs/wm_adsp.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 6f4f284..91cbbe0 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -147,6 +147,11 @@ config SND_SOC_WM_HUBS
 	default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y
 	default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m
 
+config SND_SOC_WM_ADSP
+	tristate
+	default y if SND_SOC_WM2200=y
+	default m if SND_SOC_WM2200=m
+
 config SND_SOC_AB8500_CODEC
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index c55d2d7..ffbed85 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -63,6 +63,7 @@ snd-soc-twl6040-objs := twl6040.o
 snd-soc-uda134x-objs := uda134x.o
 snd-soc-uda1380-objs := uda1380.o
 snd-soc-wl1273-objs := wl1273.o
+snd-soc-wm-adsp := wm_adsp.o
 snd-soc-wm0010-objs := wm0010.o
 snd-soc-wm1250-ev1-objs := wm1250-ev1.o
 snd-soc-wm2000-objs := wm2000.o
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
new file mode 100644
index 0000000..c72d3fa
--- /dev/null
+++ b/sound/soc/codecs/wm_adsp.c
@@ -0,0 +1,571 @@
+/*
+ * wm_adsp.c  --  Wolfson ADSP support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/registers.h>
+
+#include "wm_adsp.h"
+
+#define adsp_crit(_dsp, fmt, ...) \
+	dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_err(_dsp, fmt, ...) \
+	dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_warn(_dsp, fmt, ...) \
+	dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_info(_dsp, fmt, ...) \
+	dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_dbg(_dsp, fmt, ...) \
+	dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+
+#define ADSP1_CONTROL_1                   0x00
+#define ADSP1_CONTROL_2                   0x02
+#define ADSP1_CONTROL_3                   0x03
+#define ADSP1_CONTROL_4                   0x04
+#define ADSP1_CONTROL_5                   0x06
+#define ADSP1_CONTROL_6                   0x07
+#define ADSP1_CONTROL_7                   0x08
+#define ADSP1_CONTROL_8                   0x09
+#define ADSP1_CONTROL_9                   0x0A
+#define ADSP1_CONTROL_10                  0x0B
+#define ADSP1_CONTROL_11                  0x0C
+#define ADSP1_CONTROL_12                  0x0D
+#define ADSP1_CONTROL_13                  0x0F
+#define ADSP1_CONTROL_14                  0x10
+#define ADSP1_CONTROL_15                  0x11
+#define ADSP1_CONTROL_16                  0x12
+#define ADSP1_CONTROL_17                  0x13
+#define ADSP1_CONTROL_18                  0x14
+#define ADSP1_CONTROL_19                  0x16
+#define ADSP1_CONTROL_20                  0x17
+#define ADSP1_CONTROL_21                  0x18
+#define ADSP1_CONTROL_22                  0x1A
+#define ADSP1_CONTROL_23                  0x1B
+#define ADSP1_CONTROL_24                  0x1C
+#define ADSP1_CONTROL_25                  0x1E
+#define ADSP1_CONTROL_26                  0x20
+#define ADSP1_CONTROL_27                  0x21
+#define ADSP1_CONTROL_28                  0x22
+#define ADSP1_CONTROL_29                  0x23
+#define ADSP1_CONTROL_30                  0x24
+#define ADSP1_CONTROL_31                  0x26
+
+/*
+ * ADSP1 Control 19
+ */
+#define ADSP1_WDMA_BUFFER_LENGTH_MASK     0x00FF  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT         0  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH         8  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+
+
+/*
+ * ADSP1 Control 30
+ */
+#define ADSP1_DBG_CLK_ENA                 0x0008  /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_MASK            0x0008  /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_SHIFT                3  /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_WIDTH                1  /* DSP1_DBG_CLK_ENA */
+#define ADSP1_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
+#define ADSP1_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
+#define ADSP1_START                       0x0001  /* DSP1_START */
+#define ADSP1_START_MASK                  0x0001  /* DSP1_START */
+#define ADSP1_START_SHIFT                      0  /* DSP1_START */
+#define ADSP1_START_WIDTH                      1  /* DSP1_START */
+
+#define ADSP2_CONTROL 0
+#define ADSP2_STATUS1 4
+
+/*
+ * ADSP2 Control
+ */
+
+#define ADSP2_MEM_ENA                     0x0010  /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_MASK                0x0010  /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_SHIFT                    4  /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_WIDTH                    1  /* DSP1_MEM_ENA */
+#define ADSP2_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
+#define ADSP2_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
+#define ADSP2_START                       0x0001  /* DSP1_START */
+#define ADSP2_START_MASK                  0x0001  /* DSP1_START */
+#define ADSP2_START_SHIFT                      0  /* DSP1_START */
+#define ADSP2_START_WIDTH                      1  /* DSP1_START */
+
+/*
+ * ADSP2 Status 1
+ */
+#define ADSP2_RAM_RDY                     0x0001
+#define ADSP2_RAM_RDY_MASK                0x0001
+#define ADSP2_RAM_RDY_SHIFT                    0
+#define ADSP2_RAM_RDY_WIDTH                    1
+
+
+static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
+							int type)
+{
+	int i;
+
+	for (i = 0; i < dsp->num_mems; i++)
+		if (dsp->mem[i].type == type)
+			return &dsp->mem[i];
+
+	return NULL;
+}
+
+static int wm_adsp_load(struct wm_adsp *dsp)
+{
+	const struct firmware *firmware;
+	struct regmap *regmap = dsp->regmap;
+	unsigned int pos = 0;
+	const struct wmfw_header *header;
+	const struct wmfw_adsp1_sizes *adsp1_sizes;
+	const struct wmfw_adsp2_sizes *adsp2_sizes;
+	const struct wmfw_footer *footer;
+	const struct wmfw_region *region;
+	const struct wm_adsp_region *mem;
+	const char *region_name;
+	char *file, *text;
+	unsigned int reg;
+	int regions = 0;
+	int ret, offset, type, sizes;
+
+	file = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (file == NULL)
+		return -ENOMEM;
+
+	snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num);
+	file[PAGE_SIZE - 1] = '\0';
+
+	ret = request_firmware(&firmware, file, dsp->dev);
+	if (ret != 0) {
+		adsp_err(dsp, "Failed to request '%s'\n", file);
+		goto out;
+	}
+	ret = -EINVAL;
+
+	pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
+	if (pos >= firmware->size) {
+		adsp_err(dsp, "%s: file too short, %zu bytes\n",
+			 file, firmware->size);
+		goto out_fw;
+	}
+
+	header = (void*)&firmware->data[0];
+
+	if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
+		adsp_err(dsp, "%s: invalid magic\n", file);
+		goto out_fw;
+	}
+
+	if (header->ver != 0) {
+		adsp_err(dsp, "%s: unknown file format %d\n",
+			 file, header->ver);
+		goto out_fw;
+	}
+
+	if (header->core != dsp->type) {
+		adsp_err(dsp, "%s: invalid core %d != %d\n",
+			 file, header->core, dsp->type);
+		goto out_fw;
+	}
+
+	switch (dsp->type) {
+	case WMFW_ADSP1:
+		pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
+		adsp1_sizes = (void *)&(header[1]);
+		footer = (void *)&(adsp1_sizes[1]);
+		sizes = sizeof(*adsp1_sizes);
+
+		adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
+			 file, le32_to_cpu(adsp1_sizes->dm),
+			 le32_to_cpu(adsp1_sizes->pm),
+			 le32_to_cpu(adsp1_sizes->zm));
+		break;
+
+	case WMFW_ADSP2:
+		pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
+		adsp2_sizes = (void *)&(header[1]);
+		footer = (void *)&(adsp2_sizes[1]);
+		sizes = sizeof(*adsp2_sizes);
+
+		adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
+			 file, le32_to_cpu(adsp2_sizes->xm),
+			 le32_to_cpu(adsp2_sizes->ym),
+			 le32_to_cpu(adsp2_sizes->pm),
+			 le32_to_cpu(adsp2_sizes->zm));
+		break;
+
+	default:
+		BUG_ON(NULL == "Unknown DSP type");
+		goto out_fw;
+	}
+
+	if (le32_to_cpu(header->len) != sizeof(*header) +
+	    sizes + sizeof(*footer)) {
+		adsp_err(dsp, "%s: unexpected header length %d\n",
+			 file, le32_to_cpu(header->len));
+		goto out_fw;
+	}
+
+	adsp_dbg(dsp, "%s: timestamp %llu\n", file,
+		 le64_to_cpu(footer->timestamp));
+
+	while (pos < firmware->size &&
+	       pos - firmware->size > sizeof(*region)) {
+		region = (void *)&(firmware->data[pos]);
+		region_name = "Unknown";
+		reg = 0;
+		text = NULL;
+		offset = le32_to_cpu(region->offset) & 0xffffff;
+		type = be32_to_cpu(region->type) & 0xff;
+		mem = wm_adsp_find_region(dsp, type);
+		
+		switch (type) {
+		case WMFW_NAME_TEXT:
+			region_name = "Firmware name";
+			text = kzalloc(le32_to_cpu(region->len) + 1,
+				       GFP_KERNEL);
+			break;
+		case WMFW_INFO_TEXT:
+			region_name = "Information";
+			text = kzalloc(le32_to_cpu(region->len) + 1,
+				       GFP_KERNEL);
+			break;
+		case WMFW_ABSOLUTE:
+			region_name = "Absolute";
+			reg = offset;
+			break;
+		case WMFW_ADSP1_PM:
+			BUG_ON(!mem);
+			region_name = "PM";
+			reg = mem->base + (offset * 3);
+			break;
+		case WMFW_ADSP1_DM:
+			BUG_ON(!mem);
+			region_name = "DM";
+			reg = mem->base + (offset * 2);
+			break;
+		case WMFW_ADSP2_XM:
+			BUG_ON(!mem);
+			region_name = "XM";
+			reg = mem->base + (offset * 2);
+			break;
+		case WMFW_ADSP2_YM:
+			BUG_ON(!mem);
+			region_name = "YM";
+			reg = mem->base + (offset * 2);
+			break;
+		case WMFW_ADSP1_ZM:
+			BUG_ON(!mem);
+			region_name = "ZM";
+			reg = mem->base + (offset * 2);
+			break;
+		default:
+			adsp_warn(dsp,
+				  "%s.%d: Unknown region type %x at %d(%x)\n",
+				  file, regions, type, pos, pos);
+			break;
+		}
+
+		adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
+			 regions, le32_to_cpu(region->len), offset,
+			 region_name);
+
+		if (text) {
+			memcpy(text, region->data, le32_to_cpu(region->len));
+			adsp_info(dsp, "%s: %s\n", file, text);
+			kfree(text);
+		}
+
+		if (reg) {
+			ret = regmap_raw_write(regmap, reg, region->data,
+					       le32_to_cpu(region->len));
+			if (ret != 0) {
+				adsp_err(dsp,
+					"%s.%d: Failed to write %d bytes at %d in %s: %d\n",
+					file, regions,
+					le32_to_cpu(region->len), offset,
+					region_name, ret);
+				goto out_fw;
+			}
+		}
+
+		pos += le32_to_cpu(region->len) + sizeof(*region);
+		regions++;
+	}
+	
+	if (pos > firmware->size)
+		adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
+			  file, regions, pos - firmware->size);
+
+out_fw:
+	release_firmware(firmware);
+out:
+	kfree(file);
+
+	return ret;
+}
+
+static int wm_adsp_load_coeff(struct wm_adsp *dsp)
+{
+	struct regmap *regmap = dsp->regmap;
+	struct wmfw_coeff_hdr *hdr;
+	struct wmfw_coeff_item *blk;
+	const struct firmware *firmware;
+	const char *region_name;
+	int ret, pos, blocks, type, offset, reg;
+	char *file;
+
+	file = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (file == NULL)
+		return -ENOMEM;
+
+	snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num);
+	file[PAGE_SIZE - 1] = '\0';
+
+	ret = request_firmware(&firmware, file, dsp->dev);
+	if (ret != 0) {
+		adsp_warn(dsp, "Failed to request '%s'\n", file);
+		ret = 0;
+		goto out;
+	}
+	ret = -EINVAL;
+
+	if (sizeof(*hdr) >= firmware->size) {
+		adsp_err(dsp, "%s: file too short, %zu bytes\n",
+			file, firmware->size);
+		goto out_fw;
+	}
+
+	hdr = (void*)&firmware->data[0];
+	if (memcmp(hdr->magic, "WMDR", 4) != 0) {
+		adsp_err(dsp, "%s: invalid magic\n", file);
+		return -EINVAL;
+	}
+
+	adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
+		(le32_to_cpu(hdr->ver) >> 16) & 0xff,
+		(le32_to_cpu(hdr->ver) >>  8) & 0xff,
+		le32_to_cpu(hdr->ver) & 0xff);
+
+	pos = le32_to_cpu(hdr->len);
+
+	blocks = 0;
+	while (pos < firmware->size &&
+	       pos - firmware->size > sizeof(*blk)) {
+		blk = (void*)(&firmware->data[pos]);
+
+		type = be32_to_cpu(blk->type) & 0xff;
+		offset = le32_to_cpu(blk->offset) & 0xffffff;
+
+		adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
+			 file, blocks, le32_to_cpu(blk->id),
+			 (le32_to_cpu(blk->ver) >> 16) & 0xff,
+			 (le32_to_cpu(blk->ver) >>  8) & 0xff,
+			 le32_to_cpu(blk->ver) & 0xff);
+		adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
+			 file, blocks, le32_to_cpu(blk->len), offset, type);
+
+		reg = 0;
+		region_name = "Unknown";
+		switch (type) {
+		case WMFW_NAME_TEXT:
+		case WMFW_INFO_TEXT:
+			break;
+		case WMFW_ABSOLUTE:
+			region_name = "register";
+			reg = offset;
+			break;
+		default:
+			adsp_err(dsp, "Unknown region type %x\n", type);
+			break;
+		}
+
+		if (reg) {
+			ret = regmap_raw_write(regmap, reg, blk->data,
+					       le32_to_cpu(blk->len));
+			if (ret != 0) {
+				adsp_err(dsp,
+					"%s.%d: Failed to write to %x in %s\n",
+					file, blocks, reg, region_name);
+			}
+		}
+
+		pos += le32_to_cpu(blk->len) + sizeof(*blk);
+		blocks++;
+	}
+
+	if (pos > firmware->size)
+		adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
+			  file, blocks, pos - firmware->size);
+
+out_fw:
+	release_firmware(firmware);
+out:
+	kfree(file);
+	return 0;
+}
+
+int wm_adsp1_event(struct snd_soc_dapm_widget *w,
+		   struct snd_kcontrol *kcontrol,
+		   int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+	struct wm_adsp *dsp = &dsps[w->shift];
+	int ret;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+				   ADSP1_SYS_ENA, ADSP1_SYS_ENA);
+
+		ret = wm_adsp_load(dsp);
+		if (ret != 0)
+			goto err;
+
+		ret = wm_adsp_load_coeff(dsp);
+		if (ret != 0)
+			goto err;
+
+		/* Start the core running */
+		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+				   ADSP1_CORE_ENA | ADSP1_START,
+				   ADSP1_CORE_ENA | ADSP1_START);
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		/* Halt the core */
+		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+				   ADSP1_CORE_ENA | ADSP1_START, 0);
+
+		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
+				   ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
+
+		regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+				   ADSP1_SYS_ENA, 0);
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+
+err:
+	regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+			   ADSP1_SYS_ENA, 0);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp1_event);
+
+static int wm_adsp2_ena(struct wm_adsp *dsp)
+{
+	unsigned int val;
+	int ret, count;
+
+	ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+				 ADSP2_SYS_ENA, ADSP2_SYS_ENA);
+	if (ret != 0)
+		return ret;
+
+	/* Wait for the RAM to start, should be near instantaneous */
+	count = 0;
+	do {
+		ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
+				  &val);
+		if (ret != 0)
+			return ret;
+	} while (!(val & ADSP2_RAM_RDY) && ++count < 10);
+
+	if (!(val & ADSP2_RAM_RDY)) {
+		adsp_err(dsp, "Failed to start DSP RAM\n");
+		return -EBUSY;
+	}
+
+	adsp_dbg(dsp, "RAM ready after %d polls\n", count);
+	adsp_info(dsp, "RAM ready after %d polls\n", count);
+
+	return 0;
+}
+
+int wm_adsp2_event(struct snd_soc_dapm_widget *w,
+		   struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+	struct wm_adsp *dsp = &dsps[w->shift];
+	int ret;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		ret = wm_adsp2_ena(dsp);
+		if (ret != 0)
+			return ret;
+
+		ret = wm_adsp_load(dsp);
+		if (ret != 0)
+			goto err;
+
+		ret = wm_adsp_load_coeff(dsp);
+		if (ret != 0)
+			goto err;
+
+		ret = regmap_update_bits(dsp->regmap,
+					 dsp->base + ADSP2_CONTROL,
+					 ADSP2_SYS_ENA | ADSP2_START, 0);
+		if (ret != 0)
+			goto err;
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+				   ADSP2_SYS_ENA | ADSP2_START, 0);
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+err:
+	regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+			   ADSP2_SYS_ENA | ADSP2_START, 0);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_event);
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
new file mode 100644
index 0000000..b303b1f
--- /dev/null
+++ b/sound/soc/codecs/wm_adsp.h
@@ -0,0 +1,54 @@
+/*
+ * wm_adsp.h  --  Wolfson ADSP support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __WM_ADSP_H
+#define __WM_ADSP_H
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "wmfw.h"
+
+struct wm_adsp_region {
+	int type;
+	unsigned int base;
+};
+
+struct wm_adsp {
+	const char *part;
+	int num;
+	int type;
+	struct device *dev;
+	struct regmap *regmap;
+
+	int base;
+
+	const struct wm_adsp_region *mem;
+	int num_mems;
+};
+
+#define WM_ADSP1(wname, num) \
+	{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
+	.shift = num, .event = wm_adsp1_event, \
+	.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+
+#define WM_ADSP2(wname, num) \
+{	.id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
+	.shift = num, .event = wm_adsp2_event, \
+	.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+
+int wm_adsp1_event(struct snd_soc_dapm_widget *w,
+		   struct snd_kcontrol *kcontrol, int event);
+int wm_adsp2_event(struct snd_soc_dapm_widget *w,
+		   struct snd_kcontrol *kcontrol, int event);
+
+#endif
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h
index 5791f8e..5632ded 100644
--- a/sound/soc/codecs/wmfw.h
+++ b/sound/soc/codecs/wmfw.h
@@ -34,6 +34,13 @@ struct wmfw_adsp1_sizes {
 	__le32 zm;
 } __packed;
 
+struct wmfw_adsp2_sizes {
+	__le32 xm;
+	__le32 ym;
+	__le32 pm;
+	__le32 zm;
+} __packed;
+
 struct wmfw_region {
 	union {
 		__be32 type;
@@ -57,6 +64,14 @@ struct wmfw_adsp1_id_hdr {
 	__be32 algs;
 } __packed;
 
+struct wmfw_adsp2_id_hdr {
+	struct wmfw_id_hdr fw;
+	__be32 zm;
+	__be32 xm;
+	__be32 ym;
+	__be32 algs;
+} __packed;
+
 struct wmfw_alg_hdr {
 	__be32 id;
 	__be32 ver;
@@ -68,6 +83,13 @@ struct wmfw_adsp1_alg_hdr {
 	__be32 dm;
 } __packed;
 
+struct wmfw_adsp2_alg_hdr {
+	struct wmfw_alg_hdr alg;
+	__be32 zm;
+	__be32 xm;
+	__be32 ym;
+} __packed;
+
 struct wmfw_coeff_hdr {
 	u8 magic[4];
 	__le32 len;
@@ -86,7 +108,9 @@ struct wmfw_coeff_item {
 	__le32 len;
 	u8 data[];
 } __packed;
+
 #define WMFW_ADSP1 1
+#define WMFW_ADSP2 2
 
 #define WMFW_ABSOLUTE  0xf0
 #define WMFW_NAME_TEXT 0xfe
@@ -96,4 +120,9 @@ struct wmfw_coeff_item {
 #define WMFW_ADSP1_DM 3
 #define WMFW_ADSP1_ZM 4
 
+#define WMFW_ADSP2_PM 2
+#define WMFW_ADSP2_ZM 4
+#define WMFW_ADSP2_XM 5
+#define WMFW_ADSP2_YM 6
+
 #endif
-- 
1.7.10.4

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

* [PATCH 2/4] ASoC: wm2200: Convert over to wm_adsp for ADSP1 support
  2012-10-26 18:36 [PATCH 1/4] ASoC: adsp: Add ADSP base support Mark Brown
@ 2012-10-26 18:36 ` Mark Brown
  2012-10-26 18:36 ` [PATCH 3/4] ASoC: arizona: Define standard hookup for ADSP2 Mark Brown
  2012-10-26 18:36 ` [PATCH 4/4] ASoC: wm5102: Hook up DSP1 Mark Brown
  2 siblings, 0 replies; 4+ messages in thread
From: Mark Brown @ 2012-10-26 18:36 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, patches, Mark Brown

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 sound/soc/codecs/wm2200.c |  432 ++++-----------------------------------------
 1 file changed, 32 insertions(+), 400 deletions(-)

diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index 2148ee3..1730df8 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -34,6 +34,7 @@
 
 #include "wm2200.h"
 #include "wmfw.h"
+#include "wm_adsp.h"
 
 #define WM2200_DSP_CONTROL_1                   0x00
 #define WM2200_DSP_CONTROL_2                   0x02
@@ -83,6 +84,7 @@ struct wm2200_fll {
 
 /* codec private data */
 struct wm2200_priv {
+	struct wm_adsp dsp[2];
 	struct regmap *regmap;
 	struct device *dev;
 	struct snd_soc_codec *codec;
@@ -152,6 +154,18 @@ static const struct regmap_range_cfg wm2200_ranges[] = {
 	  .window_start = WM2200_DSP2_ZM_0, .window_len = 1024, },
 };
 
+static const struct wm_adsp_region wm2200_dsp1_regions[] = {
+	{ .type = WMFW_ADSP1_PM, .base = WM2200_DSP1_PM_BASE },
+	{ .type = WMFW_ADSP1_DM, .base = WM2200_DSP1_DM_BASE },
+	{ .type = WMFW_ADSP1_ZM, .base = WM2200_DSP1_ZM_BASE },
+};
+
+static const struct wm_adsp_region wm2200_dsp2_regions[] = {
+	{ .type = WMFW_ADSP1_PM, .base = WM2200_DSP2_PM_BASE },
+	{ .type = WMFW_ADSP1_DM, .base = WM2200_DSP2_DM_BASE },
+	{ .type = WMFW_ADSP1_ZM, .base = WM2200_DSP2_ZM_BASE },
+};
+
 static struct reg_default wm2200_reg_defaults[] = {
 	{ 0x000B, 0x0000 },   /* R11    - Tone Generator 1 */
 	{ 0x0102, 0x0000 },   /* R258   - Clocking 3 */
@@ -981,400 +995,6 @@ static int wm2200_reset(struct wm2200_priv *wm2200)
 	}
 }
 
-static int wm2200_dsp_load(struct snd_soc_codec *codec, int base)
-{
-	const struct firmware *firmware;
-	struct regmap *regmap = codec->control_data;
-	unsigned int pos = 0;
-	const struct wmfw_header *header;
-	const struct wmfw_adsp1_sizes *adsp1_sizes;
-	const struct wmfw_footer *footer;
-	const struct wmfw_region *region;
-	const char *file, *region_name;
-	char *text;
-	unsigned int dm, pm, zm, reg;
-	int regions = 0;
-	int ret, offset, type;
-
-	switch (base) {
-	case WM2200_DSP1_CONTROL_1:
-		file = "wm2200-dsp1.wmfw";
-		dm = WM2200_DSP1_DM_BASE;
-		pm = WM2200_DSP1_PM_BASE;
-		zm = WM2200_DSP1_ZM_BASE;
-		break;
-	case WM2200_DSP2_CONTROL_1:
-		file = "wm2200-dsp2.wmfw";
-		dm = WM2200_DSP2_DM_BASE;
-		pm = WM2200_DSP2_PM_BASE;
-		zm = WM2200_DSP2_ZM_BASE;
-		break;
-	default:
-		dev_err(codec->dev, "BASE %x\n", base);
-		BUG_ON(1);
-		return -EINVAL;
-	}
-
-	ret = request_firmware(&firmware, file, codec->dev);
-	if (ret != 0) {
-		dev_err(codec->dev, "Failed to request '%s'\n", file);
-		return ret;
-	}
-
-	pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
-	if (pos >= firmware->size) {
-		dev_err(codec->dev, "%s: file too short, %zu bytes\n",
-			file, firmware->size);
-		return -EINVAL;
-	}
-
-	header = (void*)&firmware->data[0];
-
-	if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
-		dev_err(codec->dev, "%s: invalid magic\n", file);
-		return -EINVAL;
-	}
-
-	if (header->ver != 0) {
-		dev_err(codec->dev, "%s: unknown file format %d\n",
-			file, header->ver);
-		return -EINVAL;
-	}
-
-	if (le32_to_cpu(header->len) != sizeof(*header) +
-	    sizeof(*adsp1_sizes) + sizeof(*footer)) {
-		dev_err(codec->dev, "%s: unexpected header length %d\n",
-			file, le32_to_cpu(header->len));
-		return -EINVAL;
-	}
-
-	if (header->core != WMFW_ADSP1) {
-		dev_err(codec->dev, "%s: invalid core %d\n",
-			file, header->core);
-		return -EINVAL;
-	}
-
-	adsp1_sizes = (void *)&(header[1]);
-	footer = (void *)&(adsp1_sizes[1]);
-
-	dev_dbg(codec->dev, "%s: %d DM, %d PM, %d ZM\n",
-		file, le32_to_cpu(adsp1_sizes->dm),
-		le32_to_cpu(adsp1_sizes->pm), le32_to_cpu(adsp1_sizes->zm));
-
-	dev_dbg(codec->dev, "%s: timestamp %llu\n", file,
-		le64_to_cpu(footer->timestamp));
-
-	while (pos < firmware->size &&
-	       pos - firmware->size > sizeof(*region)) {
-		region = (void *)&(firmware->data[pos]);
-		region_name = "Unknown";
-		reg = 0;
-		text = NULL;
-		offset = le32_to_cpu(region->offset) & 0xffffff;
-		type = be32_to_cpu(region->type) & 0xff;
-		
-		switch (type) {
-		case WMFW_NAME_TEXT:
-			region_name = "Firmware name";
-			text = kzalloc(le32_to_cpu(region->len) + 1,
-				       GFP_KERNEL);
-			break;
-		case WMFW_INFO_TEXT:
-			region_name = "Information";
-			text = kzalloc(le32_to_cpu(region->len) + 1,
-				       GFP_KERNEL);
-			break;
-		case WMFW_ABSOLUTE:
-			region_name = "Absolute";
-			reg = offset;
-			break;
-		case WMFW_ADSP1_PM:
-			region_name = "PM";
-			reg = pm + (offset * 3);
-			break;
-		case WMFW_ADSP1_DM:
-			region_name = "DM";
-			reg = dm + (offset * 2);
-			break;
-		case WMFW_ADSP1_ZM:
-			region_name = "ZM";
-			reg = zm + (offset * 2);
-			break;
-		default:
-			dev_warn(codec->dev,
-				 "%s.%d: Unknown region type %x at %d(%x)\n",
-				 file, regions, type, pos, pos);
-			break;
-		}
-
-		dev_dbg(codec->dev, "%s.%d: %d bytes at %d in %s\n", file,
-			regions, le32_to_cpu(region->len), offset,
-			region_name);
-
-		if (text) {
-			memcpy(text, region->data, le32_to_cpu(region->len));
-			dev_info(codec->dev, "%s: %s\n", file, text);
-			kfree(text);
-		}
-
-		if (reg) {
-			ret = regmap_raw_write(regmap, reg, region->data,
-					       le32_to_cpu(region->len));
-			if (ret != 0) {
-				dev_err(codec->dev,
-					"%s.%d: Failed to write %d bytes at %d in %s: %d\n",
-					file, regions,
-					le32_to_cpu(region->len), offset,
-					region_name, ret);
-				goto out;
-			}
-		}
-
-		pos += le32_to_cpu(region->len) + sizeof(*region);
-		regions++;
-	}
-
-	if (pos > firmware->size)
-		dev_warn(codec->dev, "%s.%d: %zu bytes at end of file\n",
-			 file, regions, pos - firmware->size);
-
-out:
-	release_firmware(firmware);
-	
-	return ret;
-}
-
-static int wm2200_setup_algs(struct snd_soc_codec *codec, int base)
-{
-	struct regmap *regmap = codec->control_data;
-	struct wmfw_adsp1_id_hdr id;
-	struct wmfw_adsp1_alg_hdr *alg;
-	size_t algs;
-	int zm, dm, pm, ret, i;
-	__be32 val;
-
-	switch (base) {
-	case WM2200_DSP1_CONTROL_1:
-		dm = WM2200_DSP1_DM_BASE;
-		pm = WM2200_DSP1_PM_BASE;
-		zm = WM2200_DSP1_ZM_BASE;
-		break;
-	case WM2200_DSP2_CONTROL_1:
-		dm = WM2200_DSP2_DM_BASE;
-		pm = WM2200_DSP2_PM_BASE;
-		zm = WM2200_DSP2_ZM_BASE;
-		break;
-	default:
-		dev_err(codec->dev, "BASE %x\n", base);
-		BUG_ON(1);
-		return -EINVAL;
-	}
-
-	ret = regmap_raw_read(regmap, dm, &id, sizeof(id));
-	if (ret != 0) {
-		dev_err(codec->dev, "Failed to read algorithm info: %d\n",
-			ret);
-		return ret;
-	}
-
-	algs = be32_to_cpu(id.algs);
-	dev_info(codec->dev, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
-		 be32_to_cpu(id.fw.id),
-		 (be32_to_cpu(id.fw.ver) & 0xff000) >> 16,
-		 (be32_to_cpu(id.fw.ver) & 0xff00) >> 8,
-		 be32_to_cpu(id.fw.ver) & 0xff,
-		 algs);
-
-	/* Read the terminator first to validate the length */
-	ret = regmap_raw_read(regmap, dm +
-			      (sizeof(id) + (algs * sizeof(*alg))) / 2,
-			      &val, sizeof(val));
-	if (ret != 0) {
-		dev_err(codec->dev, "Failed to read algorithm list end: %d\n",
-			ret);
-		return ret;
-	}
-
-	if (be32_to_cpu(val) != 0xbedead)
-		dev_warn(codec->dev, "Algorithm list end %zx 0x%x != 0xbeadead\n",
-			 (sizeof(id) + (algs * sizeof(*alg))) / 2,
-			 be32_to_cpu(val));
-
-	alg = kzalloc(sizeof(*alg) * algs, GFP_KERNEL);
-	if (!alg)
-		return -ENOMEM;
-
-	ret = regmap_raw_read(regmap, dm + (sizeof(id) / 2),
-			      alg, algs * sizeof(*alg));
-	if (ret != 0) {
-		dev_err(codec->dev, "Failed to read algorithm list: %d\n",
-			ret);
-		goto out;
-	}
-
-	for (i = 0; i < algs; i++) {
-		dev_info(codec->dev, "%d: ID %x v%d.%d.%d\n",
-			 i, be32_to_cpu(alg[i].alg.id),
-			 (be32_to_cpu(alg[i].alg.ver) & 0xff000) >> 16,
-			 (be32_to_cpu(alg[i].alg.ver) & 0xff00) >> 8,
-			 be32_to_cpu(alg[i].alg.ver) & 0xff);
-	}
-
-out:
-	kfree(alg);
-	return ret;
-}
-
-static int wm2200_load_coeff(struct snd_soc_codec *codec, int base)
-{
-	struct regmap *regmap = codec->control_data;
-	struct wmfw_coeff_hdr *hdr;
-	struct wmfw_coeff_item *blk;
-	const struct firmware *firmware;
-	const char *file, *region_name;
-	int ret, dm, pm, zm, pos, blocks, type, offset, reg;
-
-	switch (base) {
-	case WM2200_DSP1_CONTROL_1:
-		file = "wm2200-dsp1.bin";
-		dm = WM2200_DSP1_DM_BASE;
-		pm = WM2200_DSP1_PM_BASE;
-		zm = WM2200_DSP1_ZM_BASE;
-		break;
-	case WM2200_DSP2_CONTROL_1:
-		file = "wm2200-dsp2.bin";
-		dm = WM2200_DSP2_DM_BASE;
-		pm = WM2200_DSP2_PM_BASE;
-		zm = WM2200_DSP2_ZM_BASE;
-		break;
-	default:
-		dev_err(codec->dev, "BASE %x\n", base);
-		BUG_ON(1);
-		return -EINVAL;
-	}
-
-	ret = request_firmware(&firmware, file, codec->dev);
-	if (ret != 0) {
-		dev_err(codec->dev, "Failed to request '%s'\n", file);
-		return ret;
-	}
-
-	if (sizeof(*hdr) >= firmware->size) {
-		dev_err(codec->dev, "%s: file too short, %zu bytes\n",
-			file, firmware->size);
-		return -EINVAL;
-	}
-
-	hdr = (void*)&firmware->data[0];
-	if (memcmp(hdr->magic, "WMDR", 4) != 0) {
-		dev_err(codec->dev, "%s: invalid magic\n", file);
-		return -EINVAL;
-	}
-
-	dev_dbg(codec->dev, "%s: v%d.%d.%d\n", file,
-		(le32_to_cpu(hdr->ver) >> 16) & 0xff,
-		(le32_to_cpu(hdr->ver) >>  8) & 0xff,
-		le32_to_cpu(hdr->ver) & 0xff);
-
-	pos = le32_to_cpu(hdr->len);
-
-	blocks = 0;
-	while (pos < firmware->size &&
-	       pos - firmware->size > sizeof(*blk)) {
-		blk = (void*)(&firmware->data[pos]);
-
-		type = be32_to_cpu(blk->type) & 0xff;
-		offset = le32_to_cpu(blk->offset) & 0xffffff;
-
-		dev_dbg(codec->dev, "%s.%d: %x v%d.%d.%d\n",
-			file, blocks, le32_to_cpu(blk->id),
-			(le32_to_cpu(blk->ver) >> 16) & 0xff,
-			(le32_to_cpu(blk->ver) >>  8) & 0xff,
-			le32_to_cpu(blk->ver) & 0xff);
-		dev_dbg(codec->dev, "%s.%d: %d bytes at 0x%x in %x\n",
-			file, blocks, le32_to_cpu(blk->len), offset, type);
-
-		reg = 0;
-		region_name = "Unknown";
-		switch (type) {
-		case WMFW_NAME_TEXT:
-		case WMFW_INFO_TEXT:
-			break;
-		case WMFW_ABSOLUTE:
-			region_name = "register";
-			reg = offset;
-			break;
-		default:
-			dev_err(codec->dev, "Unknown region type %x\n", type);
-			break;
-		}
-
-		if (reg) {
-			ret = regmap_raw_write(regmap, reg, blk->data,
-					       le32_to_cpu(blk->len));
-			if (ret != 0) {
-				dev_err(codec->dev,
-					"%s.%d: Failed to write to %x in %s\n",
-					file, blocks, reg, region_name);
-			}
-		}
-
-		pos += le32_to_cpu(blk->len) + sizeof(*blk);
-		blocks++;
-	}
-
-	if (pos > firmware->size)
-		dev_warn(codec->dev, "%s.%d: %zu bytes at end of file\n",
-			 file, blocks, pos - firmware->size);
-
-	return 0;
-}
-
-static int wm2200_dsp_ev(struct snd_soc_dapm_widget *w,
-			 struct snd_kcontrol *kcontrol,
-			 int event)
-{
-	struct snd_soc_codec *codec = w->codec;
-	int base = w->reg - WM2200_DSP_CONTROL_30;
-	int ret;
-
-	switch (event) {
-	case SND_SOC_DAPM_POST_PMU:
-		ret = wm2200_dsp_load(codec, base);
-		if (ret != 0)
-			return ret;
-
-		ret = wm2200_setup_algs(codec, base);
-		if (ret != 0)
-			return ret;
-
-		ret = wm2200_load_coeff(codec, base);
-		if (ret != 0)
-			return ret;
-
-		/* Start the core running */
-		snd_soc_update_bits(codec, w->reg,
-				    WM2200_DSP1_CORE_ENA | WM2200_DSP1_START,
-				    WM2200_DSP1_CORE_ENA | WM2200_DSP1_START);
-		break;
-
-	case SND_SOC_DAPM_PRE_PMD:
-		/* Halt the core */
-		snd_soc_update_bits(codec, w->reg,
-				    WM2200_DSP1_CORE_ENA | WM2200_DSP1_START,
-				    0);
-
-		snd_soc_update_bits(codec, base + WM2200_DSP_CONTROL_19,
-				    WM2200_DSP1_WDMA_BUFFER_LENGTH_MASK, 0);
-		break;
-
-	default:
-		break;
-	}
-
-	return 0;
-}
-
 static DECLARE_TLV_DB_SCALE(in_tlv, -6300, 100, 0);
 static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
 static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0);
@@ -1722,12 +1342,8 @@ SND_SOC_DAPM_PGA("LHPF1", WM2200_HPLPF1_1, WM2200_LHPF1_ENA_SHIFT, 0,
 SND_SOC_DAPM_PGA("LHPF2", WM2200_HPLPF2_1, WM2200_LHPF2_ENA_SHIFT, 0,
 		 NULL, 0),
 
-SND_SOC_DAPM_PGA_E("DSP1", WM2200_DSP1_CONTROL_30, WM2200_DSP1_SYS_ENA_SHIFT,
-		   0, NULL, 0, wm2200_dsp_ev,
-		   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
-SND_SOC_DAPM_PGA_E("DSP2", WM2200_DSP2_CONTROL_30, WM2200_DSP2_SYS_ENA_SHIFT,
-		   0, NULL, 0, wm2200_dsp_ev,
-		   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+WM_ADSP1("DSP1", 0),
+WM_ADSP1("DSP2", 1),
 
 SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0,
 		    WM2200_AUDIO_IF_1_22, WM2200_AIF1TX1_ENA_SHIFT, 0),
@@ -2592,6 +2208,22 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
 		goto err;
 	}
 
+	for (i = 0; i < 2; i++) {
+		wm2200->dsp[i].type = WMFW_ADSP1;
+		wm2200->dsp[i].part = "wm2200";
+		wm2200->dsp[i].num = i + 1;
+		wm2200->dsp[i].dev = &i2c->dev;
+		wm2200->dsp[i].regmap = wm2200->regmap;
+	}
+
+	wm2200->dsp[0].base = WM2200_DSP1_CONTROL_1;
+	wm2200->dsp[0].mem = wm2200_dsp1_regions;
+	wm2200->dsp[0].num_mems = ARRAY_SIZE(wm2200_dsp1_regions);
+
+	wm2200->dsp[1].base = WM2200_DSP2_CONTROL_1;
+	wm2200->dsp[1].mem = wm2200_dsp2_regions;
+	wm2200->dsp[1].num_mems = ARRAY_SIZE(wm2200_dsp2_regions);
+
 	if (pdata)
 		wm2200->pdata = *pdata;
 
-- 
1.7.10.4

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

* [PATCH 3/4] ASoC: arizona: Define standard hookup for ADSP2
  2012-10-26 18:36 [PATCH 1/4] ASoC: adsp: Add ADSP base support Mark Brown
  2012-10-26 18:36 ` [PATCH 2/4] ASoC: wm2200: Convert over to wm_adsp for ADSP1 support Mark Brown
@ 2012-10-26 18:36 ` Mark Brown
  2012-10-26 18:36 ` [PATCH 4/4] ASoC: wm5102: Hook up DSP1 Mark Brown
  2 siblings, 0 replies; 4+ messages in thread
From: Mark Brown @ 2012-10-26 18:36 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, patches, Mark Brown

Many Arizona class devices contain ADSP2 cores with a standard method for
hooking them into the audio map. Define standard helpers for this.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 sound/soc/codecs/arizona.h |   47 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)

diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index 36ec649..5a09127 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -17,6 +17,8 @@
 
 #include <sound/soc.h>
 
+#include "wm_adsp.h"
+
 #define ARIZONA_CLK_SYSCLK         1
 #define ARIZONA_CLK_ASYNCCLK       2
 #define ARIZONA_CLK_OPCLK          3
@@ -46,15 +48,18 @@
 #define ARIZONA_MIXER_VOL_SHIFT                 1
 #define ARIZONA_MIXER_VOL_WIDTH                 7
 
-#define ARIZONA_MAX_DAI 3
+#define ARIZONA_MAX_DAI  4
+#define ARIZONA_MAX_ADSP 4
 
 struct arizona;
+struct wm_adsp;
 
 struct arizona_dai_priv {
 	int clk;
 };
 
 struct arizona_priv {
+	struct wm_adsp adsp[ARIZONA_MAX_ADSP];
 	struct arizona *arizona;
 	int sysclk;
 	int asyncclk;
@@ -99,6 +104,20 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
 	static ARIZONA_MUX_CTL_DECL(name##_in3); \
 	static ARIZONA_MUX_CTL_DECL(name##_in4)
 
+#define ARIZONA_DSP_AUX_ENUMS(name, base_reg) \
+	static ARIZONA_MUX_ENUM_DECL(name##_aux1_enum, base_reg);	\
+	static ARIZONA_MUX_ENUM_DECL(name##_aux2_enum, base_reg + 8);	\
+	static ARIZONA_MUX_ENUM_DECL(name##_aux3_enum, base_reg + 16);	\
+	static ARIZONA_MUX_ENUM_DECL(name##_aux4_enum, base_reg + 24);	\
+	static ARIZONA_MUX_ENUM_DECL(name##_aux5_enum, base_reg + 32);	\
+	static ARIZONA_MUX_ENUM_DECL(name##_aux6_enum, base_reg + 40);	\
+	static ARIZONA_MUX_CTL_DECL(name##_aux1); \
+	static ARIZONA_MUX_CTL_DECL(name##_aux2); \
+	static ARIZONA_MUX_CTL_DECL(name##_aux3); \
+	static ARIZONA_MUX_CTL_DECL(name##_aux4); \
+	static ARIZONA_MUX_CTL_DECL(name##_aux5); \
+	static ARIZONA_MUX_CTL_DECL(name##_aux6)
+
 #define ARIZONA_MUX(name, ctrl) \
 	SND_SOC_DAPM_VALUE_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
 
@@ -109,6 +128,16 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
 	ARIZONA_MUX(name_str " Input 4", &name##_in4_mux), \
 	SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
 
+#define ARIZONA_DSP_WIDGETS(name, name_str) \
+	ARIZONA_MIXER_WIDGETS(name##L, name_str "L"), \
+	ARIZONA_MIXER_WIDGETS(name##R, name_str "R"), \
+	ARIZONA_MUX(name_str " Aux 1", &name##_aux1_mux), \
+	ARIZONA_MUX(name_str " Aux 2", &name##_aux2_mux), \
+	ARIZONA_MUX(name_str " Aux 3", &name##_aux3_mux), \
+	ARIZONA_MUX(name_str " Aux 4", &name##_aux4_mux), \
+	ARIZONA_MUX(name_str " Aux 5", &name##_aux5_mux), \
+	ARIZONA_MUX(name_str " Aux 6", &name##_aux6_mux)
+
 #define ARIZONA_MIXER_ROUTES(widget, name) \
 	{ widget, NULL, name " Mixer" },         \
 	{ name " Mixer", NULL, name " Input 1" }, \
@@ -120,6 +149,22 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
 	ARIZONA_MIXER_INPUT_ROUTES(name " Input 3"), \
 	ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
 
+#define ARIZONA_DSP_ROUTES(name) \
+	{ name, NULL, name " Aux 1" }, \
+	{ name, NULL, name " Aux 2" }, \
+	{ name, NULL, name " Aux 3" }, \
+	{ name, NULL, name " Aux 4" }, \
+	{ name, NULL, name " Aux 5" }, \
+	{ name, NULL, name " Aux 6" }, \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \
+	ARIZONA_MIXER_ROUTES(name, name "L"), \
+	ARIZONA_MIXER_ROUTES(name, name "R")
+
 extern const struct soc_enum arizona_lhpf1_mode;
 extern const struct soc_enum arizona_lhpf2_mode;
 extern const struct soc_enum arizona_lhpf3_mode;
-- 
1.7.10.4

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

* [PATCH 4/4] ASoC: wm5102: Hook up DSP1
  2012-10-26 18:36 [PATCH 1/4] ASoC: adsp: Add ADSP base support Mark Brown
  2012-10-26 18:36 ` [PATCH 2/4] ASoC: wm2200: Convert over to wm_adsp for ADSP1 support Mark Brown
  2012-10-26 18:36 ` [PATCH 3/4] ASoC: arizona: Define standard hookup for ADSP2 Mark Brown
@ 2012-10-26 18:36 ` Mark Brown
  2 siblings, 0 replies; 4+ messages in thread
From: Mark Brown @ 2012-10-26 18:36 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: alsa-devel, patches, Mark Brown

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
 sound/soc/codecs/Kconfig  |    2 ++
 sound/soc/codecs/wm5102.c |   38 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 91cbbe0..c5c01bd 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -150,7 +150,9 @@ config SND_SOC_WM_HUBS
 config SND_SOC_WM_ADSP
 	tristate
 	default y if SND_SOC_WM2200=y
+	default y if SND_SOC_WM5102=y
 	default m if SND_SOC_WM2200=m
+	default y if SND_SOC_WM5102=m
 
 config SND_SOC_AB8500_CODEC
 	tristate
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 94d9d7a..de40204 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -31,6 +31,7 @@
 
 #include "arizona.h"
 #include "wm5102.h"
+#include "wm_adsp.h"
 
 struct wm5102_priv {
 	struct arizona_priv core;
@@ -561,6 +562,13 @@ static const struct reg_default wm5102_sysclk_reva_patch[] = {
 	{ 0x025e, 0x0112 },
 };
 
+static const struct wm_adsp_region wm5102_dsp1_regions[] = {
+	{ .type = WMFW_ADSP2_PM, .base = 0x100000 },
+	{ .type = WMFW_ADSP2_ZM, .base = 0x180000 },
+	{ .type = WMFW_ADSP2_XM, .base = 0x190000 },
+	{ .type = WMFW_ADSP2_YM, .base = 0x1a0000 },
+};
+
 static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
 			    struct snd_kcontrol *kcontrol, int event)
 {
@@ -687,6 +695,9 @@ ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
 
+ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),
+
 SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
 SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
 SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
@@ -824,6 +835,10 @@ ARIZONA_MIXER_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
 ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
 ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
 
+ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE);
+
+ARIZONA_DSP_AUX_ENUMS(DSP1, ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE);
 
 static const char *wm5102_aec_loopback_texts[] = {
 	"HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "EPOUT",
@@ -996,6 +1011,8 @@ SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
 SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
 		    ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
 
+WM_ADSP2("DSP1", ARIZONA_DSP1_CONTROL_1),
+
 SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
 		       ARIZONA_AEC_LOOPBACK_ENA, 0, &wm5102_aec_loopback_mux),
 
@@ -1076,6 +1093,8 @@ ARIZONA_MIXER_WIDGETS(ASRC1R, "ASRC1R"),
 ARIZONA_MIXER_WIDGETS(ASRC2L, "ASRC2L"),
 ARIZONA_MIXER_WIDGETS(ASRC2R, "ASRC2R"),
 
+WM_ADSP2("DSP1", 0),
+
 SND_SOC_DAPM_OUTPUT("HPOUT1L"),
 SND_SOC_DAPM_OUTPUT("HPOUT1R"),
 SND_SOC_DAPM_OUTPUT("HPOUT2L"),
@@ -1127,7 +1146,13 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
 	{ name, "ASRC1L", "ASRC1L" }, \
 	{ name, "ASRC1R", "ASRC1R" }, \
 	{ name, "ASRC2L", "ASRC2L" }, \
-	{ name, "ASRC2R", "ASRC2R" }
+	{ name, "ASRC2R", "ASRC2R" }, \
+	{ name, "DSP1.1", "DSP1" }, \
+	{ name, "DSP1.2", "DSP1" }, \
+	{ name, "DSP1.3", "DSP1" }, \
+	{ name, "DSP1.4", "DSP1" }, \
+	{ name, "DSP1.5", "DSP1" }, \
+	{ name, "DSP1.6", "DSP1" }
 
 static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
 	{ "AIF2 Capture", NULL, "DBVDD2" },
@@ -1260,6 +1285,8 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
 	ARIZONA_MIXER_ROUTES("ASRC2L", "ASRC2L"),
 	ARIZONA_MIXER_ROUTES("ASRC2R", "ASRC2R"),
 
+	ARIZONA_DSP_ROUTES("DSP1"),
+
 	{ "AEC Loopback", "HPOUT1L", "OUT1L" },
 	{ "AEC Loopback", "HPOUT1R", "OUT1R" },
 	{ "HPOUT1L", NULL, "OUT1L" },
@@ -1434,6 +1461,15 @@ static int __devinit wm5102_probe(struct platform_device *pdev)
 
 	wm5102->core.arizona = arizona;
 
+	wm5102->core.adsp[0].part = "wm5102";
+	wm5102->core.adsp[0].num = 1;
+	wm5102->core.adsp[0].type = WMFW_ADSP2;
+	wm5102->core.adsp[0].base = ARIZONA_DSP1_CONTROL_1;
+	wm5102->core.adsp[0].dev = arizona->dev;
+	wm5102->core.adsp[0].regmap = arizona->regmap;
+	wm5102->core.adsp[0].mem = wm5102_dsp1_regions;
+	wm5102->core.adsp[0].num_mems = ARRAY_SIZE(wm5102_dsp1_regions);
+
 	for (i = 0; i < ARRAY_SIZE(wm5102->fll); i++)
 		wm5102->fll[i].vco_mult = 1;
 
-- 
1.7.10.4

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

end of thread, other threads:[~2012-10-26 18:36 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-10-26 18:36 [PATCH 1/4] ASoC: adsp: Add ADSP base support Mark Brown
2012-10-26 18:36 ` [PATCH 2/4] ASoC: wm2200: Convert over to wm_adsp for ADSP1 support Mark Brown
2012-10-26 18:36 ` [PATCH 3/4] ASoC: arizona: Define standard hookup for ADSP2 Mark Brown
2012-10-26 18:36 ` [PATCH 4/4] ASoC: wm5102: Hook up DSP1 Mark Brown

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).