lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <87pmk8r6bl.wl-tiwai@suse.de>
Date:   Fri, 20 May 2022 09:50:22 +0200
From:   Takashi Iwai <tiwai@...e.de>
To:     Vitaly Rodionov <vitalyr@...nsource.cirrus.com>
Cc:     Jaroslav Kysela <perex@...ex.cz>, Takashi Iwai <tiwai@...e.com>,
        Mark Brown <broonie@...nel.org>, <alsa-devel@...a-project.org>,
        <patches@...nsource.cirrus.com>, <linux-kernel@...r.kernel.org>,
        Stefan Binding <sbinding@...nsource.cirrus.com>
Subject: Re: [PATCH v3 01/17] ALSA: hda: hda_cs_dsp_ctl: Add Library to support CS_DSP ALSA controls

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@...nsource.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@...nsource.cirrus.com>
> Signed-off-by: Vitaly Rodionov <vitalyr@...nsource.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@...nsource.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@...nsource.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@...nsource.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@...nsource.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@...nsource.cirrus.com>
> Signed-off-by: Vitaly Rodionov <vitalyr@...nsource.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@...nsource.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@...nsource.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@...nsource.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
> 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ