ASoC: wm_adsp: Support acknowledged controls
This patch handles publishing acknowledged controls through ALSA. These controls allow user-side to send events to the firmware and wait for the firmware to acknowledge it. Note that although acked controls only operate in the direction host->firmware, and therefore they are write-only as seen from user- side code, we have to make them readable to account for all the code out there that assumes that ALSA controls are always readable (amixer for example.) Signed-off-by: Richard Fitzgerald <rf@opensource.wolfsonmicro.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
8eb084d066
commit
a23ebba845
|
@ -164,6 +164,8 @@
|
|||
|
||||
#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100
|
||||
#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10
|
||||
#define WM_ADSP_ACKED_CTL_MIN_VALUE 0
|
||||
#define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF
|
||||
|
||||
/*
|
||||
* Event control messages
|
||||
|
@ -761,8 +763,20 @@ static int wm_coeff_info(struct snd_kcontrol *kctl,
|
|||
(struct soc_bytes_ext *)kctl->private_value;
|
||||
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
||||
uinfo->count = ctl->len;
|
||||
switch (ctl->type) {
|
||||
case WMFW_CTL_TYPE_ACKED:
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE;
|
||||
uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE;
|
||||
uinfo->value.integer.step = 1;
|
||||
uinfo->count = 1;
|
||||
break;
|
||||
default:
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
||||
uinfo->count = ctl->len;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -910,6 +924,30 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct soc_bytes_ext *bytes_ext =
|
||||
(struct soc_bytes_ext *)kctl->private_value;
|
||||
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
|
||||
unsigned int val = ucontrol->value.integer.value[0];
|
||||
int ret;
|
||||
|
||||
if (val == 0)
|
||||
return 0; /* 0 means no event */
|
||||
|
||||
mutex_lock(&ctl->dsp->pwr_lock);
|
||||
|
||||
if (ctl->enabled)
|
||||
ret = wm_coeff_write_acked_control(ctl, val);
|
||||
else
|
||||
ret = -EPERM;
|
||||
|
||||
mutex_unlock(&ctl->dsp->pwr_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
|
||||
void *buf, size_t len)
|
||||
{
|
||||
|
@ -1005,6 +1043,21 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
/*
|
||||
* Although it's not useful to read an acked control, we must satisfy
|
||||
* user-side assumptions that all controls are readable and that a
|
||||
* write of the same value should be filtered out (it's valid to send
|
||||
* the same event number again to the firmware). We therefore return 0,
|
||||
* meaning "no event" so valid event numbers will always be a change
|
||||
*/
|
||||
ucontrol->value.integer.value[0] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct wmfw_ctl_work {
|
||||
struct wm_adsp *dsp;
|
||||
struct wm_coeff_ctl *ctl;
|
||||
|
@ -1057,18 +1110,26 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
|
|||
|
||||
kcontrol->name = ctl->name;
|
||||
kcontrol->info = wm_coeff_info;
|
||||
kcontrol->get = wm_coeff_get;
|
||||
kcontrol->put = wm_coeff_put;
|
||||
kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
|
||||
kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
|
||||
|
||||
ctl->bytes_ext.max = ctl->len;
|
||||
ctl->bytes_ext.get = wm_coeff_tlv_get;
|
||||
ctl->bytes_ext.put = wm_coeff_tlv_put;
|
||||
|
||||
kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
|
||||
|
||||
switch (ctl->type) {
|
||||
case WMFW_CTL_TYPE_ACKED:
|
||||
kcontrol->get = wm_coeff_get_acked;
|
||||
kcontrol->put = wm_coeff_put_acked;
|
||||
break;
|
||||
default:
|
||||
kcontrol->get = wm_coeff_get;
|
||||
kcontrol->put = wm_coeff_put;
|
||||
|
||||
ctl->bytes_ext.max = ctl->len;
|
||||
ctl->bytes_ext.get = wm_coeff_tlv_get;
|
||||
ctl->bytes_ext.put = wm_coeff_tlv_put;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
|
||||
if (ret < 0)
|
||||
goto err_kcontrol;
|
||||
|
@ -1429,6 +1490,18 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
|
|||
switch (coeff_blk.ctl_type) {
|
||||
case SNDRV_CTL_ELEM_TYPE_BYTES:
|
||||
break;
|
||||
case WMFW_CTL_TYPE_ACKED:
|
||||
if (coeff_blk.flags & WMFW_CTL_FLAG_SYS)
|
||||
continue; /* ignore */
|
||||
|
||||
ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
|
||||
WMFW_CTL_FLAG_VOLATILE |
|
||||
WMFW_CTL_FLAG_WRITEABLE |
|
||||
WMFW_CTL_FLAG_READABLE,
|
||||
0);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case WMFW_CTL_TYPE_HOSTEVENT:
|
||||
ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
|
||||
WMFW_CTL_FLAG_SYS |
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#define WMFW_CTL_FLAG_READABLE 0x0001
|
||||
|
||||
/* Non-ALSA coefficient types start at 0x1000 */
|
||||
#define WMFW_CTL_TYPE_ACKED 0x1000 /* acked control */
|
||||
#define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */
|
||||
|
||||
struct wmfw_header {
|
||||
|
|
Loading…
Reference in New Issue