Received: by 2002:a6b:500f:0:0:0:0:0 with SMTP id e15csp434476iob; Wed, 11 May 2022 18:32:16 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxdgKKeHxcLluIWbIgJY7RVAVe/McmuUuNdG7YUHFDabA/HdxobNE1rt/EvILqdhSQcBJpG X-Received: by 2002:a17:906:c149:b0:6f9:2a43:ef37 with SMTP id dp9-20020a170906c14900b006f92a43ef37mr20888571ejc.17.1652319136119; Wed, 11 May 2022 18:32:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1652319136; cv=none; d=google.com; s=arc-20160816; b=x5PUisCvEkhulg1jz1Yo3ak5A7P1q+MbcwFktbpwLkh/dndK6YHrDpj+vI6XVzmB3P YcRg+R7mnR5mi6MppTY2pHh38NqN8KUOz6b4Nc0WOeQXJZgh+Nf0q4f1vqmGnoc6rUAk DI7D+YvLZ+Qvu/PR8IR4JFj4gTsvF2skdp+IoXKzOkZtGshBVF1Dcosa0Y2/2bJ5KFkD kOIR2tHkuu3ddgSpBcLML794ahSlroK1nYki1GyaE1G2rd5t+b/flR86wcSgc4k2rjEu ilji20OD9TdatZ+UM8cJJlQ574LfoYhepri4Vz5MQvZAlzpvQmBZIp+S4HsOq47xHMf+ uJbA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:mime-version:user-agent:references:in-reply-to :subject:cc:to:from:message-id:date:dkim-signature:dkim-signature; bh=ZwOv2TzvacFcBTLytW6vmWJoaiSk9tGV0LvPRFw3NuM=; b=xVYS+uHysKBMNSrtQSyFCLxz/EiBcPZHyjhsPqKfxEqc6QIPKRBFU0IyHSvnmuNIun twXY6X9jTz1tLH828Q5J0Ki6RjvY6A2JPGlhbmjw01eH6FPT8FTU5gPTz+QPsBF4hJNt O22Es5/30QuJ1K49LvsFnegCxkBY1qIVgEJNOemglNE6H+T2azhpcZ3KY7wQSeOZ3C7e UG0UkVbEeNLE+VAjEWgvJxUmkExYg6t3preL/fleaREVu+QxWzjbJJeJJbcoSj44aWg/ xuyup/J//0kEVlMWZZ/mDl6cIxfxmXfl2JPIb3pspv5sDkb0/Y7n6e+5fStl/cmykmPD bRjg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@suse.de header.s=susede2_rsa header.b=vrJvzxgZ; dkim=neutral (no key) header.i=@suse.de header.s=susede2_ed25519; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=suse.de Return-Path: Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id di10-20020a170906730a00b006fa8c68b976si4697483ejc.596.2022.05.11.18.31.50; Wed, 11 May 2022 18:32:16 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@suse.de header.s=susede2_rsa header.b=vrJvzxgZ; dkim=neutral (no key) header.i=@suse.de header.s=susede2_ed25519; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=suse.de Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244128AbiEKNtT (ORCPT + 99 others); Wed, 11 May 2022 09:49:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44804 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S244118AbiEKNtQ (ORCPT ); Wed, 11 May 2022 09:49:16 -0400 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.220.29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3437C2317E6 for ; Wed, 11 May 2022 06:49:14 -0700 (PDT) Received: from relay2.suse.de (relay2.suse.de [149.44.160.134]) by smtp-out2.suse.de (Postfix) with ESMTP id E4A221F381; Wed, 11 May 2022 13:49:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1652276952; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=ZwOv2TzvacFcBTLytW6vmWJoaiSk9tGV0LvPRFw3NuM=; b=vrJvzxgZc7X+9iKbOR4anZiPSkfy0A0nr9fEQhE0w26GobPi6Bom0WfciUsusUAH5Y02+e WuwpWLV2hKv6+6bTUYVMz+ivyOWMrII6jVaxCB6FHOw9UaPS4qp/LfpYrrGgoq+GvfQ5Nf XdjdYQQO3CoTChXfiVNim6kPzfnt+Xs= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1652276952; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=ZwOv2TzvacFcBTLytW6vmWJoaiSk9tGV0LvPRFw3NuM=; b=f/JJKWyquNxMbXSjH9VgiRwyNl3JOuhT347KAGm4M++KgbrUHuhM+eM7ipkOJdk5451Mi9 fSVq/PSGo2z0u7CQ== Received: from alsa1.suse.de (alsa1.suse.de [10.160.4.42]) by relay2.suse.de (Postfix) with ESMTP id D76662C141; Wed, 11 May 2022 13:49:12 +0000 (UTC) Date: Wed, 11 May 2022 15:49:12 +0200 Message-ID: From: Takashi Iwai To: Vitaly Rodionov Cc: Jaroslav Kysela , Takashi Iwai , Mark Brown , , , , Stefan Binding Subject: Re: [PATCH v2 10/26] ALSA: hda: hda_cs_dsp_ctl: Add Library to support CS_DSP ALSA controls In-Reply-To: <20220509214703.4482-11-vitalyr@opensource.cirrus.com> References: <20220509214703.4482-1-vitalyr@opensource.cirrus.com> <20220509214703.4482-11-vitalyr@opensource.cirrus.com> User-Agent: Wanderlust/2.15.9 (Almost Unreal) SEMI/1.14.6 (Maruoka) FLIM/1.14.9 (=?UTF-8?B?R29qxY0=?=) APEL/10.8 Emacs/25.3 (x86_64-suse-linux-gnu) MULE/6.0 (HANACHIRUSATO) MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka") Content-Type: text/plain; charset=US-ASCII X-Spam-Status: No, score=-4.4 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_MED,SPF_HELO_NONE, SPF_PASS,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Mon, 09 May 2022 23:46:47 +0200, Vitaly Rodionov wrote: > > From: Stefan Binding > > 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. Hmm, quite lots of things aren't explained here. First off, as far as I see, the control elements that are implemented in this patch are pretty unique, they don't follow the standard way. Admittedly, ASoC core (ab)uses the TLV read/write for the arbitrary data bytes, and this seems following that instead. If so, it needs more clear explanation in the comments add/or commit logs. Second, hda_cs_dsp_control_add() and _remove() functions do the addition and the removal asynchronously with a dedicated work. It's fragile as an API, and even no clarification is given why it's done so. thanks, Takashi > > Signed-off-by: Stefan Binding > Signed-off-by: Vitaly Rodionov > --- > MAINTAINERS | 1 + > sound/pci/hda/Kconfig | 4 + > sound/pci/hda/Makefile | 2 + > sound/pci/hda/hda_cs_dsp_ctl.c | 364 +++++++++++++++++++++++++++++++++ > sound/pci/hda/hda_cs_dsp_ctl.h | 34 +++ > 5 files changed, 405 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 f57e6d38a542..b7e4d2a0ef41 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -4713,6 +4713,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..e94740c5557a > --- /dev/null > +++ b/sound/pci/hda/hda_cs_dsp_ctl.c > @@ -0,0 +1,364 @@ > +// SPDX-License-Identifier: GPL-2.0 > +// > +// HDA DSP ALSA Control Driver > +// > +// Copyright 2022 Cirrus Logic, Inc. > +// > +// Author: Stefan Binding > + > +#include > +#include > +#include > +#include > +#include "hda_cs_dsp_ctl.h" > + > +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; > + > + switch (cs_ctl->type) { > + case WMFW_CTL_TYPE_ACKED: > + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; > + uinfo->value.integer.min = CS_DSP_ACKED_CTL_MIN_VALUE; > + uinfo->value.integer.max = CS_DSP_ACKED_CTL_MAX_VALUE; > + uinfo->value.integer.step = 1; > + uinfo->count = 1; > + break; > + default: > + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; > + uinfo->count = cs_ctl->len; > + break; > + } > + > + 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_tlv_put(struct snd_kcontrol *kctl, > + const unsigned int __user *bytes, unsigned int size) > +{ > + 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; > + int ret = 0; > + > + mutex_lock(&cs_ctl->dsp->pwr_lock); > + > + if (copy_from_user(cs_ctl->cache, bytes, size)) > + ret = -EFAULT; > + else > + ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, cs_ctl->cache, size); > + > + mutex_unlock(&cs_ctl->dsp->pwr_lock); > + > + return ret; > +} > + > +static int hda_cs_dsp_coeff_put_acked(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; > + unsigned int val = ucontrol->value.integer.value[0]; > + int ret; > + > + if (val == 0) > + return 0; /* 0 means no event */ > + > + mutex_lock(&cs_ctl->dsp->pwr_lock); > + > + if (cs_ctl->enabled) > + ret = cs_dsp_coeff_write_acked_control(cs_ctl, val); > + else > + ret = -EPERM; > + > + 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 int hda_cs_dsp_coeff_tlv_get(struct snd_kcontrol *kctl, > + unsigned int __user *bytes, unsigned int size) > +{ > + 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; > + int ret = 0; > + > + mutex_lock(&cs_ctl->dsp->pwr_lock); > + > + ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size); > + > + if (!ret && copy_to_user(bytes, cs_ctl->cache, size)) > + ret = -EFAULT; > + > + mutex_unlock(&cs_ctl->dsp->pwr_lock); > + > + return ret; > +} > + > +static int hda_cs_dsp_coeff_get_acked(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + /* > + * Although it's not useful to read an acked control, we must satisfy > + * user-side assumptions that all controls are readable and that a > + * write of the same value should be filtered out (it's valid to send > + * the same event number again to the firmware). We therefore return 0, > + * meaning "no event" so valid event numbers will always be a change > + */ > + ucontrol->value.integer.value[0] = 0; > + > + return 0; > +} > +#define ADSP_MAX_STD_CTRL_SIZE 512 > + > +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); > + > + switch (cs_ctl->type) { > + case WMFW_CTL_TYPE_ACKED: > + kcontrol->get = hda_cs_dsp_coeff_get_acked; > + kcontrol->put = hda_cs_dsp_coeff_put_acked; > + break; > + default: > + if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { > + ctl->bytes_ext.max = cs_ctl->len; > + ctl->bytes_ext.get = hda_cs_dsp_coeff_tlv_get; > + ctl->bytes_ext.put = hda_cs_dsp_coeff_tlv_put; > + } else { > + kcontrol->get = hda_cs_dsp_coeff_get; > + kcontrol->put = hda_cs_dsp_coeff_put; > + } > + break; > + } > + > + 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; > + } > + > + switch (cs_dsp->fw_ver) { > + case 0: > + case 1: > + ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, > + "%s %s %s %x", info->amp_name, cs_dsp->name, region_name, > + cs_ctl->alg_region.alg); > + break; > + case 2: > + ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, > + "%s %s%c %.12s %x", info->amp_name, cs_dsp->name, *region_name, > + hda_cs_dsp_fw_text[info->fw_type], cs_ctl->alg_region.alg); > + break; > + default: > + 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); > + break; > + } > + > + 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, "); > +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 > + */ > + > +#ifndef __HDA_CS_DSP_CTL_H__ > +#define __HDA_CS_DSP_CTL_H__ > + > +#include > +#include > + > +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 >