]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
ASoC: wm_adsp: Add support for TLV based binary controls
authorCharles Keepax <ckeepax@opensource.wolfsonmicro.com>
Mon, 2 May 2016 12:57:36 +0000 (13:57 +0100)
committerMark Brown <broonie@kernel.org>
Mon, 2 May 2016 14:25:52 +0000 (15:25 +0100)
This patch adds support for the arbitrary length TLV based binary
controls. This allows users to properly access controls that are
more than 512 bytes in length.

Signed-off-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/wm_adsp.c

index 3ac2e1f06ad30a26278469085c6d914c3c3a8518..f835277901d4c79d446d4c814b37b76c02ce4363 100644 (file)
 #define ADSP2_RAM_RDY_SHIFT                    0
 #define ADSP2_RAM_RDY_WIDTH                    1
 
+#define ADSP_MAX_STD_CTRL_SIZE               512
+
 struct wm_adsp_buf {
        struct list_head list;
        void *buf;
@@ -435,6 +437,7 @@ struct wm_coeff_ctl {
        size_t len;
        unsigned int set:1;
        struct snd_kcontrol *kcontrol;
+       struct soc_bytes_ext bytes_ext;
        unsigned int flags;
 };
 
@@ -711,10 +714,17 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
                 be16_to_cpu(scratch[3]));
 }
 
+static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
+{
+       return container_of(ext, struct wm_coeff_ctl, bytes_ext);
+}
+
 static int wm_coeff_info(struct snd_kcontrol *kctl,
                         struct snd_ctl_elem_info *uinfo)
 {
-       struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
+       struct soc_bytes_ext *bytes_ext =
+               (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;
@@ -763,7 +773,9 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
 static int wm_coeff_put(struct snd_kcontrol *kctl,
                        struct snd_ctl_elem_value *ucontrol)
 {
-       struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
+       struct soc_bytes_ext *bytes_ext =
+               (struct soc_bytes_ext *)kctl->private_value;
+       struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
        char *p = ucontrol->value.bytes.data;
        int ret = 0;
 
@@ -780,6 +792,29 @@ static int wm_coeff_put(struct snd_kcontrol *kctl,
        return ret;
 }
 
+static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
+                           const unsigned int __user *bytes, unsigned int size)
+{
+       struct soc_bytes_ext *bytes_ext =
+               (struct soc_bytes_ext *)kctl->private_value;
+       struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+       int ret = 0;
+
+       mutex_lock(&ctl->dsp->pwr_lock);
+
+       if (copy_from_user(ctl->cache, bytes, size)) {
+               ret = -EFAULT;
+       } else {
+               ctl->set = 1;
+               if (ctl->enabled)
+                       ret = wm_coeff_write_control(ctl, ctl->cache, size);
+       }
+
+       mutex_unlock(&ctl->dsp->pwr_lock);
+
+       return ret;
+}
+
 static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
                                 void *buf, size_t len)
 {
@@ -822,7 +857,9 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
 static int wm_coeff_get(struct snd_kcontrol *kctl,
                        struct snd_ctl_elem_value *ucontrol)
 {
-       struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
+       struct soc_bytes_ext *bytes_ext =
+               (struct soc_bytes_ext *)kctl->private_value;
+       struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
        char *p = ucontrol->value.bytes.data;
        int ret = 0;
 
@@ -845,12 +882,72 @@ static int wm_coeff_get(struct snd_kcontrol *kctl,
        return ret;
 }
 
+static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
+                           unsigned int __user *bytes, unsigned int size)
+{
+       struct soc_bytes_ext *bytes_ext =
+               (struct soc_bytes_ext *)kctl->private_value;
+       struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+       int ret = 0;
+
+       mutex_lock(&ctl->dsp->pwr_lock);
+
+       if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
+               if (ctl->enabled)
+                       ret = wm_coeff_read_control(ctl, ctl->cache, size);
+               else
+                       ret = -EPERM;
+       } else {
+               if (!ctl->flags && ctl->enabled)
+                       ret = wm_coeff_read_control(ctl, ctl->cache, size);
+       }
+
+       if (!ret && copy_to_user(bytes, ctl->cache, size))
+               ret = -EFAULT;
+
+       mutex_unlock(&ctl->dsp->pwr_lock);
+
+       return ret;
+}
+
 struct wmfw_ctl_work {
        struct wm_adsp *dsp;
        struct wm_coeff_ctl *ctl;
        struct work_struct work;
 };
 
+static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
+{
+       unsigned int out, rd, wr, vol;
+
+       if (len > ADSP_MAX_STD_CTRL_SIZE) {
+               rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+               wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;
+               vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+
+               out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+       } else {
+               rd = SNDRV_CTL_ELEM_ACCESS_READ;
+               wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
+               vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+
+               out = 0;
+       }
+
+       if (in) {
+               if (in & WMFW_CTL_FLAG_READABLE)
+                       out |= rd;
+               if (in & WMFW_CTL_FLAG_WRITEABLE)
+                       out |= wr;
+               if (in & WMFW_CTL_FLAG_VOLATILE)
+                       out |= vol;
+       } else {
+               out |= rd | wr | vol;
+       }
+
+       return out;
+}
+
 static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
 {
        struct snd_kcontrol_new *kcontrol;
@@ -868,19 +965,15 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
        kcontrol->info = wm_coeff_info;
        kcontrol->get = wm_coeff_get;
        kcontrol->put = wm_coeff_put;
-       kcontrol->private_value = (unsigned long)ctl;
+       kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
+       kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
 
-       if (ctl->flags) {
-               if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE)
-                       kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
-               if (ctl->flags & WMFW_CTL_FLAG_READABLE)
-                       kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
-               if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
-                       kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
-       } else {
-               kcontrol->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
-               kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
-       }
+       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);
 
        ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
        if (ret < 0)
@@ -1032,11 +1125,6 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
 
        ctl->flags = flags;
        ctl->offset = offset;
-       if (len > 512) {
-               adsp_warn(dsp, "Truncating control %s from %d\n",
-                         ctl->name, len);
-               len = 512;
-       }
        ctl->len = len;
        ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
        if (!ctl->cache) {