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

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
...@@ -310,6 +310,12 @@ struct wm_adsp_alg_xm_struct { ...@@ -310,6 +310,12 @@ struct wm_adsp_alg_xm_struct {
__be64 smoothed_power; __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 { struct wm_adsp_buffer {
__be32 buf1_base; /* Base addr of first buffer area */ __be32 buf1_base; /* Base addr of first buffer area */
__be32 buf1_size; /* Size of buf1 area in DSP words */ __be32 buf1_size; /* Size of buf1 area in DSP words */
...@@ -334,6 +340,7 @@ struct wm_adsp_buffer { ...@@ -334,6 +340,7 @@ struct wm_adsp_buffer {
struct wm_adsp_compr; struct wm_adsp_compr;
struct wm_adsp_compr_buf { struct wm_adsp_compr_buf {
struct list_head list;
struct wm_adsp *dsp; struct wm_adsp *dsp;
struct wm_adsp_compr *compr; struct wm_adsp_compr *compr;
...@@ -345,9 +352,12 @@ struct wm_adsp_compr_buf { ...@@ -345,9 +352,12 @@ struct wm_adsp_compr_buf {
int read_index; int read_index;
int avail; int avail;
int host_buf_mem_type; int host_buf_mem_type;
char *name;
}; };
struct wm_adsp_compr { struct wm_adsp_compr {
struct list_head list;
struct wm_adsp *dsp; struct wm_adsp *dsp;
struct wm_adsp_compr_buf *buf; struct wm_adsp_compr_buf *buf;
...@@ -358,6 +368,8 @@ struct wm_adsp_compr { ...@@ -358,6 +368,8 @@ struct wm_adsp_compr {
unsigned int copied_total; unsigned int copied_total;
unsigned int sample_rate; unsigned int sample_rate;
const char *name;
}; };
#define WM_ADSP_DATA_WORD_SIZE 3 #define WM_ADSP_DATA_WORD_SIZE 3
...@@ -375,6 +387,11 @@ struct wm_adsp_compr { ...@@ -375,6 +387,11 @@ struct wm_adsp_compr {
#define ALG_XM_FIELD(field) \ #define ALG_XM_FIELD(field) \
(offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) (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_init(struct wm_adsp *dsp);
static int wm_adsp_buffer_free(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, ...@@ -708,7 +725,7 @@ int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
mutex_lock(&dsp[e->shift_l].pwr_lock); 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; ret = -EBUSY;
else else
dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
...@@ -2430,6 +2447,8 @@ static int wm_adsp_common_init(struct wm_adsp *dsp) ...@@ -2430,6 +2447,8 @@ static int wm_adsp_common_init(struct wm_adsp *dsp)
INIT_LIST_HEAD(&dsp->alg_regions); INIT_LIST_HEAD(&dsp->alg_regions);
INIT_LIST_HEAD(&dsp->ctl_list); INIT_LIST_HEAD(&dsp->ctl_list);
INIT_LIST_HEAD(&dsp->compr_list);
INIT_LIST_HEAD(&dsp->buffer_list);
mutex_init(&dsp->pwr_lock); mutex_init(&dsp->pwr_lock);
...@@ -2972,14 +2991,19 @@ static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) ...@@ -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) static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
{ {
/* struct wm_adsp_compr_buf *buf = NULL, *tmp;
* Note this will be more complex once each DSP can support multiple
* streams list_for_each_entry(tmp, &compr->dsp->buffer_list, list) {
*/ if (!tmp->name || !strcmp(compr->name, tmp->name)) {
if (!compr->dsp->buffer) buf = tmp;
break;
}
}
if (!buf)
return -EINVAL; return -EINVAL;
compr->buf = compr->dsp->buffer; compr->buf = buf;
compr->buf->compr = compr; compr->buf->compr = compr;
return 0; return 0;
...@@ -3002,7 +3026,8 @@ static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) ...@@ -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) 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; int ret = 0;
mutex_lock(&dsp->pwr_lock); mutex_lock(&dsp->pwr_lock);
...@@ -3019,12 +3044,13 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) ...@@ -3019,12 +3044,13 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
goto out; goto out;
} }
if (dsp->compr) { list_for_each_entry(tmp, &dsp->compr_list, list) {
/* It is expect this limitation will be removed in future */ if (!strcmp(tmp->name, rtd->codec_dai->name)) {
adsp_err(dsp, "Only a single stream supported per DSP\n"); adsp_err(dsp, "Only a single stream supported per dai\n");
ret = -EBUSY; ret = -EBUSY;
goto out; goto out;
} }
}
compr = kzalloc(sizeof(*compr), GFP_KERNEL); compr = kzalloc(sizeof(*compr), GFP_KERNEL);
if (!compr) { if (!compr) {
...@@ -3034,8 +3060,9 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) ...@@ -3034,8 +3060,9 @@ int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
compr->dsp = dsp; compr->dsp = dsp;
compr->stream = stream; 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; stream->runtime->private_data = compr;
...@@ -3054,7 +3081,7 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream) ...@@ -3054,7 +3081,7 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream)
mutex_lock(&dsp->pwr_lock); mutex_lock(&dsp->pwr_lock);
wm_adsp_compr_detach(compr); wm_adsp_compr_detach(compr);
dsp->compr = NULL; list_del(&compr->list);
kfree(compr->raw_buf); kfree(compr->raw_buf);
kfree(compr); kfree(compr);
...@@ -3304,7 +3331,7 @@ static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) ...@@ -3304,7 +3331,7 @@ static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp)
wm_adsp_buffer_clear(buf); wm_adsp_buffer_clear(buf);
dsp->buffer = buf; list_add_tail(&buf->list, &dsp->buffer_list);
return buf; return buf;
} }
...@@ -3360,6 +3387,7 @@ static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) ...@@ -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) 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; struct wm_adsp_compr_buf *buf;
unsigned int val, reg; unsigned int val, reg;
int ret, i; int ret, i;
...@@ -3395,9 +3423,45 @@ static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl) ...@@ -3395,9 +3423,45 @@ static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl)
if (ret < 0) if (ret < 0)
return ret; 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); adsp_dbg(ctl->dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr);
return 0; 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) static int wm_adsp_buffer_init(struct wm_adsp *dsp)
...@@ -3416,12 +3480,13 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp) ...@@ -3416,12 +3480,13 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp)
if (ret < 0) { if (ret < 0) {
adsp_err(dsp, "Failed to parse coeff: %d\n", ret); adsp_err(dsp, "Failed to parse coeff: %d\n", ret);
goto error; goto error;
} } else if (ret == 0) {
/* Only one buffer supported for version 0 */
return 0; return 0;
} }
}
if (!dsp->buffer) { if (list_empty(&dsp->buffer_list)) {
/* Fall back to legacy support */ /* Fall back to legacy support */
ret = wm_adsp_buffer_parse_legacy(dsp); ret = wm_adsp_buffer_parse_legacy(dsp);
if (ret) { if (ret) {
...@@ -3439,13 +3504,16 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp) ...@@ -3439,13 +3504,16 @@ static int wm_adsp_buffer_init(struct wm_adsp *dsp)
static int wm_adsp_buffer_free(struct wm_adsp *dsp) static int wm_adsp_buffer_free(struct wm_adsp *dsp)
{ {
if (dsp->buffer) { struct wm_adsp_compr_buf *buf, *tmp;
wm_adsp_compr_detach(dsp->buffer->compr);
kfree(dsp->buffer->regions); list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) {
kfree(dsp->buffer); 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; return 0;
...@@ -3572,16 +3640,15 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) ...@@ -3572,16 +3640,15 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
mutex_lock(&dsp->pwr_lock); mutex_lock(&dsp->pwr_lock);
buf = dsp->buffer; if (list_empty(&dsp->buffer_list)) {
compr = dsp->compr;
if (!buf) {
ret = -ENODEV; ret = -ENODEV;
goto out; goto out;
} }
adsp_dbg(dsp, "Handling buffer IRQ\n"); 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); ret = wm_adsp_buffer_get_error(buf);
if (ret < 0) if (ret < 0)
goto out_notify; /* Wake poll to report error */ goto out_notify; /* Wake poll to report error */
...@@ -3605,6 +3672,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) ...@@ -3605,6 +3672,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
out_notify: out_notify:
if (compr && compr->stream) if (compr && compr->stream)
snd_compr_fragment_elapsed(compr->stream); snd_compr_fragment_elapsed(compr->stream);
}
out: out:
mutex_unlock(&dsp->pwr_lock); mutex_unlock(&dsp->pwr_lock);
......
...@@ -90,8 +90,8 @@ struct wm_adsp { ...@@ -90,8 +90,8 @@ struct wm_adsp {
struct work_struct boot_work; struct work_struct boot_work;
struct wm_adsp_compr *compr; struct list_head compr_list;
struct wm_adsp_compr_buf *buffer; struct list_head buffer_list;
struct mutex pwr_lock; struct mutex pwr_lock;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment