Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ALSA: hda: hda_cs_dsp_ctl: Add Library to support CS_DSP ALSA controls

The cs35l41 part contains a DSP which is able to run firmware.
The cs_dsp library can be used to control the DSP.
These controls can be exposed to userspace using ALSA controls.
This library adds apis to be able to interface between
cs_dsp and hda drivers and expose the relevant controls as
ALSA controls.

[ Note: the dependency of CONFIG_SND_HDA_CS_DSP_CONTROLS Kconfig is
corrected. Also, this Kconfig isn't enabled now but will be
actually enabled in a later patch -- tiwai ]

Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Signed-off-by: Vitaly Rodionov <vitalyr@opensource.cirrus.com>
Link: https://lore.kernel.org/r/20220630002335.366545-2-vitalyr@opensource.cirrus.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Stefan Binding and committed by
Takashi Iwai
3233b978 89422df9

+233
+1
MAINTAINERS
··· 4755 4755 F: Documentation/devicetree/bindings/sound/cirrus,cs* 4756 4756 F: include/dt-bindings/sound/cs* 4757 4757 F: sound/pci/hda/cs* 4758 + F: sound/pci/hda/hda_cs_dsp_ctl.* 4758 4759 F: sound/soc/codecs/cs* 4759 4760 4760 4761 CIRRUS LOGIC DSP FIRMWARE DRIVER
+4
sound/pci/hda/Kconfig
··· 96 96 select SND_HDA_GENERIC 97 97 select REGMAP_IRQ 98 98 99 + config SND_HDA_CS_DSP_CONTROLS 100 + tristate 101 + select CS_DSP 102 + 99 103 config SND_HDA_SCODEC_CS35L41_I2C 100 104 tristate "Build CS35L41 HD-audio side codec support for I2C Bus" 101 105 depends on I2C
+2
sound/pci/hda/Makefile
··· 31 31 snd-hda-scodec-cs35l41-objs := cs35l41_hda.o 32 32 snd-hda-scodec-cs35l41-i2c-objs := cs35l41_hda_i2c.o 33 33 snd-hda-scodec-cs35l41-spi-objs := cs35l41_hda_spi.o 34 + snd-hda-cs-dsp-ctls-objs := hda_cs_dsp_ctl.o 34 35 35 36 # common driver 36 37 obj-$(CONFIG_SND_HDA) := snd-hda-codec.o ··· 55 54 obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o 56 55 obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o 57 56 obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o 57 + obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o 58 58 59 59 # this must be the last entry after codec drivers; 60 60 # otherwise the codec patches won't be hooked before the PCI probe
+193
sound/pci/hda/hda_cs_dsp_ctl.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + // 3 + // HDA DSP ALSA Control Driver 4 + // 5 + // Copyright 2022 Cirrus Logic, Inc. 6 + // 7 + // Author: Stefan Binding <sbinding@opensource.cirrus.com> 8 + 9 + #include <linux/module.h> 10 + #include <sound/soc.h> 11 + #include <linux/firmware/cirrus/cs_dsp.h> 12 + #include <linux/firmware/cirrus/wmfw.h> 13 + #include "hda_cs_dsp_ctl.h" 14 + 15 + #define ADSP_MAX_STD_CTRL_SIZE 512 16 + 17 + struct hda_cs_dsp_coeff_ctl { 18 + struct cs_dsp_coeff_ctl *cs_ctl; 19 + struct snd_card *card; 20 + struct snd_kcontrol *kctl; 21 + }; 22 + 23 + static const char * const hda_cs_dsp_fw_text[HDA_CS_DSP_NUM_FW] = { 24 + [HDA_CS_DSP_FW_SPK_PROT] = "Prot", 25 + [HDA_CS_DSP_FW_SPK_CALI] = "Cali", 26 + [HDA_CS_DSP_FW_SPK_DIAG] = "Diag", 27 + [HDA_CS_DSP_FW_MISC] = "Misc", 28 + }; 29 + 30 + static int hda_cs_dsp_coeff_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) 31 + { 32 + struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl); 33 + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 34 + 35 + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 36 + uinfo->count = cs_ctl->len; 37 + 38 + return 0; 39 + } 40 + 41 + static int hda_cs_dsp_coeff_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) 42 + { 43 + struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl); 44 + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 45 + char *p = ucontrol->value.bytes.data; 46 + int ret = 0; 47 + 48 + mutex_lock(&cs_ctl->dsp->pwr_lock); 49 + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len); 50 + mutex_unlock(&cs_ctl->dsp->pwr_lock); 51 + 52 + return ret; 53 + } 54 + 55 + static int hda_cs_dsp_coeff_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) 56 + { 57 + struct hda_cs_dsp_coeff_ctl *ctl = (struct hda_cs_dsp_coeff_ctl *)snd_kcontrol_chip(kctl); 58 + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 59 + char *p = ucontrol->value.bytes.data; 60 + int ret; 61 + 62 + mutex_lock(&cs_ctl->dsp->pwr_lock); 63 + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len); 64 + mutex_unlock(&cs_ctl->dsp->pwr_lock); 65 + 66 + return ret; 67 + } 68 + 69 + static unsigned int wmfw_convert_flags(unsigned int in) 70 + { 71 + unsigned int out, rd, wr, vol; 72 + 73 + rd = SNDRV_CTL_ELEM_ACCESS_READ; 74 + wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 75 + vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 76 + 77 + out = 0; 78 + 79 + if (in) { 80 + out |= rd; 81 + if (in & WMFW_CTL_FLAG_WRITEABLE) 82 + out |= wr; 83 + if (in & WMFW_CTL_FLAG_VOLATILE) 84 + out |= vol; 85 + } else { 86 + out |= rd | wr | vol; 87 + } 88 + 89 + return out; 90 + } 91 + 92 + static int hda_cs_dsp_add_kcontrol(struct hda_cs_dsp_coeff_ctl *ctl, const char *name) 93 + { 94 + struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 95 + struct snd_kcontrol_new kcontrol = {0}; 96 + struct snd_kcontrol *kctl; 97 + int ret = 0; 98 + 99 + if (cs_ctl->len > ADSP_MAX_STD_CTRL_SIZE) { 100 + dev_err(cs_ctl->dsp->dev, "KControl %s: length %zu exceeds maximum %d\n", name, 101 + cs_ctl->len, ADSP_MAX_STD_CTRL_SIZE); 102 + return -EINVAL; 103 + } 104 + 105 + kcontrol.name = name; 106 + kcontrol.info = hda_cs_dsp_coeff_info; 107 + kcontrol.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 108 + kcontrol.access = wmfw_convert_flags(cs_ctl->flags); 109 + kcontrol.get = hda_cs_dsp_coeff_get; 110 + kcontrol.put = hda_cs_dsp_coeff_put; 111 + 112 + /* Save ctl inside private_data, ctl is owned by cs_dsp, 113 + * and will be freed when cs_dsp removes the control */ 114 + kctl = snd_ctl_new1(&kcontrol, (void *)ctl); 115 + if (!kctl) { 116 + ret = -ENOMEM; 117 + return ret; 118 + } 119 + 120 + ret = snd_ctl_add(ctl->card, kctl); 121 + if (ret) { 122 + dev_err(cs_ctl->dsp->dev, "Failed to add KControl %s = %d\n", kcontrol.name, ret); 123 + return ret; 124 + } 125 + 126 + dev_dbg(cs_ctl->dsp->dev, "Added KControl: %s\n", kcontrol.name); 127 + ctl->kctl = kctl; 128 + 129 + return 0; 130 + } 131 + 132 + int hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl, struct hda_cs_dsp_ctl_info *info) 133 + { 134 + struct cs_dsp *cs_dsp = cs_ctl->dsp; 135 + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 136 + struct hda_cs_dsp_coeff_ctl *ctl; 137 + const char *region_name; 138 + int ret; 139 + 140 + if (cs_ctl->flags & WMFW_CTL_FLAG_SYS) 141 + return 0; 142 + 143 + region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type); 144 + if (!region_name) { 145 + dev_err(cs_dsp->dev, "Unknown region type: %d\n", cs_ctl->alg_region.type); 146 + return -EINVAL; 147 + } 148 + 149 + ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %.12s %x", info->device_name, 150 + cs_dsp->name, hda_cs_dsp_fw_text[info->fw_type], cs_ctl->alg_region.alg); 151 + 152 + if (cs_ctl->subname) { 153 + int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 154 + int skip = 0; 155 + 156 + /* Truncate the subname from the start if it is too long */ 157 + if (cs_ctl->subname_len > avail) 158 + skip = cs_ctl->subname_len - avail; 159 + 160 + snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, 161 + " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip); 162 + } 163 + 164 + ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 165 + if (!ctl) 166 + return -ENOMEM; 167 + 168 + ctl->cs_ctl = cs_ctl; 169 + ctl->card = info->card; 170 + cs_ctl->priv = ctl; 171 + 172 + ret = hda_cs_dsp_add_kcontrol(ctl, name); 173 + if (ret) { 174 + dev_err(cs_dsp->dev, "Error (%d) adding control %s\n", ret, name); 175 + kfree(ctl); 176 + return ret; 177 + } 178 + 179 + return 0; 180 + } 181 + EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_add, SND_HDA_CS_DSP_CONTROLS); 182 + 183 + void hda_cs_dsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl) 184 + { 185 + struct hda_cs_dsp_coeff_ctl *ctl = cs_ctl->priv; 186 + 187 + kfree(ctl); 188 + } 189 + EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_remove, SND_HDA_CS_DSP_CONTROLS); 190 + 191 + MODULE_DESCRIPTION("CS_DSP ALSA Control HDA Library"); 192 + MODULE_AUTHOR("Stefan Binding, <sbinding@opensource.cirrus.com>"); 193 + MODULE_LICENSE("GPL");
+33
sound/pci/hda/hda_cs_dsp_ctl.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 2 + * 3 + * HDA DSP ALSA Control Driver 4 + * 5 + * Copyright 2022 Cirrus Logic, Inc. 6 + * 7 + * Author: Stefan Binding <sbinding@opensource.cirrus.com> 8 + */ 9 + 10 + #ifndef __HDA_CS_DSP_CTL_H__ 11 + #define __HDA_CS_DSP_CTL_H__ 12 + 13 + #include <sound/soc.h> 14 + #include <linux/firmware/cirrus/cs_dsp.h> 15 + 16 + enum hda_cs_dsp_fw_id { 17 + HDA_CS_DSP_FW_SPK_PROT, 18 + HDA_CS_DSP_FW_SPK_CALI, 19 + HDA_CS_DSP_FW_SPK_DIAG, 20 + HDA_CS_DSP_FW_MISC, 21 + HDA_CS_DSP_NUM_FW 22 + }; 23 + 24 + struct hda_cs_dsp_ctl_info { 25 + struct snd_card *card; 26 + enum hda_cs_dsp_fw_id fw_type; 27 + const char *device_name; 28 + }; 29 + 30 + int hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl, struct hda_cs_dsp_ctl_info *info); 31 + void hda_cs_dsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl); 32 + 33 + #endif /*__HDA_CS_DSP_CTL_H__*/