0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/module.h>
0010 #include <sound/soc.h>
0011 #include <linux/firmware/cirrus/cs_dsp.h>
0012 #include <linux/firmware/cirrus/wmfw.h>
0013 #include "hda_cs_dsp_ctl.h"
0014
0015 #define ADSP_MAX_STD_CTRL_SIZE 512
0016
0017 struct hda_cs_dsp_coeff_ctl {
0018 struct cs_dsp_coeff_ctl *cs_ctl;
0019 struct snd_card *card;
0020 struct snd_kcontrol *kctl;
0021 };
0022
0023 static const char * const hda_cs_dsp_fw_text[HDA_CS_DSP_NUM_FW] = {
0024 [HDA_CS_DSP_FW_SPK_PROT] = "Prot",
0025 [HDA_CS_DSP_FW_SPK_CALI] = "Cali",
0026 [HDA_CS_DSP_FW_SPK_DIAG] = "Diag",
0027 [HDA_CS_DSP_FW_MISC] = "Misc",
0028 };
0029
0030 const char * const hda_cs_dsp_fw_ids[HDA_CS_DSP_NUM_FW] = {
0031 [HDA_CS_DSP_FW_SPK_PROT] = "spk-prot",
0032 [HDA_CS_DSP_FW_SPK_CALI] = "spk-cali",
0033 [HDA_CS_DSP_FW_SPK_DIAG] = "spk-diag",
0034 [HDA_CS_DSP_FW_MISC] = "misc",
0035 };
0036 EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_fw_ids, SND_HDA_CS_DSP_CONTROLS);
0037
0038 static int hda_cs_dsp_coeff_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)
0039 {
0040 struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl);
0041 struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
0042
0043 uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
0044 uinfo->count = cs_ctl->len;
0045
0046 return 0;
0047 }
0048
0049 static int hda_cs_dsp_coeff_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
0050 {
0051 struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl);
0052 struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
0053 char *p = ucontrol->value.bytes.data;
0054 int ret = 0;
0055
0056 mutex_lock(&cs_ctl->dsp->pwr_lock);
0057 ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len);
0058 mutex_unlock(&cs_ctl->dsp->pwr_lock);
0059
0060 return ret;
0061 }
0062
0063 static int hda_cs_dsp_coeff_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol)
0064 {
0065 struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl);
0066 struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
0067 char *p = ucontrol->value.bytes.data;
0068 int ret;
0069
0070 mutex_lock(&cs_ctl->dsp->pwr_lock);
0071 ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len);
0072 mutex_unlock(&cs_ctl->dsp->pwr_lock);
0073
0074 return ret;
0075 }
0076
0077 static unsigned int wmfw_convert_flags(unsigned int in)
0078 {
0079 unsigned int out, rd, wr, vol;
0080
0081 rd = SNDRV_CTL_ELEM_ACCESS_READ;
0082 wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
0083 vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
0084
0085 out = 0;
0086
0087 if (in) {
0088 out |= rd;
0089 if (in & WMFW_CTL_FLAG_WRITEABLE)
0090 out |= wr;
0091 if (in & WMFW_CTL_FLAG_VOLATILE)
0092 out |= vol;
0093 } else {
0094 out |= rd | wr | vol;
0095 }
0096
0097 return out;
0098 }
0099
0100 static int hda_cs_dsp_add_kcontrol(struct hda_cs_dsp_coeff_ctl *ctl, const char *name)
0101 {
0102 struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
0103 struct snd_kcontrol_new kcontrol = {0};
0104 struct snd_kcontrol *kctl;
0105 int ret = 0;
0106
0107 if (cs_ctl->len > ADSP_MAX_STD_CTRL_SIZE) {
0108 dev_err(cs_ctl->dsp->dev, "KControl %s: length %zu exceeds maximum %d\n", name,
0109 cs_ctl->len, ADSP_MAX_STD_CTRL_SIZE);
0110 return -EINVAL;
0111 }
0112
0113 kcontrol.name = name;
0114 kcontrol.info = hda_cs_dsp_coeff_info;
0115 kcontrol.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
0116 kcontrol.access = wmfw_convert_flags(cs_ctl->flags);
0117 kcontrol.get = hda_cs_dsp_coeff_get;
0118 kcontrol.put = hda_cs_dsp_coeff_put;
0119
0120
0121
0122 kctl = snd_ctl_new1(&kcontrol, (void *)ctl);
0123 if (!kctl) {
0124 ret = -ENOMEM;
0125 return ret;
0126 }
0127
0128 ret = snd_ctl_add(ctl->card, kctl);
0129 if (ret) {
0130 dev_err(cs_ctl->dsp->dev, "Failed to add KControl %s = %d\n", kcontrol.name, ret);
0131 return ret;
0132 }
0133
0134 dev_dbg(cs_ctl->dsp->dev, "Added KControl: %s\n", kcontrol.name);
0135 ctl->kctl = kctl;
0136
0137 return 0;
0138 }
0139
0140 int hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl, struct hda_cs_dsp_ctl_info *info)
0141 {
0142 struct cs_dsp *cs_dsp = cs_ctl->dsp;
0143 char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
0144 struct hda_cs_dsp_coeff_ctl *ctl;
0145 const char *region_name;
0146 int ret;
0147
0148 if (cs_ctl->flags & WMFW_CTL_FLAG_SYS)
0149 return 0;
0150
0151 region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type);
0152 if (!region_name) {
0153 dev_err(cs_dsp->dev, "Unknown region type: %d\n", cs_ctl->alg_region.type);
0154 return -EINVAL;
0155 }
0156
0157 ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %.12s %x", info->device_name,
0158 cs_dsp->name, hda_cs_dsp_fw_text[info->fw_type], cs_ctl->alg_region.alg);
0159
0160 if (cs_ctl->subname) {
0161 int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
0162 int skip = 0;
0163
0164
0165 if (cs_ctl->subname_len > avail)
0166 skip = cs_ctl->subname_len - avail;
0167
0168 snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret,
0169 " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip);
0170 }
0171
0172 ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
0173 if (!ctl)
0174 return -ENOMEM;
0175
0176 ctl->cs_ctl = cs_ctl;
0177 ctl->card = info->card;
0178 cs_ctl->priv = ctl;
0179
0180 ret = hda_cs_dsp_add_kcontrol(ctl, name);
0181 if (ret) {
0182 dev_err(cs_dsp->dev, "Error (%d) adding control %s\n", ret, name);
0183 kfree(ctl);
0184 return ret;
0185 }
0186
0187 return 0;
0188 }
0189 EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_add, SND_HDA_CS_DSP_CONTROLS);
0190
0191 void hda_cs_dsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl)
0192 {
0193 struct hda_cs_dsp_coeff_ctl *ctl = cs_ctl->priv;
0194
0195 kfree(ctl);
0196 }
0197 EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_remove, SND_HDA_CS_DSP_CONTROLS);
0198
0199 int hda_cs_dsp_write_ctl(struct cs_dsp *dsp, const char *name, int type,
0200 unsigned int alg, const void *buf, size_t len)
0201 {
0202 struct cs_dsp_coeff_ctl *cs_ctl;
0203 struct hda_cs_dsp_coeff_ctl *ctl;
0204 int ret;
0205
0206 cs_ctl = cs_dsp_get_ctl(dsp, name, type, alg);
0207 if (!cs_ctl)
0208 return -EINVAL;
0209
0210 ctl = cs_ctl->priv;
0211
0212 ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len);
0213 if (ret)
0214 return ret;
0215
0216 if (cs_ctl->flags & WMFW_CTL_FLAG_SYS)
0217 return 0;
0218
0219 snd_ctl_notify(ctl->card, SNDRV_CTL_EVENT_MASK_VALUE, &ctl->kctl->id);
0220
0221 return 0;
0222 }
0223 EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_write_ctl, SND_HDA_CS_DSP_CONTROLS);
0224
0225 int hda_cs_dsp_read_ctl(struct cs_dsp *dsp, const char *name, int type,
0226 unsigned int alg, void *buf, size_t len)
0227 {
0228 struct cs_dsp_coeff_ctl *cs_ctl;
0229
0230 cs_ctl = cs_dsp_get_ctl(dsp, name, type, alg);
0231 if (!cs_ctl)
0232 return -EINVAL;
0233
0234 return cs_dsp_coeff_read_ctrl(cs_ctl, 0, buf, len);
0235 }
0236 EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_read_ctl, SND_HDA_CS_DSP_CONTROLS);
0237
0238 MODULE_DESCRIPTION("CS_DSP ALSA Control HDA Library");
0239 MODULE_AUTHOR("Stefan Binding, <sbinding@opensource.cirrus.com>");
0240 MODULE_LICENSE("GPL");