aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCharles Keepax <ckeepax@opensource.wolfsonmicro.com>2016-05-02 08:57:36 -0400
committerMark Brown <broonie@kernel.org>2016-05-02 10:25:52 -0400
commit9ee78757d5dae51decc881b293a39a605c9a6df2 (patch)
treebdd4e87bbc89742943e9ec1c9531c232983c9dee
parent5847609edb3c80be07e897e449a9bb579a0fe9d8 (diff)
ASoC: wm_adsp: Add support for TLV based binary controls
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>
-rw-r--r--sound/soc/codecs/wm_adsp.c128
1 files changed, 108 insertions, 20 deletions
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 3ac2e1f06ad3..f835277901d4 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -160,6 +160,8 @@
160#define ADSP2_RAM_RDY_SHIFT 0 160#define ADSP2_RAM_RDY_SHIFT 0
161#define ADSP2_RAM_RDY_WIDTH 1 161#define ADSP2_RAM_RDY_WIDTH 1
162 162
163#define ADSP_MAX_STD_CTRL_SIZE 512
164
163struct wm_adsp_buf { 165struct wm_adsp_buf {
164 struct list_head list; 166 struct list_head list;
165 void *buf; 167 void *buf;
@@ -435,6 +437,7 @@ struct wm_coeff_ctl {
435 size_t len; 437 size_t len;
436 unsigned int set:1; 438 unsigned int set:1;
437 struct snd_kcontrol *kcontrol; 439 struct snd_kcontrol *kcontrol;
440 struct soc_bytes_ext bytes_ext;
438 unsigned int flags; 441 unsigned int flags;
439}; 442};
440 443
@@ -711,10 +714,17 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
711 be16_to_cpu(scratch[3])); 714 be16_to_cpu(scratch[3]));
712} 715}
713 716
717static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
718{
719 return container_of(ext, struct wm_coeff_ctl, bytes_ext);
720}
721
714static int wm_coeff_info(struct snd_kcontrol *kctl, 722static int wm_coeff_info(struct snd_kcontrol *kctl,
715 struct snd_ctl_elem_info *uinfo) 723 struct snd_ctl_elem_info *uinfo)
716{ 724{
717 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; 725 struct soc_bytes_ext *bytes_ext =
726 (struct soc_bytes_ext *)kctl->private_value;
727 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
718 728
719 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 729 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
720 uinfo->count = ctl->len; 730 uinfo->count = ctl->len;
@@ -763,7 +773,9 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
763static int wm_coeff_put(struct snd_kcontrol *kctl, 773static int wm_coeff_put(struct snd_kcontrol *kctl,
764 struct snd_ctl_elem_value *ucontrol) 774 struct snd_ctl_elem_value *ucontrol)
765{ 775{
766 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; 776 struct soc_bytes_ext *bytes_ext =
777 (struct soc_bytes_ext *)kctl->private_value;
778 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
767 char *p = ucontrol->value.bytes.data; 779 char *p = ucontrol->value.bytes.data;
768 int ret = 0; 780 int ret = 0;
769 781
@@ -780,6 +792,29 @@ static int wm_coeff_put(struct snd_kcontrol *kctl,
780 return ret; 792 return ret;
781} 793}
782 794
795static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
796 const unsigned int __user *bytes, unsigned int size)
797{
798 struct soc_bytes_ext *bytes_ext =
799 (struct soc_bytes_ext *)kctl->private_value;
800 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
801 int ret = 0;
802
803 mutex_lock(&ctl->dsp->pwr_lock);
804
805 if (copy_from_user(ctl->cache, bytes, size)) {
806 ret = -EFAULT;
807 } else {
808 ctl->set = 1;
809 if (ctl->enabled)
810 ret = wm_coeff_write_control(ctl, ctl->cache, size);
811 }
812
813 mutex_unlock(&ctl->dsp->pwr_lock);
814
815 return ret;
816}
817
783static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, 818static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
784 void *buf, size_t len) 819 void *buf, size_t len)
785{ 820{
@@ -822,7 +857,9 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
822static int wm_coeff_get(struct snd_kcontrol *kctl, 857static int wm_coeff_get(struct snd_kcontrol *kctl,
823 struct snd_ctl_elem_value *ucontrol) 858 struct snd_ctl_elem_value *ucontrol)
824{ 859{
825 struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; 860 struct soc_bytes_ext *bytes_ext =
861 (struct soc_bytes_ext *)kctl->private_value;
862 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
826 char *p = ucontrol->value.bytes.data; 863 char *p = ucontrol->value.bytes.data;
827 int ret = 0; 864 int ret = 0;
828 865
@@ -845,12 +882,72 @@ static int wm_coeff_get(struct snd_kcontrol *kctl,
845 return ret; 882 return ret;
846} 883}
847 884
885static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
886 unsigned int __user *bytes, unsigned int size)
887{
888 struct soc_bytes_ext *bytes_ext =
889 (struct soc_bytes_ext *)kctl->private_value;
890 struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
891 int ret = 0;
892
893 mutex_lock(&ctl->dsp->pwr_lock);
894
895 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
896 if (ctl->enabled)
897 ret = wm_coeff_read_control(ctl, ctl->cache, size);
898 else
899 ret = -EPERM;
900 } else {
901 if (!ctl->flags && ctl->enabled)
902 ret = wm_coeff_read_control(ctl, ctl->cache, size);
903 }
904
905 if (!ret && copy_to_user(bytes, ctl->cache, size))
906 ret = -EFAULT;
907
908 mutex_unlock(&ctl->dsp->pwr_lock);
909
910 return ret;
911}
912
848struct wmfw_ctl_work { 913struct wmfw_ctl_work {
849 struct wm_adsp *dsp; 914 struct wm_adsp *dsp;
850 struct wm_coeff_ctl *ctl; 915 struct wm_coeff_ctl *ctl;
851 struct work_struct work; 916 struct work_struct work;
852}; 917};
853 918
919static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
920{
921 unsigned int out, rd, wr, vol;
922
923 if (len > ADSP_MAX_STD_CTRL_SIZE) {
924 rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;
925 wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;
926 vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
927
928 out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
929 } else {
930 rd = SNDRV_CTL_ELEM_ACCESS_READ;
931 wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
932 vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
933
934 out = 0;
935 }
936
937 if (in) {
938 if (in & WMFW_CTL_FLAG_READABLE)
939 out |= rd;
940 if (in & WMFW_CTL_FLAG_WRITEABLE)
941 out |= wr;
942 if (in & WMFW_CTL_FLAG_VOLATILE)
943 out |= vol;
944 } else {
945 out |= rd | wr | vol;
946 }
947
948 return out;
949}
950
854static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 951static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
855{ 952{
856 struct snd_kcontrol_new *kcontrol; 953 struct snd_kcontrol_new *kcontrol;
@@ -868,19 +965,15 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
868 kcontrol->info = wm_coeff_info; 965 kcontrol->info = wm_coeff_info;
869 kcontrol->get = wm_coeff_get; 966 kcontrol->get = wm_coeff_get;
870 kcontrol->put = wm_coeff_put; 967 kcontrol->put = wm_coeff_put;
871 kcontrol->private_value = (unsigned long)ctl; 968 kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
969 kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
970 kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
872 971
873 if (ctl->flags) { 972 ctl->bytes_ext.max = ctl->len;
874 if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE) 973 ctl->bytes_ext.get = wm_coeff_tlv_get;
875 kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE; 974 ctl->bytes_ext.put = wm_coeff_tlv_put;
876 if (ctl->flags & WMFW_CTL_FLAG_READABLE) 975
877 kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ; 976 kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
878 if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
879 kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
880 } else {
881 kcontrol->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
882 kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
883 }
884 977
885 ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); 978 ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
886 if (ret < 0) 979 if (ret < 0)
@@ -1032,11 +1125,6 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
1032 1125
1033 ctl->flags = flags; 1126 ctl->flags = flags;
1034 ctl->offset = offset; 1127 ctl->offset = offset;
1035 if (len > 512) {
1036 adsp_warn(dsp, "Truncating control %s from %d\n",
1037 ctl->name, len);
1038 len = 512;
1039 }
1040 ctl->len = len; 1128 ctl->len = len;
1041 ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 1129 ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
1042 if (!ctl->cache) { 1130 if (!ctl->cache) {