Received: by 2002:a25:683:0:0:0:0:0 with SMTP id 125csp1021145ybg; Mon, 1 Jun 2020 22:25:55 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyPPV8ZKkU0TuFDsaXdN4pm+IqQfb+rTmgEmWAfT1X2pf4m8B1itepisKbuAQtfUh1yQo0e X-Received: by 2002:a17:906:7848:: with SMTP id p8mr22989653ejm.244.1591075555027; Mon, 01 Jun 2020 22:25:55 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1591075555; cv=none; d=google.com; s=arc-20160816; b=m9WZgafPoJ6D0DlJax/ow4xkk3rfzDUzDAJ0YkMKUQ/Ry5h/zMfxtoq9TjLnHcIvBj shWQ4ynNorz89hNa2C8X83UGc9yE+Xmf+80bm/jfgeidfYFnwVE9XA0dUxvzPrKOcX4x QY4vMrQRgY81jdSST0sLIeE518m7SfSHZLj6ya2//trkO7k+yjvtQEQDttaBSOwYf4hM DMaGLyVjBGRFKOTa3ZAdhLcZTckCufD1jH10dHm7SXLjpdhRRXyhUHx0VH6LLEWM53ET CsjRWTcTJTcpZAH9gz5b6oNxdXZnEeuhMOVDUvW4xuKEQGb4v1TtGC9boRQ75tiZywRF ggpw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=cdsTla5NmX/8P5iE6VvBk0hZd39sy9358Y1bN31hsbc=; b=znE7gZ9riTX8TRSjeEhRwhi2Dz25VrBfPoY4SuBXy4mMF7CNgwBTXOdkTvPtG4rYhk OJ5qUuixtwVjJBjNokiuCFXUQoCwKStYSsTyz29rvtZUsaO/cfCtNtOTZBaiNgzX7q/p sYA06H0/OqAQ7USimEv5m3GR2TasE0WUAX3WCpVE7aYXzsqDRpZDS+YzfnYq8qNM46pv x1q85BNrba2sQQGrFoZM/yGcigQLrj9V+ZrMC+DYL4el++EyBcjSnGe/aWzznYQnT+JG gYVNjv6sheUXWVD0Qzg1rhjldGhSwMo6i9Q6+gTExD6kstlL/xVKJr+5abdMwo9XMxKz Re0w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=RGaaFj0f; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id r3si897081edq.265.2020.06.01.22.25.32; Mon, 01 Jun 2020 22:25:55 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=RGaaFj0f; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726140AbgFBFWc (ORCPT + 99 others); Tue, 2 Jun 2020 01:22:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59198 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725298AbgFBFWb (ORCPT ); Tue, 2 Jun 2020 01:22:31 -0400 Received: from mail-pl1-x642.google.com (mail-pl1-x642.google.com [IPv6:2607:f8b0:4864:20::642]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C9137C061A0E for ; Mon, 1 Jun 2020 22:22:31 -0700 (PDT) Received: by mail-pl1-x642.google.com with SMTP id n9so890069plk.1 for ; Mon, 01 Jun 2020 22:22:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=cdsTla5NmX/8P5iE6VvBk0hZd39sy9358Y1bN31hsbc=; b=RGaaFj0fzD7xatRLPSaerrxb8rb16z8awh9rlm6Wd6SUIQ4K3gQrCc65bVMeeiKhRO oakwF1sTZhKXTHp++xzRtoiEem7kxx7BtSkImKivL5ElEArCpp4n/IMinXnJyllGKlKH KxPdYX4WDF2zCRqFjrrRyH/98jyeE6j1DV3GP/Exfo4FGBL/kfAkqg6x87RCLv9smU1O YHQjrHtWT5xwyLAvYVeSTBJT5uKHLPC9jHPc7+8/9UQ/85TqiQK9inO0krRlpBagBc2T A9xp+3gQzDuty6iSi9jTQO4tWQBirWwkxfRyNvTAPyR4VatRsu3MNyjv3I+lbKvDfihE +uAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=cdsTla5NmX/8P5iE6VvBk0hZd39sy9358Y1bN31hsbc=; b=OOV7uwz/3b1DiBchFFu/wuJstlKjbjc5u8weVnE+IfV0wajt/DMNPgPYgYeHJNkBqk UWbqmip2FXqJwz3gWZHeoewJdKtsrCMW+/odCchGnawNzjBGfnMmOCRXmTp2fXLmkl2Q MktJS5+Kci/T8klP2aCxW/jFDLsA+KZnBv90jFMA5tNpHnK4PcBgZeczUh0sLIzI3Fff bMOTe4EBXt+GFAKXh9coR6elc13BCIeUsT6HBJqIverGP0Gcze1NtI+Kn3vKvK+RUg3X th1dPqGH0GGXcF1EW220ulNAuSbCaW8JI4XhIlAWAd+7l1P/USWb8f76Gjj8CGUTCyOG s10Q== X-Gm-Message-State: AOAM530urFSnos2cu/vu4aZ5iWs2d9ZbeSr5z0M+ciTEPEG1XZU3P9sp j6rWfIMp6QQYAY3oItdfe0E= X-Received: by 2002:a17:90b:f0e:: with SMTP id br14mr3283764pjb.78.1591075351126; Mon, 01 Jun 2020 22:22:31 -0700 (PDT) Received: from cvds-vagarw7.iind.intel.com ([192.55.55.41]) by smtp.googlemail.com with ESMTPSA id f18sm1004764pga.75.2020.06.01.22.22.20 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 01 Jun 2020 22:22:29 -0700 (PDT) From: Vaibhav Agarwal To: Greg Kroah-Hartman , Alex Elder , Johan Hovold , Mark Greer , Takashi Iwai , Jaroslav Kysela , Mark Brown , Liam Girdwood Cc: greybus-dev@lists.linaro.org, devel@driverdev.osuosl.org, alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org, Alexandre Belloni , Vaibhav Agarwal Subject: [RESEND PATCH v1 5/6] staging: greybus: audio: Add helper APIs for dynamic audio modules Date: Tue, 2 Jun 2020 10:51:14 +0530 Message-Id: X-Mailer: git-send-email 2.26.2 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Greybus Codec driver allows modules to be dynamically added and removed, which further requires updating the DAPM configurations as well. With current snd_soc architecture, dynamic audio modules is not yet supported. This patch provides helper APIs to update DAPM configurations in response to modules which are dynamically added or removed. The source is primarily based on snd_dapm.c Signed-off-by: Vaibhav Agarwal --- drivers/staging/greybus/Makefile | 2 +- drivers/staging/greybus/audio_codec.c | 13 +- drivers/staging/greybus/audio_helper.c | 197 +++++++++++++++++++++++++ drivers/staging/greybus/audio_helper.h | 17 +++ 4 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 drivers/staging/greybus/audio_helper.c create mode 100644 drivers/staging/greybus/audio_helper.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 627e44f2a983..3b4b6cabff19 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -28,7 +28,7 @@ obj-$(CONFIG_GREYBUS_VIBRATOR) += gb-vibrator.o # Greybus Audio is a bunch of modules gb-audio-module-y := audio_module.o audio_topology.o -gb-audio-codec-y := audio_codec.o +gb-audio-codec-y := audio_codec.o audio_helper.o gb-audio-gb-y := audio_gb.o gb-audio-apbridgea-y := audio_apbridgea.o gb-audio-manager-y := audio_manager.o audio_manager_module.o diff --git a/drivers/staging/greybus/audio_codec.c b/drivers/staging/greybus/audio_codec.c index bbd072acda5c..866b3342515f 100644 --- a/drivers/staging/greybus/audio_codec.c +++ b/drivers/staging/greybus/audio_codec.c @@ -14,6 +14,7 @@ #include "audio_codec.h" #include "audio_apbridgea.h" #include "audio_manager.h" +#include "audio_helper.h" static struct gbaudio_codec_info *gbcodec; @@ -874,7 +875,7 @@ int gbaudio_register_module(struct gbaudio_module_info *module) /* card already instantiated, create widgets here only */ if (component->card->instantiated) { - snd_soc_dapm_link_component_dai_widgets(component->card, + gbaudio_dapm_link_component_dai_widgets(component->card, &component->dapm); #ifdef CONFIG_SND_JACK /* @@ -1004,19 +1005,23 @@ void gbaudio_unregister_module(struct gbaudio_module_info *module) if (module->dapm_routes) { dev_dbg(component->dev, "Removing %d routes\n", module->num_dapm_routes); + /* verify routes with controls are removed */ snd_soc_dapm_del_routes(&component->dapm, module->dapm_routes, module->num_dapm_routes); } if (module->controls) { dev_dbg(component->dev, "Removing %d controls\n", module->num_controls); - snd_soc_remove_codec_controls(component, module->controls, - module->num_controls); + /* release control semaphore */ + up_write(&card->controls_rwsem); + gbaudio_remove_component_controls(component, module->controls, + module->num_controls); + down_write(&card->controls_rwsem); } if (module->dapm_widgets) { dev_dbg(component->dev, "Removing %d widgets\n", module->num_dapm_widgets); - snd_soc_dapm_free_controls(&component->dapm, + gbaudio_dapm_free_controls(&component->dapm, module->dapm_widgets, module->num_dapm_widgets); } diff --git a/drivers/staging/greybus/audio_helper.c b/drivers/staging/greybus/audio_helper.c new file mode 100644 index 000000000000..e2f113a811ee --- /dev/null +++ b/drivers/staging/greybus/audio_helper.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Greybus Audio Sound SoC helper APIs + */ + +#include +#include +#include +#include + +#define gbaudio_dapm_for_each_direction(dir) \ + for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \ + (dir)++) + +static void gbaudio_dapm_link_dai_widget(struct snd_soc_dapm_widget *dai_w, + struct snd_soc_card *card) +{ + struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *src, *sink; + struct snd_soc_dai *dai = dai_w->priv; + + /* ...find all widgets with the same stream and link them */ + list_for_each_entry(w, &card->widgets, list) { + if (w->dapm != dai_w->dapm) + continue; + + switch (w->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + continue; + default: + break; + } + + if (!w->sname || !strstr(w->sname, dai_w->sname)) + continue; + + /* + * check if widget is already linked, + * if (w->linked) + * return; + */ + + if (dai_w->id == snd_soc_dapm_dai_in) { + src = dai_w; + sink = w; + } else { + src = w; + sink = dai_w; + } + dev_dbg(dai->dev, "%s -> %s\n", src->name, sink->name); + /* Add the DAPM path and set widget's linked status + * snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL); + * w->linked = 1; + */ + } +} + +int gbaudio_dapm_link_component_dai_widgets(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm) +{ + struct snd_soc_dapm_widget *dai_w; + + /* For each DAI widget... */ + list_for_each_entry(dai_w, &card->widgets, list) { + if (dai_w->dapm != dapm) + continue; + switch (dai_w->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + break; + default: + continue; + } + gbaudio_dapm_link_dai_widget(dai_w, card); + } + + return 0; +} + +static void gbaudio_dapm_free_path(struct snd_soc_dapm_path *path) +{ + list_del(&path->list_node[SND_SOC_DAPM_DIR_IN]); + list_del(&path->list_node[SND_SOC_DAPM_DIR_OUT]); + list_del(&path->list_kcontrol); + list_del(&path->list); + kfree(path); +} + +static void gbaudio_dapm_free_widget(struct snd_soc_dapm_widget *w) +{ + struct snd_soc_dapm_path *p, *next_p; + enum snd_soc_dapm_direction dir; + + list_del(&w->list); + /* + * remove source and sink paths associated to this widget. + * While removing the path, remove reference to it from both + * source and sink widgets so that path is removed only once. + */ + gbaudio_dapm_for_each_direction(dir) { + snd_soc_dapm_widget_for_each_path_safe(w, dir, p, next_p) + gbaudio_dapm_free_path(p); + } + + kfree(w->kcontrols); + kfree_const(w->name); + kfree_const(w->sname); + kfree(w); +} + +int gbaudio_dapm_free_controls(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget, + int num) +{ + int i; + struct snd_soc_dapm_widget *w, *next_w; +#ifdef CONFIG_DEBUG_FS + struct dentry *parent = dapm->debugfs_dapm; + struct dentry *debugfs_w = NULL; +#endif + + mutex_lock(&dapm->card->dapm_mutex); + for (i = 0; i < num; i++) { + /* below logic can be optimized to identify widget pointer */ + list_for_each_entry_safe(w, next_w, &dapm->card->widgets, + list) { + if (w->dapm != dapm) + continue; + if (!strcmp(w->name, widget->name)) + break; + w = NULL; + } + if (!w) { + dev_err(dapm->dev, "%s: widget not found\n", + widget->name); + return -EINVAL; + } + widget++; +#ifdef CONFIG_DEBUG_FS + if (!parent) + debugfs_w = debugfs_lookup(w->name, parent); + debugfs_remove(debugfs_w); + debugfs_w = NULL; +#endif + gbaudio_dapm_free_widget(w); + } + mutex_unlock(&dapm->card->dapm_mutex); + return 0; +} + +static int gbaudio_remove_controls(struct snd_card *card, struct device *dev, + const struct snd_kcontrol_new *controls, + int num_controls, const char *prefix) +{ + int i, err; + + for (i = 0; i < num_controls; i++) { + const struct snd_kcontrol_new *control = &controls[i]; + struct snd_ctl_elem_id id; + struct snd_kcontrol *kctl; + + if (prefix) + snprintf(id.name, sizeof(id.name), "%s %s", prefix, + control->name); + else + strlcpy(id.name, control->name, sizeof(id.name)); + id.numid = 0; + id.iface = control->iface; + id.device = control->device; + id.subdevice = control->subdevice; + id.index = control->index; + kctl = snd_ctl_find_id(card, &id); + if (!kctl) { + dev_err(dev, "%d: Failed to find %s\n", err, + control->name); + return -ENOENT; + } + err = snd_ctl_remove(card, kctl); + if (err < 0) { + dev_err(dev, "%d: Failed to remove %s\n", err, + control->name); + return err; + } + } + return 0; +} + +int gbaudio_remove_component_controls(struct snd_soc_component *component, + const struct snd_kcontrol_new *controls, + unsigned int num_controls) +{ + struct snd_card *card = component->card->snd_card; + + return gbaudio_remove_controls(card, component->dev, controls, + num_controls, component->name_prefix); +} diff --git a/drivers/staging/greybus/audio_helper.h b/drivers/staging/greybus/audio_helper.h new file mode 100644 index 000000000000..5cf1c6d7d3ea --- /dev/null +++ b/drivers/staging/greybus/audio_helper.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Greybus Audio Sound SoC helper APIs + */ + +#ifndef __LINUX_GBAUDIO_HELPER_H +#define __LINUX_GBAUDIO_HELPER_H + +int gbaudio_dapm_link_component_dai_widgets(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm); +int gbaudio_dapm_free_controls(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget, + int num); +int gbaudio_remove_component_controls(struct snd_soc_component *component, + const struct snd_kcontrol_new *controls, + unsigned int num_controls); +#endif -- 2.26.2