On Thu, 19 May 2022 19:47:33 +0200, Vitaly Rodionov wrote: On Thu, 19 May 2022 19:47:33 +0200, Vitaly Rodionov wrote:
From: Stefan Binding sbinding@opensource.cirrus.com
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.
The apis to add and remove the controls start new threads when adding/removing controls since it is possible that setting an ALSA control would end up calling this api, which would then deadlock.
Well, I still don't understand why the addition/deletion itself has to be in a work. As far as I see, it's simple calls of snd_ctl_add() and snd_ctl_remove_id().
And, if the problem is that you're calling snd_ctl_add() from another control callback, it's rather the problem of the caller's side, not here. IOW, the async implementation should be rather in the caller side.
thanks,
Takashi
In the future, it will be necessary to load/unload firmware (and firmware ALSA controls) using a separate ALSA control, which means that the ability to call these apis from another ALSA control is required.
Signed-off-by: Stefan Binding sbinding@opensource.cirrus.com Signed-off-by: Vitaly Rodionov vitalyr@opensource.cirrus.com
Changes since v2:
- Updated commit message to describe reasons for adding/removing controls asynchronously
- Removed unnecessary code which handled unused tlv or acked controls.
- Removed code which handled outdate firmware versions when adding controls
MAINTAINERS | 1 + sound/pci/hda/Kconfig | 4 + sound/pci/hda/Makefile | 2 + sound/pci/hda/hda_cs_dsp_ctl.c | 242 +++++++++++++++++++++++++++++++++ sound/pci/hda/hda_cs_dsp_ctl.h | 34 +++++ 5 files changed, 283 insertions(+) create mode 100644 sound/pci/hda/hda_cs_dsp_ctl.c create mode 100644 sound/pci/hda/hda_cs_dsp_ctl.h
diff --git a/MAINTAINERS b/MAINTAINERS index 11ed24f1f9bb..0c1f6817d8a3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4729,6 +4729,7 @@ S: Maintained F: Documentation/devicetree/bindings/sound/cirrus,cs* F: include/dt-bindings/sound/cs* F: sound/pci/hda/cs* +F: sound/pci/hda/hda_cs_dsp_ctl.* F: sound/soc/codecs/cs*
CIRRUS LOGIC DSP FIRMWARE DRIVER diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 79ade4787d95..d1fd6cf82beb 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -94,6 +94,10 @@ config SND_HDA_PATCH_LOADER config SND_HDA_SCODEC_CS35L41 tristate
+config SND_HDA_CS_DSP_CONTROLS
- tristate
- depends on CS_DSP
config SND_HDA_SCODEC_CS35L41_I2C tristate "Build CS35L41 HD-audio side codec support for I2C Bus" depends on I2C diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 3e7bc608d45f..00d306104484 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -31,6 +31,7 @@ snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o snd-hda-scodec-cs35l41-objs := cs35l41_hda.o snd-hda-scodec-cs35l41-i2c-objs := cs35l41_hda_i2c.o snd-hda-scodec-cs35l41-spi-objs := cs35l41_hda_spi.o +snd-hda-cs-dsp-ctls-objs := hda_cs_dsp_ctl.o
# common driver obj-$(CONFIG_SND_HDA) := snd-hda-codec.o @@ -54,6 +55,7 @@ obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o +obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o
# this must be the last entry after codec drivers; # otherwise the codec patches won't be hooked before the PCI probe diff --git a/sound/pci/hda/hda_cs_dsp_ctl.c b/sound/pci/hda/hda_cs_dsp_ctl.c new file mode 100644 index 000000000000..46df48ff2ae1 --- /dev/null +++ b/sound/pci/hda/hda_cs_dsp_ctl.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// HDA DSP ALSA Control Driver +// +// Copyright 2022 Cirrus Logic, Inc. +// +// Author: Stefan Binding sbinding@opensource.cirrus.com
+#include <linux/module.h> +#include <sound/soc.h> +#include <linux/firmware/cirrus/cs_dsp.h> +#include <linux/firmware/cirrus/wmfw.h> +#include "hda_cs_dsp_ctl.h"
+#define ADSP_MAX_STD_CTRL_SIZE 512
+struct hda_cs_dsp_coeff_ctl {
- const char *name;
- struct cs_dsp_coeff_ctl *cs_ctl;
- struct snd_card *card;
- struct soc_bytes_ext bytes_ext;
- struct work_struct add_work;
- struct work_struct remove_work;
+};
+static const char * const hda_cs_dsp_fw_text[HDA_CS_DSP_NUM_FW] = {
- [HDA_CS_DSP_FW_SPK_PROT] = "Prot",
- [HDA_CS_DSP_FW_SPK_CALI] = "Cali",
- [HDA_CS_DSP_FW_SPK_DIAG] = "Diag",
- [HDA_CS_DSP_FW_MISC] = "Misc",
+};
+static inline struct hda_cs_dsp_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) +{
- return container_of(ext, struct hda_cs_dsp_coeff_ctl, bytes_ext);
+}
+static int hda_cs_dsp_coeff_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{
- struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
- struct hda_cs_dsp_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
- struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = cs_ctl->len;
- return 0;
+}
+static int hda_cs_dsp_coeff_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +{
- struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
- struct hda_cs_dsp_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
- struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
- char *p = ucontrol->value.bytes.data;
- int ret = 0;
- mutex_lock(&cs_ctl->dsp->pwr_lock);
- ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len);
- mutex_unlock(&cs_ctl->dsp->pwr_lock);
- return ret;
+}
+static int hda_cs_dsp_coeff_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +{
- struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
- struct hda_cs_dsp_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
- struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
- char *p = ucontrol->value.bytes.data;
- int ret;
- mutex_lock(&cs_ctl->dsp->pwr_lock);
- ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len);
- mutex_unlock(&cs_ctl->dsp->pwr_lock);
- return ret;
+}
+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) {
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 void hda_cs_dsp_ctl_add_work(struct work_struct *work) +{
- struct hda_cs_dsp_coeff_ctl *ctl = container_of(work,
struct hda_cs_dsp_coeff_ctl,
add_work);
- struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
- struct snd_kcontrol_new *kcontrol;
- kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
- if (!kcontrol)
return;
- kcontrol->name = ctl->name;
- kcontrol->info = hda_cs_dsp_coeff_info;
- kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
- kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
- kcontrol->access = wmfw_convert_flags(cs_ctl->flags, cs_ctl->len);
- kcontrol->get = hda_cs_dsp_coeff_get;
- kcontrol->put = hda_cs_dsp_coeff_put;
- if (snd_ctl_add(ctl->card, snd_ctl_new1(kcontrol, NULL)))
dev_err(cs_ctl->dsp->dev, "Failed to add KControl: %s\n", kcontrol->name);
- else
dev_dbg(cs_ctl->dsp->dev, "Added KControl: %s\n", kcontrol->name);
- kfree(kcontrol);
+}
+int hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl, struct hda_cs_dsp_ctl_info *info) +{
- struct cs_dsp *cs_dsp = cs_ctl->dsp;
- char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
- struct hda_cs_dsp_coeff_ctl *ctl;
- const char *region_name;
- int ret;
- if (cs_ctl->flags & WMFW_CTL_FLAG_SYS) {
dev_dbg(cs_dsp->dev, "cs_ctl->flags = WMFW_CTL_FLAG_SYS\n");
return 0;
- }
- region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type);
- if (!region_name) {
dev_err(cs_dsp->dev, "Unknown region type: %d\n", cs_ctl->alg_region.type);
return -EINVAL;
- }
- ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %.12s %x", info->amp_name,
cs_dsp->name, hda_cs_dsp_fw_text[info->fw_type], cs_ctl->alg_region.alg);
- if (cs_ctl->subname) {
int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
int skip = 0;
/* Truncate the subname from the start if it is too long */
if (cs_ctl->subname_len > avail)
skip = cs_ctl->subname_len - avail;
snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret,
" %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip);
- }
- ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
- if (!ctl)
return -ENOMEM;
- ctl->cs_ctl = cs_ctl;
- ctl->card = info->card;
- ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
- if (!ctl->name) {
ret = -ENOMEM;
dev_err(cs_dsp->dev, "Cannot save ctl name\n");
goto err_ctl;
- }
- cs_ctl->priv = ctl;
- INIT_WORK(&ctl->add_work, hda_cs_dsp_ctl_add_work);
- schedule_work(&ctl->add_work);
- return 0;
+err_ctl:
- dev_err(cs_dsp->dev, "Error adding control: %s\n", name);
- kfree(ctl);
- return ret;
+} +EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_add, SND_HDA_CS_DSP_CONTROLS);
+int hda_cs_dsp_remove_kcontrol(struct snd_card *card, const char *name) +{
- struct snd_kcontrol *kctl;
- list_for_each_entry(kctl, &card->controls, list)
if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name)))
return snd_ctl_remove_id(card, &kctl->id);
- return -EINVAL;
+} +EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_remove_kcontrol, SND_HDA_CS_DSP_CONTROLS);
+static void hda_cs_dsp_ctl_del_work(struct work_struct *work) +{
- struct hda_cs_dsp_coeff_ctl *ctl = container_of(work,
struct hda_cs_dsp_coeff_ctl,
remove_work);
- cancel_work_sync(&ctl->add_work);
- hda_cs_dsp_remove_kcontrol(ctl->card, ctl->name);
- kfree(ctl->name);
- kfree(ctl);
+}
+void hda_cs_dsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl) +{
- struct hda_cs_dsp_coeff_ctl *ctl = cs_ctl->priv;
- INIT_WORK(&ctl->remove_work, hda_cs_dsp_ctl_del_work);
- schedule_work(&ctl->remove_work);
+} +EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_remove, SND_HDA_CS_DSP_CONTROLS);
+MODULE_DESCRIPTION("CS_DSP ALSA Control HDA Library"); +MODULE_AUTHOR("Stefan Binding, sbinding@opensource.cirrus.com"); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_cs_dsp_ctl.h b/sound/pci/hda/hda_cs_dsp_ctl.h new file mode 100644 index 000000000000..3c90312b45d6 --- /dev/null +++ b/sound/pci/hda/hda_cs_dsp_ctl.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0
- HDA DSP ALSA Control Driver
- Copyright 2022 Cirrus Logic, Inc.
- Author: Stefan Binding sbinding@opensource.cirrus.com
- */
+#ifndef __HDA_CS_DSP_CTL_H__ +#define __HDA_CS_DSP_CTL_H__
+#include <sound/soc.h> +#include <linux/firmware/cirrus/cs_dsp.h>
+enum hda_cs_dsp_fw_id {
- HDA_CS_DSP_FW_SPK_PROT,
- HDA_CS_DSP_FW_SPK_CALI,
- HDA_CS_DSP_FW_SPK_DIAG,
- HDA_CS_DSP_FW_MISC,
- HDA_CS_DSP_NUM_FW
+};
+struct hda_cs_dsp_ctl_info {
- struct snd_card *card;
- enum hda_cs_dsp_fw_id fw_type;
- const char *amp_name;
+};
+int hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl, struct hda_cs_dsp_ctl_info *info); +void hda_cs_dsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl); +int hda_cs_dsp_remove_kcontrol(struct snd_card *card, const char *name);
+#endif /*__HDA_CS_DSP_CTL_H__*/
2.34.1
From: Stefan Binding sbinding@opensource.cirrus.com
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.
The apis to add and remove the controls start new threads when adding/removing controls since it is possible that setting an ALSA control would end up calling this api, which would then deadlock.
In the future, it will be necessary to load/unload firmware (and firmware ALSA controls) using a separate ALSA control, which means that the ability to call these apis from another ALSA control is required.
Signed-off-by: Stefan Binding sbinding@opensource.cirrus.com Signed-off-by: Vitaly Rodionov vitalyr@opensource.cirrus.com
Changes since v2:
- Updated commit message to describe reasons for adding/removing controls asynchronously
- Removed unnecessary code which handled unused tlv or acked controls.
- Removed code which handled outdate firmware versions when adding controls
MAINTAINERS | 1 + sound/pci/hda/Kconfig | 4 + sound/pci/hda/Makefile | 2 + sound/pci/hda/hda_cs_dsp_ctl.c | 242 +++++++++++++++++++++++++++++++++ sound/pci/hda/hda_cs_dsp_ctl.h | 34 +++++ 5 files changed, 283 insertions(+) create mode 100644 sound/pci/hda/hda_cs_dsp_ctl.c create mode 100644 sound/pci/hda/hda_cs_dsp_ctl.h
diff --git a/MAINTAINERS b/MAINTAINERS index 11ed24f1f9bb..0c1f6817d8a3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4729,6 +4729,7 @@ S: Maintained F: Documentation/devicetree/bindings/sound/cirrus,cs* F: include/dt-bindings/sound/cs* F: sound/pci/hda/cs* +F: sound/pci/hda/hda_cs_dsp_ctl.* F: sound/soc/codecs/cs*
CIRRUS LOGIC DSP FIRMWARE DRIVER diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 79ade4787d95..d1fd6cf82beb 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -94,6 +94,10 @@ config SND_HDA_PATCH_LOADER config SND_HDA_SCODEC_CS35L41 tristate
+config SND_HDA_CS_DSP_CONTROLS
- tristate
- depends on CS_DSP
config SND_HDA_SCODEC_CS35L41_I2C tristate "Build CS35L41 HD-audio side codec support for I2C Bus" depends on I2C diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 3e7bc608d45f..00d306104484 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -31,6 +31,7 @@ snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o snd-hda-scodec-cs35l41-objs := cs35l41_hda.o snd-hda-scodec-cs35l41-i2c-objs := cs35l41_hda_i2c.o snd-hda-scodec-cs35l41-spi-objs := cs35l41_hda_spi.o +snd-hda-cs-dsp-ctls-objs := hda_cs_dsp_ctl.o
# common driver obj-$(CONFIG_SND_HDA) := snd-hda-codec.o @@ -54,6 +55,7 @@ obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41) += snd-hda-scodec-cs35l41.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_I2C) += snd-hda-scodec-cs35l41-i2c.o obj-$(CONFIG_SND_HDA_SCODEC_CS35L41_SPI) += snd-hda-scodec-cs35l41-spi.o +obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o
# this must be the last entry after codec drivers; # otherwise the codec patches won't be hooked before the PCI probe diff --git a/sound/pci/hda/hda_cs_dsp_ctl.c b/sound/pci/hda/hda_cs_dsp_ctl.c new file mode 100644 index 000000000000..46df48ff2ae1 --- /dev/null +++ b/sound/pci/hda/hda_cs_dsp_ctl.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// HDA DSP ALSA Control Driver +// +// Copyright 2022 Cirrus Logic, Inc. +// +// Author: Stefan Binding sbinding@opensource.cirrus.com
+#include <linux/module.h> +#include <sound/soc.h> +#include <linux/firmware/cirrus/cs_dsp.h> +#include <linux/firmware/cirrus/wmfw.h> +#include "hda_cs_dsp_ctl.h"
+#define ADSP_MAX_STD_CTRL_SIZE 512
+struct hda_cs_dsp_coeff_ctl {
- const char *name;
- struct cs_dsp_coeff_ctl *cs_ctl;
- struct snd_card *card;
- struct soc_bytes_ext bytes_ext;
- struct work_struct add_work;
- struct work_struct remove_work;
+};
+static const char * const hda_cs_dsp_fw_text[HDA_CS_DSP_NUM_FW] = {
- [HDA_CS_DSP_FW_SPK_PROT] = "Prot",
- [HDA_CS_DSP_FW_SPK_CALI] = "Cali",
- [HDA_CS_DSP_FW_SPK_DIAG] = "Diag",
- [HDA_CS_DSP_FW_MISC] = "Misc",
+};
+static inline struct hda_cs_dsp_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) +{
- return container_of(ext, struct hda_cs_dsp_coeff_ctl, bytes_ext);
+}
+static int hda_cs_dsp_coeff_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +{
- struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
- struct hda_cs_dsp_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
- struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = cs_ctl->len;
- return 0;
+}
+static int hda_cs_dsp_coeff_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +{
- struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
- struct hda_cs_dsp_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
- struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
- char *p = ucontrol->value.bytes.data;
- int ret = 0;
- mutex_lock(&cs_ctl->dsp->pwr_lock);
- ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len);
- mutex_unlock(&cs_ctl->dsp->pwr_lock);
- return ret;
+}
+static int hda_cs_dsp_coeff_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +{
- struct soc_bytes_ext *bytes_ext =
(struct soc_bytes_ext *)kctl->private_value;
- struct hda_cs_dsp_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
- struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
- char *p = ucontrol->value.bytes.data;
- int ret;
- mutex_lock(&cs_ctl->dsp->pwr_lock);
- ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len);
- mutex_unlock(&cs_ctl->dsp->pwr_lock);
- return ret;
+}
+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) {
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 void hda_cs_dsp_ctl_add_work(struct work_struct *work) +{
- struct hda_cs_dsp_coeff_ctl *ctl = container_of(work,
struct hda_cs_dsp_coeff_ctl,
add_work);
- struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
- struct snd_kcontrol_new *kcontrol;
- kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
- if (!kcontrol)
return;
- kcontrol->name = ctl->name;
- kcontrol->info = hda_cs_dsp_coeff_info;
- kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
- kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
- kcontrol->access = wmfw_convert_flags(cs_ctl->flags, cs_ctl->len);
- kcontrol->get = hda_cs_dsp_coeff_get;
- kcontrol->put = hda_cs_dsp_coeff_put;
- if (snd_ctl_add(ctl->card, snd_ctl_new1(kcontrol, NULL)))
dev_err(cs_ctl->dsp->dev, "Failed to add KControl: %s\n", kcontrol->name);
- else
dev_dbg(cs_ctl->dsp->dev, "Added KControl: %s\n", kcontrol->name);
- kfree(kcontrol);
+}
+int hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl, struct hda_cs_dsp_ctl_info *info) +{
- struct cs_dsp *cs_dsp = cs_ctl->dsp;
- char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
- struct hda_cs_dsp_coeff_ctl *ctl;
- const char *region_name;
- int ret;
- if (cs_ctl->flags & WMFW_CTL_FLAG_SYS) {
dev_dbg(cs_dsp->dev, "cs_ctl->flags = WMFW_CTL_FLAG_SYS\n");
return 0;
- }
- region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type);
- if (!region_name) {
dev_err(cs_dsp->dev, "Unknown region type: %d\n", cs_ctl->alg_region.type);
return -EINVAL;
- }
- ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %.12s %x", info->amp_name,
cs_dsp->name, hda_cs_dsp_fw_text[info->fw_type], cs_ctl->alg_region.alg);
- if (cs_ctl->subname) {
int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
int skip = 0;
/* Truncate the subname from the start if it is too long */
if (cs_ctl->subname_len > avail)
skip = cs_ctl->subname_len - avail;
snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret,
" %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip);
- }
- ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
- if (!ctl)
return -ENOMEM;
- ctl->cs_ctl = cs_ctl;
- ctl->card = info->card;
- ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
- if (!ctl->name) {
ret = -ENOMEM;
dev_err(cs_dsp->dev, "Cannot save ctl name\n");
goto err_ctl;
- }
- cs_ctl->priv = ctl;
- INIT_WORK(&ctl->add_work, hda_cs_dsp_ctl_add_work);
- schedule_work(&ctl->add_work);
- return 0;
+err_ctl:
- dev_err(cs_dsp->dev, "Error adding control: %s\n", name);
- kfree(ctl);
- return ret;
+} +EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_add, SND_HDA_CS_DSP_CONTROLS);
+int hda_cs_dsp_remove_kcontrol(struct snd_card *card, const char *name) +{
- struct snd_kcontrol *kctl;
- list_for_each_entry(kctl, &card->controls, list)
if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name)))
return snd_ctl_remove_id(card, &kctl->id);
- return -EINVAL;
+} +EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_remove_kcontrol, SND_HDA_CS_DSP_CONTROLS);
+static void hda_cs_dsp_ctl_del_work(struct work_struct *work) +{
- struct hda_cs_dsp_coeff_ctl *ctl = container_of(work,
struct hda_cs_dsp_coeff_ctl,
remove_work);
- cancel_work_sync(&ctl->add_work);
- hda_cs_dsp_remove_kcontrol(ctl->card, ctl->name);
- kfree(ctl->name);
- kfree(ctl);
+}
+void hda_cs_dsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl) +{
- struct hda_cs_dsp_coeff_ctl *ctl = cs_ctl->priv;
- INIT_WORK(&ctl->remove_work, hda_cs_dsp_ctl_del_work);
- schedule_work(&ctl->remove_work);
+} +EXPORT_SYMBOL_NS_GPL(hda_cs_dsp_control_remove, SND_HDA_CS_DSP_CONTROLS);
+MODULE_DESCRIPTION("CS_DSP ALSA Control HDA Library"); +MODULE_AUTHOR("Stefan Binding, sbinding@opensource.cirrus.com"); +MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_cs_dsp_ctl.h b/sound/pci/hda/hda_cs_dsp_ctl.h new file mode 100644 index 000000000000..3c90312b45d6 --- /dev/null +++ b/sound/pci/hda/hda_cs_dsp_ctl.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0
- HDA DSP ALSA Control Driver
- Copyright 2022 Cirrus Logic, Inc.
- Author: Stefan Binding sbinding@opensource.cirrus.com
- */
+#ifndef __HDA_CS_DSP_CTL_H__ +#define __HDA_CS_DSP_CTL_H__
+#include <sound/soc.h> +#include <linux/firmware/cirrus/cs_dsp.h>
+enum hda_cs_dsp_fw_id {
- HDA_CS_DSP_FW_SPK_PROT,
- HDA_CS_DSP_FW_SPK_CALI,
- HDA_CS_DSP_FW_SPK_DIAG,
- HDA_CS_DSP_FW_MISC,
- HDA_CS_DSP_NUM_FW
+};
+struct hda_cs_dsp_ctl_info {
- struct snd_card *card;
- enum hda_cs_dsp_fw_id fw_type;
- const char *amp_name;
+};
+int hda_cs_dsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl, struct hda_cs_dsp_ctl_info *info); +void hda_cs_dsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl); +int hda_cs_dsp_remove_kcontrol(struct snd_card *card, const char *name);
+#endif /*__HDA_CS_DSP_CTL_H__*/
2.34.1