Received: by 2002:a6b:500f:0:0:0:0:0 with SMTP id e15csp2094805iob; Fri, 20 May 2022 01:40:47 -0700 (PDT) X-Google-Smtp-Source: ABdhPJz/0kcRs8TYlfDUZTIME5TVEx9lo+X5NKBhWDErZvDsDFNsUJveouicU6s1RViNuRRpIObE X-Received: by 2002:a17:906:c113:b0:6f8:faee:2f22 with SMTP id do19-20020a170906c11300b006f8faee2f22mr7634793ejc.447.1653036046853; Fri, 20 May 2022 01:40:46 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1653036046; cv=none; d=google.com; s=arc-20160816; b=z1Fk1Z0v4q7r/7H8YsoH9SblPxmz4Mjky4I7vm5T1Af0qCFluAMV3ZQb8+HRZ1cwnb uXrMT07I2vsvwiQ1JY7ckHBrDjFHNTiPduA0LvU03yeva0asMvbYbSfGbjyqmt6DSj8M RzgvYFCtHYOL2/O3AcP2+GklVjNnUu7UUNbkfFqrsBlM6/FrlfLQ38YP9JEDUKPIVjVo ESi3Q5qOOS8LAed+BUM5aDsp7K2gZmoBTPs2bGQ7adeCzp4KTPwdcaysAPyYuf56wnPw JVic72eGvYOl4kOsiXQMiqRC+bali8ZK+AkBTpMp0se65ZI1pgIZ5Nq1GWiySP7PJwAx MPag== 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=BrmMRa+8XiLzAiyg4LlKY78CvSJzftPPmGvHj5WWouI=; b=pbuXWgj34bLDKWtBUEw+zk8EDkzr0H9w+1r5ZcQY5H2YWEY+EiWnXMhC16rRRsLlk+ SYZDM5Ge4IVEwHipntnqbysTwMHvWcion1ElphYQOMS9Qh0DHLRf49kDr9u9dkRdV+dK rbD3vo4JofaTBQFHQ3PrrtQaY73CAuKRNEvKjQ6Ot8K3qCQdXHDhBZoqAdTGkdTsS4aU QoBLdgmZK/juvL6/L42CdnjOILYjmJA6dlpgFf1m9eGQmoAD3ns2Q7fEf7A76rRfQBis PB0ZnJGvQJqHjPDdOAjPbBFPzVYS4GhOSKUg4lu5Rs11gnriVhDBgDf6WK52KOjDmhYq hNHA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@suse.de header.s=susede2_rsa header.b=qKBNICvL; dkim=neutral (no key) header.i=@suse.de header.s=susede2_ed25519 header.b=JzhJJXvc; 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 i11-20020a1709064fcb00b006f3b8834625si4382783ejw.423.2022.05.20.01.40.20; Fri, 20 May 2022 01:40:46 -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=qKBNICvL; dkim=neutral (no key) header.i=@suse.de header.s=susede2_ed25519 header.b=JzhJJXvc; 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 S1346636AbiETHu3 (ORCPT + 99 others); Fri, 20 May 2022 03:50:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43418 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1346133AbiETHu0 (ORCPT ); Fri, 20 May 2022 03:50:26 -0400 Received: from smtp-out2.suse.de (smtp-out2.suse.de [195.135.220.29]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 78BD5EAB88 for ; Fri, 20 May 2022 00:50:24 -0700 (PDT) Received: from relay2.suse.de (relay2.suse.de [149.44.160.134]) by smtp-out2.suse.de (Postfix) with ESMTP id BBF7C1F894; Fri, 20 May 2022 07:50:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1653033022; 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=BrmMRa+8XiLzAiyg4LlKY78CvSJzftPPmGvHj5WWouI=; b=qKBNICvLrYQmSNMM1y+dqAT+xA5A0sMMJ+L7N2ZQ/UXe0z/yTOyvzfe7tQkJ9c3bvCA9Qj p9pAYMivrxXYBDUmK/F6O+3fCqgZ77YFz9QSxUOFfvrAH1QKXRYORnHlgEB2lhG4f8jt/h npMPPshtHq4A1y3HnQiHqUdDlig5QfI= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1653033022; 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=BrmMRa+8XiLzAiyg4LlKY78CvSJzftPPmGvHj5WWouI=; b=JzhJJXvcShlEDBJ4N6B2mId2c5knx4wT130fK/Ct+6u5LEgMOhOMJ7c64juYTwfsbpA7fz zQd0TmC8K9iTfFAg== Received: from valkyrie.site.suse.de (unknown [10.163.18.242]) by relay2.suse.de (Postfix) with ESMTP id 8AE9A2C141; Fri, 20 May 2022 07:50:22 +0000 (UTC) Date: Fri, 20 May 2022 09:50:22 +0200 Message-ID: <87pmk8r6bl.wl-tiwai@suse.de> From: Takashi Iwai To: Vitaly Rodionov Cc: Jaroslav Kysela , Takashi Iwai , Mark Brown , , , , Stefan Binding Subject: Re: [PATCH v3 01/17] ALSA: hda: hda_cs_dsp_ctl: Add Library to support CS_DSP ALSA controls In-Reply-To: <20220519174749.15459-2-vitalyr@opensource.cirrus.com> References: <20220519174749.15459-1-vitalyr@opensource.cirrus.com> <20220519174749.15459-2-vitalyr@opensource.cirrus.com> User-Agent: Wanderlust/2.15.9 (Almost Unreal) SEMI/1.14.6 (Maruoka) FLIM/1.14.9 (=?ISO-2022-JP-2?B?R29qGyQoRCtXGyhC?=) APEL/10.8 Emacs/27 (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 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 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 > > 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 > Signed-off-by: Vitaly Rodionov > --- > 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 > + > +#include > +#include > +#include > +#include > +#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, "); > +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 > > > 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. > > 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 > Signed-off-by: Vitaly Rodionov > --- > 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 > + > +#include > +#include > +#include > +#include > +#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, "); > +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 >