Unverified Commit 4f2d4eab authored by Stuart Henderson's avatar Stuart Henderson Committed by Mark Brown
Browse files

ASoC: wm_adsp: Add support for multiple compressed buffers



Currently, only a single compressed stream is supported per firmware.
Add support for multiple compressed streams on a single firmware, this
allows additional features like completely independent trigger words or
separate debug capture streams to be implemented.

Signed-off-by: default avatarStuart Henderson <stuarth@opensource.cirrus.com>
Signed-off-by: default avatarCharles Keepax <ckeepax@opensource.cirrus.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent a792af69
Loading
Loading
Loading
Loading
+117 −49
Original line number Diff line number Diff line
@@ -310,6 +310,12 @@ struct wm_adsp_alg_xm_struct {
	__be64 smoothed_power;
};

struct wm_adsp_host_buf_coeff_v1 {
	__be32 host_buf_ptr;		/* Host buffer pointer */
	__be32 versions;		/* Version numbers */
	__be32 name[4];			/* The buffer name */
};

struct wm_adsp_buffer {
	__be32 buf1_base;		/* Base addr of first buffer area */
	__be32 buf1_size;		/* Size of buf1 area in DSP words */
@@ -334,6 +340,7 @@ struct wm_adsp_buffer {
struct wm_adsp_compr;

struct wm_adsp_compr_buf {
	struct list_head list;
	struct wm_adsp *dsp;
	struct wm_adsp_compr *compr;

@@ -345,9 +352,12 @@ struct wm_adsp_compr_buf {
	int read_index;
	int avail;
	int host_buf_mem_type;

	char *name;
};

struct wm_adsp_compr {
	struct list_head list;
	struct wm_adsp *dsp;
	struct wm_adsp_compr_buf *buf;

@@ -358,6 +368,8 @@ struct wm_adsp_compr {
	unsigned int copied_total;

	unsigned int sample_rate;

	const char *name;
};

#define WM_ADSP_DATA_WORD_SIZE         3
@@ -375,6 +387,11 @@ struct wm_adsp_compr {
#define ALG_XM_FIELD(field) \
	(offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32))

#define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER	1

#define HOST_BUF_COEFF_COMPAT_VER_MASK		0xFF00
#define HOST_BUF_COEFF_COMPAT_VER_SHIFT		8

static int wm_adsp_buffer_init(struct wm_adsp *dsp);
static int wm_adsp_buffer_free(struct wm_adsp *dsp);

@@ -708,7 +725,7 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,

	mutex_lock(&dsp[e->shift_l].pwr_lock);

	if (dsp[e->shift_l].booted || dsp[e->shift_l].compr)
	if (dsp[e->shift_l].booted || !list_empty(&dsp[e->shift_l].compr_list))
		ret = -EBUSY;
	else
		dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
@@ -2430,6 +2447,8 @@ static int wm_adsp_common_init(struct wm_adsp *dsp)

	INIT_LIST_HEAD(&dsp->alg_regions);
	INIT_LIST_HEAD(&dsp->ctl_list);
	INIT_LIST_HEAD(&dsp->compr_list);
	INIT_LIST_HEAD(&dsp->buffer_list);

	mutex_init(&dsp->pwr_lock);

@@ -2972,14 +2991,19 @@ static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)

static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
{
	/*
	 * Note this will be more complex once each DSP can support multiple
	 * streams
	 */
	if (!compr->dsp->buffer)
	struct wm_adsp_compr_buf *buf = NULL, *tmp;

	list_for_each_entry(tmp, &compr->dsp->buffer_list, list) {
		if (!tmp->name || !strcmp(compr->name, tmp->name)) {
			buf = tmp;
			break;
		}
	}

	if (!buf)
		return -EINVAL;

	compr->buf = compr->dsp->buffer;
	compr->buf = buf;
	compr->buf->compr = compr;

	return 0;
@@ -3002,7 +3026,8 @@ static void wm_adsp_compr_detach(struct wm_adsp_compr *compr)

int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
{
	struct wm_adsp_compr *compr;
	struct wm_adsp_compr *compr, *tmp;
	struct snd_soc_pcm_runtime *rtd = stream->private_data;
	int ret = 0;

	mutex_lock(&dsp->pwr_lock);
@@ -3019,12 +3044,13 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
		goto out;
	}

	if (dsp->compr) {
		/* It is expect this limitation will be removed in future */
		adsp_err(dsp, "Only a single stream supported per DSP\n");
	list_for_each_entry(tmp, &dsp->compr_list, list) {
		if (!strcmp(tmp->name, rtd->codec_dai->name)) {
			adsp_err(dsp, "Only a single stream supported per dai\n");
			ret = -EBUSY;
			goto out;
		}
	}

	compr = kzalloc(sizeof(*compr), GFP_KERNEL);
	if (!compr) {
@@ -3034,8 +3060,9 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)

	compr->dsp = dsp;
	compr->stream = stream;
	compr->name = rtd->codec_dai->name;

	dsp->compr = compr;
	list_add_tail(&compr->list, &dsp->compr_list);

	stream->runtime->private_data = compr;

@@ -3054,7 +3081,7 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream)
	mutex_lock(&dsp->pwr_lock);

	wm_adsp_compr_detach(compr);
	dsp->compr = NULL;
	list_del(&compr->list);

	kfree(compr->raw_buf);
	kfree(compr);
@@ -3304,7 +3331,7 @@ static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp)

	wm_adsp_buffer_clear(buf);

	dsp->buffer = buf;
	list_add_tail(&buf->list, &dsp->buffer_list);

	return buf;
}
@@ -3360,6 +3387,7 @@ static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp)

static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl)
{
	struct wm_adsp_host_buf_coeff_v1 coeff_v1;
	struct wm_adsp_compr_buf *buf;
	unsigned int val, reg;
	int ret, i;
@@ -3395,11 +3423,47 @@ static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl)
	if (ret < 0)
		return ret;

	/*
	 * v0 host_buffer coefficients didn't have versioning, so if the
	 * control is one word, assume version 0.
	 */
	if (ctl->len == 4) {
		adsp_dbg(ctl->dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr);

		return 0;
	}

	ret = regmap_raw_read(ctl->dsp->regmap, reg, &coeff_v1,
			      sizeof(coeff_v1));
	if (ret < 0)
		return ret;

	coeff_v1.versions = be32_to_cpu(coeff_v1.versions);
	val = coeff_v1.versions & HOST_BUF_COEFF_COMPAT_VER_MASK;
	val >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT;

	if (val > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) {
		adsp_err(ctl->dsp,
			 "Host buffer coeff ver %u > supported version %u\n",
			 val, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER);
		return -EINVAL;
	}

	for (i = 0; i < ARRAY_SIZE(coeff_v1.name); i++)
		coeff_v1.name[i] = be32_to_cpu(coeff_v1.name[i]);

	wm_adsp_remove_padding((u32 *)&coeff_v1.name,
			       ARRAY_SIZE(coeff_v1.name),
			       WM_ADSP_DATA_WORD_SIZE);

	buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", ctl->dsp->part,
			      (char *)&coeff_v1.name);

	adsp_dbg(ctl->dsp, "host_buf_ptr=%x coeff version %u\n",
		 buf->host_buf_ptr, val);

	return val;
}

static int wm_adsp_buffer_init(struct wm_adsp *dsp)
{
	struct wm_coeff_ctl *ctl;
@@ -3416,12 +3480,13 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp)
		if (ret < 0) {
			adsp_err(dsp, "Failed to parse coeff: %d\n", ret);
			goto error;
		}

		} else if (ret == 0) {
			/* Only one buffer supported for version 0 */
			return 0;
		}
	}

	if (!dsp->buffer) {
	if (list_empty(&dsp->buffer_list)) {
		/* Fall back to legacy support */
		ret = wm_adsp_buffer_parse_legacy(dsp);
		if (ret) {
@@ -3439,13 +3504,16 @@ error:

static int wm_adsp_buffer_free(struct wm_adsp *dsp)
{
	if (dsp->buffer) {
		wm_adsp_compr_detach(dsp->buffer->compr);
	struct wm_adsp_compr_buf *buf, *tmp;

		kfree(dsp->buffer->regions);
		kfree(dsp->buffer);
	list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) {
		if (buf->compr)
			wm_adsp_compr_detach(buf->compr);

		dsp->buffer = NULL;
		kfree(buf->name);
		kfree(buf->regions);
		list_del(&buf->list);
		kfree(buf);
	}

	return 0;
@@ -3572,16 +3640,15 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)

	mutex_lock(&dsp->pwr_lock);

	buf = dsp->buffer;
	compr = dsp->compr;

	if (!buf) {
	if (list_empty(&dsp->buffer_list)) {
		ret = -ENODEV;
		goto out;
	}

	adsp_dbg(dsp, "Handling buffer IRQ\n");

	list_for_each_entry(buf, &dsp->buffer_list, list) {
		compr = buf->compr;

		ret = wm_adsp_buffer_get_error(buf);
		if (ret < 0)
			goto out_notify; /* Wake poll to report error */
@@ -3605,6 +3672,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
out_notify:
		if (compr && compr->stream)
			snd_compr_fragment_elapsed(compr->stream);
	}

out:
	mutex_unlock(&dsp->pwr_lock);
+2 −2
Original line number Diff line number Diff line
@@ -90,8 +90,8 @@ struct wm_adsp {

	struct work_struct boot_work;

	struct wm_adsp_compr *compr;
	struct wm_adsp_compr_buf *buffer;
	struct list_head compr_list;
	struct list_head buffer_list;

	struct mutex pwr_lock;