18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// soc-topology.c -- ALSA SoC Topology 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2012 Texas Instruments Inc. 68c2ecf20Sopenharmony_ci// Copyright (C) 2015 Intel Corporation. 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> 98c2ecf20Sopenharmony_ci// K, Mythri P <mythri.p.k@intel.com> 108c2ecf20Sopenharmony_ci// Prusty, Subhransu S <subhransu.s.prusty@intel.com> 118c2ecf20Sopenharmony_ci// B, Jayachandran <jayachandran.b@intel.com> 128c2ecf20Sopenharmony_ci// Abdullah, Omair M <omair.m.abdullah@intel.com> 138c2ecf20Sopenharmony_ci// Jin, Yao <yao.jin@intel.com> 148c2ecf20Sopenharmony_ci// Lin, Mengdong <mengdong.lin@intel.com> 158c2ecf20Sopenharmony_ci// 168c2ecf20Sopenharmony_ci// Add support to read audio firmware topology alongside firmware text. The 178c2ecf20Sopenharmony_ci// topology data can contain kcontrols, DAPM graphs, widgets, DAIs, DAI links, 188c2ecf20Sopenharmony_ci// equalizers, firmware, coefficients etc. 198c2ecf20Sopenharmony_ci// 208c2ecf20Sopenharmony_ci// This file only manages the core ALSA and ASoC components, all other bespoke 218c2ecf20Sopenharmony_ci// firmware topology data is passed to component drivers for bespoke handling. 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/export.h> 258c2ecf20Sopenharmony_ci#include <linux/list.h> 268c2ecf20Sopenharmony_ci#include <linux/firmware.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <sound/soc.h> 298c2ecf20Sopenharmony_ci#include <sound/soc-dapm.h> 308c2ecf20Sopenharmony_ci#include <sound/soc-topology.h> 318c2ecf20Sopenharmony_ci#include <sound/tlv.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define SOC_TPLG_MAGIC_BIG_ENDIAN 0x436F5341 /* ASoC in reverse */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* 368c2ecf20Sopenharmony_ci * We make several passes over the data (since it wont necessarily be ordered) 378c2ecf20Sopenharmony_ci * and process objects in the following order. This guarantees the component 388c2ecf20Sopenharmony_ci * drivers will be ready with any vendor data before the mixers and DAPM objects 398c2ecf20Sopenharmony_ci * are loaded (that may make use of the vendor data). 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci#define SOC_TPLG_PASS_MANIFEST 0 428c2ecf20Sopenharmony_ci#define SOC_TPLG_PASS_VENDOR 1 438c2ecf20Sopenharmony_ci#define SOC_TPLG_PASS_MIXER 2 448c2ecf20Sopenharmony_ci#define SOC_TPLG_PASS_WIDGET 3 458c2ecf20Sopenharmony_ci#define SOC_TPLG_PASS_PCM_DAI 4 468c2ecf20Sopenharmony_ci#define SOC_TPLG_PASS_GRAPH 5 478c2ecf20Sopenharmony_ci#define SOC_TPLG_PASS_PINS 6 488c2ecf20Sopenharmony_ci#define SOC_TPLG_PASS_BE_DAI 7 498c2ecf20Sopenharmony_ci#define SOC_TPLG_PASS_LINK 8 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST 528c2ecf20Sopenharmony_ci#define SOC_TPLG_PASS_END SOC_TPLG_PASS_LINK 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* topology context */ 558c2ecf20Sopenharmony_cistruct soc_tplg { 568c2ecf20Sopenharmony_ci const struct firmware *fw; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* runtime FW parsing */ 598c2ecf20Sopenharmony_ci const u8 *pos; /* read postion */ 608c2ecf20Sopenharmony_ci const u8 *hdr_pos; /* header position */ 618c2ecf20Sopenharmony_ci unsigned int pass; /* pass number */ 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* component caller */ 648c2ecf20Sopenharmony_ci struct device *dev; 658c2ecf20Sopenharmony_ci struct snd_soc_component *comp; 668c2ecf20Sopenharmony_ci u32 index; /* current block index */ 678c2ecf20Sopenharmony_ci u32 req_index; /* required index, only loaded/free matching blocks */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* vendor specific kcontrol operations */ 708c2ecf20Sopenharmony_ci const struct snd_soc_tplg_kcontrol_ops *io_ops; 718c2ecf20Sopenharmony_ci int io_ops_count; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* vendor specific bytes ext handlers, for TLV bytes controls */ 748c2ecf20Sopenharmony_ci const struct snd_soc_tplg_bytes_ext_ops *bytes_ext_ops; 758c2ecf20Sopenharmony_ci int bytes_ext_ops_count; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* optional fw loading callbacks to component drivers */ 788c2ecf20Sopenharmony_ci struct snd_soc_tplg_ops *ops; 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int soc_tplg_process_headers(struct soc_tplg *tplg); 828c2ecf20Sopenharmony_cistatic void soc_tplg_complete(struct soc_tplg *tplg); 838c2ecf20Sopenharmony_cistatic void soc_tplg_denum_remove_texts(struct soc_enum *se); 848c2ecf20Sopenharmony_cistatic void soc_tplg_denum_remove_values(struct soc_enum *se); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* check we dont overflow the data for this control chunk */ 878c2ecf20Sopenharmony_cistatic int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size, 888c2ecf20Sopenharmony_ci unsigned int count, size_t bytes, const char *elem_type) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci const u8 *end = tplg->pos + elem_size * count; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (end > tplg->fw->data + tplg->fw->size) { 938c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: %s overflow end of data\n", 948c2ecf20Sopenharmony_ci elem_type); 958c2ecf20Sopenharmony_ci return -EINVAL; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* check there is enough room in chunk for control. 998c2ecf20Sopenharmony_ci extra bytes at the end of control are for vendor data here */ 1008c2ecf20Sopenharmony_ci if (elem_size * count > bytes) { 1018c2ecf20Sopenharmony_ci dev_err(tplg->dev, 1028c2ecf20Sopenharmony_ci "ASoC: %s count %d of size %zu is bigger than chunk %zu\n", 1038c2ecf20Sopenharmony_ci elem_type, count, elem_size, bytes); 1048c2ecf20Sopenharmony_ci return -EINVAL; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic inline int soc_tplg_is_eof(struct soc_tplg *tplg) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci const u8 *end = tplg->hdr_pos; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (end >= tplg->fw->data + tplg->fw->size) 1158c2ecf20Sopenharmony_ci return 1; 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic inline unsigned long soc_tplg_get_hdr_offset(struct soc_tplg *tplg) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci return (unsigned long)(tplg->hdr_pos - tplg->fw->data); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic inline unsigned long soc_tplg_get_offset(struct soc_tplg *tplg) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci return (unsigned long)(tplg->pos - tplg->fw->data); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* mapping of Kcontrol types and associated operations. */ 1308c2ecf20Sopenharmony_cistatic const struct snd_soc_tplg_kcontrol_ops io_ops[] = { 1318c2ecf20Sopenharmony_ci {SND_SOC_TPLG_CTL_VOLSW, snd_soc_get_volsw, 1328c2ecf20Sopenharmony_ci snd_soc_put_volsw, snd_soc_info_volsw}, 1338c2ecf20Sopenharmony_ci {SND_SOC_TPLG_CTL_VOLSW_SX, snd_soc_get_volsw_sx, 1348c2ecf20Sopenharmony_ci snd_soc_put_volsw_sx, NULL}, 1358c2ecf20Sopenharmony_ci {SND_SOC_TPLG_CTL_ENUM, snd_soc_get_enum_double, 1368c2ecf20Sopenharmony_ci snd_soc_put_enum_double, snd_soc_info_enum_double}, 1378c2ecf20Sopenharmony_ci {SND_SOC_TPLG_CTL_ENUM_VALUE, snd_soc_get_enum_double, 1388c2ecf20Sopenharmony_ci snd_soc_put_enum_double, NULL}, 1398c2ecf20Sopenharmony_ci {SND_SOC_TPLG_CTL_BYTES, snd_soc_bytes_get, 1408c2ecf20Sopenharmony_ci snd_soc_bytes_put, snd_soc_bytes_info}, 1418c2ecf20Sopenharmony_ci {SND_SOC_TPLG_CTL_RANGE, snd_soc_get_volsw_range, 1428c2ecf20Sopenharmony_ci snd_soc_put_volsw_range, snd_soc_info_volsw_range}, 1438c2ecf20Sopenharmony_ci {SND_SOC_TPLG_CTL_VOLSW_XR_SX, snd_soc_get_xr_sx, 1448c2ecf20Sopenharmony_ci snd_soc_put_xr_sx, snd_soc_info_xr_sx}, 1458c2ecf20Sopenharmony_ci {SND_SOC_TPLG_CTL_STROBE, snd_soc_get_strobe, 1468c2ecf20Sopenharmony_ci snd_soc_put_strobe, NULL}, 1478c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_CTL_VOLSW, snd_soc_dapm_get_volsw, 1488c2ecf20Sopenharmony_ci snd_soc_dapm_put_volsw, snd_soc_info_volsw}, 1498c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE, snd_soc_dapm_get_enum_double, 1508c2ecf20Sopenharmony_ci snd_soc_dapm_put_enum_double, snd_soc_info_enum_double}, 1518c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT, snd_soc_dapm_get_enum_double, 1528c2ecf20Sopenharmony_ci snd_soc_dapm_put_enum_double, NULL}, 1538c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE, snd_soc_dapm_get_enum_double, 1548c2ecf20Sopenharmony_ci snd_soc_dapm_put_enum_double, NULL}, 1558c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_CTL_PIN, snd_soc_dapm_get_pin_switch, 1568c2ecf20Sopenharmony_ci snd_soc_dapm_put_pin_switch, snd_soc_dapm_info_pin_switch}, 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistruct soc_tplg_map { 1608c2ecf20Sopenharmony_ci int uid; 1618c2ecf20Sopenharmony_ci int kid; 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* mapping of widget types from UAPI IDs to kernel IDs */ 1658c2ecf20Sopenharmony_cistatic const struct soc_tplg_map dapm_map[] = { 1668c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_INPUT, snd_soc_dapm_input}, 1678c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_OUTPUT, snd_soc_dapm_output}, 1688c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_MUX, snd_soc_dapm_mux}, 1698c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_MIXER, snd_soc_dapm_mixer}, 1708c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_PGA, snd_soc_dapm_pga}, 1718c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_OUT_DRV, snd_soc_dapm_out_drv}, 1728c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_ADC, snd_soc_dapm_adc}, 1738c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_DAC, snd_soc_dapm_dac}, 1748c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_SWITCH, snd_soc_dapm_switch}, 1758c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_PRE, snd_soc_dapm_pre}, 1768c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_POST, snd_soc_dapm_post}, 1778c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_AIF_IN, snd_soc_dapm_aif_in}, 1788c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_AIF_OUT, snd_soc_dapm_aif_out}, 1798c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_DAI_IN, snd_soc_dapm_dai_in}, 1808c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_DAI_OUT, snd_soc_dapm_dai_out}, 1818c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_DAI_LINK, snd_soc_dapm_dai_link}, 1828c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_BUFFER, snd_soc_dapm_buffer}, 1838c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_SCHEDULER, snd_soc_dapm_scheduler}, 1848c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_EFFECT, snd_soc_dapm_effect}, 1858c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_SIGGEN, snd_soc_dapm_siggen}, 1868c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_SRC, snd_soc_dapm_src}, 1878c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_ASRC, snd_soc_dapm_asrc}, 1888c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_ENCODER, snd_soc_dapm_encoder}, 1898c2ecf20Sopenharmony_ci {SND_SOC_TPLG_DAPM_DECODER, snd_soc_dapm_decoder}, 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int tplc_chan_get_reg(struct soc_tplg *tplg, 1938c2ecf20Sopenharmony_ci struct snd_soc_tplg_channel *chan, int map) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci int i; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) { 1988c2ecf20Sopenharmony_ci if (le32_to_cpu(chan[i].id) == map) 1998c2ecf20Sopenharmony_ci return le32_to_cpu(chan[i].reg); 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return -EINVAL; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int tplc_chan_get_shift(struct soc_tplg *tplg, 2068c2ecf20Sopenharmony_ci struct snd_soc_tplg_channel *chan, int map) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int i; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) { 2118c2ecf20Sopenharmony_ci if (le32_to_cpu(chan[i].id) == map) 2128c2ecf20Sopenharmony_ci return le32_to_cpu(chan[i].shift); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return -EINVAL; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int get_widget_id(int tplg_type) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci int i; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dapm_map); i++) { 2238c2ecf20Sopenharmony_ci if (tplg_type == dapm_map[i].uid) 2248c2ecf20Sopenharmony_ci return dapm_map[i].kid; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return -EINVAL; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic inline void soc_bind_err(struct soc_tplg *tplg, 2318c2ecf20Sopenharmony_ci struct snd_soc_tplg_ctl_hdr *hdr, int index) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci dev_err(tplg->dev, 2348c2ecf20Sopenharmony_ci "ASoC: invalid control type (g,p,i) %d:%d:%d index %d at 0x%lx\n", 2358c2ecf20Sopenharmony_ci hdr->ops.get, hdr->ops.put, hdr->ops.info, index, 2368c2ecf20Sopenharmony_ci soc_tplg_get_offset(tplg)); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic inline void soc_control_err(struct soc_tplg *tplg, 2408c2ecf20Sopenharmony_ci struct snd_soc_tplg_ctl_hdr *hdr, const char *name) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci dev_err(tplg->dev, 2438c2ecf20Sopenharmony_ci "ASoC: no complete mixer IO handler for %s type (g,p,i) %d:%d:%d at 0x%lx\n", 2448c2ecf20Sopenharmony_ci name, hdr->ops.get, hdr->ops.put, hdr->ops.info, 2458c2ecf20Sopenharmony_ci soc_tplg_get_offset(tplg)); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* pass vendor data to component driver for processing */ 2498c2ecf20Sopenharmony_cistatic int soc_tplg_vendor_load(struct soc_tplg *tplg, 2508c2ecf20Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci int ret = 0; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (tplg->ops && tplg->ops->vendor_load) 2558c2ecf20Sopenharmony_ci ret = tplg->ops->vendor_load(tplg->comp, tplg->index, hdr); 2568c2ecf20Sopenharmony_ci else { 2578c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n", 2588c2ecf20Sopenharmony_ci hdr->vendor_type); 2598c2ecf20Sopenharmony_ci return -EINVAL; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (ret < 0) 2638c2ecf20Sopenharmony_ci dev_err(tplg->dev, 2648c2ecf20Sopenharmony_ci "ASoC: vendor load failed at hdr offset %ld/0x%lx for type %d:%d\n", 2658c2ecf20Sopenharmony_ci soc_tplg_get_hdr_offset(tplg), 2668c2ecf20Sopenharmony_ci soc_tplg_get_hdr_offset(tplg), 2678c2ecf20Sopenharmony_ci hdr->type, hdr->vendor_type); 2688c2ecf20Sopenharmony_ci return ret; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/* optionally pass new dynamic widget to component driver. This is mainly for 2728c2ecf20Sopenharmony_ci * external widgets where we can assign private data/ops */ 2738c2ecf20Sopenharmony_cistatic int soc_tplg_widget_load(struct soc_tplg *tplg, 2748c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci if (tplg->ops && tplg->ops->widget_load) 2778c2ecf20Sopenharmony_ci return tplg->ops->widget_load(tplg->comp, tplg->index, w, 2788c2ecf20Sopenharmony_ci tplg_w); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* optionally pass new dynamic widget to component driver. This is mainly for 2848c2ecf20Sopenharmony_ci * external widgets where we can assign private data/ops */ 2858c2ecf20Sopenharmony_cistatic int soc_tplg_widget_ready(struct soc_tplg *tplg, 2868c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci if (tplg->ops && tplg->ops->widget_ready) 2898c2ecf20Sopenharmony_ci return tplg->ops->widget_ready(tplg->comp, tplg->index, w, 2908c2ecf20Sopenharmony_ci tplg_w); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/* pass DAI configurations to component driver for extra initialization */ 2968c2ecf20Sopenharmony_cistatic int soc_tplg_dai_load(struct soc_tplg *tplg, 2978c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *dai_drv, 2988c2ecf20Sopenharmony_ci struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci if (tplg->ops && tplg->ops->dai_load) 3018c2ecf20Sopenharmony_ci return tplg->ops->dai_load(tplg->comp, tplg->index, dai_drv, 3028c2ecf20Sopenharmony_ci pcm, dai); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci/* pass link configurations to component driver for extra initialization */ 3088c2ecf20Sopenharmony_cistatic int soc_tplg_dai_link_load(struct soc_tplg *tplg, 3098c2ecf20Sopenharmony_ci struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci if (tplg->ops && tplg->ops->link_load) 3128c2ecf20Sopenharmony_ci return tplg->ops->link_load(tplg->comp, tplg->index, link, cfg); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci/* tell the component driver that all firmware has been loaded in this request */ 3188c2ecf20Sopenharmony_cistatic void soc_tplg_complete(struct soc_tplg *tplg) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci if (tplg->ops && tplg->ops->complete) 3218c2ecf20Sopenharmony_ci tplg->ops->complete(tplg->comp); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci/* add a dynamic kcontrol */ 3258c2ecf20Sopenharmony_cistatic int soc_tplg_add_dcontrol(struct snd_card *card, struct device *dev, 3268c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *control_new, const char *prefix, 3278c2ecf20Sopenharmony_ci void *data, struct snd_kcontrol **kcontrol) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci int err; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci *kcontrol = snd_soc_cnew(control_new, data, control_new->name, prefix); 3328c2ecf20Sopenharmony_ci if (*kcontrol == NULL) { 3338c2ecf20Sopenharmony_ci dev_err(dev, "ASoC: Failed to create new kcontrol %s\n", 3348c2ecf20Sopenharmony_ci control_new->name); 3358c2ecf20Sopenharmony_ci return -ENOMEM; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci err = snd_ctl_add(card, *kcontrol); 3398c2ecf20Sopenharmony_ci if (err < 0) { 3408c2ecf20Sopenharmony_ci dev_err(dev, "ASoC: Failed to add %s: %d\n", 3418c2ecf20Sopenharmony_ci control_new->name, err); 3428c2ecf20Sopenharmony_ci return err; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci/* add a dynamic kcontrol for component driver */ 3498c2ecf20Sopenharmony_cistatic int soc_tplg_add_kcontrol(struct soc_tplg *tplg, 3508c2ecf20Sopenharmony_ci struct snd_kcontrol_new *k, struct snd_kcontrol **kcontrol) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct snd_soc_component *comp = tplg->comp; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return soc_tplg_add_dcontrol(comp->card->snd_card, 3558c2ecf20Sopenharmony_ci comp->dev, k, comp->name_prefix, comp, kcontrol); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/* remove a mixer kcontrol */ 3598c2ecf20Sopenharmony_cistatic void remove_mixer(struct snd_soc_component *comp, 3608c2ecf20Sopenharmony_ci struct snd_soc_dobj *dobj, int pass) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct snd_card *card = comp->card->snd_card; 3638c2ecf20Sopenharmony_ci struct soc_mixer_control *sm = 3648c2ecf20Sopenharmony_ci container_of(dobj, struct soc_mixer_control, dobj); 3658c2ecf20Sopenharmony_ci const unsigned int *p = NULL; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (pass != SOC_TPLG_PASS_MIXER) 3688c2ecf20Sopenharmony_ci return; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (dobj->ops && dobj->ops->control_unload) 3718c2ecf20Sopenharmony_ci dobj->ops->control_unload(comp, dobj); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (dobj->control.kcontrol->tlv.p) 3748c2ecf20Sopenharmony_ci p = dobj->control.kcontrol->tlv.p; 3758c2ecf20Sopenharmony_ci snd_ctl_remove(card, dobj->control.kcontrol); 3768c2ecf20Sopenharmony_ci list_del(&dobj->list); 3778c2ecf20Sopenharmony_ci kfree(sm); 3788c2ecf20Sopenharmony_ci kfree(p); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/* remove an enum kcontrol */ 3828c2ecf20Sopenharmony_cistatic void remove_enum(struct snd_soc_component *comp, 3838c2ecf20Sopenharmony_ci struct snd_soc_dobj *dobj, int pass) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct snd_card *card = comp->card->snd_card; 3868c2ecf20Sopenharmony_ci struct soc_enum *se = container_of(dobj, struct soc_enum, dobj); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (pass != SOC_TPLG_PASS_MIXER) 3898c2ecf20Sopenharmony_ci return; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (dobj->ops && dobj->ops->control_unload) 3928c2ecf20Sopenharmony_ci dobj->ops->control_unload(comp, dobj); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci snd_ctl_remove(card, dobj->control.kcontrol); 3958c2ecf20Sopenharmony_ci list_del(&dobj->list); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci soc_tplg_denum_remove_values(se); 3988c2ecf20Sopenharmony_ci soc_tplg_denum_remove_texts(se); 3998c2ecf20Sopenharmony_ci kfree(se); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci/* remove a byte kcontrol */ 4038c2ecf20Sopenharmony_cistatic void remove_bytes(struct snd_soc_component *comp, 4048c2ecf20Sopenharmony_ci struct snd_soc_dobj *dobj, int pass) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct snd_card *card = comp->card->snd_card; 4078c2ecf20Sopenharmony_ci struct soc_bytes_ext *sb = 4088c2ecf20Sopenharmony_ci container_of(dobj, struct soc_bytes_ext, dobj); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (pass != SOC_TPLG_PASS_MIXER) 4118c2ecf20Sopenharmony_ci return; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (dobj->ops && dobj->ops->control_unload) 4148c2ecf20Sopenharmony_ci dobj->ops->control_unload(comp, dobj); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci snd_ctl_remove(card, dobj->control.kcontrol); 4178c2ecf20Sopenharmony_ci list_del(&dobj->list); 4188c2ecf20Sopenharmony_ci kfree(sb); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/* remove a route */ 4228c2ecf20Sopenharmony_cistatic void remove_route(struct snd_soc_component *comp, 4238c2ecf20Sopenharmony_ci struct snd_soc_dobj *dobj, int pass) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct snd_soc_dapm_route *route = 4268c2ecf20Sopenharmony_ci container_of(dobj, struct snd_soc_dapm_route, dobj); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (pass != SOC_TPLG_PASS_GRAPH) 4298c2ecf20Sopenharmony_ci return; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (dobj->ops && dobj->ops->dapm_route_unload) 4328c2ecf20Sopenharmony_ci dobj->ops->dapm_route_unload(comp, dobj); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci list_del(&dobj->list); 4358c2ecf20Sopenharmony_ci kfree(route); 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci/* remove a widget and it's kcontrols - routes must be removed first */ 4398c2ecf20Sopenharmony_cistatic void remove_widget(struct snd_soc_component *comp, 4408c2ecf20Sopenharmony_ci struct snd_soc_dobj *dobj, int pass) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct snd_card *card = comp->card->snd_card; 4438c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w = 4448c2ecf20Sopenharmony_ci container_of(dobj, struct snd_soc_dapm_widget, dobj); 4458c2ecf20Sopenharmony_ci int i; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (pass != SOC_TPLG_PASS_WIDGET) 4488c2ecf20Sopenharmony_ci return; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (dobj->ops && dobj->ops->widget_unload) 4518c2ecf20Sopenharmony_ci dobj->ops->widget_unload(comp, dobj); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (!w->kcontrols) 4548c2ecf20Sopenharmony_ci goto free_news; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* 4578c2ecf20Sopenharmony_ci * Dynamic Widgets either have 1..N enum kcontrols or mixers. 4588c2ecf20Sopenharmony_ci * The enum may either have an array of values or strings. 4598c2ecf20Sopenharmony_ci */ 4608c2ecf20Sopenharmony_ci if (dobj->widget.kcontrol_type == SND_SOC_TPLG_TYPE_ENUM) { 4618c2ecf20Sopenharmony_ci /* enumerated widget mixer */ 4628c2ecf20Sopenharmony_ci for (i = 0; w->kcontrols != NULL && i < w->num_kcontrols; i++) { 4638c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol = w->kcontrols[i]; 4648c2ecf20Sopenharmony_ci struct soc_enum *se = 4658c2ecf20Sopenharmony_ci (struct soc_enum *)kcontrol->private_value; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci snd_ctl_remove(card, kcontrol); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* free enum kcontrol's dvalues and dtexts */ 4708c2ecf20Sopenharmony_ci soc_tplg_denum_remove_values(se); 4718c2ecf20Sopenharmony_ci soc_tplg_denum_remove_texts(se); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci kfree(se); 4748c2ecf20Sopenharmony_ci kfree(w->kcontrol_news[i].name); 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci } else { 4778c2ecf20Sopenharmony_ci /* volume mixer or bytes controls */ 4788c2ecf20Sopenharmony_ci for (i = 0; w->kcontrols != NULL && i < w->num_kcontrols; i++) { 4798c2ecf20Sopenharmony_ci struct snd_kcontrol *kcontrol = w->kcontrols[i]; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (dobj->widget.kcontrol_type 4828c2ecf20Sopenharmony_ci == SND_SOC_TPLG_TYPE_MIXER) 4838c2ecf20Sopenharmony_ci kfree(kcontrol->tlv.p); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* Private value is used as struct soc_mixer_control 4868c2ecf20Sopenharmony_ci * for volume mixers or soc_bytes_ext for bytes 4878c2ecf20Sopenharmony_ci * controls. 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci kfree((void *)kcontrol->private_value); 4908c2ecf20Sopenharmony_ci snd_ctl_remove(card, kcontrol); 4918c2ecf20Sopenharmony_ci kfree(w->kcontrol_news[i].name); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cifree_news: 4968c2ecf20Sopenharmony_ci kfree(w->kcontrol_news); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci list_del(&dobj->list); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* widget w is freed by soc-dapm.c */ 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/* remove DAI configurations */ 5048c2ecf20Sopenharmony_cistatic void remove_dai(struct snd_soc_component *comp, 5058c2ecf20Sopenharmony_ci struct snd_soc_dobj *dobj, int pass) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *dai_drv = 5088c2ecf20Sopenharmony_ci container_of(dobj, struct snd_soc_dai_driver, dobj); 5098c2ecf20Sopenharmony_ci struct snd_soc_dai *dai, *_dai; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (pass != SOC_TPLG_PASS_PCM_DAI) 5128c2ecf20Sopenharmony_ci return; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (dobj->ops && dobj->ops->dai_unload) 5158c2ecf20Sopenharmony_ci dobj->ops->dai_unload(comp, dobj); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci for_each_component_dais_safe(comp, dai, _dai) 5188c2ecf20Sopenharmony_ci if (dai->driver == dai_drv) 5198c2ecf20Sopenharmony_ci snd_soc_unregister_dai(dai); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci kfree(dai_drv->playback.stream_name); 5228c2ecf20Sopenharmony_ci kfree(dai_drv->capture.stream_name); 5238c2ecf20Sopenharmony_ci kfree(dai_drv->name); 5248c2ecf20Sopenharmony_ci list_del(&dobj->list); 5258c2ecf20Sopenharmony_ci kfree(dai_drv); 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci/* remove link configurations */ 5298c2ecf20Sopenharmony_cistatic void remove_link(struct snd_soc_component *comp, 5308c2ecf20Sopenharmony_ci struct snd_soc_dobj *dobj, int pass) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct snd_soc_dai_link *link = 5338c2ecf20Sopenharmony_ci container_of(dobj, struct snd_soc_dai_link, dobj); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (pass != SOC_TPLG_PASS_PCM_DAI) 5368c2ecf20Sopenharmony_ci return; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (dobj->ops && dobj->ops->link_unload) 5398c2ecf20Sopenharmony_ci dobj->ops->link_unload(comp, dobj); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci list_del(&dobj->list); 5428c2ecf20Sopenharmony_ci snd_soc_remove_pcm_runtime(comp->card, 5438c2ecf20Sopenharmony_ci snd_soc_get_pcm_runtime(comp->card, link)); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci kfree(link->name); 5468c2ecf20Sopenharmony_ci kfree(link->stream_name); 5478c2ecf20Sopenharmony_ci kfree(link->cpus->dai_name); 5488c2ecf20Sopenharmony_ci kfree(link); 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci/* unload dai link */ 5528c2ecf20Sopenharmony_cistatic void remove_backend_link(struct snd_soc_component *comp, 5538c2ecf20Sopenharmony_ci struct snd_soc_dobj *dobj, int pass) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci if (pass != SOC_TPLG_PASS_LINK) 5568c2ecf20Sopenharmony_ci return; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (dobj->ops && dobj->ops->link_unload) 5598c2ecf20Sopenharmony_ci dobj->ops->link_unload(comp, dobj); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* 5628c2ecf20Sopenharmony_ci * We don't free the link here as what remove_link() do since BE 5638c2ecf20Sopenharmony_ci * links are not allocated by topology. 5648c2ecf20Sopenharmony_ci * We however need to reset the dobj type to its initial values 5658c2ecf20Sopenharmony_ci */ 5668c2ecf20Sopenharmony_ci dobj->type = SND_SOC_DOBJ_NONE; 5678c2ecf20Sopenharmony_ci list_del(&dobj->list); 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci/* bind a kcontrol to it's IO handlers */ 5718c2ecf20Sopenharmony_cistatic int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, 5728c2ecf20Sopenharmony_ci struct snd_kcontrol_new *k, 5738c2ecf20Sopenharmony_ci const struct soc_tplg *tplg) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci const struct snd_soc_tplg_kcontrol_ops *ops; 5768c2ecf20Sopenharmony_ci const struct snd_soc_tplg_bytes_ext_ops *ext_ops; 5778c2ecf20Sopenharmony_ci int num_ops, i; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (le32_to_cpu(hdr->ops.info) == SND_SOC_TPLG_CTL_BYTES 5808c2ecf20Sopenharmony_ci && k->iface & SNDRV_CTL_ELEM_IFACE_MIXER 5818c2ecf20Sopenharmony_ci && (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ 5828c2ecf20Sopenharmony_ci || k->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) 5838c2ecf20Sopenharmony_ci && k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { 5848c2ecf20Sopenharmony_ci struct soc_bytes_ext *sbe; 5858c2ecf20Sopenharmony_ci struct snd_soc_tplg_bytes_control *be; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci sbe = (struct soc_bytes_ext *)k->private_value; 5888c2ecf20Sopenharmony_ci be = container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* TLV bytes controls need standard kcontrol info handler, 5918c2ecf20Sopenharmony_ci * TLV callback and extended put/get handlers. 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_ci k->info = snd_soc_bytes_info_ext; 5948c2ecf20Sopenharmony_ci k->tlv.c = snd_soc_bytes_tlv_callback; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* 5978c2ecf20Sopenharmony_ci * When a topology-based implementation abuses the 5988c2ecf20Sopenharmony_ci * control interface and uses bytes_ext controls of 5998c2ecf20Sopenharmony_ci * more than 512 bytes, we need to disable the size 6008c2ecf20Sopenharmony_ci * checks, otherwise accesses to such controls will 6018c2ecf20Sopenharmony_ci * return an -EINVAL error and prevent the card from 6028c2ecf20Sopenharmony_ci * being configured. 6038c2ecf20Sopenharmony_ci */ 6048c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_CTL_VALIDATION) && sbe->max > 512) 6058c2ecf20Sopenharmony_ci k->access |= SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci ext_ops = tplg->bytes_ext_ops; 6088c2ecf20Sopenharmony_ci num_ops = tplg->bytes_ext_ops_count; 6098c2ecf20Sopenharmony_ci for (i = 0; i < num_ops; i++) { 6108c2ecf20Sopenharmony_ci if (!sbe->put && 6118c2ecf20Sopenharmony_ci ext_ops[i].id == le32_to_cpu(be->ext_ops.put)) 6128c2ecf20Sopenharmony_ci sbe->put = ext_ops[i].put; 6138c2ecf20Sopenharmony_ci if (!sbe->get && 6148c2ecf20Sopenharmony_ci ext_ops[i].id == le32_to_cpu(be->ext_ops.get)) 6158c2ecf20Sopenharmony_ci sbe->get = ext_ops[i].get; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) && !sbe->get) 6198c2ecf20Sopenharmony_ci return -EINVAL; 6208c2ecf20Sopenharmony_ci if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) && !sbe->put) 6218c2ecf20Sopenharmony_ci return -EINVAL; 6228c2ecf20Sopenharmony_ci return 0; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* try and map vendor specific kcontrol handlers first */ 6268c2ecf20Sopenharmony_ci ops = tplg->io_ops; 6278c2ecf20Sopenharmony_ci num_ops = tplg->io_ops_count; 6288c2ecf20Sopenharmony_ci for (i = 0; i < num_ops; i++) { 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (k->put == NULL && ops[i].id == le32_to_cpu(hdr->ops.put)) 6318c2ecf20Sopenharmony_ci k->put = ops[i].put; 6328c2ecf20Sopenharmony_ci if (k->get == NULL && ops[i].id == le32_to_cpu(hdr->ops.get)) 6338c2ecf20Sopenharmony_ci k->get = ops[i].get; 6348c2ecf20Sopenharmony_ci if (k->info == NULL && ops[i].id == le32_to_cpu(hdr->ops.info)) 6358c2ecf20Sopenharmony_ci k->info = ops[i].info; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci /* vendor specific handlers found ? */ 6398c2ecf20Sopenharmony_ci if (k->put && k->get && k->info) 6408c2ecf20Sopenharmony_ci return 0; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* none found so try standard kcontrol handlers */ 6438c2ecf20Sopenharmony_ci ops = io_ops; 6448c2ecf20Sopenharmony_ci num_ops = ARRAY_SIZE(io_ops); 6458c2ecf20Sopenharmony_ci for (i = 0; i < num_ops; i++) { 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (k->put == NULL && ops[i].id == le32_to_cpu(hdr->ops.put)) 6488c2ecf20Sopenharmony_ci k->put = ops[i].put; 6498c2ecf20Sopenharmony_ci if (k->get == NULL && ops[i].id == le32_to_cpu(hdr->ops.get)) 6508c2ecf20Sopenharmony_ci k->get = ops[i].get; 6518c2ecf20Sopenharmony_ci if (k->info == NULL && ops[i].id == le32_to_cpu(hdr->ops.info)) 6528c2ecf20Sopenharmony_ci k->info = ops[i].info; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* standard handlers found ? */ 6568c2ecf20Sopenharmony_ci if (k->put && k->get && k->info) 6578c2ecf20Sopenharmony_ci return 0; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* nothing to bind */ 6608c2ecf20Sopenharmony_ci return -EINVAL; 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci/* bind a widgets to it's evnt handlers */ 6648c2ecf20Sopenharmony_ciint snd_soc_tplg_widget_bind_event(struct snd_soc_dapm_widget *w, 6658c2ecf20Sopenharmony_ci const struct snd_soc_tplg_widget_events *events, 6668c2ecf20Sopenharmony_ci int num_events, u16 event_type) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci int i; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci w->event = NULL; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci for (i = 0; i < num_events; i++) { 6738c2ecf20Sopenharmony_ci if (event_type == events[i].type) { 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* found - so assign event */ 6768c2ecf20Sopenharmony_ci w->event = events[i].event_handler; 6778c2ecf20Sopenharmony_ci return 0; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci /* not found */ 6828c2ecf20Sopenharmony_ci return -EINVAL; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_tplg_widget_bind_event); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci/* optionally pass new dynamic kcontrol to component driver. */ 6878c2ecf20Sopenharmony_cistatic int soc_tplg_init_kcontrol(struct soc_tplg *tplg, 6888c2ecf20Sopenharmony_ci struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci if (tplg->ops && tplg->ops->control_load) 6918c2ecf20Sopenharmony_ci return tplg->ops->control_load(tplg->comp, tplg->index, k, 6928c2ecf20Sopenharmony_ci hdr); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci return 0; 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic int soc_tplg_create_tlv_db_scale(struct soc_tplg *tplg, 6998c2ecf20Sopenharmony_ci struct snd_kcontrol_new *kc, struct snd_soc_tplg_tlv_dbscale *scale) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci unsigned int item_len = 2 * sizeof(unsigned int); 7028c2ecf20Sopenharmony_ci unsigned int *p; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci p = kzalloc(item_len + 2 * sizeof(unsigned int), GFP_KERNEL); 7058c2ecf20Sopenharmony_ci if (!p) 7068c2ecf20Sopenharmony_ci return -ENOMEM; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci p[0] = SNDRV_CTL_TLVT_DB_SCALE; 7098c2ecf20Sopenharmony_ci p[1] = item_len; 7108c2ecf20Sopenharmony_ci p[2] = le32_to_cpu(scale->min); 7118c2ecf20Sopenharmony_ci p[3] = (le32_to_cpu(scale->step) & TLV_DB_SCALE_MASK) 7128c2ecf20Sopenharmony_ci | (le32_to_cpu(scale->mute) ? TLV_DB_SCALE_MUTE : 0); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci kc->tlv.p = (void *)p; 7158c2ecf20Sopenharmony_ci return 0; 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic int soc_tplg_create_tlv(struct soc_tplg *tplg, 7198c2ecf20Sopenharmony_ci struct snd_kcontrol_new *kc, struct snd_soc_tplg_ctl_hdr *tc) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci struct snd_soc_tplg_ctl_tlv *tplg_tlv; 7228c2ecf20Sopenharmony_ci u32 access = le32_to_cpu(tc->access); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (!(access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)) 7258c2ecf20Sopenharmony_ci return 0; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (!(access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) { 7288c2ecf20Sopenharmony_ci tplg_tlv = &tc->tlv; 7298c2ecf20Sopenharmony_ci switch (le32_to_cpu(tplg_tlv->type)) { 7308c2ecf20Sopenharmony_ci case SNDRV_CTL_TLVT_DB_SCALE: 7318c2ecf20Sopenharmony_ci return soc_tplg_create_tlv_db_scale(tplg, kc, 7328c2ecf20Sopenharmony_ci &tplg_tlv->scale); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci /* TODO: add support for other TLV types */ 7358c2ecf20Sopenharmony_ci default: 7368c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, "Unsupported TLV type %d\n", 7378c2ecf20Sopenharmony_ci tplg_tlv->type); 7388c2ecf20Sopenharmony_ci return -EINVAL; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci return 0; 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_cistatic inline void soc_tplg_free_tlv(struct soc_tplg *tplg, 7468c2ecf20Sopenharmony_ci struct snd_kcontrol_new *kc) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci kfree(kc->tlv.p); 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count, 7528c2ecf20Sopenharmony_ci size_t size) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct snd_soc_tplg_bytes_control *be; 7558c2ecf20Sopenharmony_ci struct soc_bytes_ext *sbe; 7568c2ecf20Sopenharmony_ci struct snd_kcontrol_new kc; 7578c2ecf20Sopenharmony_ci int i; 7588c2ecf20Sopenharmony_ci int err = 0; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if (soc_tplg_check_elem_count(tplg, 7618c2ecf20Sopenharmony_ci sizeof(struct snd_soc_tplg_bytes_control), count, 7628c2ecf20Sopenharmony_ci size, "mixer bytes")) { 7638c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: Invalid count %d for byte control\n", 7648c2ecf20Sopenharmony_ci count); 7658c2ecf20Sopenharmony_ci return -EINVAL; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 7698c2ecf20Sopenharmony_ci be = (struct snd_soc_tplg_bytes_control *)tplg->pos; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* validate kcontrol */ 7728c2ecf20Sopenharmony_ci if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 7738c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 7748c2ecf20Sopenharmony_ci return -EINVAL; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci sbe = kzalloc(sizeof(*sbe), GFP_KERNEL); 7778c2ecf20Sopenharmony_ci if (sbe == NULL) 7788c2ecf20Sopenharmony_ci return -ENOMEM; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + 7818c2ecf20Sopenharmony_ci le32_to_cpu(be->priv.size)); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, 7848c2ecf20Sopenharmony_ci "ASoC: adding bytes kcontrol %s with access 0x%x\n", 7858c2ecf20Sopenharmony_ci be->hdr.name, be->hdr.access); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci memset(&kc, 0, sizeof(kc)); 7888c2ecf20Sopenharmony_ci kc.name = be->hdr.name; 7898c2ecf20Sopenharmony_ci kc.private_value = (long)sbe; 7908c2ecf20Sopenharmony_ci kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 7918c2ecf20Sopenharmony_ci kc.access = le32_to_cpu(be->hdr.access); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci sbe->max = le32_to_cpu(be->max); 7948c2ecf20Sopenharmony_ci sbe->dobj.type = SND_SOC_DOBJ_BYTES; 7958c2ecf20Sopenharmony_ci sbe->dobj.ops = tplg->ops; 7968c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sbe->dobj.list); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* map io handlers */ 7998c2ecf20Sopenharmony_ci err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg); 8008c2ecf20Sopenharmony_ci if (err) { 8018c2ecf20Sopenharmony_ci soc_control_err(tplg, &be->hdr, be->hdr.name); 8028c2ecf20Sopenharmony_ci kfree(sbe); 8038c2ecf20Sopenharmony_ci break; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* pass control to driver for optional further init */ 8078c2ecf20Sopenharmony_ci err = soc_tplg_init_kcontrol(tplg, &kc, 8088c2ecf20Sopenharmony_ci (struct snd_soc_tplg_ctl_hdr *)be); 8098c2ecf20Sopenharmony_ci if (err < 0) { 8108c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to init %s\n", 8118c2ecf20Sopenharmony_ci be->hdr.name); 8128c2ecf20Sopenharmony_ci kfree(sbe); 8138c2ecf20Sopenharmony_ci break; 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci /* register control here */ 8178c2ecf20Sopenharmony_ci err = soc_tplg_add_kcontrol(tplg, &kc, 8188c2ecf20Sopenharmony_ci &sbe->dobj.control.kcontrol); 8198c2ecf20Sopenharmony_ci if (err < 0) { 8208c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to add %s\n", 8218c2ecf20Sopenharmony_ci be->hdr.name); 8228c2ecf20Sopenharmony_ci kfree(sbe); 8238c2ecf20Sopenharmony_ci break; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci list_add(&sbe->dobj.list, &tplg->comp->dobj_list); 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci return err; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci} 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_cistatic int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count, 8338c2ecf20Sopenharmony_ci size_t size) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci struct snd_soc_tplg_mixer_control *mc; 8368c2ecf20Sopenharmony_ci struct soc_mixer_control *sm; 8378c2ecf20Sopenharmony_ci struct snd_kcontrol_new kc; 8388c2ecf20Sopenharmony_ci int i; 8398c2ecf20Sopenharmony_ci int err = 0; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (soc_tplg_check_elem_count(tplg, 8428c2ecf20Sopenharmony_ci sizeof(struct snd_soc_tplg_mixer_control), 8438c2ecf20Sopenharmony_ci count, size, "mixers")) { 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid count %d for controls\n", 8468c2ecf20Sopenharmony_ci count); 8478c2ecf20Sopenharmony_ci return -EINVAL; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 8518c2ecf20Sopenharmony_ci mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* validate kcontrol */ 8548c2ecf20Sopenharmony_ci if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 8558c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 8568c2ecf20Sopenharmony_ci return -EINVAL; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci sm = kzalloc(sizeof(*sm), GFP_KERNEL); 8598c2ecf20Sopenharmony_ci if (sm == NULL) 8608c2ecf20Sopenharmony_ci return -ENOMEM; 8618c2ecf20Sopenharmony_ci tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) + 8628c2ecf20Sopenharmony_ci le32_to_cpu(mc->priv.size)); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, 8658c2ecf20Sopenharmony_ci "ASoC: adding mixer kcontrol %s with access 0x%x\n", 8668c2ecf20Sopenharmony_ci mc->hdr.name, mc->hdr.access); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci memset(&kc, 0, sizeof(kc)); 8698c2ecf20Sopenharmony_ci kc.name = mc->hdr.name; 8708c2ecf20Sopenharmony_ci kc.private_value = (long)sm; 8718c2ecf20Sopenharmony_ci kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 8728c2ecf20Sopenharmony_ci kc.access = le32_to_cpu(mc->hdr.access); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* we only support FL/FR channel mapping atm */ 8758c2ecf20Sopenharmony_ci sm->reg = tplc_chan_get_reg(tplg, mc->channel, 8768c2ecf20Sopenharmony_ci SNDRV_CHMAP_FL); 8778c2ecf20Sopenharmony_ci sm->rreg = tplc_chan_get_reg(tplg, mc->channel, 8788c2ecf20Sopenharmony_ci SNDRV_CHMAP_FR); 8798c2ecf20Sopenharmony_ci sm->shift = tplc_chan_get_shift(tplg, mc->channel, 8808c2ecf20Sopenharmony_ci SNDRV_CHMAP_FL); 8818c2ecf20Sopenharmony_ci sm->rshift = tplc_chan_get_shift(tplg, mc->channel, 8828c2ecf20Sopenharmony_ci SNDRV_CHMAP_FR); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci sm->max = le32_to_cpu(mc->max); 8858c2ecf20Sopenharmony_ci sm->min = le32_to_cpu(mc->min); 8868c2ecf20Sopenharmony_ci sm->invert = le32_to_cpu(mc->invert); 8878c2ecf20Sopenharmony_ci sm->platform_max = le32_to_cpu(mc->platform_max); 8888c2ecf20Sopenharmony_ci sm->dobj.index = tplg->index; 8898c2ecf20Sopenharmony_ci sm->dobj.ops = tplg->ops; 8908c2ecf20Sopenharmony_ci sm->dobj.type = SND_SOC_DOBJ_MIXER; 8918c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sm->dobj.list); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* map io handlers */ 8948c2ecf20Sopenharmony_ci err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg); 8958c2ecf20Sopenharmony_ci if (err) { 8968c2ecf20Sopenharmony_ci soc_control_err(tplg, &mc->hdr, mc->hdr.name); 8978c2ecf20Sopenharmony_ci kfree(sm); 8988c2ecf20Sopenharmony_ci break; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* create any TLV data */ 9028c2ecf20Sopenharmony_ci err = soc_tplg_create_tlv(tplg, &kc, &mc->hdr); 9038c2ecf20Sopenharmony_ci if (err < 0) { 9048c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", 9058c2ecf20Sopenharmony_ci mc->hdr.name); 9068c2ecf20Sopenharmony_ci kfree(sm); 9078c2ecf20Sopenharmony_ci break; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* pass control to driver for optional further init */ 9118c2ecf20Sopenharmony_ci err = soc_tplg_init_kcontrol(tplg, &kc, 9128c2ecf20Sopenharmony_ci (struct snd_soc_tplg_ctl_hdr *) mc); 9138c2ecf20Sopenharmony_ci if (err < 0) { 9148c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to init %s\n", 9158c2ecf20Sopenharmony_ci mc->hdr.name); 9168c2ecf20Sopenharmony_ci soc_tplg_free_tlv(tplg, &kc); 9178c2ecf20Sopenharmony_ci kfree(sm); 9188c2ecf20Sopenharmony_ci break; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci /* register control here */ 9228c2ecf20Sopenharmony_ci err = soc_tplg_add_kcontrol(tplg, &kc, 9238c2ecf20Sopenharmony_ci &sm->dobj.control.kcontrol); 9248c2ecf20Sopenharmony_ci if (err < 0) { 9258c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to add %s\n", 9268c2ecf20Sopenharmony_ci mc->hdr.name); 9278c2ecf20Sopenharmony_ci soc_tplg_free_tlv(tplg, &kc); 9288c2ecf20Sopenharmony_ci kfree(sm); 9298c2ecf20Sopenharmony_ci break; 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci list_add(&sm->dobj.list, &tplg->comp->dobj_list); 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci return err; 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_cistatic int soc_tplg_denum_create_texts(struct soc_enum *se, 9398c2ecf20Sopenharmony_ci struct snd_soc_tplg_enum_control *ec) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci int i, ret; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci se->dobj.control.dtexts = 9448c2ecf20Sopenharmony_ci kcalloc(le32_to_cpu(ec->items), sizeof(char *), GFP_KERNEL); 9458c2ecf20Sopenharmony_ci if (se->dobj.control.dtexts == NULL) 9468c2ecf20Sopenharmony_ci return -ENOMEM; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci for (i = 0; i < le32_to_cpu(ec->items); i++) { 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (strnlen(ec->texts[i], SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 9518c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { 9528c2ecf20Sopenharmony_ci ret = -EINVAL; 9538c2ecf20Sopenharmony_ci goto err; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci se->dobj.control.dtexts[i] = kstrdup(ec->texts[i], GFP_KERNEL); 9578c2ecf20Sopenharmony_ci if (!se->dobj.control.dtexts[i]) { 9588c2ecf20Sopenharmony_ci ret = -ENOMEM; 9598c2ecf20Sopenharmony_ci goto err; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci se->items = le32_to_cpu(ec->items); 9648c2ecf20Sopenharmony_ci se->texts = (const char * const *)se->dobj.control.dtexts; 9658c2ecf20Sopenharmony_ci return 0; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cierr: 9688c2ecf20Sopenharmony_ci se->items = i; 9698c2ecf20Sopenharmony_ci soc_tplg_denum_remove_texts(se); 9708c2ecf20Sopenharmony_ci return ret; 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_cistatic inline void soc_tplg_denum_remove_texts(struct soc_enum *se) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci int i = se->items; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci for (--i; i >= 0; i--) 9788c2ecf20Sopenharmony_ci kfree(se->dobj.control.dtexts[i]); 9798c2ecf20Sopenharmony_ci kfree(se->dobj.control.dtexts); 9808c2ecf20Sopenharmony_ci} 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cistatic int soc_tplg_denum_create_values(struct soc_enum *se, 9838c2ecf20Sopenharmony_ci struct snd_soc_tplg_enum_control *ec) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci int i; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci if (le32_to_cpu(ec->items) > sizeof(*ec->values)) 9888c2ecf20Sopenharmony_ci return -EINVAL; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci se->dobj.control.dvalues = kzalloc(le32_to_cpu(ec->items) * 9918c2ecf20Sopenharmony_ci sizeof(*se->dobj.control.dvalues), 9928c2ecf20Sopenharmony_ci GFP_KERNEL); 9938c2ecf20Sopenharmony_ci if (!se->dobj.control.dvalues) 9948c2ecf20Sopenharmony_ci return -ENOMEM; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* convert from little-endian */ 9978c2ecf20Sopenharmony_ci for (i = 0; i < le32_to_cpu(ec->items); i++) { 9988c2ecf20Sopenharmony_ci se->dobj.control.dvalues[i] = le32_to_cpu(ec->values[i]); 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci return 0; 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic inline void soc_tplg_denum_remove_values(struct soc_enum *se) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci kfree(se->dobj.control.dvalues); 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_cistatic int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count, 10108c2ecf20Sopenharmony_ci size_t size) 10118c2ecf20Sopenharmony_ci{ 10128c2ecf20Sopenharmony_ci struct snd_soc_tplg_enum_control *ec; 10138c2ecf20Sopenharmony_ci struct soc_enum *se; 10148c2ecf20Sopenharmony_ci struct snd_kcontrol_new kc; 10158c2ecf20Sopenharmony_ci int i; 10168c2ecf20Sopenharmony_ci int err = 0; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci if (soc_tplg_check_elem_count(tplg, 10198c2ecf20Sopenharmony_ci sizeof(struct snd_soc_tplg_enum_control), 10208c2ecf20Sopenharmony_ci count, size, "enums")) { 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid count %d for enum controls\n", 10238c2ecf20Sopenharmony_ci count); 10248c2ecf20Sopenharmony_ci return -EINVAL; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 10288c2ecf20Sopenharmony_ci ec = (struct snd_soc_tplg_enum_control *)tplg->pos; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* validate kcontrol */ 10318c2ecf20Sopenharmony_ci if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 10328c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 10338c2ecf20Sopenharmony_ci return -EINVAL; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci se = kzalloc((sizeof(*se)), GFP_KERNEL); 10368c2ecf20Sopenharmony_ci if (se == NULL) 10378c2ecf20Sopenharmony_ci return -ENOMEM; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + 10408c2ecf20Sopenharmony_ci le32_to_cpu(ec->priv.size)); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", 10438c2ecf20Sopenharmony_ci ec->hdr.name, ec->items); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci memset(&kc, 0, sizeof(kc)); 10468c2ecf20Sopenharmony_ci kc.name = ec->hdr.name; 10478c2ecf20Sopenharmony_ci kc.private_value = (long)se; 10488c2ecf20Sopenharmony_ci kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 10498c2ecf20Sopenharmony_ci kc.access = le32_to_cpu(ec->hdr.access); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); 10528c2ecf20Sopenharmony_ci se->shift_l = tplc_chan_get_shift(tplg, ec->channel, 10538c2ecf20Sopenharmony_ci SNDRV_CHMAP_FL); 10548c2ecf20Sopenharmony_ci se->shift_r = tplc_chan_get_shift(tplg, ec->channel, 10558c2ecf20Sopenharmony_ci SNDRV_CHMAP_FL); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci se->mask = le32_to_cpu(ec->mask); 10588c2ecf20Sopenharmony_ci se->dobj.index = tplg->index; 10598c2ecf20Sopenharmony_ci se->dobj.type = SND_SOC_DOBJ_ENUM; 10608c2ecf20Sopenharmony_ci se->dobj.ops = tplg->ops; 10618c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&se->dobj.list); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci switch (le32_to_cpu(ec->hdr.ops.info)) { 10648c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: 10658c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM_VALUE: 10668c2ecf20Sopenharmony_ci err = soc_tplg_denum_create_values(se, ec); 10678c2ecf20Sopenharmony_ci if (err < 0) { 10688c2ecf20Sopenharmony_ci dev_err(tplg->dev, 10698c2ecf20Sopenharmony_ci "ASoC: could not create values for %s\n", 10708c2ecf20Sopenharmony_ci ec->hdr.name); 10718c2ecf20Sopenharmony_ci goto err_denum; 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci fallthrough; 10748c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM: 10758c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: 10768c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: 10778c2ecf20Sopenharmony_ci err = soc_tplg_denum_create_texts(se, ec); 10788c2ecf20Sopenharmony_ci if (err < 0) { 10798c2ecf20Sopenharmony_ci dev_err(tplg->dev, 10808c2ecf20Sopenharmony_ci "ASoC: could not create texts for %s\n", 10818c2ecf20Sopenharmony_ci ec->hdr.name); 10828c2ecf20Sopenharmony_ci goto err_denum; 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci break; 10858c2ecf20Sopenharmony_ci default: 10868c2ecf20Sopenharmony_ci err = -EINVAL; 10878c2ecf20Sopenharmony_ci dev_err(tplg->dev, 10888c2ecf20Sopenharmony_ci "ASoC: invalid enum control type %d for %s\n", 10898c2ecf20Sopenharmony_ci ec->hdr.ops.info, ec->hdr.name); 10908c2ecf20Sopenharmony_ci goto err_denum; 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci /* map io handlers */ 10948c2ecf20Sopenharmony_ci err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg); 10958c2ecf20Sopenharmony_ci if (err) { 10968c2ecf20Sopenharmony_ci soc_control_err(tplg, &ec->hdr, ec->hdr.name); 10978c2ecf20Sopenharmony_ci goto err_denum; 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci /* pass control to driver for optional further init */ 11018c2ecf20Sopenharmony_ci err = soc_tplg_init_kcontrol(tplg, &kc, 11028c2ecf20Sopenharmony_ci (struct snd_soc_tplg_ctl_hdr *) ec); 11038c2ecf20Sopenharmony_ci if (err < 0) { 11048c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to init %s\n", 11058c2ecf20Sopenharmony_ci ec->hdr.name); 11068c2ecf20Sopenharmony_ci goto err_denum; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci /* register control here */ 11108c2ecf20Sopenharmony_ci err = soc_tplg_add_kcontrol(tplg, 11118c2ecf20Sopenharmony_ci &kc, &se->dobj.control.kcontrol); 11128c2ecf20Sopenharmony_ci if (err < 0) { 11138c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n", 11148c2ecf20Sopenharmony_ci ec->hdr.name); 11158c2ecf20Sopenharmony_ci goto err_denum; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci list_add(&se->dobj.list, &tplg->comp->dobj_list); 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci return 0; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_cierr_denum: 11238c2ecf20Sopenharmony_ci kfree(se); 11248c2ecf20Sopenharmony_ci return err; 11258c2ecf20Sopenharmony_ci} 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_cistatic int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, 11288c2ecf20Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci struct snd_soc_tplg_ctl_hdr *control_hdr; 11318c2ecf20Sopenharmony_ci int ret; 11328c2ecf20Sopenharmony_ci int i; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: adding %d kcontrols at 0x%lx\n", hdr->count, 11358c2ecf20Sopenharmony_ci soc_tplg_get_offset(tplg)); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci for (i = 0; i < le32_to_cpu(hdr->count); i++) { 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (le32_to_cpu(control_hdr->size) != sizeof(*control_hdr)) { 11428c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid control size\n"); 11438c2ecf20Sopenharmony_ci return -EINVAL; 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci switch (le32_to_cpu(control_hdr->ops.info)) { 11478c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW: 11488c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_STROBE: 11498c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW_SX: 11508c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW_XR_SX: 11518c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_RANGE: 11528c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_VOLSW: 11538c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_PIN: 11548c2ecf20Sopenharmony_ci ret = soc_tplg_dmixer_create(tplg, 1, 11558c2ecf20Sopenharmony_ci le32_to_cpu(hdr->payload_size)); 11568c2ecf20Sopenharmony_ci break; 11578c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM: 11588c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM_VALUE: 11598c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: 11608c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: 11618c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: 11628c2ecf20Sopenharmony_ci ret = soc_tplg_denum_create(tplg, 1, 11638c2ecf20Sopenharmony_ci le32_to_cpu(hdr->payload_size)); 11648c2ecf20Sopenharmony_ci break; 11658c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_BYTES: 11668c2ecf20Sopenharmony_ci ret = soc_tplg_dbytes_create(tplg, 1, 11678c2ecf20Sopenharmony_ci le32_to_cpu(hdr->payload_size)); 11688c2ecf20Sopenharmony_ci break; 11698c2ecf20Sopenharmony_ci default: 11708c2ecf20Sopenharmony_ci soc_bind_err(tplg, control_hdr, i); 11718c2ecf20Sopenharmony_ci return -EINVAL; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci if (ret < 0) { 11748c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid control\n"); 11758c2ecf20Sopenharmony_ci return ret; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci return 0; 11818c2ecf20Sopenharmony_ci} 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci/* optionally pass new dynamic kcontrol to component driver. */ 11848c2ecf20Sopenharmony_cistatic int soc_tplg_add_route(struct soc_tplg *tplg, 11858c2ecf20Sopenharmony_ci struct snd_soc_dapm_route *route) 11868c2ecf20Sopenharmony_ci{ 11878c2ecf20Sopenharmony_ci if (tplg->ops && tplg->ops->dapm_route_load) 11888c2ecf20Sopenharmony_ci return tplg->ops->dapm_route_load(tplg->comp, tplg->index, 11898c2ecf20Sopenharmony_ci route); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci return 0; 11928c2ecf20Sopenharmony_ci} 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_cistatic int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, 11958c2ecf20Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = &tplg->comp->dapm; 11988c2ecf20Sopenharmony_ci struct snd_soc_tplg_dapm_graph_elem *elem; 11998c2ecf20Sopenharmony_ci struct snd_soc_dapm_route **routes; 12008c2ecf20Sopenharmony_ci int count, i, j; 12018c2ecf20Sopenharmony_ci int ret = 0; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci count = le32_to_cpu(hdr->count); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (soc_tplg_check_elem_count(tplg, 12068c2ecf20Sopenharmony_ci sizeof(struct snd_soc_tplg_dapm_graph_elem), 12078c2ecf20Sopenharmony_ci count, le32_to_cpu(hdr->payload_size), "graph")) { 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid count %d for DAPM routes\n", 12108c2ecf20Sopenharmony_ci count); 12118c2ecf20Sopenharmony_ci return -EINVAL; 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes for index %d\n", count, 12158c2ecf20Sopenharmony_ci hdr->index); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci /* allocate memory for pointer to array of dapm routes */ 12188c2ecf20Sopenharmony_ci routes = kcalloc(count, sizeof(struct snd_soc_dapm_route *), 12198c2ecf20Sopenharmony_ci GFP_KERNEL); 12208c2ecf20Sopenharmony_ci if (!routes) 12218c2ecf20Sopenharmony_ci return -ENOMEM; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci /* 12248c2ecf20Sopenharmony_ci * allocate memory for each dapm route in the array. 12258c2ecf20Sopenharmony_ci * This needs to be done individually so that 12268c2ecf20Sopenharmony_ci * each route can be freed when it is removed in remove_route(). 12278c2ecf20Sopenharmony_ci */ 12288c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 12298c2ecf20Sopenharmony_ci routes[i] = kzalloc(sizeof(*routes[i]), GFP_KERNEL); 12308c2ecf20Sopenharmony_ci if (!routes[i]) { 12318c2ecf20Sopenharmony_ci /* free previously allocated memory */ 12328c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) 12338c2ecf20Sopenharmony_ci kfree(routes[j]); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci kfree(routes); 12368c2ecf20Sopenharmony_ci return -ENOMEM; 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 12418c2ecf20Sopenharmony_ci elem = (struct snd_soc_tplg_dapm_graph_elem *)tplg->pos; 12428c2ecf20Sopenharmony_ci tplg->pos += sizeof(struct snd_soc_tplg_dapm_graph_elem); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci /* validate routes */ 12458c2ecf20Sopenharmony_ci if (strnlen(elem->source, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 12468c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { 12478c2ecf20Sopenharmony_ci ret = -EINVAL; 12488c2ecf20Sopenharmony_ci break; 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci if (strnlen(elem->sink, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 12518c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { 12528c2ecf20Sopenharmony_ci ret = -EINVAL; 12538c2ecf20Sopenharmony_ci break; 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 12568c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { 12578c2ecf20Sopenharmony_ci ret = -EINVAL; 12588c2ecf20Sopenharmony_ci break; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci routes[i]->source = elem->source; 12628c2ecf20Sopenharmony_ci routes[i]->sink = elem->sink; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci /* set to NULL atm for tplg users */ 12658c2ecf20Sopenharmony_ci routes[i]->connected = NULL; 12668c2ecf20Sopenharmony_ci if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0) 12678c2ecf20Sopenharmony_ci routes[i]->control = NULL; 12688c2ecf20Sopenharmony_ci else 12698c2ecf20Sopenharmony_ci routes[i]->control = elem->control; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci /* add route dobj to dobj_list */ 12728c2ecf20Sopenharmony_ci routes[i]->dobj.type = SND_SOC_DOBJ_GRAPH; 12738c2ecf20Sopenharmony_ci routes[i]->dobj.ops = tplg->ops; 12748c2ecf20Sopenharmony_ci routes[i]->dobj.index = tplg->index; 12758c2ecf20Sopenharmony_ci list_add(&routes[i]->dobj.list, &tplg->comp->dobj_list); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci ret = soc_tplg_add_route(tplg, routes[i]); 12788c2ecf20Sopenharmony_ci if (ret < 0) { 12798c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: topology: add_route failed: %d\n", ret); 12808c2ecf20Sopenharmony_ci /* 12818c2ecf20Sopenharmony_ci * this route was added to the list, it will 12828c2ecf20Sopenharmony_ci * be freed in remove_route() so increment the 12838c2ecf20Sopenharmony_ci * counter to skip it in the error handling 12848c2ecf20Sopenharmony_ci * below. 12858c2ecf20Sopenharmony_ci */ 12868c2ecf20Sopenharmony_ci i++; 12878c2ecf20Sopenharmony_ci break; 12888c2ecf20Sopenharmony_ci } 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci /* add route, but keep going if some fail */ 12918c2ecf20Sopenharmony_ci snd_soc_dapm_add_routes(dapm, routes[i], 1); 12928c2ecf20Sopenharmony_ci } 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci /* 12958c2ecf20Sopenharmony_ci * free memory allocated for all dapm routes not added to the 12968c2ecf20Sopenharmony_ci * list in case of error 12978c2ecf20Sopenharmony_ci */ 12988c2ecf20Sopenharmony_ci if (ret < 0) { 12998c2ecf20Sopenharmony_ci while (i < count) 13008c2ecf20Sopenharmony_ci kfree(routes[i++]); 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci /* 13048c2ecf20Sopenharmony_ci * free pointer to array of dapm routes as this is no longer needed. 13058c2ecf20Sopenharmony_ci * The memory allocated for each dapm route will be freed 13068c2ecf20Sopenharmony_ci * when it is removed in remove_route(). 13078c2ecf20Sopenharmony_ci */ 13088c2ecf20Sopenharmony_ci kfree(routes); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci return ret; 13118c2ecf20Sopenharmony_ci} 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create( 13148c2ecf20Sopenharmony_ci struct soc_tplg *tplg, int num_kcontrols) 13158c2ecf20Sopenharmony_ci{ 13168c2ecf20Sopenharmony_ci struct snd_kcontrol_new *kc; 13178c2ecf20Sopenharmony_ci struct soc_mixer_control *sm; 13188c2ecf20Sopenharmony_ci struct snd_soc_tplg_mixer_control *mc; 13198c2ecf20Sopenharmony_ci int i, err; 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL); 13228c2ecf20Sopenharmony_ci if (kc == NULL) 13238c2ecf20Sopenharmony_ci return NULL; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci for (i = 0; i < num_kcontrols; i++) { 13268c2ecf20Sopenharmony_ci mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci /* validate kcontrol */ 13298c2ecf20Sopenharmony_ci if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 13308c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 13318c2ecf20Sopenharmony_ci goto err_sm; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci sm = kzalloc(sizeof(*sm), GFP_KERNEL); 13348c2ecf20Sopenharmony_ci if (sm == NULL) 13358c2ecf20Sopenharmony_ci goto err_sm; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) + 13388c2ecf20Sopenharmony_ci le32_to_cpu(mc->priv.size)); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, " adding DAPM widget mixer control %s at %d\n", 13418c2ecf20Sopenharmony_ci mc->hdr.name, i); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci kc[i].private_value = (long)sm; 13448c2ecf20Sopenharmony_ci kc[i].name = kstrdup(mc->hdr.name, GFP_KERNEL); 13458c2ecf20Sopenharmony_ci if (kc[i].name == NULL) 13468c2ecf20Sopenharmony_ci goto err_sm; 13478c2ecf20Sopenharmony_ci kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; 13488c2ecf20Sopenharmony_ci kc[i].access = le32_to_cpu(mc->hdr.access); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci /* we only support FL/FR channel mapping atm */ 13518c2ecf20Sopenharmony_ci sm->reg = tplc_chan_get_reg(tplg, mc->channel, 13528c2ecf20Sopenharmony_ci SNDRV_CHMAP_FL); 13538c2ecf20Sopenharmony_ci sm->rreg = tplc_chan_get_reg(tplg, mc->channel, 13548c2ecf20Sopenharmony_ci SNDRV_CHMAP_FR); 13558c2ecf20Sopenharmony_ci sm->shift = tplc_chan_get_shift(tplg, mc->channel, 13568c2ecf20Sopenharmony_ci SNDRV_CHMAP_FL); 13578c2ecf20Sopenharmony_ci sm->rshift = tplc_chan_get_shift(tplg, mc->channel, 13588c2ecf20Sopenharmony_ci SNDRV_CHMAP_FR); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci sm->max = le32_to_cpu(mc->max); 13618c2ecf20Sopenharmony_ci sm->min = le32_to_cpu(mc->min); 13628c2ecf20Sopenharmony_ci sm->invert = le32_to_cpu(mc->invert); 13638c2ecf20Sopenharmony_ci sm->platform_max = le32_to_cpu(mc->platform_max); 13648c2ecf20Sopenharmony_ci sm->dobj.index = tplg->index; 13658c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sm->dobj.list); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci /* map io handlers */ 13688c2ecf20Sopenharmony_ci err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc[i], tplg); 13698c2ecf20Sopenharmony_ci if (err) { 13708c2ecf20Sopenharmony_ci soc_control_err(tplg, &mc->hdr, mc->hdr.name); 13718c2ecf20Sopenharmony_ci goto err_sm; 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci /* create any TLV data */ 13758c2ecf20Sopenharmony_ci err = soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr); 13768c2ecf20Sopenharmony_ci if (err < 0) { 13778c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", 13788c2ecf20Sopenharmony_ci mc->hdr.name); 13798c2ecf20Sopenharmony_ci goto err_sm; 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci /* pass control to driver for optional further init */ 13838c2ecf20Sopenharmony_ci err = soc_tplg_init_kcontrol(tplg, &kc[i], 13848c2ecf20Sopenharmony_ci (struct snd_soc_tplg_ctl_hdr *)mc); 13858c2ecf20Sopenharmony_ci if (err < 0) { 13868c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to init %s\n", 13878c2ecf20Sopenharmony_ci mc->hdr.name); 13888c2ecf20Sopenharmony_ci goto err_sm; 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci } 13918c2ecf20Sopenharmony_ci return kc; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_cierr_sm: 13948c2ecf20Sopenharmony_ci for (; i >= 0; i--) { 13958c2ecf20Sopenharmony_ci soc_tplg_free_tlv(tplg, &kc[i]); 13968c2ecf20Sopenharmony_ci sm = (struct soc_mixer_control *)kc[i].private_value; 13978c2ecf20Sopenharmony_ci kfree(sm); 13988c2ecf20Sopenharmony_ci kfree(kc[i].name); 13998c2ecf20Sopenharmony_ci } 14008c2ecf20Sopenharmony_ci kfree(kc); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci return NULL; 14038c2ecf20Sopenharmony_ci} 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create( 14068c2ecf20Sopenharmony_ci struct soc_tplg *tplg, int num_kcontrols) 14078c2ecf20Sopenharmony_ci{ 14088c2ecf20Sopenharmony_ci struct snd_kcontrol_new *kc; 14098c2ecf20Sopenharmony_ci struct snd_soc_tplg_enum_control *ec; 14108c2ecf20Sopenharmony_ci struct soc_enum *se; 14118c2ecf20Sopenharmony_ci int i, err; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL); 14148c2ecf20Sopenharmony_ci if (kc == NULL) 14158c2ecf20Sopenharmony_ci return NULL; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci for (i = 0; i < num_kcontrols; i++) { 14188c2ecf20Sopenharmony_ci ec = (struct snd_soc_tplg_enum_control *)tplg->pos; 14198c2ecf20Sopenharmony_ci /* validate kcontrol */ 14208c2ecf20Sopenharmony_ci if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 14218c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 14228c2ecf20Sopenharmony_ci goto err_se; 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci se = kzalloc(sizeof(*se), GFP_KERNEL); 14258c2ecf20Sopenharmony_ci if (se == NULL) 14268c2ecf20Sopenharmony_ci goto err_se; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + 14298c2ecf20Sopenharmony_ci le32_to_cpu(ec->priv.size)); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n", 14328c2ecf20Sopenharmony_ci ec->hdr.name); 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci kc[i].private_value = (long)se; 14358c2ecf20Sopenharmony_ci kc[i].name = kstrdup(ec->hdr.name, GFP_KERNEL); 14368c2ecf20Sopenharmony_ci if (kc[i].name == NULL) 14378c2ecf20Sopenharmony_ci goto err_se; 14388c2ecf20Sopenharmony_ci kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; 14398c2ecf20Sopenharmony_ci kc[i].access = le32_to_cpu(ec->hdr.access); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci /* we only support FL/FR channel mapping atm */ 14428c2ecf20Sopenharmony_ci se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); 14438c2ecf20Sopenharmony_ci se->shift_l = tplc_chan_get_shift(tplg, ec->channel, 14448c2ecf20Sopenharmony_ci SNDRV_CHMAP_FL); 14458c2ecf20Sopenharmony_ci se->shift_r = tplc_chan_get_shift(tplg, ec->channel, 14468c2ecf20Sopenharmony_ci SNDRV_CHMAP_FR); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci se->items = le32_to_cpu(ec->items); 14498c2ecf20Sopenharmony_ci se->mask = le32_to_cpu(ec->mask); 14508c2ecf20Sopenharmony_ci se->dobj.index = tplg->index; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci switch (le32_to_cpu(ec->hdr.ops.info)) { 14538c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM_VALUE: 14548c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: 14558c2ecf20Sopenharmony_ci err = soc_tplg_denum_create_values(se, ec); 14568c2ecf20Sopenharmony_ci if (err < 0) { 14578c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: could not create values for %s\n", 14588c2ecf20Sopenharmony_ci ec->hdr.name); 14598c2ecf20Sopenharmony_ci goto err_se; 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci fallthrough; 14628c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM: 14638c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: 14648c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: 14658c2ecf20Sopenharmony_ci err = soc_tplg_denum_create_texts(se, ec); 14668c2ecf20Sopenharmony_ci if (err < 0) { 14678c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: could not create texts for %s\n", 14688c2ecf20Sopenharmony_ci ec->hdr.name); 14698c2ecf20Sopenharmony_ci goto err_se; 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci break; 14728c2ecf20Sopenharmony_ci default: 14738c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", 14748c2ecf20Sopenharmony_ci ec->hdr.ops.info, ec->hdr.name); 14758c2ecf20Sopenharmony_ci goto err_se; 14768c2ecf20Sopenharmony_ci } 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci /* map io handlers */ 14798c2ecf20Sopenharmony_ci err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc[i], tplg); 14808c2ecf20Sopenharmony_ci if (err) { 14818c2ecf20Sopenharmony_ci soc_control_err(tplg, &ec->hdr, ec->hdr.name); 14828c2ecf20Sopenharmony_ci goto err_se; 14838c2ecf20Sopenharmony_ci } 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci /* pass control to driver for optional further init */ 14868c2ecf20Sopenharmony_ci err = soc_tplg_init_kcontrol(tplg, &kc[i], 14878c2ecf20Sopenharmony_ci (struct snd_soc_tplg_ctl_hdr *)ec); 14888c2ecf20Sopenharmony_ci if (err < 0) { 14898c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to init %s\n", 14908c2ecf20Sopenharmony_ci ec->hdr.name); 14918c2ecf20Sopenharmony_ci goto err_se; 14928c2ecf20Sopenharmony_ci } 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci return kc; 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_cierr_se: 14988c2ecf20Sopenharmony_ci for (; i >= 0; i--) { 14998c2ecf20Sopenharmony_ci /* free values and texts */ 15008c2ecf20Sopenharmony_ci se = (struct soc_enum *)kc[i].private_value; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci if (se) { 15038c2ecf20Sopenharmony_ci soc_tplg_denum_remove_values(se); 15048c2ecf20Sopenharmony_ci soc_tplg_denum_remove_texts(se); 15058c2ecf20Sopenharmony_ci } 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci kfree(se); 15088c2ecf20Sopenharmony_ci kfree(kc[i].name); 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci kfree(kc); 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci return NULL; 15138c2ecf20Sopenharmony_ci} 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_cistatic struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create( 15168c2ecf20Sopenharmony_ci struct soc_tplg *tplg, int num_kcontrols) 15178c2ecf20Sopenharmony_ci{ 15188c2ecf20Sopenharmony_ci struct snd_soc_tplg_bytes_control *be; 15198c2ecf20Sopenharmony_ci struct soc_bytes_ext *sbe; 15208c2ecf20Sopenharmony_ci struct snd_kcontrol_new *kc; 15218c2ecf20Sopenharmony_ci int i, err; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL); 15248c2ecf20Sopenharmony_ci if (!kc) 15258c2ecf20Sopenharmony_ci return NULL; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci for (i = 0; i < num_kcontrols; i++) { 15288c2ecf20Sopenharmony_ci be = (struct snd_soc_tplg_bytes_control *)tplg->pos; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci /* validate kcontrol */ 15318c2ecf20Sopenharmony_ci if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 15328c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 15338c2ecf20Sopenharmony_ci goto err_sbe; 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci sbe = kzalloc(sizeof(*sbe), GFP_KERNEL); 15368c2ecf20Sopenharmony_ci if (sbe == NULL) 15378c2ecf20Sopenharmony_ci goto err_sbe; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + 15408c2ecf20Sopenharmony_ci le32_to_cpu(be->priv.size)); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, 15438c2ecf20Sopenharmony_ci "ASoC: adding bytes kcontrol %s with access 0x%x\n", 15448c2ecf20Sopenharmony_ci be->hdr.name, be->hdr.access); 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci kc[i].private_value = (long)sbe; 15478c2ecf20Sopenharmony_ci kc[i].name = kstrdup(be->hdr.name, GFP_KERNEL); 15488c2ecf20Sopenharmony_ci if (kc[i].name == NULL) 15498c2ecf20Sopenharmony_ci goto err_sbe; 15508c2ecf20Sopenharmony_ci kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; 15518c2ecf20Sopenharmony_ci kc[i].access = le32_to_cpu(be->hdr.access); 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci sbe->max = le32_to_cpu(be->max); 15548c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sbe->dobj.list); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci /* map standard io handlers and check for external handlers */ 15578c2ecf20Sopenharmony_ci err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc[i], tplg); 15588c2ecf20Sopenharmony_ci if (err) { 15598c2ecf20Sopenharmony_ci soc_control_err(tplg, &be->hdr, be->hdr.name); 15608c2ecf20Sopenharmony_ci goto err_sbe; 15618c2ecf20Sopenharmony_ci } 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci /* pass control to driver for optional further init */ 15648c2ecf20Sopenharmony_ci err = soc_tplg_init_kcontrol(tplg, &kc[i], 15658c2ecf20Sopenharmony_ci (struct snd_soc_tplg_ctl_hdr *)be); 15668c2ecf20Sopenharmony_ci if (err < 0) { 15678c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to init %s\n", 15688c2ecf20Sopenharmony_ci be->hdr.name); 15698c2ecf20Sopenharmony_ci goto err_sbe; 15708c2ecf20Sopenharmony_ci } 15718c2ecf20Sopenharmony_ci } 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci return kc; 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_cierr_sbe: 15768c2ecf20Sopenharmony_ci for (; i >= 0; i--) { 15778c2ecf20Sopenharmony_ci sbe = (struct soc_bytes_ext *)kc[i].private_value; 15788c2ecf20Sopenharmony_ci kfree(sbe); 15798c2ecf20Sopenharmony_ci kfree(kc[i].name); 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci kfree(kc); 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci return NULL; 15848c2ecf20Sopenharmony_ci} 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_cistatic int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, 15878c2ecf20Sopenharmony_ci struct snd_soc_tplg_dapm_widget *w) 15888c2ecf20Sopenharmony_ci{ 15898c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = &tplg->comp->dapm; 15908c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget template, *widget; 15918c2ecf20Sopenharmony_ci struct snd_soc_tplg_ctl_hdr *control_hdr; 15928c2ecf20Sopenharmony_ci struct snd_soc_card *card = tplg->comp->card; 15938c2ecf20Sopenharmony_ci unsigned int kcontrol_type; 15948c2ecf20Sopenharmony_ci int ret = 0; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci if (strnlen(w->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 15978c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 15988c2ecf20Sopenharmony_ci return -EINVAL; 15998c2ecf20Sopenharmony_ci if (strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 16008c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 16018c2ecf20Sopenharmony_ci return -EINVAL; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: creating DAPM widget %s id %d\n", 16048c2ecf20Sopenharmony_ci w->name, w->id); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci memset(&template, 0, sizeof(template)); 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci /* map user to kernel widget ID */ 16098c2ecf20Sopenharmony_ci template.id = get_widget_id(le32_to_cpu(w->id)); 16108c2ecf20Sopenharmony_ci if ((int)template.id < 0) 16118c2ecf20Sopenharmony_ci return template.id; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci /* strings are allocated here, but used and freed by the widget */ 16148c2ecf20Sopenharmony_ci template.name = kstrdup(w->name, GFP_KERNEL); 16158c2ecf20Sopenharmony_ci if (!template.name) 16168c2ecf20Sopenharmony_ci return -ENOMEM; 16178c2ecf20Sopenharmony_ci template.sname = kstrdup(w->sname, GFP_KERNEL); 16188c2ecf20Sopenharmony_ci if (!template.sname) { 16198c2ecf20Sopenharmony_ci ret = -ENOMEM; 16208c2ecf20Sopenharmony_ci goto err; 16218c2ecf20Sopenharmony_ci } 16228c2ecf20Sopenharmony_ci template.reg = le32_to_cpu(w->reg); 16238c2ecf20Sopenharmony_ci template.shift = le32_to_cpu(w->shift); 16248c2ecf20Sopenharmony_ci template.mask = le32_to_cpu(w->mask); 16258c2ecf20Sopenharmony_ci template.subseq = le32_to_cpu(w->subseq); 16268c2ecf20Sopenharmony_ci template.on_val = w->invert ? 0 : 1; 16278c2ecf20Sopenharmony_ci template.off_val = w->invert ? 1 : 0; 16288c2ecf20Sopenharmony_ci template.ignore_suspend = le32_to_cpu(w->ignore_suspend); 16298c2ecf20Sopenharmony_ci template.event_flags = le16_to_cpu(w->event_flags); 16308c2ecf20Sopenharmony_ci template.dobj.index = tplg->index; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci tplg->pos += 16338c2ecf20Sopenharmony_ci (sizeof(struct snd_soc_tplg_dapm_widget) + 16348c2ecf20Sopenharmony_ci le32_to_cpu(w->priv.size)); 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci if (w->num_kcontrols == 0) { 16378c2ecf20Sopenharmony_ci kcontrol_type = 0; 16388c2ecf20Sopenharmony_ci template.num_kcontrols = 0; 16398c2ecf20Sopenharmony_ci goto widget; 16408c2ecf20Sopenharmony_ci } 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos; 16438c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: template %s has %d controls of type %x\n", 16448c2ecf20Sopenharmony_ci w->name, w->num_kcontrols, control_hdr->type); 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci switch (le32_to_cpu(control_hdr->ops.info)) { 16478c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW: 16488c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_STROBE: 16498c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW_SX: 16508c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW_XR_SX: 16518c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_RANGE: 16528c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_VOLSW: 16538c2ecf20Sopenharmony_ci kcontrol_type = SND_SOC_TPLG_TYPE_MIXER; /* volume mixer */ 16548c2ecf20Sopenharmony_ci template.num_kcontrols = le32_to_cpu(w->num_kcontrols); 16558c2ecf20Sopenharmony_ci template.kcontrol_news = 16568c2ecf20Sopenharmony_ci soc_tplg_dapm_widget_dmixer_create(tplg, 16578c2ecf20Sopenharmony_ci template.num_kcontrols); 16588c2ecf20Sopenharmony_ci if (!template.kcontrol_news) { 16598c2ecf20Sopenharmony_ci ret = -ENOMEM; 16608c2ecf20Sopenharmony_ci goto hdr_err; 16618c2ecf20Sopenharmony_ci } 16628c2ecf20Sopenharmony_ci break; 16638c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM: 16648c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM_VALUE: 16658c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: 16668c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: 16678c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: 16688c2ecf20Sopenharmony_ci kcontrol_type = SND_SOC_TPLG_TYPE_ENUM; /* enumerated mixer */ 16698c2ecf20Sopenharmony_ci template.num_kcontrols = le32_to_cpu(w->num_kcontrols); 16708c2ecf20Sopenharmony_ci template.kcontrol_news = 16718c2ecf20Sopenharmony_ci soc_tplg_dapm_widget_denum_create(tplg, 16728c2ecf20Sopenharmony_ci template.num_kcontrols); 16738c2ecf20Sopenharmony_ci if (!template.kcontrol_news) { 16748c2ecf20Sopenharmony_ci ret = -ENOMEM; 16758c2ecf20Sopenharmony_ci goto hdr_err; 16768c2ecf20Sopenharmony_ci } 16778c2ecf20Sopenharmony_ci break; 16788c2ecf20Sopenharmony_ci case SND_SOC_TPLG_CTL_BYTES: 16798c2ecf20Sopenharmony_ci kcontrol_type = SND_SOC_TPLG_TYPE_BYTES; /* bytes control */ 16808c2ecf20Sopenharmony_ci template.num_kcontrols = le32_to_cpu(w->num_kcontrols); 16818c2ecf20Sopenharmony_ci template.kcontrol_news = 16828c2ecf20Sopenharmony_ci soc_tplg_dapm_widget_dbytes_create(tplg, 16838c2ecf20Sopenharmony_ci template.num_kcontrols); 16848c2ecf20Sopenharmony_ci if (!template.kcontrol_news) { 16858c2ecf20Sopenharmony_ci ret = -ENOMEM; 16868c2ecf20Sopenharmony_ci goto hdr_err; 16878c2ecf20Sopenharmony_ci } 16888c2ecf20Sopenharmony_ci break; 16898c2ecf20Sopenharmony_ci default: 16908c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid widget control type %d:%d:%d\n", 16918c2ecf20Sopenharmony_ci control_hdr->ops.get, control_hdr->ops.put, 16928c2ecf20Sopenharmony_ci le32_to_cpu(control_hdr->ops.info)); 16938c2ecf20Sopenharmony_ci ret = -EINVAL; 16948c2ecf20Sopenharmony_ci goto hdr_err; 16958c2ecf20Sopenharmony_ci } 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ciwidget: 16988c2ecf20Sopenharmony_ci ret = soc_tplg_widget_load(tplg, &template, w); 16998c2ecf20Sopenharmony_ci if (ret < 0) 17008c2ecf20Sopenharmony_ci goto hdr_err; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci /* card dapm mutex is held by the core if we are loading topology 17038c2ecf20Sopenharmony_ci * data during sound card init. */ 17048c2ecf20Sopenharmony_ci if (card->instantiated) 17058c2ecf20Sopenharmony_ci widget = snd_soc_dapm_new_control(dapm, &template); 17068c2ecf20Sopenharmony_ci else 17078c2ecf20Sopenharmony_ci widget = snd_soc_dapm_new_control_unlocked(dapm, &template); 17088c2ecf20Sopenharmony_ci if (IS_ERR(widget)) { 17098c2ecf20Sopenharmony_ci ret = PTR_ERR(widget); 17108c2ecf20Sopenharmony_ci goto hdr_err; 17118c2ecf20Sopenharmony_ci } 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci widget->dobj.type = SND_SOC_DOBJ_WIDGET; 17148c2ecf20Sopenharmony_ci widget->dobj.widget.kcontrol_type = kcontrol_type; 17158c2ecf20Sopenharmony_ci widget->dobj.ops = tplg->ops; 17168c2ecf20Sopenharmony_ci widget->dobj.index = tplg->index; 17178c2ecf20Sopenharmony_ci list_add(&widget->dobj.list, &tplg->comp->dobj_list); 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci ret = soc_tplg_widget_ready(tplg, widget, w); 17208c2ecf20Sopenharmony_ci if (ret < 0) 17218c2ecf20Sopenharmony_ci goto ready_err; 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci kfree(template.sname); 17248c2ecf20Sopenharmony_ci kfree(template.name); 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci return 0; 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ciready_err: 17298c2ecf20Sopenharmony_ci snd_soc_tplg_widget_remove(widget); 17308c2ecf20Sopenharmony_ci snd_soc_dapm_free_widget(widget); 17318c2ecf20Sopenharmony_cihdr_err: 17328c2ecf20Sopenharmony_ci kfree(template.sname); 17338c2ecf20Sopenharmony_cierr: 17348c2ecf20Sopenharmony_ci kfree(template.name); 17358c2ecf20Sopenharmony_ci return ret; 17368c2ecf20Sopenharmony_ci} 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_cistatic int soc_tplg_dapm_widget_elems_load(struct soc_tplg *tplg, 17398c2ecf20Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 17408c2ecf20Sopenharmony_ci{ 17418c2ecf20Sopenharmony_ci struct snd_soc_tplg_dapm_widget *widget; 17428c2ecf20Sopenharmony_ci int ret, count, i; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci count = le32_to_cpu(hdr->count); 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: adding %d DAPM widgets\n", count); 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 17498c2ecf20Sopenharmony_ci widget = (struct snd_soc_tplg_dapm_widget *) tplg->pos; 17508c2ecf20Sopenharmony_ci if (le32_to_cpu(widget->size) != sizeof(*widget)) { 17518c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid widget size\n"); 17528c2ecf20Sopenharmony_ci return -EINVAL; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci ret = soc_tplg_dapm_widget_create(tplg, widget); 17568c2ecf20Sopenharmony_ci if (ret < 0) { 17578c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to load widget %s\n", 17588c2ecf20Sopenharmony_ci widget->name); 17598c2ecf20Sopenharmony_ci return ret; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci } 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci return 0; 17648c2ecf20Sopenharmony_ci} 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_cistatic int soc_tplg_dapm_complete(struct soc_tplg *tplg) 17678c2ecf20Sopenharmony_ci{ 17688c2ecf20Sopenharmony_ci struct snd_soc_card *card = tplg->comp->card; 17698c2ecf20Sopenharmony_ci int ret; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci /* Card might not have been registered at this point. 17728c2ecf20Sopenharmony_ci * If so, just return success. 17738c2ecf20Sopenharmony_ci */ 17748c2ecf20Sopenharmony_ci if (!card || !card->instantiated) { 17758c2ecf20Sopenharmony_ci dev_warn(tplg->dev, "ASoC: Parent card not yet available," 17768c2ecf20Sopenharmony_ci " widget card binding deferred\n"); 17778c2ecf20Sopenharmony_ci return 0; 17788c2ecf20Sopenharmony_ci } 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci ret = snd_soc_dapm_new_widgets(card); 17818c2ecf20Sopenharmony_ci if (ret < 0) 17828c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n", 17838c2ecf20Sopenharmony_ci ret); 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci return 0; 17868c2ecf20Sopenharmony_ci} 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_cistatic int set_stream_info(struct snd_soc_pcm_stream *stream, 17898c2ecf20Sopenharmony_ci struct snd_soc_tplg_stream_caps *caps) 17908c2ecf20Sopenharmony_ci{ 17918c2ecf20Sopenharmony_ci stream->stream_name = kstrdup(caps->name, GFP_KERNEL); 17928c2ecf20Sopenharmony_ci if (!stream->stream_name) 17938c2ecf20Sopenharmony_ci return -ENOMEM; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci stream->channels_min = le32_to_cpu(caps->channels_min); 17968c2ecf20Sopenharmony_ci stream->channels_max = le32_to_cpu(caps->channels_max); 17978c2ecf20Sopenharmony_ci stream->rates = le32_to_cpu(caps->rates); 17988c2ecf20Sopenharmony_ci stream->rate_min = le32_to_cpu(caps->rate_min); 17998c2ecf20Sopenharmony_ci stream->rate_max = le32_to_cpu(caps->rate_max); 18008c2ecf20Sopenharmony_ci stream->formats = le64_to_cpu(caps->formats); 18018c2ecf20Sopenharmony_ci stream->sig_bits = le32_to_cpu(caps->sig_bits); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci return 0; 18048c2ecf20Sopenharmony_ci} 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_cistatic void set_dai_flags(struct snd_soc_dai_driver *dai_drv, 18078c2ecf20Sopenharmony_ci unsigned int flag_mask, unsigned int flags) 18088c2ecf20Sopenharmony_ci{ 18098c2ecf20Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES) 18108c2ecf20Sopenharmony_ci dai_drv->symmetric_rates = 18118c2ecf20Sopenharmony_ci flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES ? 1 : 0; 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS) 18148c2ecf20Sopenharmony_ci dai_drv->symmetric_channels = 18158c2ecf20Sopenharmony_ci flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS ? 18168c2ecf20Sopenharmony_ci 1 : 0; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS) 18198c2ecf20Sopenharmony_ci dai_drv->symmetric_samplebits = 18208c2ecf20Sopenharmony_ci flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS ? 18218c2ecf20Sopenharmony_ci 1 : 0; 18228c2ecf20Sopenharmony_ci} 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_cistatic int soc_tplg_dai_create(struct soc_tplg *tplg, 18258c2ecf20Sopenharmony_ci struct snd_soc_tplg_pcm *pcm) 18268c2ecf20Sopenharmony_ci{ 18278c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *dai_drv; 18288c2ecf20Sopenharmony_ci struct snd_soc_pcm_stream *stream; 18298c2ecf20Sopenharmony_ci struct snd_soc_tplg_stream_caps *caps; 18308c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 18318c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = 18328c2ecf20Sopenharmony_ci snd_soc_component_get_dapm(tplg->comp); 18338c2ecf20Sopenharmony_ci int ret; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci dai_drv = kzalloc(sizeof(struct snd_soc_dai_driver), GFP_KERNEL); 18368c2ecf20Sopenharmony_ci if (dai_drv == NULL) 18378c2ecf20Sopenharmony_ci return -ENOMEM; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci if (strlen(pcm->dai_name)) { 18408c2ecf20Sopenharmony_ci dai_drv->name = kstrdup(pcm->dai_name, GFP_KERNEL); 18418c2ecf20Sopenharmony_ci if (!dai_drv->name) { 18428c2ecf20Sopenharmony_ci ret = -ENOMEM; 18438c2ecf20Sopenharmony_ci goto err; 18448c2ecf20Sopenharmony_ci } 18458c2ecf20Sopenharmony_ci } 18468c2ecf20Sopenharmony_ci dai_drv->id = le32_to_cpu(pcm->dai_id); 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci if (pcm->playback) { 18498c2ecf20Sopenharmony_ci stream = &dai_drv->playback; 18508c2ecf20Sopenharmony_ci caps = &pcm->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; 18518c2ecf20Sopenharmony_ci ret = set_stream_info(stream, caps); 18528c2ecf20Sopenharmony_ci if (ret < 0) 18538c2ecf20Sopenharmony_ci goto err; 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci if (pcm->capture) { 18578c2ecf20Sopenharmony_ci stream = &dai_drv->capture; 18588c2ecf20Sopenharmony_ci caps = &pcm->caps[SND_SOC_TPLG_STREAM_CAPTURE]; 18598c2ecf20Sopenharmony_ci ret = set_stream_info(stream, caps); 18608c2ecf20Sopenharmony_ci if (ret < 0) 18618c2ecf20Sopenharmony_ci goto err; 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci if (pcm->compress) 18658c2ecf20Sopenharmony_ci dai_drv->compress_new = snd_soc_new_compress; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci /* pass control to component driver for optional further init */ 18688c2ecf20Sopenharmony_ci ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL); 18698c2ecf20Sopenharmony_ci if (ret < 0) { 18708c2ecf20Sopenharmony_ci dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); 18718c2ecf20Sopenharmony_ci goto err; 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci dai_drv->dobj.index = tplg->index; 18758c2ecf20Sopenharmony_ci dai_drv->dobj.ops = tplg->ops; 18768c2ecf20Sopenharmony_ci dai_drv->dobj.type = SND_SOC_DOBJ_PCM; 18778c2ecf20Sopenharmony_ci list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list); 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci /* register the DAI to the component */ 18808c2ecf20Sopenharmony_ci dai = snd_soc_register_dai(tplg->comp, dai_drv, false); 18818c2ecf20Sopenharmony_ci if (!dai) 18828c2ecf20Sopenharmony_ci return -ENOMEM; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci /* Create the DAI widgets here */ 18858c2ecf20Sopenharmony_ci ret = snd_soc_dapm_new_dai_widgets(dapm, dai); 18868c2ecf20Sopenharmony_ci if (ret != 0) { 18878c2ecf20Sopenharmony_ci dev_err(dai->dev, "Failed to create DAI widgets %d\n", ret); 18888c2ecf20Sopenharmony_ci snd_soc_unregister_dai(dai); 18898c2ecf20Sopenharmony_ci return ret; 18908c2ecf20Sopenharmony_ci } 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci return 0; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_cierr: 18958c2ecf20Sopenharmony_ci kfree(dai_drv->playback.stream_name); 18968c2ecf20Sopenharmony_ci kfree(dai_drv->capture.stream_name); 18978c2ecf20Sopenharmony_ci kfree(dai_drv->name); 18988c2ecf20Sopenharmony_ci kfree(dai_drv); 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci return ret; 19018c2ecf20Sopenharmony_ci} 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_cistatic void set_link_flags(struct snd_soc_dai_link *link, 19048c2ecf20Sopenharmony_ci unsigned int flag_mask, unsigned int flags) 19058c2ecf20Sopenharmony_ci{ 19068c2ecf20Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES) 19078c2ecf20Sopenharmony_ci link->symmetric_rates = 19088c2ecf20Sopenharmony_ci flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES ? 1 : 0; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS) 19118c2ecf20Sopenharmony_ci link->symmetric_channels = 19128c2ecf20Sopenharmony_ci flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS ? 19138c2ecf20Sopenharmony_ci 1 : 0; 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS) 19168c2ecf20Sopenharmony_ci link->symmetric_samplebits = 19178c2ecf20Sopenharmony_ci flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS ? 19188c2ecf20Sopenharmony_ci 1 : 0; 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP) 19218c2ecf20Sopenharmony_ci link->ignore_suspend = 19228c2ecf20Sopenharmony_ci flags & SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP ? 19238c2ecf20Sopenharmony_ci 1 : 0; 19248c2ecf20Sopenharmony_ci} 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci/* create the FE DAI link */ 19278c2ecf20Sopenharmony_cistatic int soc_tplg_fe_link_create(struct soc_tplg *tplg, 19288c2ecf20Sopenharmony_ci struct snd_soc_tplg_pcm *pcm) 19298c2ecf20Sopenharmony_ci{ 19308c2ecf20Sopenharmony_ci struct snd_soc_dai_link *link; 19318c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component *dlc; 19328c2ecf20Sopenharmony_ci int ret; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci /* link + cpu + codec + platform */ 19358c2ecf20Sopenharmony_ci link = kzalloc(sizeof(*link) + (3 * sizeof(*dlc)), GFP_KERNEL); 19368c2ecf20Sopenharmony_ci if (link == NULL) 19378c2ecf20Sopenharmony_ci return -ENOMEM; 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci dlc = (struct snd_soc_dai_link_component *)(link + 1); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci link->cpus = &dlc[0]; 19428c2ecf20Sopenharmony_ci link->codecs = &dlc[1]; 19438c2ecf20Sopenharmony_ci link->platforms = &dlc[2]; 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci link->num_cpus = 1; 19468c2ecf20Sopenharmony_ci link->num_codecs = 1; 19478c2ecf20Sopenharmony_ci link->num_platforms = 1; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci link->dobj.index = tplg->index; 19508c2ecf20Sopenharmony_ci link->dobj.ops = tplg->ops; 19518c2ecf20Sopenharmony_ci link->dobj.type = SND_SOC_DOBJ_DAI_LINK; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci if (strlen(pcm->pcm_name)) { 19548c2ecf20Sopenharmony_ci link->name = kstrdup(pcm->pcm_name, GFP_KERNEL); 19558c2ecf20Sopenharmony_ci link->stream_name = kstrdup(pcm->pcm_name, GFP_KERNEL); 19568c2ecf20Sopenharmony_ci if (!link->name || !link->stream_name) { 19578c2ecf20Sopenharmony_ci ret = -ENOMEM; 19588c2ecf20Sopenharmony_ci goto err; 19598c2ecf20Sopenharmony_ci } 19608c2ecf20Sopenharmony_ci } 19618c2ecf20Sopenharmony_ci link->id = le32_to_cpu(pcm->pcm_id); 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci if (strlen(pcm->dai_name)) { 19648c2ecf20Sopenharmony_ci link->cpus->dai_name = kstrdup(pcm->dai_name, GFP_KERNEL); 19658c2ecf20Sopenharmony_ci if (!link->cpus->dai_name) { 19668c2ecf20Sopenharmony_ci ret = -ENOMEM; 19678c2ecf20Sopenharmony_ci goto err; 19688c2ecf20Sopenharmony_ci } 19698c2ecf20Sopenharmony_ci } 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci link->codecs->name = "snd-soc-dummy"; 19728c2ecf20Sopenharmony_ci link->codecs->dai_name = "snd-soc-dummy-dai"; 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci link->platforms->name = "snd-soc-dummy"; 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci /* enable DPCM */ 19778c2ecf20Sopenharmony_ci link->dynamic = 1; 19788c2ecf20Sopenharmony_ci link->dpcm_playback = le32_to_cpu(pcm->playback); 19798c2ecf20Sopenharmony_ci link->dpcm_capture = le32_to_cpu(pcm->capture); 19808c2ecf20Sopenharmony_ci if (pcm->flag_mask) 19818c2ecf20Sopenharmony_ci set_link_flags(link, 19828c2ecf20Sopenharmony_ci le32_to_cpu(pcm->flag_mask), 19838c2ecf20Sopenharmony_ci le32_to_cpu(pcm->flags)); 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci /* pass control to component driver for optional further init */ 19868c2ecf20Sopenharmony_ci ret = soc_tplg_dai_link_load(tplg, link, NULL); 19878c2ecf20Sopenharmony_ci if (ret < 0) { 19888c2ecf20Sopenharmony_ci dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n"); 19898c2ecf20Sopenharmony_ci goto err; 19908c2ecf20Sopenharmony_ci } 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci ret = snd_soc_add_pcm_runtime(tplg->comp->card, link); 19938c2ecf20Sopenharmony_ci if (ret < 0) { 19948c2ecf20Sopenharmony_ci dev_err(tplg->comp->dev, "ASoC: adding FE link failed\n"); 19958c2ecf20Sopenharmony_ci goto err; 19968c2ecf20Sopenharmony_ci } 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci list_add(&link->dobj.list, &tplg->comp->dobj_list); 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci return 0; 20018c2ecf20Sopenharmony_cierr: 20028c2ecf20Sopenharmony_ci kfree(link->name); 20038c2ecf20Sopenharmony_ci kfree(link->stream_name); 20048c2ecf20Sopenharmony_ci kfree(link->cpus->dai_name); 20058c2ecf20Sopenharmony_ci kfree(link); 20068c2ecf20Sopenharmony_ci return ret; 20078c2ecf20Sopenharmony_ci} 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci/* create a FE DAI and DAI link from the PCM object */ 20108c2ecf20Sopenharmony_cistatic int soc_tplg_pcm_create(struct soc_tplg *tplg, 20118c2ecf20Sopenharmony_ci struct snd_soc_tplg_pcm *pcm) 20128c2ecf20Sopenharmony_ci{ 20138c2ecf20Sopenharmony_ci int ret; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci ret = soc_tplg_dai_create(tplg, pcm); 20168c2ecf20Sopenharmony_ci if (ret < 0) 20178c2ecf20Sopenharmony_ci return ret; 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci return soc_tplg_fe_link_create(tplg, pcm); 20208c2ecf20Sopenharmony_ci} 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci/* copy stream caps from the old version 4 of source */ 20238c2ecf20Sopenharmony_cistatic void stream_caps_new_ver(struct snd_soc_tplg_stream_caps *dest, 20248c2ecf20Sopenharmony_ci struct snd_soc_tplg_stream_caps_v4 *src) 20258c2ecf20Sopenharmony_ci{ 20268c2ecf20Sopenharmony_ci dest->size = cpu_to_le32(sizeof(*dest)); 20278c2ecf20Sopenharmony_ci memcpy(dest->name, src->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 20288c2ecf20Sopenharmony_ci dest->formats = src->formats; 20298c2ecf20Sopenharmony_ci dest->rates = src->rates; 20308c2ecf20Sopenharmony_ci dest->rate_min = src->rate_min; 20318c2ecf20Sopenharmony_ci dest->rate_max = src->rate_max; 20328c2ecf20Sopenharmony_ci dest->channels_min = src->channels_min; 20338c2ecf20Sopenharmony_ci dest->channels_max = src->channels_max; 20348c2ecf20Sopenharmony_ci dest->periods_min = src->periods_min; 20358c2ecf20Sopenharmony_ci dest->periods_max = src->periods_max; 20368c2ecf20Sopenharmony_ci dest->period_size_min = src->period_size_min; 20378c2ecf20Sopenharmony_ci dest->period_size_max = src->period_size_max; 20388c2ecf20Sopenharmony_ci dest->buffer_size_min = src->buffer_size_min; 20398c2ecf20Sopenharmony_ci dest->buffer_size_max = src->buffer_size_max; 20408c2ecf20Sopenharmony_ci} 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci/** 20438c2ecf20Sopenharmony_ci * pcm_new_ver - Create the new version of PCM from the old version. 20448c2ecf20Sopenharmony_ci * @tplg: topology context 20458c2ecf20Sopenharmony_ci * @src: older version of pcm as a source 20468c2ecf20Sopenharmony_ci * @pcm: latest version of pcm created from the source 20478c2ecf20Sopenharmony_ci * 20488c2ecf20Sopenharmony_ci * Support from vesion 4. User should free the returned pcm manually. 20498c2ecf20Sopenharmony_ci */ 20508c2ecf20Sopenharmony_cistatic int pcm_new_ver(struct soc_tplg *tplg, 20518c2ecf20Sopenharmony_ci struct snd_soc_tplg_pcm *src, 20528c2ecf20Sopenharmony_ci struct snd_soc_tplg_pcm **pcm) 20538c2ecf20Sopenharmony_ci{ 20548c2ecf20Sopenharmony_ci struct snd_soc_tplg_pcm *dest; 20558c2ecf20Sopenharmony_ci struct snd_soc_tplg_pcm_v4 *src_v4; 20568c2ecf20Sopenharmony_ci int i; 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci *pcm = NULL; 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci if (le32_to_cpu(src->size) != sizeof(*src_v4)) { 20618c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid PCM size\n"); 20628c2ecf20Sopenharmony_ci return -EINVAL; 20638c2ecf20Sopenharmony_ci } 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci dev_warn(tplg->dev, "ASoC: old version of PCM\n"); 20668c2ecf20Sopenharmony_ci src_v4 = (struct snd_soc_tplg_pcm_v4 *)src; 20678c2ecf20Sopenharmony_ci dest = kzalloc(sizeof(*dest), GFP_KERNEL); 20688c2ecf20Sopenharmony_ci if (!dest) 20698c2ecf20Sopenharmony_ci return -ENOMEM; 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci dest->size = cpu_to_le32(sizeof(*dest)); /* size of latest abi version */ 20728c2ecf20Sopenharmony_ci memcpy(dest->pcm_name, src_v4->pcm_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 20738c2ecf20Sopenharmony_ci memcpy(dest->dai_name, src_v4->dai_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 20748c2ecf20Sopenharmony_ci dest->pcm_id = src_v4->pcm_id; 20758c2ecf20Sopenharmony_ci dest->dai_id = src_v4->dai_id; 20768c2ecf20Sopenharmony_ci dest->playback = src_v4->playback; 20778c2ecf20Sopenharmony_ci dest->capture = src_v4->capture; 20788c2ecf20Sopenharmony_ci dest->compress = src_v4->compress; 20798c2ecf20Sopenharmony_ci dest->num_streams = src_v4->num_streams; 20808c2ecf20Sopenharmony_ci for (i = 0; i < le32_to_cpu(dest->num_streams); i++) 20818c2ecf20Sopenharmony_ci memcpy(&dest->stream[i], &src_v4->stream[i], 20828c2ecf20Sopenharmony_ci sizeof(struct snd_soc_tplg_stream)); 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) 20858c2ecf20Sopenharmony_ci stream_caps_new_ver(&dest->caps[i], &src_v4->caps[i]); 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci *pcm = dest; 20888c2ecf20Sopenharmony_ci return 0; 20898c2ecf20Sopenharmony_ci} 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_cistatic int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, 20928c2ecf20Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 20938c2ecf20Sopenharmony_ci{ 20948c2ecf20Sopenharmony_ci struct snd_soc_tplg_pcm *pcm, *_pcm; 20958c2ecf20Sopenharmony_ci int count; 20968c2ecf20Sopenharmony_ci int size; 20978c2ecf20Sopenharmony_ci int i; 20988c2ecf20Sopenharmony_ci bool abi_match; 20998c2ecf20Sopenharmony_ci int ret; 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci count = le32_to_cpu(hdr->count); 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci /* check the element size and count */ 21048c2ecf20Sopenharmony_ci pcm = (struct snd_soc_tplg_pcm *)tplg->pos; 21058c2ecf20Sopenharmony_ci size = le32_to_cpu(pcm->size); 21068c2ecf20Sopenharmony_ci if (size > sizeof(struct snd_soc_tplg_pcm) 21078c2ecf20Sopenharmony_ci || size < sizeof(struct snd_soc_tplg_pcm_v4)) { 21088c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid size %d for PCM elems\n", 21098c2ecf20Sopenharmony_ci size); 21108c2ecf20Sopenharmony_ci return -EINVAL; 21118c2ecf20Sopenharmony_ci } 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci if (soc_tplg_check_elem_count(tplg, 21148c2ecf20Sopenharmony_ci size, count, 21158c2ecf20Sopenharmony_ci le32_to_cpu(hdr->payload_size), 21168c2ecf20Sopenharmony_ci "PCM DAI")) { 21178c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n", 21188c2ecf20Sopenharmony_ci count); 21198c2ecf20Sopenharmony_ci return -EINVAL; 21208c2ecf20Sopenharmony_ci } 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 21238c2ecf20Sopenharmony_ci pcm = (struct snd_soc_tplg_pcm *)tplg->pos; 21248c2ecf20Sopenharmony_ci size = le32_to_cpu(pcm->size); 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci /* check ABI version by size, create a new version of pcm 21278c2ecf20Sopenharmony_ci * if abi not match. 21288c2ecf20Sopenharmony_ci */ 21298c2ecf20Sopenharmony_ci if (size == sizeof(*pcm)) { 21308c2ecf20Sopenharmony_ci abi_match = true; 21318c2ecf20Sopenharmony_ci _pcm = pcm; 21328c2ecf20Sopenharmony_ci } else { 21338c2ecf20Sopenharmony_ci abi_match = false; 21348c2ecf20Sopenharmony_ci ret = pcm_new_ver(tplg, pcm, &_pcm); 21358c2ecf20Sopenharmony_ci if (ret < 0) 21368c2ecf20Sopenharmony_ci return ret; 21378c2ecf20Sopenharmony_ci } 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci /* create the FE DAIs and DAI links */ 21408c2ecf20Sopenharmony_ci ret = soc_tplg_pcm_create(tplg, _pcm); 21418c2ecf20Sopenharmony_ci if (ret < 0) { 21428c2ecf20Sopenharmony_ci if (!abi_match) 21438c2ecf20Sopenharmony_ci kfree(_pcm); 21448c2ecf20Sopenharmony_ci return ret; 21458c2ecf20Sopenharmony_ci } 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci /* offset by version-specific struct size and 21488c2ecf20Sopenharmony_ci * real priv data size 21498c2ecf20Sopenharmony_ci */ 21508c2ecf20Sopenharmony_ci tplg->pos += size + le32_to_cpu(_pcm->priv.size); 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci if (!abi_match) 21538c2ecf20Sopenharmony_ci kfree(_pcm); /* free the duplicated one */ 21548c2ecf20Sopenharmony_ci } 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci return 0; 21598c2ecf20Sopenharmony_ci} 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci/** 21628c2ecf20Sopenharmony_ci * set_link_hw_format - Set the HW audio format of the physical DAI link. 21638c2ecf20Sopenharmony_ci * @link: &snd_soc_dai_link which should be updated 21648c2ecf20Sopenharmony_ci * @cfg: physical link configs. 21658c2ecf20Sopenharmony_ci * 21668c2ecf20Sopenharmony_ci * Topology context contains a list of supported HW formats (configs) and 21678c2ecf20Sopenharmony_ci * a default format ID for the physical link. This function will use this 21688c2ecf20Sopenharmony_ci * default ID to choose the HW format to set the link's DAI format for init. 21698c2ecf20Sopenharmony_ci */ 21708c2ecf20Sopenharmony_cistatic void set_link_hw_format(struct snd_soc_dai_link *link, 21718c2ecf20Sopenharmony_ci struct snd_soc_tplg_link_config *cfg) 21728c2ecf20Sopenharmony_ci{ 21738c2ecf20Sopenharmony_ci struct snd_soc_tplg_hw_config *hw_config; 21748c2ecf20Sopenharmony_ci unsigned char bclk_master, fsync_master; 21758c2ecf20Sopenharmony_ci unsigned char invert_bclk, invert_fsync; 21768c2ecf20Sopenharmony_ci int i; 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci for (i = 0; i < le32_to_cpu(cfg->num_hw_configs); i++) { 21798c2ecf20Sopenharmony_ci hw_config = &cfg->hw_config[i]; 21808c2ecf20Sopenharmony_ci if (hw_config->id != cfg->default_hw_config_id) 21818c2ecf20Sopenharmony_ci continue; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci link->dai_fmt = le32_to_cpu(hw_config->fmt) & 21848c2ecf20Sopenharmony_ci SND_SOC_DAIFMT_FORMAT_MASK; 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci /* clock gating */ 21878c2ecf20Sopenharmony_ci switch (hw_config->clock_gated) { 21888c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAI_CLK_GATE_GATED: 21898c2ecf20Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_GATED; 21908c2ecf20Sopenharmony_ci break; 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci case SND_SOC_TPLG_DAI_CLK_GATE_CONT: 21938c2ecf20Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_CONT; 21948c2ecf20Sopenharmony_ci break; 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci default: 21978c2ecf20Sopenharmony_ci /* ignore the value */ 21988c2ecf20Sopenharmony_ci break; 21998c2ecf20Sopenharmony_ci } 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci /* clock signal polarity */ 22028c2ecf20Sopenharmony_ci invert_bclk = hw_config->invert_bclk; 22038c2ecf20Sopenharmony_ci invert_fsync = hw_config->invert_fsync; 22048c2ecf20Sopenharmony_ci if (!invert_bclk && !invert_fsync) 22058c2ecf20Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_NB_NF; 22068c2ecf20Sopenharmony_ci else if (!invert_bclk && invert_fsync) 22078c2ecf20Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_NB_IF; 22088c2ecf20Sopenharmony_ci else if (invert_bclk && !invert_fsync) 22098c2ecf20Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_IB_NF; 22108c2ecf20Sopenharmony_ci else 22118c2ecf20Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_IB_IF; 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci /* clock masters */ 22148c2ecf20Sopenharmony_ci bclk_master = (hw_config->bclk_master == 22158c2ecf20Sopenharmony_ci SND_SOC_TPLG_BCLK_CM); 22168c2ecf20Sopenharmony_ci fsync_master = (hw_config->fsync_master == 22178c2ecf20Sopenharmony_ci SND_SOC_TPLG_FSYNC_CM); 22188c2ecf20Sopenharmony_ci if (bclk_master && fsync_master) 22198c2ecf20Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; 22208c2ecf20Sopenharmony_ci else if (!bclk_master && fsync_master) 22218c2ecf20Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; 22228c2ecf20Sopenharmony_ci else if (bclk_master && !fsync_master) 22238c2ecf20Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; 22248c2ecf20Sopenharmony_ci else 22258c2ecf20Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; 22268c2ecf20Sopenharmony_ci } 22278c2ecf20Sopenharmony_ci} 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci/** 22308c2ecf20Sopenharmony_ci * link_new_ver - Create a new physical link config from the old 22318c2ecf20Sopenharmony_ci * version of source. 22328c2ecf20Sopenharmony_ci * @tplg: topology context 22338c2ecf20Sopenharmony_ci * @src: old version of phyical link config as a source 22348c2ecf20Sopenharmony_ci * @link: latest version of physical link config created from the source 22358c2ecf20Sopenharmony_ci * 22368c2ecf20Sopenharmony_ci * Support from vesion 4. User need free the returned link config manually. 22378c2ecf20Sopenharmony_ci */ 22388c2ecf20Sopenharmony_cistatic int link_new_ver(struct soc_tplg *tplg, 22398c2ecf20Sopenharmony_ci struct snd_soc_tplg_link_config *src, 22408c2ecf20Sopenharmony_ci struct snd_soc_tplg_link_config **link) 22418c2ecf20Sopenharmony_ci{ 22428c2ecf20Sopenharmony_ci struct snd_soc_tplg_link_config *dest; 22438c2ecf20Sopenharmony_ci struct snd_soc_tplg_link_config_v4 *src_v4; 22448c2ecf20Sopenharmony_ci int i; 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci *link = NULL; 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci if (le32_to_cpu(src->size) != 22498c2ecf20Sopenharmony_ci sizeof(struct snd_soc_tplg_link_config_v4)) { 22508c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid physical link config size\n"); 22518c2ecf20Sopenharmony_ci return -EINVAL; 22528c2ecf20Sopenharmony_ci } 22538c2ecf20Sopenharmony_ci 22548c2ecf20Sopenharmony_ci dev_warn(tplg->dev, "ASoC: old version of physical link config\n"); 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci src_v4 = (struct snd_soc_tplg_link_config_v4 *)src; 22578c2ecf20Sopenharmony_ci dest = kzalloc(sizeof(*dest), GFP_KERNEL); 22588c2ecf20Sopenharmony_ci if (!dest) 22598c2ecf20Sopenharmony_ci return -ENOMEM; 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci dest->size = cpu_to_le32(sizeof(*dest)); 22628c2ecf20Sopenharmony_ci dest->id = src_v4->id; 22638c2ecf20Sopenharmony_ci dest->num_streams = src_v4->num_streams; 22648c2ecf20Sopenharmony_ci for (i = 0; i < le32_to_cpu(dest->num_streams); i++) 22658c2ecf20Sopenharmony_ci memcpy(&dest->stream[i], &src_v4->stream[i], 22668c2ecf20Sopenharmony_ci sizeof(struct snd_soc_tplg_stream)); 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci *link = dest; 22698c2ecf20Sopenharmony_ci return 0; 22708c2ecf20Sopenharmony_ci} 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci/** 22738c2ecf20Sopenharmony_ci * snd_soc_find_dai_link - Find a DAI link 22748c2ecf20Sopenharmony_ci * 22758c2ecf20Sopenharmony_ci * @card: soc card 22768c2ecf20Sopenharmony_ci * @id: DAI link ID to match 22778c2ecf20Sopenharmony_ci * @name: DAI link name to match, optional 22788c2ecf20Sopenharmony_ci * @stream_name: DAI link stream name to match, optional 22798c2ecf20Sopenharmony_ci * 22808c2ecf20Sopenharmony_ci * This function will search all existing DAI links of the soc card to 22818c2ecf20Sopenharmony_ci * find the link of the same ID. Since DAI links may not have their 22828c2ecf20Sopenharmony_ci * unique ID, so name and stream name should also match if being 22838c2ecf20Sopenharmony_ci * specified. 22848c2ecf20Sopenharmony_ci * 22858c2ecf20Sopenharmony_ci * Return: pointer of DAI link, or NULL if not found. 22868c2ecf20Sopenharmony_ci */ 22878c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card, 22888c2ecf20Sopenharmony_ci int id, const char *name, 22898c2ecf20Sopenharmony_ci const char *stream_name) 22908c2ecf20Sopenharmony_ci{ 22918c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 22928c2ecf20Sopenharmony_ci struct snd_soc_dai_link *link; 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci for_each_card_rtds(card, rtd) { 22958c2ecf20Sopenharmony_ci link = rtd->dai_link; 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci if (link->id != id) 22988c2ecf20Sopenharmony_ci continue; 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci if (name && (!link->name || strcmp(name, link->name))) 23018c2ecf20Sopenharmony_ci continue; 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci if (stream_name && (!link->stream_name 23048c2ecf20Sopenharmony_ci || strcmp(stream_name, link->stream_name))) 23058c2ecf20Sopenharmony_ci continue; 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci return link; 23088c2ecf20Sopenharmony_ci } 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci return NULL; 23118c2ecf20Sopenharmony_ci} 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci/* Find and configure an existing physical DAI link */ 23148c2ecf20Sopenharmony_cistatic int soc_tplg_link_config(struct soc_tplg *tplg, 23158c2ecf20Sopenharmony_ci struct snd_soc_tplg_link_config *cfg) 23168c2ecf20Sopenharmony_ci{ 23178c2ecf20Sopenharmony_ci struct snd_soc_dai_link *link; 23188c2ecf20Sopenharmony_ci const char *name, *stream_name; 23198c2ecf20Sopenharmony_ci size_t len; 23208c2ecf20Sopenharmony_ci int ret; 23218c2ecf20Sopenharmony_ci 23228c2ecf20Sopenharmony_ci len = strnlen(cfg->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 23238c2ecf20Sopenharmony_ci if (len == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 23248c2ecf20Sopenharmony_ci return -EINVAL; 23258c2ecf20Sopenharmony_ci else if (len) 23268c2ecf20Sopenharmony_ci name = cfg->name; 23278c2ecf20Sopenharmony_ci else 23288c2ecf20Sopenharmony_ci name = NULL; 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci len = strnlen(cfg->stream_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 23318c2ecf20Sopenharmony_ci if (len == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 23328c2ecf20Sopenharmony_ci return -EINVAL; 23338c2ecf20Sopenharmony_ci else if (len) 23348c2ecf20Sopenharmony_ci stream_name = cfg->stream_name; 23358c2ecf20Sopenharmony_ci else 23368c2ecf20Sopenharmony_ci stream_name = NULL; 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci link = snd_soc_find_dai_link(tplg->comp->card, le32_to_cpu(cfg->id), 23398c2ecf20Sopenharmony_ci name, stream_name); 23408c2ecf20Sopenharmony_ci if (!link) { 23418c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: physical link %s (id %d) not exist\n", 23428c2ecf20Sopenharmony_ci name, cfg->id); 23438c2ecf20Sopenharmony_ci return -EINVAL; 23448c2ecf20Sopenharmony_ci } 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci /* hw format */ 23478c2ecf20Sopenharmony_ci if (cfg->num_hw_configs) 23488c2ecf20Sopenharmony_ci set_link_hw_format(link, cfg); 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ci /* flags */ 23518c2ecf20Sopenharmony_ci if (cfg->flag_mask) 23528c2ecf20Sopenharmony_ci set_link_flags(link, 23538c2ecf20Sopenharmony_ci le32_to_cpu(cfg->flag_mask), 23548c2ecf20Sopenharmony_ci le32_to_cpu(cfg->flags)); 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_ci /* pass control to component driver for optional further init */ 23578c2ecf20Sopenharmony_ci ret = soc_tplg_dai_link_load(tplg, link, cfg); 23588c2ecf20Sopenharmony_ci if (ret < 0) { 23598c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: physical link loading failed\n"); 23608c2ecf20Sopenharmony_ci return ret; 23618c2ecf20Sopenharmony_ci } 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci /* for unloading it in snd_soc_tplg_component_remove */ 23648c2ecf20Sopenharmony_ci link->dobj.index = tplg->index; 23658c2ecf20Sopenharmony_ci link->dobj.ops = tplg->ops; 23668c2ecf20Sopenharmony_ci link->dobj.type = SND_SOC_DOBJ_BACKEND_LINK; 23678c2ecf20Sopenharmony_ci list_add(&link->dobj.list, &tplg->comp->dobj_list); 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci return 0; 23708c2ecf20Sopenharmony_ci} 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci/* Load physical link config elements from the topology context */ 23748c2ecf20Sopenharmony_cistatic int soc_tplg_link_elems_load(struct soc_tplg *tplg, 23758c2ecf20Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 23768c2ecf20Sopenharmony_ci{ 23778c2ecf20Sopenharmony_ci struct snd_soc_tplg_link_config *link, *_link; 23788c2ecf20Sopenharmony_ci int count; 23798c2ecf20Sopenharmony_ci int size; 23808c2ecf20Sopenharmony_ci int i, ret; 23818c2ecf20Sopenharmony_ci bool abi_match; 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci count = le32_to_cpu(hdr->count); 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_ci /* check the element size and count */ 23868c2ecf20Sopenharmony_ci link = (struct snd_soc_tplg_link_config *)tplg->pos; 23878c2ecf20Sopenharmony_ci size = le32_to_cpu(link->size); 23888c2ecf20Sopenharmony_ci if (size > sizeof(struct snd_soc_tplg_link_config) 23898c2ecf20Sopenharmony_ci || size < sizeof(struct snd_soc_tplg_link_config_v4)) { 23908c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid size %d for physical link elems\n", 23918c2ecf20Sopenharmony_ci size); 23928c2ecf20Sopenharmony_ci return -EINVAL; 23938c2ecf20Sopenharmony_ci } 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci if (soc_tplg_check_elem_count(tplg, 23968c2ecf20Sopenharmony_ci size, count, 23978c2ecf20Sopenharmony_ci le32_to_cpu(hdr->payload_size), 23988c2ecf20Sopenharmony_ci "physical link config")) { 23998c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid count %d for physical link elems\n", 24008c2ecf20Sopenharmony_ci count); 24018c2ecf20Sopenharmony_ci return -EINVAL; 24028c2ecf20Sopenharmony_ci } 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci /* config physical DAI links */ 24058c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 24068c2ecf20Sopenharmony_ci link = (struct snd_soc_tplg_link_config *)tplg->pos; 24078c2ecf20Sopenharmony_ci size = le32_to_cpu(link->size); 24088c2ecf20Sopenharmony_ci if (size == sizeof(*link)) { 24098c2ecf20Sopenharmony_ci abi_match = true; 24108c2ecf20Sopenharmony_ci _link = link; 24118c2ecf20Sopenharmony_ci } else { 24128c2ecf20Sopenharmony_ci abi_match = false; 24138c2ecf20Sopenharmony_ci ret = link_new_ver(tplg, link, &_link); 24148c2ecf20Sopenharmony_ci if (ret < 0) 24158c2ecf20Sopenharmony_ci return ret; 24168c2ecf20Sopenharmony_ci } 24178c2ecf20Sopenharmony_ci 24188c2ecf20Sopenharmony_ci ret = soc_tplg_link_config(tplg, _link); 24198c2ecf20Sopenharmony_ci if (ret < 0) { 24208c2ecf20Sopenharmony_ci if (!abi_match) 24218c2ecf20Sopenharmony_ci kfree(_link); 24228c2ecf20Sopenharmony_ci return ret; 24238c2ecf20Sopenharmony_ci } 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci /* offset by version-specific struct size and 24268c2ecf20Sopenharmony_ci * real priv data size 24278c2ecf20Sopenharmony_ci */ 24288c2ecf20Sopenharmony_ci tplg->pos += size + le32_to_cpu(_link->priv.size); 24298c2ecf20Sopenharmony_ci 24308c2ecf20Sopenharmony_ci if (!abi_match) 24318c2ecf20Sopenharmony_ci kfree(_link); /* free the duplicated one */ 24328c2ecf20Sopenharmony_ci } 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_ci return 0; 24358c2ecf20Sopenharmony_ci} 24368c2ecf20Sopenharmony_ci 24378c2ecf20Sopenharmony_ci/** 24388c2ecf20Sopenharmony_ci * soc_tplg_dai_config - Find and configure an existing physical DAI. 24398c2ecf20Sopenharmony_ci * @tplg: topology context 24408c2ecf20Sopenharmony_ci * @d: physical DAI configs. 24418c2ecf20Sopenharmony_ci * 24428c2ecf20Sopenharmony_ci * The physical dai should already be registered by the platform driver. 24438c2ecf20Sopenharmony_ci * The platform driver should specify the DAI name and ID for matching. 24448c2ecf20Sopenharmony_ci */ 24458c2ecf20Sopenharmony_cistatic int soc_tplg_dai_config(struct soc_tplg *tplg, 24468c2ecf20Sopenharmony_ci struct snd_soc_tplg_dai *d) 24478c2ecf20Sopenharmony_ci{ 24488c2ecf20Sopenharmony_ci struct snd_soc_dai_link_component dai_component; 24498c2ecf20Sopenharmony_ci struct snd_soc_dai *dai; 24508c2ecf20Sopenharmony_ci struct snd_soc_dai_driver *dai_drv; 24518c2ecf20Sopenharmony_ci struct snd_soc_pcm_stream *stream; 24528c2ecf20Sopenharmony_ci struct snd_soc_tplg_stream_caps *caps; 24538c2ecf20Sopenharmony_ci int ret; 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci memset(&dai_component, 0, sizeof(dai_component)); 24568c2ecf20Sopenharmony_ci 24578c2ecf20Sopenharmony_ci dai_component.dai_name = d->dai_name; 24588c2ecf20Sopenharmony_ci dai = snd_soc_find_dai(&dai_component); 24598c2ecf20Sopenharmony_ci if (!dai) { 24608c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: physical DAI %s not registered\n", 24618c2ecf20Sopenharmony_ci d->dai_name); 24628c2ecf20Sopenharmony_ci return -EINVAL; 24638c2ecf20Sopenharmony_ci } 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci if (le32_to_cpu(d->dai_id) != dai->id) { 24668c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: physical DAI %s id mismatch\n", 24678c2ecf20Sopenharmony_ci d->dai_name); 24688c2ecf20Sopenharmony_ci return -EINVAL; 24698c2ecf20Sopenharmony_ci } 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci dai_drv = dai->driver; 24728c2ecf20Sopenharmony_ci if (!dai_drv) 24738c2ecf20Sopenharmony_ci return -EINVAL; 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ci if (d->playback) { 24768c2ecf20Sopenharmony_ci stream = &dai_drv->playback; 24778c2ecf20Sopenharmony_ci caps = &d->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; 24788c2ecf20Sopenharmony_ci ret = set_stream_info(stream, caps); 24798c2ecf20Sopenharmony_ci if (ret < 0) 24808c2ecf20Sopenharmony_ci goto err; 24818c2ecf20Sopenharmony_ci } 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci if (d->capture) { 24848c2ecf20Sopenharmony_ci stream = &dai_drv->capture; 24858c2ecf20Sopenharmony_ci caps = &d->caps[SND_SOC_TPLG_STREAM_CAPTURE]; 24868c2ecf20Sopenharmony_ci ret = set_stream_info(stream, caps); 24878c2ecf20Sopenharmony_ci if (ret < 0) 24888c2ecf20Sopenharmony_ci goto err; 24898c2ecf20Sopenharmony_ci } 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci if (d->flag_mask) 24928c2ecf20Sopenharmony_ci set_dai_flags(dai_drv, 24938c2ecf20Sopenharmony_ci le32_to_cpu(d->flag_mask), 24948c2ecf20Sopenharmony_ci le32_to_cpu(d->flags)); 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci /* pass control to component driver for optional further init */ 24978c2ecf20Sopenharmony_ci ret = soc_tplg_dai_load(tplg, dai_drv, NULL, dai); 24988c2ecf20Sopenharmony_ci if (ret < 0) { 24998c2ecf20Sopenharmony_ci dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); 25008c2ecf20Sopenharmony_ci goto err; 25018c2ecf20Sopenharmony_ci } 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci return 0; 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_cierr: 25068c2ecf20Sopenharmony_ci kfree(dai_drv->playback.stream_name); 25078c2ecf20Sopenharmony_ci kfree(dai_drv->capture.stream_name); 25088c2ecf20Sopenharmony_ci return ret; 25098c2ecf20Sopenharmony_ci} 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci/* load physical DAI elements */ 25128c2ecf20Sopenharmony_cistatic int soc_tplg_dai_elems_load(struct soc_tplg *tplg, 25138c2ecf20Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 25148c2ecf20Sopenharmony_ci{ 25158c2ecf20Sopenharmony_ci struct snd_soc_tplg_dai *dai; 25168c2ecf20Sopenharmony_ci int count; 25178c2ecf20Sopenharmony_ci int i, ret; 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci count = le32_to_cpu(hdr->count); 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci /* config the existing BE DAIs */ 25228c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 25238c2ecf20Sopenharmony_ci dai = (struct snd_soc_tplg_dai *)tplg->pos; 25248c2ecf20Sopenharmony_ci if (le32_to_cpu(dai->size) != sizeof(*dai)) { 25258c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid physical DAI size\n"); 25268c2ecf20Sopenharmony_ci return -EINVAL; 25278c2ecf20Sopenharmony_ci } 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci ret = soc_tplg_dai_config(tplg, dai); 25308c2ecf20Sopenharmony_ci if (ret < 0) { 25318c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to configure DAI\n"); 25328c2ecf20Sopenharmony_ci return ret; 25338c2ecf20Sopenharmony_ci } 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_ci tplg->pos += (sizeof(*dai) + le32_to_cpu(dai->priv.size)); 25368c2ecf20Sopenharmony_ci } 25378c2ecf20Sopenharmony_ci 25388c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: Configure %d BE DAIs\n", count); 25398c2ecf20Sopenharmony_ci return 0; 25408c2ecf20Sopenharmony_ci} 25418c2ecf20Sopenharmony_ci 25428c2ecf20Sopenharmony_ci/** 25438c2ecf20Sopenharmony_ci * manifest_new_ver - Create a new version of manifest from the old version 25448c2ecf20Sopenharmony_ci * of source. 25458c2ecf20Sopenharmony_ci * @tplg: topology context 25468c2ecf20Sopenharmony_ci * @src: old version of manifest as a source 25478c2ecf20Sopenharmony_ci * @manifest: latest version of manifest created from the source 25488c2ecf20Sopenharmony_ci * 25498c2ecf20Sopenharmony_ci * Support from vesion 4. Users need free the returned manifest manually. 25508c2ecf20Sopenharmony_ci */ 25518c2ecf20Sopenharmony_cistatic int manifest_new_ver(struct soc_tplg *tplg, 25528c2ecf20Sopenharmony_ci struct snd_soc_tplg_manifest *src, 25538c2ecf20Sopenharmony_ci struct snd_soc_tplg_manifest **manifest) 25548c2ecf20Sopenharmony_ci{ 25558c2ecf20Sopenharmony_ci struct snd_soc_tplg_manifest *dest; 25568c2ecf20Sopenharmony_ci struct snd_soc_tplg_manifest_v4 *src_v4; 25578c2ecf20Sopenharmony_ci int size; 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci *manifest = NULL; 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci size = le32_to_cpu(src->size); 25628c2ecf20Sopenharmony_ci if (size != sizeof(*src_v4)) { 25638c2ecf20Sopenharmony_ci dev_warn(tplg->dev, "ASoC: invalid manifest size %d\n", 25648c2ecf20Sopenharmony_ci size); 25658c2ecf20Sopenharmony_ci if (size) 25668c2ecf20Sopenharmony_ci return -EINVAL; 25678c2ecf20Sopenharmony_ci src->size = cpu_to_le32(sizeof(*src_v4)); 25688c2ecf20Sopenharmony_ci } 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci dev_warn(tplg->dev, "ASoC: old version of manifest\n"); 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci src_v4 = (struct snd_soc_tplg_manifest_v4 *)src; 25738c2ecf20Sopenharmony_ci dest = kzalloc(sizeof(*dest) + le32_to_cpu(src_v4->priv.size), 25748c2ecf20Sopenharmony_ci GFP_KERNEL); 25758c2ecf20Sopenharmony_ci if (!dest) 25768c2ecf20Sopenharmony_ci return -ENOMEM; 25778c2ecf20Sopenharmony_ci 25788c2ecf20Sopenharmony_ci dest->size = cpu_to_le32(sizeof(*dest)); /* size of latest abi version */ 25798c2ecf20Sopenharmony_ci dest->control_elems = src_v4->control_elems; 25808c2ecf20Sopenharmony_ci dest->widget_elems = src_v4->widget_elems; 25818c2ecf20Sopenharmony_ci dest->graph_elems = src_v4->graph_elems; 25828c2ecf20Sopenharmony_ci dest->pcm_elems = src_v4->pcm_elems; 25838c2ecf20Sopenharmony_ci dest->dai_link_elems = src_v4->dai_link_elems; 25848c2ecf20Sopenharmony_ci dest->priv.size = src_v4->priv.size; 25858c2ecf20Sopenharmony_ci if (dest->priv.size) 25868c2ecf20Sopenharmony_ci memcpy(dest->priv.data, src_v4->priv.data, 25878c2ecf20Sopenharmony_ci le32_to_cpu(src_v4->priv.size)); 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_ci *manifest = dest; 25908c2ecf20Sopenharmony_ci return 0; 25918c2ecf20Sopenharmony_ci} 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_cistatic int soc_tplg_manifest_load(struct soc_tplg *tplg, 25948c2ecf20Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 25958c2ecf20Sopenharmony_ci{ 25968c2ecf20Sopenharmony_ci struct snd_soc_tplg_manifest *manifest, *_manifest; 25978c2ecf20Sopenharmony_ci bool abi_match; 25988c2ecf20Sopenharmony_ci int ret = 0; 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_ci manifest = (struct snd_soc_tplg_manifest *)tplg->pos; 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci /* check ABI version by size, create a new manifest if abi not match */ 26038c2ecf20Sopenharmony_ci if (le32_to_cpu(manifest->size) == sizeof(*manifest)) { 26048c2ecf20Sopenharmony_ci abi_match = true; 26058c2ecf20Sopenharmony_ci _manifest = manifest; 26068c2ecf20Sopenharmony_ci } else { 26078c2ecf20Sopenharmony_ci abi_match = false; 26088c2ecf20Sopenharmony_ci ret = manifest_new_ver(tplg, manifest, &_manifest); 26098c2ecf20Sopenharmony_ci if (ret < 0) 26108c2ecf20Sopenharmony_ci return ret; 26118c2ecf20Sopenharmony_ci } 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci /* pass control to component driver for optional further init */ 26148c2ecf20Sopenharmony_ci if (tplg->ops && tplg->ops->manifest) 26158c2ecf20Sopenharmony_ci ret = tplg->ops->manifest(tplg->comp, tplg->index, _manifest); 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci if (!abi_match) /* free the duplicated one */ 26188c2ecf20Sopenharmony_ci kfree(_manifest); 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci return ret; 26218c2ecf20Sopenharmony_ci} 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_ci/* validate header magic, size and type */ 26248c2ecf20Sopenharmony_cistatic int soc_valid_header(struct soc_tplg *tplg, 26258c2ecf20Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 26268c2ecf20Sopenharmony_ci{ 26278c2ecf20Sopenharmony_ci if (soc_tplg_get_hdr_offset(tplg) >= tplg->fw->size) 26288c2ecf20Sopenharmony_ci return 0; 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_ci if (le32_to_cpu(hdr->size) != sizeof(*hdr)) { 26318c2ecf20Sopenharmony_ci dev_err(tplg->dev, 26328c2ecf20Sopenharmony_ci "ASoC: invalid header size for type %d at offset 0x%lx size 0x%zx.\n", 26338c2ecf20Sopenharmony_ci le32_to_cpu(hdr->type), soc_tplg_get_hdr_offset(tplg), 26348c2ecf20Sopenharmony_ci tplg->fw->size); 26358c2ecf20Sopenharmony_ci return -EINVAL; 26368c2ecf20Sopenharmony_ci } 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci /* big endian firmware objects not supported atm */ 26398c2ecf20Sopenharmony_ci if (le32_to_cpu(hdr->magic) == SOC_TPLG_MAGIC_BIG_ENDIAN) { 26408c2ecf20Sopenharmony_ci dev_err(tplg->dev, 26418c2ecf20Sopenharmony_ci "ASoC: pass %d big endian not supported header got %x at offset 0x%lx size 0x%zx.\n", 26428c2ecf20Sopenharmony_ci tplg->pass, hdr->magic, 26438c2ecf20Sopenharmony_ci soc_tplg_get_hdr_offset(tplg), tplg->fw->size); 26448c2ecf20Sopenharmony_ci return -EINVAL; 26458c2ecf20Sopenharmony_ci } 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_ci if (le32_to_cpu(hdr->magic) != SND_SOC_TPLG_MAGIC) { 26488c2ecf20Sopenharmony_ci dev_err(tplg->dev, 26498c2ecf20Sopenharmony_ci "ASoC: pass %d does not have a valid header got %x at offset 0x%lx size 0x%zx.\n", 26508c2ecf20Sopenharmony_ci tplg->pass, hdr->magic, 26518c2ecf20Sopenharmony_ci soc_tplg_get_hdr_offset(tplg), tplg->fw->size); 26528c2ecf20Sopenharmony_ci return -EINVAL; 26538c2ecf20Sopenharmony_ci } 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_ci /* Support ABI from version 4 */ 26568c2ecf20Sopenharmony_ci if (le32_to_cpu(hdr->abi) > SND_SOC_TPLG_ABI_VERSION || 26578c2ecf20Sopenharmony_ci le32_to_cpu(hdr->abi) < SND_SOC_TPLG_ABI_VERSION_MIN) { 26588c2ecf20Sopenharmony_ci dev_err(tplg->dev, 26598c2ecf20Sopenharmony_ci "ASoC: pass %d invalid ABI version got 0x%x need 0x%x at offset 0x%lx size 0x%zx.\n", 26608c2ecf20Sopenharmony_ci tplg->pass, hdr->abi, 26618c2ecf20Sopenharmony_ci SND_SOC_TPLG_ABI_VERSION, soc_tplg_get_hdr_offset(tplg), 26628c2ecf20Sopenharmony_ci tplg->fw->size); 26638c2ecf20Sopenharmony_ci return -EINVAL; 26648c2ecf20Sopenharmony_ci } 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_ci if (hdr->payload_size == 0) { 26678c2ecf20Sopenharmony_ci dev_err(tplg->dev, "ASoC: header has 0 size at offset 0x%lx.\n", 26688c2ecf20Sopenharmony_ci soc_tplg_get_hdr_offset(tplg)); 26698c2ecf20Sopenharmony_ci return -EINVAL; 26708c2ecf20Sopenharmony_ci } 26718c2ecf20Sopenharmony_ci 26728c2ecf20Sopenharmony_ci return 1; 26738c2ecf20Sopenharmony_ci} 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci/* check header type and call appropriate handler */ 26768c2ecf20Sopenharmony_cistatic int soc_tplg_load_header(struct soc_tplg *tplg, 26778c2ecf20Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 26788c2ecf20Sopenharmony_ci{ 26798c2ecf20Sopenharmony_ci int (*elem_load)(struct soc_tplg *tplg, 26808c2ecf20Sopenharmony_ci struct snd_soc_tplg_hdr *hdr); 26818c2ecf20Sopenharmony_ci unsigned int hdr_pass; 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_ci tplg->pos = tplg->hdr_pos + sizeof(struct snd_soc_tplg_hdr); 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci /* check for matching ID */ 26868c2ecf20Sopenharmony_ci if (le32_to_cpu(hdr->index) != tplg->req_index && 26878c2ecf20Sopenharmony_ci tplg->req_index != SND_SOC_TPLG_INDEX_ALL) 26888c2ecf20Sopenharmony_ci return 0; 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci tplg->index = le32_to_cpu(hdr->index); 26918c2ecf20Sopenharmony_ci 26928c2ecf20Sopenharmony_ci switch (le32_to_cpu(hdr->type)) { 26938c2ecf20Sopenharmony_ci case SND_SOC_TPLG_TYPE_MIXER: 26948c2ecf20Sopenharmony_ci case SND_SOC_TPLG_TYPE_ENUM: 26958c2ecf20Sopenharmony_ci case SND_SOC_TPLG_TYPE_BYTES: 26968c2ecf20Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_MIXER; 26978c2ecf20Sopenharmony_ci elem_load = soc_tplg_kcontrol_elems_load; 26988c2ecf20Sopenharmony_ci break; 26998c2ecf20Sopenharmony_ci case SND_SOC_TPLG_TYPE_DAPM_GRAPH: 27008c2ecf20Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_GRAPH; 27018c2ecf20Sopenharmony_ci elem_load = soc_tplg_dapm_graph_elems_load; 27028c2ecf20Sopenharmony_ci break; 27038c2ecf20Sopenharmony_ci case SND_SOC_TPLG_TYPE_DAPM_WIDGET: 27048c2ecf20Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_WIDGET; 27058c2ecf20Sopenharmony_ci elem_load = soc_tplg_dapm_widget_elems_load; 27068c2ecf20Sopenharmony_ci break; 27078c2ecf20Sopenharmony_ci case SND_SOC_TPLG_TYPE_PCM: 27088c2ecf20Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_PCM_DAI; 27098c2ecf20Sopenharmony_ci elem_load = soc_tplg_pcm_elems_load; 27108c2ecf20Sopenharmony_ci break; 27118c2ecf20Sopenharmony_ci case SND_SOC_TPLG_TYPE_DAI: 27128c2ecf20Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_BE_DAI; 27138c2ecf20Sopenharmony_ci elem_load = soc_tplg_dai_elems_load; 27148c2ecf20Sopenharmony_ci break; 27158c2ecf20Sopenharmony_ci case SND_SOC_TPLG_TYPE_DAI_LINK: 27168c2ecf20Sopenharmony_ci case SND_SOC_TPLG_TYPE_BACKEND_LINK: 27178c2ecf20Sopenharmony_ci /* physical link configurations */ 27188c2ecf20Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_LINK; 27198c2ecf20Sopenharmony_ci elem_load = soc_tplg_link_elems_load; 27208c2ecf20Sopenharmony_ci break; 27218c2ecf20Sopenharmony_ci case SND_SOC_TPLG_TYPE_MANIFEST: 27228c2ecf20Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_MANIFEST; 27238c2ecf20Sopenharmony_ci elem_load = soc_tplg_manifest_load; 27248c2ecf20Sopenharmony_ci break; 27258c2ecf20Sopenharmony_ci default: 27268c2ecf20Sopenharmony_ci /* bespoke vendor data object */ 27278c2ecf20Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_VENDOR; 27288c2ecf20Sopenharmony_ci elem_load = soc_tplg_vendor_load; 27298c2ecf20Sopenharmony_ci break; 27308c2ecf20Sopenharmony_ci } 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci if (tplg->pass == hdr_pass) { 27338c2ecf20Sopenharmony_ci dev_dbg(tplg->dev, 27348c2ecf20Sopenharmony_ci "ASoC: Got 0x%x bytes of type %d version %d vendor %d at pass %d\n", 27358c2ecf20Sopenharmony_ci hdr->payload_size, hdr->type, hdr->version, 27368c2ecf20Sopenharmony_ci hdr->vendor_type, tplg->pass); 27378c2ecf20Sopenharmony_ci return elem_load(tplg, hdr); 27388c2ecf20Sopenharmony_ci } 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci return 0; 27418c2ecf20Sopenharmony_ci} 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci/* process the topology file headers */ 27448c2ecf20Sopenharmony_cistatic int soc_tplg_process_headers(struct soc_tplg *tplg) 27458c2ecf20Sopenharmony_ci{ 27468c2ecf20Sopenharmony_ci struct snd_soc_tplg_hdr *hdr; 27478c2ecf20Sopenharmony_ci int ret; 27488c2ecf20Sopenharmony_ci 27498c2ecf20Sopenharmony_ci tplg->pass = SOC_TPLG_PASS_START; 27508c2ecf20Sopenharmony_ci 27518c2ecf20Sopenharmony_ci /* process the header types from start to end */ 27528c2ecf20Sopenharmony_ci while (tplg->pass <= SOC_TPLG_PASS_END) { 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci tplg->hdr_pos = tplg->fw->data; 27558c2ecf20Sopenharmony_ci hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos; 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci while (!soc_tplg_is_eof(tplg)) { 27588c2ecf20Sopenharmony_ci 27598c2ecf20Sopenharmony_ci /* make sure header is valid before loading */ 27608c2ecf20Sopenharmony_ci ret = soc_valid_header(tplg, hdr); 27618c2ecf20Sopenharmony_ci if (ret < 0) { 27628c2ecf20Sopenharmony_ci dev_err(tplg->dev, 27638c2ecf20Sopenharmony_ci "ASoC: topology: invalid header: %d\n", ret); 27648c2ecf20Sopenharmony_ci return ret; 27658c2ecf20Sopenharmony_ci } else if (ret == 0) { 27668c2ecf20Sopenharmony_ci break; 27678c2ecf20Sopenharmony_ci } 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_ci /* load the header object */ 27708c2ecf20Sopenharmony_ci ret = soc_tplg_load_header(tplg, hdr); 27718c2ecf20Sopenharmony_ci if (ret < 0) { 27728c2ecf20Sopenharmony_ci dev_err(tplg->dev, 27738c2ecf20Sopenharmony_ci "ASoC: topology: could not load header: %d\n", ret); 27748c2ecf20Sopenharmony_ci return ret; 27758c2ecf20Sopenharmony_ci } 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci /* goto next header */ 27788c2ecf20Sopenharmony_ci tplg->hdr_pos += le32_to_cpu(hdr->payload_size) + 27798c2ecf20Sopenharmony_ci sizeof(struct snd_soc_tplg_hdr); 27808c2ecf20Sopenharmony_ci hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos; 27818c2ecf20Sopenharmony_ci } 27828c2ecf20Sopenharmony_ci 27838c2ecf20Sopenharmony_ci /* next data type pass */ 27848c2ecf20Sopenharmony_ci tplg->pass++; 27858c2ecf20Sopenharmony_ci } 27868c2ecf20Sopenharmony_ci 27878c2ecf20Sopenharmony_ci /* signal DAPM we are complete */ 27888c2ecf20Sopenharmony_ci ret = soc_tplg_dapm_complete(tplg); 27898c2ecf20Sopenharmony_ci if (ret < 0) 27908c2ecf20Sopenharmony_ci dev_err(tplg->dev, 27918c2ecf20Sopenharmony_ci "ASoC: failed to initialise DAPM from Firmware\n"); 27928c2ecf20Sopenharmony_ci 27938c2ecf20Sopenharmony_ci return ret; 27948c2ecf20Sopenharmony_ci} 27958c2ecf20Sopenharmony_ci 27968c2ecf20Sopenharmony_cistatic int soc_tplg_load(struct soc_tplg *tplg) 27978c2ecf20Sopenharmony_ci{ 27988c2ecf20Sopenharmony_ci int ret; 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci ret = soc_tplg_process_headers(tplg); 28018c2ecf20Sopenharmony_ci if (ret == 0) 28028c2ecf20Sopenharmony_ci soc_tplg_complete(tplg); 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_ci return ret; 28058c2ecf20Sopenharmony_ci} 28068c2ecf20Sopenharmony_ci 28078c2ecf20Sopenharmony_ci/* load audio component topology from "firmware" file */ 28088c2ecf20Sopenharmony_ciint snd_soc_tplg_component_load(struct snd_soc_component *comp, 28098c2ecf20Sopenharmony_ci struct snd_soc_tplg_ops *ops, const struct firmware *fw, u32 id) 28108c2ecf20Sopenharmony_ci{ 28118c2ecf20Sopenharmony_ci struct soc_tplg tplg; 28128c2ecf20Sopenharmony_ci int ret; 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ci /* component needs to exist to keep and reference data while parsing */ 28158c2ecf20Sopenharmony_ci if (!comp) 28168c2ecf20Sopenharmony_ci return -EINVAL; 28178c2ecf20Sopenharmony_ci 28188c2ecf20Sopenharmony_ci /* setup parsing context */ 28198c2ecf20Sopenharmony_ci memset(&tplg, 0, sizeof(tplg)); 28208c2ecf20Sopenharmony_ci tplg.fw = fw; 28218c2ecf20Sopenharmony_ci tplg.dev = comp->dev; 28228c2ecf20Sopenharmony_ci tplg.comp = comp; 28238c2ecf20Sopenharmony_ci tplg.ops = ops; 28248c2ecf20Sopenharmony_ci tplg.req_index = id; 28258c2ecf20Sopenharmony_ci tplg.io_ops = ops->io_ops; 28268c2ecf20Sopenharmony_ci tplg.io_ops_count = ops->io_ops_count; 28278c2ecf20Sopenharmony_ci tplg.bytes_ext_ops = ops->bytes_ext_ops; 28288c2ecf20Sopenharmony_ci tplg.bytes_ext_ops_count = ops->bytes_ext_ops_count; 28298c2ecf20Sopenharmony_ci 28308c2ecf20Sopenharmony_ci ret = soc_tplg_load(&tplg); 28318c2ecf20Sopenharmony_ci /* free the created components if fail to load topology */ 28328c2ecf20Sopenharmony_ci if (ret) 28338c2ecf20Sopenharmony_ci snd_soc_tplg_component_remove(comp, SND_SOC_TPLG_INDEX_ALL); 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci return ret; 28368c2ecf20Sopenharmony_ci} 28378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_tplg_component_load); 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_ci/* remove this dynamic widget */ 28408c2ecf20Sopenharmony_civoid snd_soc_tplg_widget_remove(struct snd_soc_dapm_widget *w) 28418c2ecf20Sopenharmony_ci{ 28428c2ecf20Sopenharmony_ci /* make sure we are a widget */ 28438c2ecf20Sopenharmony_ci if (w->dobj.type != SND_SOC_DOBJ_WIDGET) 28448c2ecf20Sopenharmony_ci return; 28458c2ecf20Sopenharmony_ci 28468c2ecf20Sopenharmony_ci remove_widget(w->dapm->component, &w->dobj, SOC_TPLG_PASS_WIDGET); 28478c2ecf20Sopenharmony_ci} 28488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove); 28498c2ecf20Sopenharmony_ci 28508c2ecf20Sopenharmony_ci/* remove all dynamic widgets from this DAPM context */ 28518c2ecf20Sopenharmony_civoid snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, 28528c2ecf20Sopenharmony_ci u32 index) 28538c2ecf20Sopenharmony_ci{ 28548c2ecf20Sopenharmony_ci struct snd_soc_dapm_widget *w, *next_w; 28558c2ecf20Sopenharmony_ci 28568c2ecf20Sopenharmony_ci for_each_card_widgets_safe(dapm->card, w, next_w) { 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci /* make sure we are a widget with correct context */ 28598c2ecf20Sopenharmony_ci if (w->dobj.type != SND_SOC_DOBJ_WIDGET || w->dapm != dapm) 28608c2ecf20Sopenharmony_ci continue; 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci /* match ID */ 28638c2ecf20Sopenharmony_ci if (w->dobj.index != index && 28648c2ecf20Sopenharmony_ci w->dobj.index != SND_SOC_TPLG_INDEX_ALL) 28658c2ecf20Sopenharmony_ci continue; 28668c2ecf20Sopenharmony_ci /* check and free and dynamic widget kcontrols */ 28678c2ecf20Sopenharmony_ci snd_soc_tplg_widget_remove(w); 28688c2ecf20Sopenharmony_ci snd_soc_dapm_free_widget(w); 28698c2ecf20Sopenharmony_ci } 28708c2ecf20Sopenharmony_ci snd_soc_dapm_reset_cache(dapm); 28718c2ecf20Sopenharmony_ci} 28728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all); 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_ci/* remove dynamic controls from the component driver */ 28758c2ecf20Sopenharmony_ciint snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index) 28768c2ecf20Sopenharmony_ci{ 28778c2ecf20Sopenharmony_ci struct snd_card *card = comp->card->snd_card; 28788c2ecf20Sopenharmony_ci struct snd_soc_dobj *dobj, *next_dobj; 28798c2ecf20Sopenharmony_ci int pass = SOC_TPLG_PASS_END; 28808c2ecf20Sopenharmony_ci 28818c2ecf20Sopenharmony_ci /* process the header types from end to start */ 28828c2ecf20Sopenharmony_ci while (pass >= SOC_TPLG_PASS_START) { 28838c2ecf20Sopenharmony_ci 28848c2ecf20Sopenharmony_ci /* remove mixer controls */ 28858c2ecf20Sopenharmony_ci down_write(&card->controls_rwsem); 28868c2ecf20Sopenharmony_ci list_for_each_entry_safe(dobj, next_dobj, &comp->dobj_list, 28878c2ecf20Sopenharmony_ci list) { 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_ci /* match index */ 28908c2ecf20Sopenharmony_ci if (dobj->index != index && 28918c2ecf20Sopenharmony_ci index != SND_SOC_TPLG_INDEX_ALL) 28928c2ecf20Sopenharmony_ci continue; 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_ci switch (dobj->type) { 28958c2ecf20Sopenharmony_ci case SND_SOC_DOBJ_MIXER: 28968c2ecf20Sopenharmony_ci remove_mixer(comp, dobj, pass); 28978c2ecf20Sopenharmony_ci break; 28988c2ecf20Sopenharmony_ci case SND_SOC_DOBJ_ENUM: 28998c2ecf20Sopenharmony_ci remove_enum(comp, dobj, pass); 29008c2ecf20Sopenharmony_ci break; 29018c2ecf20Sopenharmony_ci case SND_SOC_DOBJ_BYTES: 29028c2ecf20Sopenharmony_ci remove_bytes(comp, dobj, pass); 29038c2ecf20Sopenharmony_ci break; 29048c2ecf20Sopenharmony_ci case SND_SOC_DOBJ_GRAPH: 29058c2ecf20Sopenharmony_ci remove_route(comp, dobj, pass); 29068c2ecf20Sopenharmony_ci break; 29078c2ecf20Sopenharmony_ci case SND_SOC_DOBJ_WIDGET: 29088c2ecf20Sopenharmony_ci remove_widget(comp, dobj, pass); 29098c2ecf20Sopenharmony_ci break; 29108c2ecf20Sopenharmony_ci case SND_SOC_DOBJ_PCM: 29118c2ecf20Sopenharmony_ci remove_dai(comp, dobj, pass); 29128c2ecf20Sopenharmony_ci break; 29138c2ecf20Sopenharmony_ci case SND_SOC_DOBJ_DAI_LINK: 29148c2ecf20Sopenharmony_ci remove_link(comp, dobj, pass); 29158c2ecf20Sopenharmony_ci break; 29168c2ecf20Sopenharmony_ci case SND_SOC_DOBJ_BACKEND_LINK: 29178c2ecf20Sopenharmony_ci /* 29188c2ecf20Sopenharmony_ci * call link_unload ops if extra 29198c2ecf20Sopenharmony_ci * deinitialization is needed. 29208c2ecf20Sopenharmony_ci */ 29218c2ecf20Sopenharmony_ci remove_backend_link(comp, dobj, pass); 29228c2ecf20Sopenharmony_ci break; 29238c2ecf20Sopenharmony_ci default: 29248c2ecf20Sopenharmony_ci dev_err(comp->dev, "ASoC: invalid component type %d for removal\n", 29258c2ecf20Sopenharmony_ci dobj->type); 29268c2ecf20Sopenharmony_ci break; 29278c2ecf20Sopenharmony_ci } 29288c2ecf20Sopenharmony_ci } 29298c2ecf20Sopenharmony_ci up_write(&card->controls_rwsem); 29308c2ecf20Sopenharmony_ci pass--; 29318c2ecf20Sopenharmony_ci } 29328c2ecf20Sopenharmony_ci 29338c2ecf20Sopenharmony_ci /* let caller know if FW can be freed when no objects are left */ 29348c2ecf20Sopenharmony_ci return !list_empty(&comp->dobj_list); 29358c2ecf20Sopenharmony_ci} 29368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_tplg_component_remove); 2937