162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// soc-topology.c -- ALSA SoC Topology 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) 2012 Texas Instruments Inc. 662306a36Sopenharmony_ci// Copyright (C) 2015 Intel Corporation. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> 962306a36Sopenharmony_ci// K, Mythri P <mythri.p.k@intel.com> 1062306a36Sopenharmony_ci// Prusty, Subhransu S <subhransu.s.prusty@intel.com> 1162306a36Sopenharmony_ci// B, Jayachandran <jayachandran.b@intel.com> 1262306a36Sopenharmony_ci// Abdullah, Omair M <omair.m.abdullah@intel.com> 1362306a36Sopenharmony_ci// Jin, Yao <yao.jin@intel.com> 1462306a36Sopenharmony_ci// Lin, Mengdong <mengdong.lin@intel.com> 1562306a36Sopenharmony_ci// 1662306a36Sopenharmony_ci// Add support to read audio firmware topology alongside firmware text. The 1762306a36Sopenharmony_ci// topology data can contain kcontrols, DAPM graphs, widgets, DAIs, DAI links, 1862306a36Sopenharmony_ci// equalizers, firmware, coefficients etc. 1962306a36Sopenharmony_ci// 2062306a36Sopenharmony_ci// This file only manages the core ALSA and ASoC components, all other bespoke 2162306a36Sopenharmony_ci// firmware topology data is passed to component drivers for bespoke handling. 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/kernel.h> 2462306a36Sopenharmony_ci#include <linux/export.h> 2562306a36Sopenharmony_ci#include <linux/list.h> 2662306a36Sopenharmony_ci#include <linux/firmware.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci#include <sound/soc.h> 2962306a36Sopenharmony_ci#include <sound/soc-dapm.h> 3062306a36Sopenharmony_ci#include <sound/soc-topology.h> 3162306a36Sopenharmony_ci#include <sound/tlv.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define SOC_TPLG_MAGIC_BIG_ENDIAN 0x436F5341 /* ASoC in reverse */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * We make several passes over the data (since it wont necessarily be ordered) 3762306a36Sopenharmony_ci * and process objects in the following order. This guarantees the component 3862306a36Sopenharmony_ci * drivers will be ready with any vendor data before the mixers and DAPM objects 3962306a36Sopenharmony_ci * are loaded (that may make use of the vendor data). 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci#define SOC_TPLG_PASS_MANIFEST 0 4262306a36Sopenharmony_ci#define SOC_TPLG_PASS_VENDOR 1 4362306a36Sopenharmony_ci#define SOC_TPLG_PASS_CONTROL 2 4462306a36Sopenharmony_ci#define SOC_TPLG_PASS_WIDGET 3 4562306a36Sopenharmony_ci#define SOC_TPLG_PASS_PCM_DAI 4 4662306a36Sopenharmony_ci#define SOC_TPLG_PASS_GRAPH 5 4762306a36Sopenharmony_ci#define SOC_TPLG_PASS_BE_DAI 6 4862306a36Sopenharmony_ci#define SOC_TPLG_PASS_LINK 7 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST 5162306a36Sopenharmony_ci#define SOC_TPLG_PASS_END SOC_TPLG_PASS_LINK 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* topology context */ 5462306a36Sopenharmony_cistruct soc_tplg { 5562306a36Sopenharmony_ci const struct firmware *fw; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* runtime FW parsing */ 5862306a36Sopenharmony_ci const u8 *pos; /* read position */ 5962306a36Sopenharmony_ci const u8 *hdr_pos; /* header position */ 6062306a36Sopenharmony_ci unsigned int pass; /* pass number */ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* component caller */ 6362306a36Sopenharmony_ci struct device *dev; 6462306a36Sopenharmony_ci struct snd_soc_component *comp; 6562306a36Sopenharmony_ci u32 index; /* current block index */ 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* vendor specific kcontrol operations */ 6862306a36Sopenharmony_ci const struct snd_soc_tplg_kcontrol_ops *io_ops; 6962306a36Sopenharmony_ci int io_ops_count; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* vendor specific bytes ext handlers, for TLV bytes controls */ 7262306a36Sopenharmony_ci const struct snd_soc_tplg_bytes_ext_ops *bytes_ext_ops; 7362306a36Sopenharmony_ci int bytes_ext_ops_count; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* optional fw loading callbacks to component drivers */ 7662306a36Sopenharmony_ci struct snd_soc_tplg_ops *ops; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* check we dont overflow the data for this control chunk */ 8062306a36Sopenharmony_cistatic int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size, 8162306a36Sopenharmony_ci unsigned int count, size_t bytes, const char *elem_type) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci const u8 *end = tplg->pos + elem_size * count; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (end > tplg->fw->data + tplg->fw->size) { 8662306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: %s overflow end of data\n", 8762306a36Sopenharmony_ci elem_type); 8862306a36Sopenharmony_ci return -EINVAL; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* check there is enough room in chunk for control. 9262306a36Sopenharmony_ci extra bytes at the end of control are for vendor data here */ 9362306a36Sopenharmony_ci if (elem_size * count > bytes) { 9462306a36Sopenharmony_ci dev_err(tplg->dev, 9562306a36Sopenharmony_ci "ASoC: %s count %d of size %zu is bigger than chunk %zu\n", 9662306a36Sopenharmony_ci elem_type, count, elem_size, bytes); 9762306a36Sopenharmony_ci return -EINVAL; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic inline bool soc_tplg_is_eof(struct soc_tplg *tplg) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci const u8 *end = tplg->hdr_pos; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (end >= tplg->fw->data + tplg->fw->size) 10862306a36Sopenharmony_ci return true; 10962306a36Sopenharmony_ci return false; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic inline unsigned long soc_tplg_get_hdr_offset(struct soc_tplg *tplg) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci return (unsigned long)(tplg->hdr_pos - tplg->fw->data); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic inline unsigned long soc_tplg_get_offset(struct soc_tplg *tplg) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci return (unsigned long)(tplg->pos - tplg->fw->data); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* mapping of Kcontrol types and associated operations. */ 12362306a36Sopenharmony_cistatic const struct snd_soc_tplg_kcontrol_ops io_ops[] = { 12462306a36Sopenharmony_ci {SND_SOC_TPLG_CTL_VOLSW, snd_soc_get_volsw, 12562306a36Sopenharmony_ci snd_soc_put_volsw, snd_soc_info_volsw}, 12662306a36Sopenharmony_ci {SND_SOC_TPLG_CTL_VOLSW_SX, snd_soc_get_volsw_sx, 12762306a36Sopenharmony_ci snd_soc_put_volsw_sx, NULL}, 12862306a36Sopenharmony_ci {SND_SOC_TPLG_CTL_ENUM, snd_soc_get_enum_double, 12962306a36Sopenharmony_ci snd_soc_put_enum_double, snd_soc_info_enum_double}, 13062306a36Sopenharmony_ci {SND_SOC_TPLG_CTL_ENUM_VALUE, snd_soc_get_enum_double, 13162306a36Sopenharmony_ci snd_soc_put_enum_double, NULL}, 13262306a36Sopenharmony_ci {SND_SOC_TPLG_CTL_BYTES, snd_soc_bytes_get, 13362306a36Sopenharmony_ci snd_soc_bytes_put, snd_soc_bytes_info}, 13462306a36Sopenharmony_ci {SND_SOC_TPLG_CTL_RANGE, snd_soc_get_volsw_range, 13562306a36Sopenharmony_ci snd_soc_put_volsw_range, snd_soc_info_volsw_range}, 13662306a36Sopenharmony_ci {SND_SOC_TPLG_CTL_VOLSW_XR_SX, snd_soc_get_xr_sx, 13762306a36Sopenharmony_ci snd_soc_put_xr_sx, snd_soc_info_xr_sx}, 13862306a36Sopenharmony_ci {SND_SOC_TPLG_CTL_STROBE, snd_soc_get_strobe, 13962306a36Sopenharmony_ci snd_soc_put_strobe, NULL}, 14062306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_CTL_VOLSW, snd_soc_dapm_get_volsw, 14162306a36Sopenharmony_ci snd_soc_dapm_put_volsw, snd_soc_info_volsw}, 14262306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE, snd_soc_dapm_get_enum_double, 14362306a36Sopenharmony_ci snd_soc_dapm_put_enum_double, snd_soc_info_enum_double}, 14462306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT, snd_soc_dapm_get_enum_double, 14562306a36Sopenharmony_ci snd_soc_dapm_put_enum_double, NULL}, 14662306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE, snd_soc_dapm_get_enum_double, 14762306a36Sopenharmony_ci snd_soc_dapm_put_enum_double, NULL}, 14862306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_CTL_PIN, snd_soc_dapm_get_pin_switch, 14962306a36Sopenharmony_ci snd_soc_dapm_put_pin_switch, snd_soc_dapm_info_pin_switch}, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistruct soc_tplg_map { 15362306a36Sopenharmony_ci int uid; 15462306a36Sopenharmony_ci int kid; 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* mapping of widget types from UAPI IDs to kernel IDs */ 15862306a36Sopenharmony_cistatic const struct soc_tplg_map dapm_map[] = { 15962306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_INPUT, snd_soc_dapm_input}, 16062306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_OUTPUT, snd_soc_dapm_output}, 16162306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_MUX, snd_soc_dapm_mux}, 16262306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_MIXER, snd_soc_dapm_mixer}, 16362306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_PGA, snd_soc_dapm_pga}, 16462306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_OUT_DRV, snd_soc_dapm_out_drv}, 16562306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_ADC, snd_soc_dapm_adc}, 16662306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_DAC, snd_soc_dapm_dac}, 16762306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_SWITCH, snd_soc_dapm_switch}, 16862306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_PRE, snd_soc_dapm_pre}, 16962306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_POST, snd_soc_dapm_post}, 17062306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_AIF_IN, snd_soc_dapm_aif_in}, 17162306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_AIF_OUT, snd_soc_dapm_aif_out}, 17262306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_DAI_IN, snd_soc_dapm_dai_in}, 17362306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_DAI_OUT, snd_soc_dapm_dai_out}, 17462306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_DAI_LINK, snd_soc_dapm_dai_link}, 17562306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_BUFFER, snd_soc_dapm_buffer}, 17662306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_SCHEDULER, snd_soc_dapm_scheduler}, 17762306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_EFFECT, snd_soc_dapm_effect}, 17862306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_SIGGEN, snd_soc_dapm_siggen}, 17962306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_SRC, snd_soc_dapm_src}, 18062306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_ASRC, snd_soc_dapm_asrc}, 18162306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_ENCODER, snd_soc_dapm_encoder}, 18262306a36Sopenharmony_ci {SND_SOC_TPLG_DAPM_DECODER, snd_soc_dapm_decoder}, 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int tplg_chan_get_reg(struct soc_tplg *tplg, 18662306a36Sopenharmony_ci struct snd_soc_tplg_channel *chan, int map) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci int i; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) { 19162306a36Sopenharmony_ci if (le32_to_cpu(chan[i].id) == map) 19262306a36Sopenharmony_ci return le32_to_cpu(chan[i].reg); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return -EINVAL; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int tplg_chan_get_shift(struct soc_tplg *tplg, 19962306a36Sopenharmony_ci struct snd_soc_tplg_channel *chan, int map) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci int i; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) { 20462306a36Sopenharmony_ci if (le32_to_cpu(chan[i].id) == map) 20562306a36Sopenharmony_ci return le32_to_cpu(chan[i].shift); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return -EINVAL; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int get_widget_id(int tplg_type) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci int i; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dapm_map); i++) { 21662306a36Sopenharmony_ci if (tplg_type == dapm_map[i].uid) 21762306a36Sopenharmony_ci return dapm_map[i].kid; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return -EINVAL; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic inline void soc_bind_err(struct soc_tplg *tplg, 22462306a36Sopenharmony_ci struct snd_soc_tplg_ctl_hdr *hdr, int index) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci dev_err(tplg->dev, 22762306a36Sopenharmony_ci "ASoC: invalid control type (g,p,i) %d:%d:%d index %d at 0x%lx\n", 22862306a36Sopenharmony_ci hdr->ops.get, hdr->ops.put, hdr->ops.info, index, 22962306a36Sopenharmony_ci soc_tplg_get_offset(tplg)); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic inline void soc_control_err(struct soc_tplg *tplg, 23362306a36Sopenharmony_ci struct snd_soc_tplg_ctl_hdr *hdr, const char *name) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci dev_err(tplg->dev, 23662306a36Sopenharmony_ci "ASoC: no complete control IO handler for %s type (g,p,i) %d:%d:%d at 0x%lx\n", 23762306a36Sopenharmony_ci name, hdr->ops.get, hdr->ops.put, hdr->ops.info, 23862306a36Sopenharmony_ci soc_tplg_get_offset(tplg)); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* pass vendor data to component driver for processing */ 24262306a36Sopenharmony_cistatic int soc_tplg_vendor_load(struct soc_tplg *tplg, 24362306a36Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci int ret = 0; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (tplg->ops && tplg->ops->vendor_load) 24862306a36Sopenharmony_ci ret = tplg->ops->vendor_load(tplg->comp, tplg->index, hdr); 24962306a36Sopenharmony_ci else { 25062306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n", 25162306a36Sopenharmony_ci hdr->vendor_type); 25262306a36Sopenharmony_ci return -EINVAL; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (ret < 0) 25662306a36Sopenharmony_ci dev_err(tplg->dev, 25762306a36Sopenharmony_ci "ASoC: vendor load failed at hdr offset %ld/0x%lx for type %d:%d\n", 25862306a36Sopenharmony_ci soc_tplg_get_hdr_offset(tplg), 25962306a36Sopenharmony_ci soc_tplg_get_hdr_offset(tplg), 26062306a36Sopenharmony_ci hdr->type, hdr->vendor_type); 26162306a36Sopenharmony_ci return ret; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/* optionally pass new dynamic widget to component driver. This is mainly for 26562306a36Sopenharmony_ci * external widgets where we can assign private data/ops */ 26662306a36Sopenharmony_cistatic int soc_tplg_widget_load(struct soc_tplg *tplg, 26762306a36Sopenharmony_ci struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci if (tplg->ops && tplg->ops->widget_load) 27062306a36Sopenharmony_ci return tplg->ops->widget_load(tplg->comp, tplg->index, w, 27162306a36Sopenharmony_ci tplg_w); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return 0; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/* optionally pass new dynamic widget to component driver. This is mainly for 27762306a36Sopenharmony_ci * external widgets where we can assign private data/ops */ 27862306a36Sopenharmony_cistatic int soc_tplg_widget_ready(struct soc_tplg *tplg, 27962306a36Sopenharmony_ci struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci if (tplg->ops && tplg->ops->widget_ready) 28262306a36Sopenharmony_ci return tplg->ops->widget_ready(tplg->comp, tplg->index, w, 28362306a36Sopenharmony_ci tplg_w); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* pass DAI configurations to component driver for extra initialization */ 28962306a36Sopenharmony_cistatic int soc_tplg_dai_load(struct soc_tplg *tplg, 29062306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv, 29162306a36Sopenharmony_ci struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci if (tplg->ops && tplg->ops->dai_load) 29462306a36Sopenharmony_ci return tplg->ops->dai_load(tplg->comp, tplg->index, dai_drv, 29562306a36Sopenharmony_ci pcm, dai); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci/* pass link configurations to component driver for extra initialization */ 30162306a36Sopenharmony_cistatic int soc_tplg_dai_link_load(struct soc_tplg *tplg, 30262306a36Sopenharmony_ci struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci if (tplg->ops && tplg->ops->link_load) 30562306a36Sopenharmony_ci return tplg->ops->link_load(tplg->comp, tplg->index, link, cfg); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* tell the component driver that all firmware has been loaded in this request */ 31162306a36Sopenharmony_cistatic int soc_tplg_complete(struct soc_tplg *tplg) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci if (tplg->ops && tplg->ops->complete) 31462306a36Sopenharmony_ci return tplg->ops->complete(tplg->comp); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* add a dynamic kcontrol */ 32062306a36Sopenharmony_cistatic int soc_tplg_add_dcontrol(struct snd_card *card, struct device *dev, 32162306a36Sopenharmony_ci const struct snd_kcontrol_new *control_new, const char *prefix, 32262306a36Sopenharmony_ci void *data, struct snd_kcontrol **kcontrol) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci int err; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci *kcontrol = snd_soc_cnew(control_new, data, control_new->name, prefix); 32762306a36Sopenharmony_ci if (*kcontrol == NULL) { 32862306a36Sopenharmony_ci dev_err(dev, "ASoC: Failed to create new kcontrol %s\n", 32962306a36Sopenharmony_ci control_new->name); 33062306a36Sopenharmony_ci return -ENOMEM; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci err = snd_ctl_add(card, *kcontrol); 33462306a36Sopenharmony_ci if (err < 0) { 33562306a36Sopenharmony_ci dev_err(dev, "ASoC: Failed to add %s: %d\n", 33662306a36Sopenharmony_ci control_new->name, err); 33762306a36Sopenharmony_ci return err; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci/* add a dynamic kcontrol for component driver */ 34462306a36Sopenharmony_cistatic int soc_tplg_add_kcontrol(struct soc_tplg *tplg, 34562306a36Sopenharmony_ci struct snd_kcontrol_new *k, struct snd_kcontrol **kcontrol) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct snd_soc_component *comp = tplg->comp; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return soc_tplg_add_dcontrol(comp->card->snd_card, 35062306a36Sopenharmony_ci tplg->dev, k, comp->name_prefix, comp, kcontrol); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci/* remove kcontrol */ 35462306a36Sopenharmony_cistatic void soc_tplg_remove_kcontrol(struct snd_soc_component *comp, struct snd_soc_dobj *dobj, 35562306a36Sopenharmony_ci int pass) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct snd_card *card = comp->card->snd_card; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (pass != SOC_TPLG_PASS_CONTROL) 36062306a36Sopenharmony_ci return; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (dobj->unload) 36362306a36Sopenharmony_ci dobj->unload(comp, dobj); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci snd_ctl_remove(card, dobj->control.kcontrol); 36662306a36Sopenharmony_ci list_del(&dobj->list); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/* remove a route */ 37062306a36Sopenharmony_cistatic void soc_tplg_remove_route(struct snd_soc_component *comp, 37162306a36Sopenharmony_ci struct snd_soc_dobj *dobj, int pass) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci if (pass != SOC_TPLG_PASS_GRAPH) 37462306a36Sopenharmony_ci return; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (dobj->unload) 37762306a36Sopenharmony_ci dobj->unload(comp, dobj); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci list_del(&dobj->list); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci/* remove a widget and it's kcontrols - routes must be removed first */ 38362306a36Sopenharmony_cistatic void soc_tplg_remove_widget(struct snd_soc_component *comp, 38462306a36Sopenharmony_ci struct snd_soc_dobj *dobj, int pass) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct snd_card *card = comp->card->snd_card; 38762306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = 38862306a36Sopenharmony_ci container_of(dobj, struct snd_soc_dapm_widget, dobj); 38962306a36Sopenharmony_ci int i; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (pass != SOC_TPLG_PASS_WIDGET) 39262306a36Sopenharmony_ci return; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (dobj->unload) 39562306a36Sopenharmony_ci dobj->unload(comp, dobj); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (!w->kcontrols) 39862306a36Sopenharmony_ci goto free_news; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci for (i = 0; w->kcontrols && i < w->num_kcontrols; i++) 40162306a36Sopenharmony_ci snd_ctl_remove(card, w->kcontrols[i]); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cifree_news: 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci list_del(&dobj->list); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* widget w is freed by soc-dapm.c */ 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* remove DAI configurations */ 41162306a36Sopenharmony_cistatic void soc_tplg_remove_dai(struct snd_soc_component *comp, 41262306a36Sopenharmony_ci struct snd_soc_dobj *dobj, int pass) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv = 41562306a36Sopenharmony_ci container_of(dobj, struct snd_soc_dai_driver, dobj); 41662306a36Sopenharmony_ci struct snd_soc_dai *dai, *_dai; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (pass != SOC_TPLG_PASS_PCM_DAI) 41962306a36Sopenharmony_ci return; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (dobj->unload) 42262306a36Sopenharmony_ci dobj->unload(comp, dobj); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci for_each_component_dais_safe(comp, dai, _dai) 42562306a36Sopenharmony_ci if (dai->driver == dai_drv) 42662306a36Sopenharmony_ci snd_soc_unregister_dai(dai); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci list_del(&dobj->list); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci/* remove link configurations */ 43262306a36Sopenharmony_cistatic void soc_tplg_remove_link(struct snd_soc_component *comp, 43362306a36Sopenharmony_ci struct snd_soc_dobj *dobj, int pass) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct snd_soc_dai_link *link = 43662306a36Sopenharmony_ci container_of(dobj, struct snd_soc_dai_link, dobj); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (pass != SOC_TPLG_PASS_PCM_DAI) 43962306a36Sopenharmony_ci return; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (dobj->unload) 44262306a36Sopenharmony_ci dobj->unload(comp, dobj); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci list_del(&dobj->list); 44562306a36Sopenharmony_ci snd_soc_remove_pcm_runtime(comp->card, 44662306a36Sopenharmony_ci snd_soc_get_pcm_runtime(comp->card, link)); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* unload dai link */ 45062306a36Sopenharmony_cistatic void remove_backend_link(struct snd_soc_component *comp, 45162306a36Sopenharmony_ci struct snd_soc_dobj *dobj, int pass) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci if (pass != SOC_TPLG_PASS_LINK) 45462306a36Sopenharmony_ci return; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (dobj->unload) 45762306a36Sopenharmony_ci dobj->unload(comp, dobj); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* 46062306a36Sopenharmony_ci * We don't free the link here as what soc_tplg_remove_link() do since BE 46162306a36Sopenharmony_ci * links are not allocated by topology. 46262306a36Sopenharmony_ci * We however need to reset the dobj type to its initial values 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_ci dobj->type = SND_SOC_DOBJ_NONE; 46562306a36Sopenharmony_ci list_del(&dobj->list); 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci/* bind a kcontrol to it's IO handlers */ 46962306a36Sopenharmony_cistatic int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, 47062306a36Sopenharmony_ci struct snd_kcontrol_new *k, 47162306a36Sopenharmony_ci const struct soc_tplg *tplg) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci const struct snd_soc_tplg_kcontrol_ops *ops; 47462306a36Sopenharmony_ci const struct snd_soc_tplg_bytes_ext_ops *ext_ops; 47562306a36Sopenharmony_ci int num_ops, i; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (le32_to_cpu(hdr->ops.info) == SND_SOC_TPLG_CTL_BYTES 47862306a36Sopenharmony_ci && k->iface & SNDRV_CTL_ELEM_IFACE_MIXER 47962306a36Sopenharmony_ci && (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ 48062306a36Sopenharmony_ci || k->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) 48162306a36Sopenharmony_ci && k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { 48262306a36Sopenharmony_ci struct soc_bytes_ext *sbe; 48362306a36Sopenharmony_ci struct snd_soc_tplg_bytes_control *be; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci sbe = (struct soc_bytes_ext *)k->private_value; 48662306a36Sopenharmony_ci be = container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* TLV bytes controls need standard kcontrol info handler, 48962306a36Sopenharmony_ci * TLV callback and extended put/get handlers. 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_ci k->info = snd_soc_bytes_info_ext; 49262306a36Sopenharmony_ci k->tlv.c = snd_soc_bytes_tlv_callback; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* 49562306a36Sopenharmony_ci * When a topology-based implementation abuses the 49662306a36Sopenharmony_ci * control interface and uses bytes_ext controls of 49762306a36Sopenharmony_ci * more than 512 bytes, we need to disable the size 49862306a36Sopenharmony_ci * checks, otherwise accesses to such controls will 49962306a36Sopenharmony_ci * return an -EINVAL error and prevent the card from 50062306a36Sopenharmony_ci * being configured. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_ci if (sbe->max > 512) 50362306a36Sopenharmony_ci k->access |= SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci ext_ops = tplg->bytes_ext_ops; 50662306a36Sopenharmony_ci num_ops = tplg->bytes_ext_ops_count; 50762306a36Sopenharmony_ci for (i = 0; i < num_ops; i++) { 50862306a36Sopenharmony_ci if (!sbe->put && 50962306a36Sopenharmony_ci ext_ops[i].id == le32_to_cpu(be->ext_ops.put)) 51062306a36Sopenharmony_ci sbe->put = ext_ops[i].put; 51162306a36Sopenharmony_ci if (!sbe->get && 51262306a36Sopenharmony_ci ext_ops[i].id == le32_to_cpu(be->ext_ops.get)) 51362306a36Sopenharmony_ci sbe->get = ext_ops[i].get; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) && !sbe->get) 51762306a36Sopenharmony_ci return -EINVAL; 51862306a36Sopenharmony_ci if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) && !sbe->put) 51962306a36Sopenharmony_ci return -EINVAL; 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* try and map vendor specific kcontrol handlers first */ 52462306a36Sopenharmony_ci ops = tplg->io_ops; 52562306a36Sopenharmony_ci num_ops = tplg->io_ops_count; 52662306a36Sopenharmony_ci for (i = 0; i < num_ops; i++) { 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (k->put == NULL && ops[i].id == le32_to_cpu(hdr->ops.put)) 52962306a36Sopenharmony_ci k->put = ops[i].put; 53062306a36Sopenharmony_ci if (k->get == NULL && ops[i].id == le32_to_cpu(hdr->ops.get)) 53162306a36Sopenharmony_ci k->get = ops[i].get; 53262306a36Sopenharmony_ci if (k->info == NULL && ops[i].id == le32_to_cpu(hdr->ops.info)) 53362306a36Sopenharmony_ci k->info = ops[i].info; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* vendor specific handlers found ? */ 53762306a36Sopenharmony_ci if (k->put && k->get && k->info) 53862306a36Sopenharmony_ci return 0; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* none found so try standard kcontrol handlers */ 54162306a36Sopenharmony_ci ops = io_ops; 54262306a36Sopenharmony_ci num_ops = ARRAY_SIZE(io_ops); 54362306a36Sopenharmony_ci for (i = 0; i < num_ops; i++) { 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (k->put == NULL && ops[i].id == le32_to_cpu(hdr->ops.put)) 54662306a36Sopenharmony_ci k->put = ops[i].put; 54762306a36Sopenharmony_ci if (k->get == NULL && ops[i].id == le32_to_cpu(hdr->ops.get)) 54862306a36Sopenharmony_ci k->get = ops[i].get; 54962306a36Sopenharmony_ci if (k->info == NULL && ops[i].id == le32_to_cpu(hdr->ops.info)) 55062306a36Sopenharmony_ci k->info = ops[i].info; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* standard handlers found ? */ 55462306a36Sopenharmony_ci if (k->put && k->get && k->info) 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* nothing to bind */ 55862306a36Sopenharmony_ci return -EINVAL; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/* bind a widgets to it's evnt handlers */ 56262306a36Sopenharmony_ciint snd_soc_tplg_widget_bind_event(struct snd_soc_dapm_widget *w, 56362306a36Sopenharmony_ci const struct snd_soc_tplg_widget_events *events, 56462306a36Sopenharmony_ci int num_events, u16 event_type) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci int i; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci w->event = NULL; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci for (i = 0; i < num_events; i++) { 57162306a36Sopenharmony_ci if (event_type == events[i].type) { 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* found - so assign event */ 57462306a36Sopenharmony_ci w->event = events[i].event_handler; 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* not found */ 58062306a36Sopenharmony_ci return -EINVAL; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_tplg_widget_bind_event); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci/* optionally pass new dynamic kcontrol to component driver. */ 58562306a36Sopenharmony_cistatic int soc_tplg_control_load(struct soc_tplg *tplg, 58662306a36Sopenharmony_ci struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci int ret = 0; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (tplg->ops && tplg->ops->control_load) 59162306a36Sopenharmony_ci ret = tplg->ops->control_load(tplg->comp, tplg->index, k, hdr); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (ret) 59462306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to init %s\n", hdr->name); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return ret; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic int soc_tplg_create_tlv_db_scale(struct soc_tplg *tplg, 60162306a36Sopenharmony_ci struct snd_kcontrol_new *kc, struct snd_soc_tplg_tlv_dbscale *scale) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci unsigned int item_len = 2 * sizeof(unsigned int); 60462306a36Sopenharmony_ci unsigned int *p; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci p = devm_kzalloc(tplg->dev, item_len + 2 * sizeof(unsigned int), GFP_KERNEL); 60762306a36Sopenharmony_ci if (!p) 60862306a36Sopenharmony_ci return -ENOMEM; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci p[0] = SNDRV_CTL_TLVT_DB_SCALE; 61162306a36Sopenharmony_ci p[1] = item_len; 61262306a36Sopenharmony_ci p[2] = le32_to_cpu(scale->min); 61362306a36Sopenharmony_ci p[3] = (le32_to_cpu(scale->step) & TLV_DB_SCALE_MASK) 61462306a36Sopenharmony_ci | (le32_to_cpu(scale->mute) ? TLV_DB_SCALE_MUTE : 0); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci kc->tlv.p = (void *)p; 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic int soc_tplg_create_tlv(struct soc_tplg *tplg, 62162306a36Sopenharmony_ci struct snd_kcontrol_new *kc, struct snd_soc_tplg_ctl_hdr *tc) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct snd_soc_tplg_ctl_tlv *tplg_tlv; 62462306a36Sopenharmony_ci u32 access = le32_to_cpu(tc->access); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (!(access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)) 62762306a36Sopenharmony_ci return 0; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (!(access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) { 63062306a36Sopenharmony_ci tplg_tlv = &tc->tlv; 63162306a36Sopenharmony_ci switch (le32_to_cpu(tplg_tlv->type)) { 63262306a36Sopenharmony_ci case SNDRV_CTL_TLVT_DB_SCALE: 63362306a36Sopenharmony_ci return soc_tplg_create_tlv_db_scale(tplg, kc, 63462306a36Sopenharmony_ci &tplg_tlv->scale); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* TODO: add support for other TLV types */ 63762306a36Sopenharmony_ci default: 63862306a36Sopenharmony_ci dev_dbg(tplg->dev, "Unsupported TLV type %d\n", 63962306a36Sopenharmony_ci tplg_tlv->type); 64062306a36Sopenharmony_ci return -EINVAL; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct snd_soc_tplg_bytes_control *be; 65062306a36Sopenharmony_ci struct soc_bytes_ext *sbe; 65162306a36Sopenharmony_ci struct snd_kcontrol_new kc; 65262306a36Sopenharmony_ci int ret = 0; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (soc_tplg_check_elem_count(tplg, 65562306a36Sopenharmony_ci sizeof(struct snd_soc_tplg_bytes_control), 65662306a36Sopenharmony_ci 1, size, "mixer bytes")) 65762306a36Sopenharmony_ci return -EINVAL; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci be = (struct snd_soc_tplg_bytes_control *)tplg->pos; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci /* validate kcontrol */ 66262306a36Sopenharmony_ci if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 66362306a36Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 66462306a36Sopenharmony_ci return -EINVAL; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL); 66762306a36Sopenharmony_ci if (sbe == NULL) 66862306a36Sopenharmony_ci return -ENOMEM; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + 67162306a36Sopenharmony_ci le32_to_cpu(be->priv.size)); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci dev_dbg(tplg->dev, 67462306a36Sopenharmony_ci "ASoC: adding bytes kcontrol %s with access 0x%x\n", 67562306a36Sopenharmony_ci be->hdr.name, be->hdr.access); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci memset(&kc, 0, sizeof(kc)); 67862306a36Sopenharmony_ci kc.name = be->hdr.name; 67962306a36Sopenharmony_ci kc.private_value = (long)sbe; 68062306a36Sopenharmony_ci kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 68162306a36Sopenharmony_ci kc.access = le32_to_cpu(be->hdr.access); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci sbe->max = le32_to_cpu(be->max); 68462306a36Sopenharmony_ci sbe->dobj.type = SND_SOC_DOBJ_BYTES; 68562306a36Sopenharmony_ci if (tplg->ops) 68662306a36Sopenharmony_ci sbe->dobj.unload = tplg->ops->control_unload; 68762306a36Sopenharmony_ci INIT_LIST_HEAD(&sbe->dobj.list); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* map io handlers */ 69062306a36Sopenharmony_ci ret = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg); 69162306a36Sopenharmony_ci if (ret) { 69262306a36Sopenharmony_ci soc_control_err(tplg, &be->hdr, be->hdr.name); 69362306a36Sopenharmony_ci goto err; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* pass control to driver for optional further init */ 69762306a36Sopenharmony_ci ret = soc_tplg_control_load(tplg, &kc, &be->hdr); 69862306a36Sopenharmony_ci if (ret < 0) 69962306a36Sopenharmony_ci goto err; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* register control here */ 70262306a36Sopenharmony_ci ret = soc_tplg_add_kcontrol(tplg, &kc, &sbe->dobj.control.kcontrol); 70362306a36Sopenharmony_ci if (ret < 0) 70462306a36Sopenharmony_ci goto err; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci list_add(&sbe->dobj.list, &tplg->comp->dobj_list); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cierr: 70962306a36Sopenharmony_ci return ret; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci struct snd_soc_tplg_mixer_control *mc; 71562306a36Sopenharmony_ci struct soc_mixer_control *sm; 71662306a36Sopenharmony_ci struct snd_kcontrol_new kc; 71762306a36Sopenharmony_ci int ret = 0; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci if (soc_tplg_check_elem_count(tplg, 72062306a36Sopenharmony_ci sizeof(struct snd_soc_tplg_mixer_control), 72162306a36Sopenharmony_ci 1, size, "mixers")) 72262306a36Sopenharmony_ci return -EINVAL; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* validate kcontrol */ 72762306a36Sopenharmony_ci if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 72862306a36Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 72962306a36Sopenharmony_ci return -EINVAL; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL); 73262306a36Sopenharmony_ci if (sm == NULL) 73362306a36Sopenharmony_ci return -ENOMEM; 73462306a36Sopenharmony_ci tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) + 73562306a36Sopenharmony_ci le32_to_cpu(mc->priv.size)); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci dev_dbg(tplg->dev, 73862306a36Sopenharmony_ci "ASoC: adding mixer kcontrol %s with access 0x%x\n", 73962306a36Sopenharmony_ci mc->hdr.name, mc->hdr.access); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci memset(&kc, 0, sizeof(kc)); 74262306a36Sopenharmony_ci kc.name = mc->hdr.name; 74362306a36Sopenharmony_ci kc.private_value = (long)sm; 74462306a36Sopenharmony_ci kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 74562306a36Sopenharmony_ci kc.access = le32_to_cpu(mc->hdr.access); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* we only support FL/FR channel mapping atm */ 74862306a36Sopenharmony_ci sm->reg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL); 74962306a36Sopenharmony_ci sm->rreg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR); 75062306a36Sopenharmony_ci sm->shift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL); 75162306a36Sopenharmony_ci sm->rshift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci sm->max = le32_to_cpu(mc->max); 75462306a36Sopenharmony_ci sm->min = le32_to_cpu(mc->min); 75562306a36Sopenharmony_ci sm->invert = le32_to_cpu(mc->invert); 75662306a36Sopenharmony_ci sm->platform_max = le32_to_cpu(mc->platform_max); 75762306a36Sopenharmony_ci sm->dobj.index = tplg->index; 75862306a36Sopenharmony_ci sm->dobj.type = SND_SOC_DOBJ_MIXER; 75962306a36Sopenharmony_ci if (tplg->ops) 76062306a36Sopenharmony_ci sm->dobj.unload = tplg->ops->control_unload; 76162306a36Sopenharmony_ci INIT_LIST_HEAD(&sm->dobj.list); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* map io handlers */ 76462306a36Sopenharmony_ci ret = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg); 76562306a36Sopenharmony_ci if (ret) { 76662306a36Sopenharmony_ci soc_control_err(tplg, &mc->hdr, mc->hdr.name); 76762306a36Sopenharmony_ci goto err; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* create any TLV data */ 77162306a36Sopenharmony_ci ret = soc_tplg_create_tlv(tplg, &kc, &mc->hdr); 77262306a36Sopenharmony_ci if (ret < 0) { 77362306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", mc->hdr.name); 77462306a36Sopenharmony_ci goto err; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* pass control to driver for optional further init */ 77862306a36Sopenharmony_ci ret = soc_tplg_control_load(tplg, &kc, &mc->hdr); 77962306a36Sopenharmony_ci if (ret < 0) 78062306a36Sopenharmony_ci goto err; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* register control here */ 78362306a36Sopenharmony_ci ret = soc_tplg_add_kcontrol(tplg, &kc, &sm->dobj.control.kcontrol); 78462306a36Sopenharmony_ci if (ret < 0) 78562306a36Sopenharmony_ci goto err; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci list_add(&sm->dobj.list, &tplg->comp->dobj_list); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cierr: 79062306a36Sopenharmony_ci return ret; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic int soc_tplg_denum_create_texts(struct soc_tplg *tplg, struct soc_enum *se, 79462306a36Sopenharmony_ci struct snd_soc_tplg_enum_control *ec) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci int i, ret; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (le32_to_cpu(ec->items) > ARRAY_SIZE(ec->texts)) 79962306a36Sopenharmony_ci return -EINVAL; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci se->dobj.control.dtexts = 80262306a36Sopenharmony_ci devm_kcalloc(tplg->dev, le32_to_cpu(ec->items), sizeof(char *), GFP_KERNEL); 80362306a36Sopenharmony_ci if (se->dobj.control.dtexts == NULL) 80462306a36Sopenharmony_ci return -ENOMEM; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci for (i = 0; i < le32_to_cpu(ec->items); i++) { 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (strnlen(ec->texts[i], SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 80962306a36Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { 81062306a36Sopenharmony_ci ret = -EINVAL; 81162306a36Sopenharmony_ci goto err; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci se->dobj.control.dtexts[i] = devm_kstrdup(tplg->dev, ec->texts[i], GFP_KERNEL); 81562306a36Sopenharmony_ci if (!se->dobj.control.dtexts[i]) { 81662306a36Sopenharmony_ci ret = -ENOMEM; 81762306a36Sopenharmony_ci goto err; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci se->items = le32_to_cpu(ec->items); 82262306a36Sopenharmony_ci se->texts = (const char * const *)se->dobj.control.dtexts; 82362306a36Sopenharmony_ci return 0; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cierr: 82662306a36Sopenharmony_ci return ret; 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic int soc_tplg_denum_create_values(struct soc_tplg *tplg, struct soc_enum *se, 83062306a36Sopenharmony_ci struct snd_soc_tplg_enum_control *ec) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci int i; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci /* 83562306a36Sopenharmony_ci * Following "if" checks if we have at most SND_SOC_TPLG_NUM_TEXTS 83662306a36Sopenharmony_ci * values instead of using ARRAY_SIZE(ec->values) due to the fact that 83762306a36Sopenharmony_ci * it is oversized for its purpose. Additionally it is done so because 83862306a36Sopenharmony_ci * it is defined in UAPI header where it can't be easily changed. 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ci if (le32_to_cpu(ec->items) > SND_SOC_TPLG_NUM_TEXTS) 84162306a36Sopenharmony_ci return -EINVAL; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci se->dobj.control.dvalues = devm_kcalloc(tplg->dev, le32_to_cpu(ec->items), 84462306a36Sopenharmony_ci sizeof(*se->dobj.control.dvalues), 84562306a36Sopenharmony_ci GFP_KERNEL); 84662306a36Sopenharmony_ci if (!se->dobj.control.dvalues) 84762306a36Sopenharmony_ci return -ENOMEM; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* convert from little-endian */ 85062306a36Sopenharmony_ci for (i = 0; i < le32_to_cpu(ec->items); i++) { 85162306a36Sopenharmony_ci se->dobj.control.dvalues[i] = le32_to_cpu(ec->values[i]); 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci return 0; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci struct snd_soc_tplg_enum_control *ec; 86062306a36Sopenharmony_ci struct soc_enum *se; 86162306a36Sopenharmony_ci struct snd_kcontrol_new kc; 86262306a36Sopenharmony_ci int ret = 0; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (soc_tplg_check_elem_count(tplg, 86562306a36Sopenharmony_ci sizeof(struct snd_soc_tplg_enum_control), 86662306a36Sopenharmony_ci 1, size, "enums")) 86762306a36Sopenharmony_ci return -EINVAL; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci ec = (struct snd_soc_tplg_enum_control *)tplg->pos; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* validate kcontrol */ 87262306a36Sopenharmony_ci if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 87362306a36Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 87462306a36Sopenharmony_ci return -EINVAL; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci se = devm_kzalloc(tplg->dev, (sizeof(*se)), GFP_KERNEL); 87762306a36Sopenharmony_ci if (se == NULL) 87862306a36Sopenharmony_ci return -ENOMEM; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + 88162306a36Sopenharmony_ci le32_to_cpu(ec->priv.size)); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", 88462306a36Sopenharmony_ci ec->hdr.name, ec->items); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci memset(&kc, 0, sizeof(kc)); 88762306a36Sopenharmony_ci kc.name = ec->hdr.name; 88862306a36Sopenharmony_ci kc.private_value = (long)se; 88962306a36Sopenharmony_ci kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 89062306a36Sopenharmony_ci kc.access = le32_to_cpu(ec->hdr.access); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci se->reg = tplg_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); 89362306a36Sopenharmony_ci se->shift_l = tplg_chan_get_shift(tplg, ec->channel, 89462306a36Sopenharmony_ci SNDRV_CHMAP_FL); 89562306a36Sopenharmony_ci se->shift_r = tplg_chan_get_shift(tplg, ec->channel, 89662306a36Sopenharmony_ci SNDRV_CHMAP_FL); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci se->mask = le32_to_cpu(ec->mask); 89962306a36Sopenharmony_ci se->dobj.index = tplg->index; 90062306a36Sopenharmony_ci se->dobj.type = SND_SOC_DOBJ_ENUM; 90162306a36Sopenharmony_ci if (tplg->ops) 90262306a36Sopenharmony_ci se->dobj.unload = tplg->ops->control_unload; 90362306a36Sopenharmony_ci INIT_LIST_HEAD(&se->dobj.list); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci switch (le32_to_cpu(ec->hdr.ops.info)) { 90662306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: 90762306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM_VALUE: 90862306a36Sopenharmony_ci ret = soc_tplg_denum_create_values(tplg, se, ec); 90962306a36Sopenharmony_ci if (ret < 0) { 91062306a36Sopenharmony_ci dev_err(tplg->dev, 91162306a36Sopenharmony_ci "ASoC: could not create values for %s\n", 91262306a36Sopenharmony_ci ec->hdr.name); 91362306a36Sopenharmony_ci goto err; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci fallthrough; 91662306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM: 91762306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: 91862306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: 91962306a36Sopenharmony_ci ret = soc_tplg_denum_create_texts(tplg, se, ec); 92062306a36Sopenharmony_ci if (ret < 0) { 92162306a36Sopenharmony_ci dev_err(tplg->dev, 92262306a36Sopenharmony_ci "ASoC: could not create texts for %s\n", 92362306a36Sopenharmony_ci ec->hdr.name); 92462306a36Sopenharmony_ci goto err; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci break; 92762306a36Sopenharmony_ci default: 92862306a36Sopenharmony_ci ret = -EINVAL; 92962306a36Sopenharmony_ci dev_err(tplg->dev, 93062306a36Sopenharmony_ci "ASoC: invalid enum control type %d for %s\n", 93162306a36Sopenharmony_ci ec->hdr.ops.info, ec->hdr.name); 93262306a36Sopenharmony_ci goto err; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* map io handlers */ 93662306a36Sopenharmony_ci ret = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg); 93762306a36Sopenharmony_ci if (ret) { 93862306a36Sopenharmony_ci soc_control_err(tplg, &ec->hdr, ec->hdr.name); 93962306a36Sopenharmony_ci goto err; 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci /* pass control to driver for optional further init */ 94362306a36Sopenharmony_ci ret = soc_tplg_control_load(tplg, &kc, &ec->hdr); 94462306a36Sopenharmony_ci if (ret < 0) 94562306a36Sopenharmony_ci goto err; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci /* register control here */ 94862306a36Sopenharmony_ci ret = soc_tplg_add_kcontrol(tplg, &kc, &se->dobj.control.kcontrol); 94962306a36Sopenharmony_ci if (ret < 0) 95062306a36Sopenharmony_ci goto err; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci list_add(&se->dobj.list, &tplg->comp->dobj_list); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_cierr: 95562306a36Sopenharmony_ci return ret; 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_cistatic int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, 95962306a36Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci int ret; 96262306a36Sopenharmony_ci int i; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: adding %d kcontrols at 0x%lx\n", hdr->count, 96562306a36Sopenharmony_ci soc_tplg_get_offset(tplg)); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci for (i = 0; i < le32_to_cpu(hdr->count); i++) { 96862306a36Sopenharmony_ci struct snd_soc_tplg_ctl_hdr *control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (le32_to_cpu(control_hdr->size) != sizeof(*control_hdr)) { 97162306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid control size\n"); 97262306a36Sopenharmony_ci return -EINVAL; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci switch (le32_to_cpu(control_hdr->ops.info)) { 97662306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW: 97762306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_STROBE: 97862306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW_SX: 97962306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW_XR_SX: 98062306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_RANGE: 98162306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_VOLSW: 98262306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_PIN: 98362306a36Sopenharmony_ci ret = soc_tplg_dmixer_create(tplg, le32_to_cpu(hdr->payload_size)); 98462306a36Sopenharmony_ci break; 98562306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM: 98662306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM_VALUE: 98762306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: 98862306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: 98962306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: 99062306a36Sopenharmony_ci ret = soc_tplg_denum_create(tplg, le32_to_cpu(hdr->payload_size)); 99162306a36Sopenharmony_ci break; 99262306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_BYTES: 99362306a36Sopenharmony_ci ret = soc_tplg_dbytes_create(tplg, le32_to_cpu(hdr->payload_size)); 99462306a36Sopenharmony_ci break; 99562306a36Sopenharmony_ci default: 99662306a36Sopenharmony_ci soc_bind_err(tplg, control_hdr, i); 99762306a36Sopenharmony_ci return -EINVAL; 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci if (ret < 0) { 100062306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid control\n"); 100162306a36Sopenharmony_ci return ret; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci return 0; 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci/* optionally pass new dynamic kcontrol to component driver. */ 101062306a36Sopenharmony_cistatic int soc_tplg_add_route(struct soc_tplg *tplg, 101162306a36Sopenharmony_ci struct snd_soc_dapm_route *route) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci if (tplg->ops && tplg->ops->dapm_route_load) 101462306a36Sopenharmony_ci return tplg->ops->dapm_route_load(tplg->comp, tplg->index, 101562306a36Sopenharmony_ci route); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci return 0; 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, 102162306a36Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = &tplg->comp->dapm; 102462306a36Sopenharmony_ci struct snd_soc_tplg_dapm_graph_elem *elem; 102562306a36Sopenharmony_ci struct snd_soc_dapm_route *route; 102662306a36Sopenharmony_ci int count, i; 102762306a36Sopenharmony_ci int ret = 0; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci count = le32_to_cpu(hdr->count); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (soc_tplg_check_elem_count(tplg, 103262306a36Sopenharmony_ci sizeof(struct snd_soc_tplg_dapm_graph_elem), 103362306a36Sopenharmony_ci count, le32_to_cpu(hdr->payload_size), "graph")) 103462306a36Sopenharmony_ci return -EINVAL; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes for index %d\n", count, 103762306a36Sopenharmony_ci hdr->index); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci for (i = 0; i < count; i++) { 104062306a36Sopenharmony_ci route = devm_kzalloc(tplg->dev, sizeof(*route), GFP_KERNEL); 104162306a36Sopenharmony_ci if (!route) 104262306a36Sopenharmony_ci return -ENOMEM; 104362306a36Sopenharmony_ci elem = (struct snd_soc_tplg_dapm_graph_elem *)tplg->pos; 104462306a36Sopenharmony_ci tplg->pos += sizeof(struct snd_soc_tplg_dapm_graph_elem); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci /* validate routes */ 104762306a36Sopenharmony_ci if (strnlen(elem->source, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 104862306a36Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { 104962306a36Sopenharmony_ci ret = -EINVAL; 105062306a36Sopenharmony_ci break; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci if (strnlen(elem->sink, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 105362306a36Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { 105462306a36Sopenharmony_ci ret = -EINVAL; 105562306a36Sopenharmony_ci break; 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 105862306a36Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { 105962306a36Sopenharmony_ci ret = -EINVAL; 106062306a36Sopenharmony_ci break; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci route->source = elem->source; 106462306a36Sopenharmony_ci route->sink = elem->sink; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci /* set to NULL atm for tplg users */ 106762306a36Sopenharmony_ci route->connected = NULL; 106862306a36Sopenharmony_ci if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0) 106962306a36Sopenharmony_ci route->control = NULL; 107062306a36Sopenharmony_ci else 107162306a36Sopenharmony_ci route->control = elem->control; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* add route dobj to dobj_list */ 107462306a36Sopenharmony_ci route->dobj.type = SND_SOC_DOBJ_GRAPH; 107562306a36Sopenharmony_ci if (tplg->ops) 107662306a36Sopenharmony_ci route->dobj.unload = tplg->ops->dapm_route_unload; 107762306a36Sopenharmony_ci route->dobj.index = tplg->index; 107862306a36Sopenharmony_ci list_add(&route->dobj.list, &tplg->comp->dobj_list); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci ret = soc_tplg_add_route(tplg, route); 108162306a36Sopenharmony_ci if (ret < 0) { 108262306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: topology: add_route failed: %d\n", ret); 108362306a36Sopenharmony_ci break; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* add route, but keep going if some fail */ 108762306a36Sopenharmony_ci snd_soc_dapm_add_routes(dapm, route, 1); 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci return ret; 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) 109462306a36Sopenharmony_ci{ 109562306a36Sopenharmony_ci struct soc_mixer_control *sm; 109662306a36Sopenharmony_ci struct snd_soc_tplg_mixer_control *mc; 109762306a36Sopenharmony_ci int err; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* validate kcontrol */ 110262306a36Sopenharmony_ci if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 110362306a36Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 110462306a36Sopenharmony_ci return -EINVAL; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci sm = devm_kzalloc(tplg->dev, sizeof(*sm), GFP_KERNEL); 110762306a36Sopenharmony_ci if (!sm) 110862306a36Sopenharmony_ci return -ENOMEM; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci tplg->pos += sizeof(struct snd_soc_tplg_mixer_control) + 111162306a36Sopenharmony_ci le32_to_cpu(mc->priv.size); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci dev_dbg(tplg->dev, " adding DAPM widget mixer control %s\n", 111462306a36Sopenharmony_ci mc->hdr.name); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci kc->private_value = (long)sm; 111762306a36Sopenharmony_ci kc->name = devm_kstrdup(tplg->dev, mc->hdr.name, GFP_KERNEL); 111862306a36Sopenharmony_ci if (!kc->name) 111962306a36Sopenharmony_ci return -ENOMEM; 112062306a36Sopenharmony_ci kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 112162306a36Sopenharmony_ci kc->access = le32_to_cpu(mc->hdr.access); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci /* we only support FL/FR channel mapping atm */ 112462306a36Sopenharmony_ci sm->reg = tplg_chan_get_reg(tplg, mc->channel, 112562306a36Sopenharmony_ci SNDRV_CHMAP_FL); 112662306a36Sopenharmony_ci sm->rreg = tplg_chan_get_reg(tplg, mc->channel, 112762306a36Sopenharmony_ci SNDRV_CHMAP_FR); 112862306a36Sopenharmony_ci sm->shift = tplg_chan_get_shift(tplg, mc->channel, 112962306a36Sopenharmony_ci SNDRV_CHMAP_FL); 113062306a36Sopenharmony_ci sm->rshift = tplg_chan_get_shift(tplg, mc->channel, 113162306a36Sopenharmony_ci SNDRV_CHMAP_FR); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci sm->max = le32_to_cpu(mc->max); 113462306a36Sopenharmony_ci sm->min = le32_to_cpu(mc->min); 113562306a36Sopenharmony_ci sm->invert = le32_to_cpu(mc->invert); 113662306a36Sopenharmony_ci sm->platform_max = le32_to_cpu(mc->platform_max); 113762306a36Sopenharmony_ci sm->dobj.index = tplg->index; 113862306a36Sopenharmony_ci INIT_LIST_HEAD(&sm->dobj.list); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci /* map io handlers */ 114162306a36Sopenharmony_ci err = soc_tplg_kcontrol_bind_io(&mc->hdr, kc, tplg); 114262306a36Sopenharmony_ci if (err) { 114362306a36Sopenharmony_ci soc_control_err(tplg, &mc->hdr, mc->hdr.name); 114462306a36Sopenharmony_ci return err; 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci /* create any TLV data */ 114862306a36Sopenharmony_ci err = soc_tplg_create_tlv(tplg, kc, &mc->hdr); 114962306a36Sopenharmony_ci if (err < 0) { 115062306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to create TLV %s\n", 115162306a36Sopenharmony_ci mc->hdr.name); 115262306a36Sopenharmony_ci return err; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci /* pass control to driver for optional further init */ 115662306a36Sopenharmony_ci err = soc_tplg_control_load(tplg, kc, &mc->hdr); 115762306a36Sopenharmony_ci if (err < 0) 115862306a36Sopenharmony_ci return err; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci return 0; 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_cistatic int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) 116462306a36Sopenharmony_ci{ 116562306a36Sopenharmony_ci struct snd_soc_tplg_enum_control *ec; 116662306a36Sopenharmony_ci struct soc_enum *se; 116762306a36Sopenharmony_ci int err; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci ec = (struct snd_soc_tplg_enum_control *)tplg->pos; 117062306a36Sopenharmony_ci /* validate kcontrol */ 117162306a36Sopenharmony_ci if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 117262306a36Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 117362306a36Sopenharmony_ci return -EINVAL; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci se = devm_kzalloc(tplg->dev, sizeof(*se), GFP_KERNEL); 117662306a36Sopenharmony_ci if (!se) 117762306a36Sopenharmony_ci return -ENOMEM; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + 118062306a36Sopenharmony_ci le32_to_cpu(ec->priv.size)); 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n", 118362306a36Sopenharmony_ci ec->hdr.name); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci kc->private_value = (long)se; 118662306a36Sopenharmony_ci kc->name = devm_kstrdup(tplg->dev, ec->hdr.name, GFP_KERNEL); 118762306a36Sopenharmony_ci if (!kc->name) 118862306a36Sopenharmony_ci return -ENOMEM; 118962306a36Sopenharmony_ci kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 119062306a36Sopenharmony_ci kc->access = le32_to_cpu(ec->hdr.access); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci /* we only support FL/FR channel mapping atm */ 119362306a36Sopenharmony_ci se->reg = tplg_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); 119462306a36Sopenharmony_ci se->shift_l = tplg_chan_get_shift(tplg, ec->channel, 119562306a36Sopenharmony_ci SNDRV_CHMAP_FL); 119662306a36Sopenharmony_ci se->shift_r = tplg_chan_get_shift(tplg, ec->channel, 119762306a36Sopenharmony_ci SNDRV_CHMAP_FR); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci se->items = le32_to_cpu(ec->items); 120062306a36Sopenharmony_ci se->mask = le32_to_cpu(ec->mask); 120162306a36Sopenharmony_ci se->dobj.index = tplg->index; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci switch (le32_to_cpu(ec->hdr.ops.info)) { 120462306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM_VALUE: 120562306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: 120662306a36Sopenharmony_ci err = soc_tplg_denum_create_values(tplg, se, ec); 120762306a36Sopenharmony_ci if (err < 0) { 120862306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: could not create values for %s\n", 120962306a36Sopenharmony_ci ec->hdr.name); 121062306a36Sopenharmony_ci return err; 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci fallthrough; 121362306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM: 121462306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: 121562306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: 121662306a36Sopenharmony_ci err = soc_tplg_denum_create_texts(tplg, se, ec); 121762306a36Sopenharmony_ci if (err < 0) { 121862306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: could not create texts for %s\n", 121962306a36Sopenharmony_ci ec->hdr.name); 122062306a36Sopenharmony_ci return err; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci break; 122362306a36Sopenharmony_ci default: 122462306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", 122562306a36Sopenharmony_ci ec->hdr.ops.info, ec->hdr.name); 122662306a36Sopenharmony_ci return -EINVAL; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* map io handlers */ 123062306a36Sopenharmony_ci err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg); 123162306a36Sopenharmony_ci if (err) { 123262306a36Sopenharmony_ci soc_control_err(tplg, &ec->hdr, ec->hdr.name); 123362306a36Sopenharmony_ci return err; 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci /* pass control to driver for optional further init */ 123762306a36Sopenharmony_ci err = soc_tplg_control_load(tplg, kc, &ec->hdr); 123862306a36Sopenharmony_ci if (err < 0) 123962306a36Sopenharmony_ci return err; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci return 0; 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_kcontrol_new *kc) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct snd_soc_tplg_bytes_control *be; 124762306a36Sopenharmony_ci struct soc_bytes_ext *sbe; 124862306a36Sopenharmony_ci int err; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci be = (struct snd_soc_tplg_bytes_control *)tplg->pos; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci /* validate kcontrol */ 125362306a36Sopenharmony_ci if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 125462306a36Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 125562306a36Sopenharmony_ci return -EINVAL; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci sbe = devm_kzalloc(tplg->dev, sizeof(*sbe), GFP_KERNEL); 125862306a36Sopenharmony_ci if (!sbe) 125962306a36Sopenharmony_ci return -ENOMEM; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + 126262306a36Sopenharmony_ci le32_to_cpu(be->priv.size)); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci dev_dbg(tplg->dev, 126562306a36Sopenharmony_ci "ASoC: adding bytes kcontrol %s with access 0x%x\n", 126662306a36Sopenharmony_ci be->hdr.name, be->hdr.access); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci kc->private_value = (long)sbe; 126962306a36Sopenharmony_ci kc->name = devm_kstrdup(tplg->dev, be->hdr.name, GFP_KERNEL); 127062306a36Sopenharmony_ci if (!kc->name) 127162306a36Sopenharmony_ci return -ENOMEM; 127262306a36Sopenharmony_ci kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 127362306a36Sopenharmony_ci kc->access = le32_to_cpu(be->hdr.access); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci sbe->max = le32_to_cpu(be->max); 127662306a36Sopenharmony_ci INIT_LIST_HEAD(&sbe->dobj.list); 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci /* map standard io handlers and check for external handlers */ 127962306a36Sopenharmony_ci err = soc_tplg_kcontrol_bind_io(&be->hdr, kc, tplg); 128062306a36Sopenharmony_ci if (err) { 128162306a36Sopenharmony_ci soc_control_err(tplg, &be->hdr, be->hdr.name); 128262306a36Sopenharmony_ci return err; 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci /* pass control to driver for optional further init */ 128662306a36Sopenharmony_ci err = soc_tplg_control_load(tplg, kc, &be->hdr); 128762306a36Sopenharmony_ci if (err < 0) 128862306a36Sopenharmony_ci return err; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci return 0; 129162306a36Sopenharmony_ci} 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_cistatic int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, 129462306a36Sopenharmony_ci struct snd_soc_tplg_dapm_widget *w) 129562306a36Sopenharmony_ci{ 129662306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = &tplg->comp->dapm; 129762306a36Sopenharmony_ci struct snd_soc_dapm_widget template, *widget; 129862306a36Sopenharmony_ci struct snd_soc_tplg_ctl_hdr *control_hdr; 129962306a36Sopenharmony_ci struct snd_soc_card *card = tplg->comp->card; 130062306a36Sopenharmony_ci unsigned int *kcontrol_type = NULL; 130162306a36Sopenharmony_ci struct snd_kcontrol_new *kc; 130262306a36Sopenharmony_ci int mixer_count = 0; 130362306a36Sopenharmony_ci int bytes_count = 0; 130462306a36Sopenharmony_ci int enum_count = 0; 130562306a36Sopenharmony_ci int ret = 0; 130662306a36Sopenharmony_ci int i; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci if (strnlen(w->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 130962306a36Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 131062306a36Sopenharmony_ci return -EINVAL; 131162306a36Sopenharmony_ci if (strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 131262306a36Sopenharmony_ci SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 131362306a36Sopenharmony_ci return -EINVAL; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: creating DAPM widget %s id %d\n", 131662306a36Sopenharmony_ci w->name, w->id); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci memset(&template, 0, sizeof(template)); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci /* map user to kernel widget ID */ 132162306a36Sopenharmony_ci template.id = get_widget_id(le32_to_cpu(w->id)); 132262306a36Sopenharmony_ci if ((int)template.id < 0) 132362306a36Sopenharmony_ci return template.id; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci /* strings are allocated here, but used and freed by the widget */ 132662306a36Sopenharmony_ci template.name = kstrdup(w->name, GFP_KERNEL); 132762306a36Sopenharmony_ci if (!template.name) 132862306a36Sopenharmony_ci return -ENOMEM; 132962306a36Sopenharmony_ci template.sname = kstrdup(w->sname, GFP_KERNEL); 133062306a36Sopenharmony_ci if (!template.sname) { 133162306a36Sopenharmony_ci ret = -ENOMEM; 133262306a36Sopenharmony_ci goto err; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci template.reg = le32_to_cpu(w->reg); 133562306a36Sopenharmony_ci template.shift = le32_to_cpu(w->shift); 133662306a36Sopenharmony_ci template.mask = le32_to_cpu(w->mask); 133762306a36Sopenharmony_ci template.subseq = le32_to_cpu(w->subseq); 133862306a36Sopenharmony_ci template.on_val = w->invert ? 0 : 1; 133962306a36Sopenharmony_ci template.off_val = w->invert ? 1 : 0; 134062306a36Sopenharmony_ci template.ignore_suspend = le32_to_cpu(w->ignore_suspend); 134162306a36Sopenharmony_ci template.event_flags = le16_to_cpu(w->event_flags); 134262306a36Sopenharmony_ci template.dobj.index = tplg->index; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci tplg->pos += 134562306a36Sopenharmony_ci (sizeof(struct snd_soc_tplg_dapm_widget) + 134662306a36Sopenharmony_ci le32_to_cpu(w->priv.size)); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci if (w->num_kcontrols == 0) { 134962306a36Sopenharmony_ci template.num_kcontrols = 0; 135062306a36Sopenharmony_ci goto widget; 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci template.num_kcontrols = le32_to_cpu(w->num_kcontrols); 135462306a36Sopenharmony_ci kc = devm_kcalloc(tplg->dev, le32_to_cpu(w->num_kcontrols), sizeof(*kc), GFP_KERNEL); 135562306a36Sopenharmony_ci if (!kc) { 135662306a36Sopenharmony_ci ret = -ENOMEM; 135762306a36Sopenharmony_ci goto hdr_err; 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci kcontrol_type = devm_kcalloc(tplg->dev, le32_to_cpu(w->num_kcontrols), sizeof(unsigned int), 136162306a36Sopenharmony_ci GFP_KERNEL); 136262306a36Sopenharmony_ci if (!kcontrol_type) { 136362306a36Sopenharmony_ci ret = -ENOMEM; 136462306a36Sopenharmony_ci goto hdr_err; 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci for (i = 0; i < le32_to_cpu(w->num_kcontrols); i++) { 136862306a36Sopenharmony_ci control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos; 136962306a36Sopenharmony_ci switch (le32_to_cpu(control_hdr->ops.info)) { 137062306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW: 137162306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_STROBE: 137262306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW_SX: 137362306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW_XR_SX: 137462306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_RANGE: 137562306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_VOLSW: 137662306a36Sopenharmony_ci /* volume mixer */ 137762306a36Sopenharmony_ci kc[i].index = mixer_count; 137862306a36Sopenharmony_ci kcontrol_type[i] = SND_SOC_TPLG_TYPE_MIXER; 137962306a36Sopenharmony_ci mixer_count++; 138062306a36Sopenharmony_ci ret = soc_tplg_dapm_widget_dmixer_create(tplg, &kc[i]); 138162306a36Sopenharmony_ci if (ret < 0) 138262306a36Sopenharmony_ci goto hdr_err; 138362306a36Sopenharmony_ci break; 138462306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM: 138562306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM_VALUE: 138662306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: 138762306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: 138862306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: 138962306a36Sopenharmony_ci /* enumerated mixer */ 139062306a36Sopenharmony_ci kc[i].index = enum_count; 139162306a36Sopenharmony_ci kcontrol_type[i] = SND_SOC_TPLG_TYPE_ENUM; 139262306a36Sopenharmony_ci enum_count++; 139362306a36Sopenharmony_ci ret = soc_tplg_dapm_widget_denum_create(tplg, &kc[i]); 139462306a36Sopenharmony_ci if (ret < 0) 139562306a36Sopenharmony_ci goto hdr_err; 139662306a36Sopenharmony_ci break; 139762306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_BYTES: 139862306a36Sopenharmony_ci /* bytes control */ 139962306a36Sopenharmony_ci kc[i].index = bytes_count; 140062306a36Sopenharmony_ci kcontrol_type[i] = SND_SOC_TPLG_TYPE_BYTES; 140162306a36Sopenharmony_ci bytes_count++; 140262306a36Sopenharmony_ci ret = soc_tplg_dapm_widget_dbytes_create(tplg, &kc[i]); 140362306a36Sopenharmony_ci if (ret < 0) 140462306a36Sopenharmony_ci goto hdr_err; 140562306a36Sopenharmony_ci break; 140662306a36Sopenharmony_ci default: 140762306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid widget control type %d:%d:%d\n", 140862306a36Sopenharmony_ci control_hdr->ops.get, control_hdr->ops.put, 140962306a36Sopenharmony_ci le32_to_cpu(control_hdr->ops.info)); 141062306a36Sopenharmony_ci ret = -EINVAL; 141162306a36Sopenharmony_ci goto hdr_err; 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci template.kcontrol_news = kc; 141662306a36Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: template %s with %d/%d/%d (mixer/enum/bytes) control\n", 141762306a36Sopenharmony_ci w->name, mixer_count, enum_count, bytes_count); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ciwidget: 142062306a36Sopenharmony_ci ret = soc_tplg_widget_load(tplg, &template, w); 142162306a36Sopenharmony_ci if (ret < 0) 142262306a36Sopenharmony_ci goto hdr_err; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci /* card dapm mutex is held by the core if we are loading topology 142562306a36Sopenharmony_ci * data during sound card init. */ 142662306a36Sopenharmony_ci if (snd_soc_card_is_instantiated(card)) 142762306a36Sopenharmony_ci widget = snd_soc_dapm_new_control(dapm, &template); 142862306a36Sopenharmony_ci else 142962306a36Sopenharmony_ci widget = snd_soc_dapm_new_control_unlocked(dapm, &template); 143062306a36Sopenharmony_ci if (IS_ERR(widget)) { 143162306a36Sopenharmony_ci ret = PTR_ERR(widget); 143262306a36Sopenharmony_ci goto hdr_err; 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci widget->dobj.type = SND_SOC_DOBJ_WIDGET; 143662306a36Sopenharmony_ci widget->dobj.widget.kcontrol_type = kcontrol_type; 143762306a36Sopenharmony_ci if (tplg->ops) 143862306a36Sopenharmony_ci widget->dobj.unload = tplg->ops->widget_unload; 143962306a36Sopenharmony_ci widget->dobj.index = tplg->index; 144062306a36Sopenharmony_ci list_add(&widget->dobj.list, &tplg->comp->dobj_list); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci ret = soc_tplg_widget_ready(tplg, widget, w); 144362306a36Sopenharmony_ci if (ret < 0) 144462306a36Sopenharmony_ci goto ready_err; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci kfree(template.sname); 144762306a36Sopenharmony_ci kfree(template.name); 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci return 0; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ciready_err: 145262306a36Sopenharmony_ci soc_tplg_remove_widget(widget->dapm->component, &widget->dobj, SOC_TPLG_PASS_WIDGET); 145362306a36Sopenharmony_ci snd_soc_dapm_free_widget(widget); 145462306a36Sopenharmony_cihdr_err: 145562306a36Sopenharmony_ci kfree(template.sname); 145662306a36Sopenharmony_cierr: 145762306a36Sopenharmony_ci kfree(template.name); 145862306a36Sopenharmony_ci return ret; 145962306a36Sopenharmony_ci} 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_cistatic int soc_tplg_dapm_widget_elems_load(struct soc_tplg *tplg, 146262306a36Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 146362306a36Sopenharmony_ci{ 146462306a36Sopenharmony_ci int count, i; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci count = le32_to_cpu(hdr->count); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: adding %d DAPM widgets\n", count); 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci for (i = 0; i < count; i++) { 147162306a36Sopenharmony_ci struct snd_soc_tplg_dapm_widget *widget = (struct snd_soc_tplg_dapm_widget *) tplg->pos; 147262306a36Sopenharmony_ci int ret; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci /* 147562306a36Sopenharmony_ci * check if widget itself fits within topology file 147662306a36Sopenharmony_ci * use sizeof instead of widget->size, as we can't be sure 147762306a36Sopenharmony_ci * it is set properly yet (file may end before it is present) 147862306a36Sopenharmony_ci */ 147962306a36Sopenharmony_ci if (soc_tplg_get_offset(tplg) + sizeof(*widget) >= tplg->fw->size) { 148062306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid widget data size\n"); 148162306a36Sopenharmony_ci return -EINVAL; 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci /* check if widget has proper size */ 148562306a36Sopenharmony_ci if (le32_to_cpu(widget->size) != sizeof(*widget)) { 148662306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid widget size\n"); 148762306a36Sopenharmony_ci return -EINVAL; 148862306a36Sopenharmony_ci } 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci /* check if widget private data fits within topology file */ 149162306a36Sopenharmony_ci if (soc_tplg_get_offset(tplg) + le32_to_cpu(widget->priv.size) >= tplg->fw->size) { 149262306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid widget private data size\n"); 149362306a36Sopenharmony_ci return -EINVAL; 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci ret = soc_tplg_dapm_widget_create(tplg, widget); 149762306a36Sopenharmony_ci if (ret < 0) { 149862306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to load widget %s\n", 149962306a36Sopenharmony_ci widget->name); 150062306a36Sopenharmony_ci return ret; 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci } 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci return 0; 150562306a36Sopenharmony_ci} 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_cistatic int soc_tplg_dapm_complete(struct soc_tplg *tplg) 150862306a36Sopenharmony_ci{ 150962306a36Sopenharmony_ci struct snd_soc_card *card = tplg->comp->card; 151062306a36Sopenharmony_ci int ret; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci /* Card might not have been registered at this point. 151362306a36Sopenharmony_ci * If so, just return success. 151462306a36Sopenharmony_ci */ 151562306a36Sopenharmony_ci if (!snd_soc_card_is_instantiated(card)) { 151662306a36Sopenharmony_ci dev_warn(tplg->dev, "ASoC: Parent card not yet available, widget card binding deferred\n"); 151762306a36Sopenharmony_ci return 0; 151862306a36Sopenharmony_ci } 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci ret = snd_soc_dapm_new_widgets(card); 152162306a36Sopenharmony_ci if (ret < 0) 152262306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n", ret); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci return ret; 152562306a36Sopenharmony_ci} 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_cistatic int set_stream_info(struct soc_tplg *tplg, struct snd_soc_pcm_stream *stream, 152862306a36Sopenharmony_ci struct snd_soc_tplg_stream_caps *caps) 152962306a36Sopenharmony_ci{ 153062306a36Sopenharmony_ci stream->stream_name = devm_kstrdup(tplg->dev, caps->name, GFP_KERNEL); 153162306a36Sopenharmony_ci if (!stream->stream_name) 153262306a36Sopenharmony_ci return -ENOMEM; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci stream->channels_min = le32_to_cpu(caps->channels_min); 153562306a36Sopenharmony_ci stream->channels_max = le32_to_cpu(caps->channels_max); 153662306a36Sopenharmony_ci stream->rates = le32_to_cpu(caps->rates); 153762306a36Sopenharmony_ci stream->rate_min = le32_to_cpu(caps->rate_min); 153862306a36Sopenharmony_ci stream->rate_max = le32_to_cpu(caps->rate_max); 153962306a36Sopenharmony_ci stream->formats = le64_to_cpu(caps->formats); 154062306a36Sopenharmony_ci stream->sig_bits = le32_to_cpu(caps->sig_bits); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci return 0; 154362306a36Sopenharmony_ci} 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_cistatic void set_dai_flags(struct snd_soc_dai_driver *dai_drv, 154662306a36Sopenharmony_ci unsigned int flag_mask, unsigned int flags) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES) 154962306a36Sopenharmony_ci dai_drv->symmetric_rate = 155062306a36Sopenharmony_ci (flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES) ? 1 : 0; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS) 155362306a36Sopenharmony_ci dai_drv->symmetric_channels = 155462306a36Sopenharmony_ci (flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS) ? 155562306a36Sopenharmony_ci 1 : 0; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS) 155862306a36Sopenharmony_ci dai_drv->symmetric_sample_bits = 155962306a36Sopenharmony_ci (flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS) ? 156062306a36Sopenharmony_ci 1 : 0; 156162306a36Sopenharmony_ci} 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_cistatic const struct snd_soc_dai_ops tplg_dai_ops = { 156462306a36Sopenharmony_ci .compress_new = snd_soc_new_compress, 156562306a36Sopenharmony_ci}; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_cistatic int soc_tplg_dai_create(struct soc_tplg *tplg, 156862306a36Sopenharmony_ci struct snd_soc_tplg_pcm *pcm) 156962306a36Sopenharmony_ci{ 157062306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv; 157162306a36Sopenharmony_ci struct snd_soc_pcm_stream *stream; 157262306a36Sopenharmony_ci struct snd_soc_tplg_stream_caps *caps; 157362306a36Sopenharmony_ci struct snd_soc_dai *dai; 157462306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = 157562306a36Sopenharmony_ci snd_soc_component_get_dapm(tplg->comp); 157662306a36Sopenharmony_ci int ret; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci dai_drv = devm_kzalloc(tplg->dev, sizeof(struct snd_soc_dai_driver), GFP_KERNEL); 157962306a36Sopenharmony_ci if (dai_drv == NULL) 158062306a36Sopenharmony_ci return -ENOMEM; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci if (strlen(pcm->dai_name)) { 158362306a36Sopenharmony_ci dai_drv->name = devm_kstrdup(tplg->dev, pcm->dai_name, GFP_KERNEL); 158462306a36Sopenharmony_ci if (!dai_drv->name) { 158562306a36Sopenharmony_ci ret = -ENOMEM; 158662306a36Sopenharmony_ci goto err; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci } 158962306a36Sopenharmony_ci dai_drv->id = le32_to_cpu(pcm->dai_id); 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci if (pcm->playback) { 159262306a36Sopenharmony_ci stream = &dai_drv->playback; 159362306a36Sopenharmony_ci caps = &pcm->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; 159462306a36Sopenharmony_ci ret = set_stream_info(tplg, stream, caps); 159562306a36Sopenharmony_ci if (ret < 0) 159662306a36Sopenharmony_ci goto err; 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci if (pcm->capture) { 160062306a36Sopenharmony_ci stream = &dai_drv->capture; 160162306a36Sopenharmony_ci caps = &pcm->caps[SND_SOC_TPLG_STREAM_CAPTURE]; 160262306a36Sopenharmony_ci ret = set_stream_info(tplg, stream, caps); 160362306a36Sopenharmony_ci if (ret < 0) 160462306a36Sopenharmony_ci goto err; 160562306a36Sopenharmony_ci } 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci if (pcm->compress) 160862306a36Sopenharmony_ci dai_drv->ops = &tplg_dai_ops; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci /* pass control to component driver for optional further init */ 161162306a36Sopenharmony_ci ret = soc_tplg_dai_load(tplg, dai_drv, pcm, NULL); 161262306a36Sopenharmony_ci if (ret < 0) { 161362306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: DAI loading failed\n"); 161462306a36Sopenharmony_ci goto err; 161562306a36Sopenharmony_ci } 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci dai_drv->dobj.index = tplg->index; 161862306a36Sopenharmony_ci dai_drv->dobj.type = SND_SOC_DOBJ_PCM; 161962306a36Sopenharmony_ci if (tplg->ops) 162062306a36Sopenharmony_ci dai_drv->dobj.unload = tplg->ops->dai_unload; 162162306a36Sopenharmony_ci list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list); 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci /* register the DAI to the component */ 162462306a36Sopenharmony_ci dai = snd_soc_register_dai(tplg->comp, dai_drv, false); 162562306a36Sopenharmony_ci if (!dai) 162662306a36Sopenharmony_ci return -ENOMEM; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci /* Create the DAI widgets here */ 162962306a36Sopenharmony_ci ret = snd_soc_dapm_new_dai_widgets(dapm, dai); 163062306a36Sopenharmony_ci if (ret != 0) { 163162306a36Sopenharmony_ci dev_err(dai->dev, "Failed to create DAI widgets %d\n", ret); 163262306a36Sopenharmony_ci snd_soc_unregister_dai(dai); 163362306a36Sopenharmony_ci return ret; 163462306a36Sopenharmony_ci } 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci return 0; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_cierr: 163962306a36Sopenharmony_ci return ret; 164062306a36Sopenharmony_ci} 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_cistatic void set_link_flags(struct snd_soc_dai_link *link, 164362306a36Sopenharmony_ci unsigned int flag_mask, unsigned int flags) 164462306a36Sopenharmony_ci{ 164562306a36Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES) 164662306a36Sopenharmony_ci link->symmetric_rate = 164762306a36Sopenharmony_ci (flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES) ? 1 : 0; 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS) 165062306a36Sopenharmony_ci link->symmetric_channels = 165162306a36Sopenharmony_ci (flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS) ? 165262306a36Sopenharmony_ci 1 : 0; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS) 165562306a36Sopenharmony_ci link->symmetric_sample_bits = 165662306a36Sopenharmony_ci (flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS) ? 165762306a36Sopenharmony_ci 1 : 0; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP) 166062306a36Sopenharmony_ci link->ignore_suspend = 166162306a36Sopenharmony_ci (flags & SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP) ? 166262306a36Sopenharmony_ci 1 : 0; 166362306a36Sopenharmony_ci} 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci/* create the FE DAI link */ 166662306a36Sopenharmony_cistatic int soc_tplg_fe_link_create(struct soc_tplg *tplg, 166762306a36Sopenharmony_ci struct snd_soc_tplg_pcm *pcm) 166862306a36Sopenharmony_ci{ 166962306a36Sopenharmony_ci struct snd_soc_dai_link *link; 167062306a36Sopenharmony_ci struct snd_soc_dai_link_component *dlc; 167162306a36Sopenharmony_ci int ret; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci /* link + cpu + codec + platform */ 167462306a36Sopenharmony_ci link = devm_kzalloc(tplg->dev, sizeof(*link) + (3 * sizeof(*dlc)), GFP_KERNEL); 167562306a36Sopenharmony_ci if (link == NULL) 167662306a36Sopenharmony_ci return -ENOMEM; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci dlc = (struct snd_soc_dai_link_component *)(link + 1); 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci link->cpus = &dlc[0]; 168162306a36Sopenharmony_ci link->num_cpus = 1; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci link->dobj.index = tplg->index; 168462306a36Sopenharmony_ci link->dobj.type = SND_SOC_DOBJ_DAI_LINK; 168562306a36Sopenharmony_ci if (tplg->ops) 168662306a36Sopenharmony_ci link->dobj.unload = tplg->ops->link_unload; 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci if (strlen(pcm->pcm_name)) { 168962306a36Sopenharmony_ci link->name = devm_kstrdup(tplg->dev, pcm->pcm_name, GFP_KERNEL); 169062306a36Sopenharmony_ci link->stream_name = devm_kstrdup(tplg->dev, pcm->pcm_name, GFP_KERNEL); 169162306a36Sopenharmony_ci if (!link->name || !link->stream_name) { 169262306a36Sopenharmony_ci ret = -ENOMEM; 169362306a36Sopenharmony_ci goto err; 169462306a36Sopenharmony_ci } 169562306a36Sopenharmony_ci } 169662306a36Sopenharmony_ci link->id = le32_to_cpu(pcm->pcm_id); 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci if (strlen(pcm->dai_name)) { 169962306a36Sopenharmony_ci link->cpus->dai_name = devm_kstrdup(tplg->dev, pcm->dai_name, GFP_KERNEL); 170062306a36Sopenharmony_ci if (!link->cpus->dai_name) { 170162306a36Sopenharmony_ci ret = -ENOMEM; 170262306a36Sopenharmony_ci goto err; 170362306a36Sopenharmony_ci } 170462306a36Sopenharmony_ci } 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci /* 170762306a36Sopenharmony_ci * Many topology are assuming link has Codec / Platform, and 170862306a36Sopenharmony_ci * these might be overwritten at soc_tplg_dai_link_load(). 170962306a36Sopenharmony_ci * Don't use &asoc_dummy_dlc here. 171062306a36Sopenharmony_ci */ 171162306a36Sopenharmony_ci link->codecs = &dlc[1]; /* Don't use &asoc_dummy_dlc here */ 171262306a36Sopenharmony_ci link->codecs->name = "snd-soc-dummy"; 171362306a36Sopenharmony_ci link->codecs->dai_name = "snd-soc-dummy-dai"; 171462306a36Sopenharmony_ci link->num_codecs = 1; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci link->platforms = &dlc[2]; /* Don't use &asoc_dummy_dlc here */ 171762306a36Sopenharmony_ci link->platforms->name = "snd-soc-dummy"; 171862306a36Sopenharmony_ci link->num_platforms = 1; 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci /* enable DPCM */ 172162306a36Sopenharmony_ci link->dynamic = 1; 172262306a36Sopenharmony_ci link->ignore_pmdown_time = 1; 172362306a36Sopenharmony_ci link->dpcm_playback = le32_to_cpu(pcm->playback); 172462306a36Sopenharmony_ci link->dpcm_capture = le32_to_cpu(pcm->capture); 172562306a36Sopenharmony_ci if (pcm->flag_mask) 172662306a36Sopenharmony_ci set_link_flags(link, 172762306a36Sopenharmony_ci le32_to_cpu(pcm->flag_mask), 172862306a36Sopenharmony_ci le32_to_cpu(pcm->flags)); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci /* pass control to component driver for optional further init */ 173162306a36Sopenharmony_ci ret = soc_tplg_dai_link_load(tplg, link, NULL); 173262306a36Sopenharmony_ci if (ret < 0) { 173362306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: FE link loading failed\n"); 173462306a36Sopenharmony_ci goto err; 173562306a36Sopenharmony_ci } 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci ret = snd_soc_add_pcm_runtimes(tplg->comp->card, link, 1); 173862306a36Sopenharmony_ci if (ret < 0) { 173962306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) 174062306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: adding FE link failed\n"); 174162306a36Sopenharmony_ci goto err; 174262306a36Sopenharmony_ci } 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci list_add(&link->dobj.list, &tplg->comp->dobj_list); 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci return 0; 174762306a36Sopenharmony_cierr: 174862306a36Sopenharmony_ci return ret; 174962306a36Sopenharmony_ci} 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci/* create a FE DAI and DAI link from the PCM object */ 175262306a36Sopenharmony_cistatic int soc_tplg_pcm_create(struct soc_tplg *tplg, 175362306a36Sopenharmony_ci struct snd_soc_tplg_pcm *pcm) 175462306a36Sopenharmony_ci{ 175562306a36Sopenharmony_ci int ret; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci ret = soc_tplg_dai_create(tplg, pcm); 175862306a36Sopenharmony_ci if (ret < 0) 175962306a36Sopenharmony_ci return ret; 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci return soc_tplg_fe_link_create(tplg, pcm); 176262306a36Sopenharmony_ci} 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci/* copy stream caps from the old version 4 of source */ 176562306a36Sopenharmony_cistatic void stream_caps_new_ver(struct snd_soc_tplg_stream_caps *dest, 176662306a36Sopenharmony_ci struct snd_soc_tplg_stream_caps_v4 *src) 176762306a36Sopenharmony_ci{ 176862306a36Sopenharmony_ci dest->size = cpu_to_le32(sizeof(*dest)); 176962306a36Sopenharmony_ci memcpy(dest->name, src->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 177062306a36Sopenharmony_ci dest->formats = src->formats; 177162306a36Sopenharmony_ci dest->rates = src->rates; 177262306a36Sopenharmony_ci dest->rate_min = src->rate_min; 177362306a36Sopenharmony_ci dest->rate_max = src->rate_max; 177462306a36Sopenharmony_ci dest->channels_min = src->channels_min; 177562306a36Sopenharmony_ci dest->channels_max = src->channels_max; 177662306a36Sopenharmony_ci dest->periods_min = src->periods_min; 177762306a36Sopenharmony_ci dest->periods_max = src->periods_max; 177862306a36Sopenharmony_ci dest->period_size_min = src->period_size_min; 177962306a36Sopenharmony_ci dest->period_size_max = src->period_size_max; 178062306a36Sopenharmony_ci dest->buffer_size_min = src->buffer_size_min; 178162306a36Sopenharmony_ci dest->buffer_size_max = src->buffer_size_max; 178262306a36Sopenharmony_ci} 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci/** 178562306a36Sopenharmony_ci * pcm_new_ver - Create the new version of PCM from the old version. 178662306a36Sopenharmony_ci * @tplg: topology context 178762306a36Sopenharmony_ci * @src: older version of pcm as a source 178862306a36Sopenharmony_ci * @pcm: latest version of pcm created from the source 178962306a36Sopenharmony_ci * 179062306a36Sopenharmony_ci * Support from version 4. User should free the returned pcm manually. 179162306a36Sopenharmony_ci */ 179262306a36Sopenharmony_cistatic int pcm_new_ver(struct soc_tplg *tplg, 179362306a36Sopenharmony_ci struct snd_soc_tplg_pcm *src, 179462306a36Sopenharmony_ci struct snd_soc_tplg_pcm **pcm) 179562306a36Sopenharmony_ci{ 179662306a36Sopenharmony_ci struct snd_soc_tplg_pcm *dest; 179762306a36Sopenharmony_ci struct snd_soc_tplg_pcm_v4 *src_v4; 179862306a36Sopenharmony_ci int i; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci *pcm = NULL; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci if (le32_to_cpu(src->size) != sizeof(*src_v4)) { 180362306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid PCM size\n"); 180462306a36Sopenharmony_ci return -EINVAL; 180562306a36Sopenharmony_ci } 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci dev_warn(tplg->dev, "ASoC: old version of PCM\n"); 180862306a36Sopenharmony_ci src_v4 = (struct snd_soc_tplg_pcm_v4 *)src; 180962306a36Sopenharmony_ci dest = kzalloc(sizeof(*dest), GFP_KERNEL); 181062306a36Sopenharmony_ci if (!dest) 181162306a36Sopenharmony_ci return -ENOMEM; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci dest->size = cpu_to_le32(sizeof(*dest)); /* size of latest abi version */ 181462306a36Sopenharmony_ci memcpy(dest->pcm_name, src_v4->pcm_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 181562306a36Sopenharmony_ci memcpy(dest->dai_name, src_v4->dai_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 181662306a36Sopenharmony_ci dest->pcm_id = src_v4->pcm_id; 181762306a36Sopenharmony_ci dest->dai_id = src_v4->dai_id; 181862306a36Sopenharmony_ci dest->playback = src_v4->playback; 181962306a36Sopenharmony_ci dest->capture = src_v4->capture; 182062306a36Sopenharmony_ci dest->compress = src_v4->compress; 182162306a36Sopenharmony_ci dest->num_streams = src_v4->num_streams; 182262306a36Sopenharmony_ci for (i = 0; i < le32_to_cpu(dest->num_streams); i++) 182362306a36Sopenharmony_ci memcpy(&dest->stream[i], &src_v4->stream[i], 182462306a36Sopenharmony_ci sizeof(struct snd_soc_tplg_stream)); 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci for (i = 0; i < 2; i++) 182762306a36Sopenharmony_ci stream_caps_new_ver(&dest->caps[i], &src_v4->caps[i]); 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci *pcm = dest; 183062306a36Sopenharmony_ci return 0; 183162306a36Sopenharmony_ci} 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_cistatic int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, 183462306a36Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 183562306a36Sopenharmony_ci{ 183662306a36Sopenharmony_ci struct snd_soc_tplg_pcm *pcm, *_pcm; 183762306a36Sopenharmony_ci int count; 183862306a36Sopenharmony_ci int size; 183962306a36Sopenharmony_ci int i; 184062306a36Sopenharmony_ci bool abi_match; 184162306a36Sopenharmony_ci int ret; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci count = le32_to_cpu(hdr->count); 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci /* check the element size and count */ 184662306a36Sopenharmony_ci pcm = (struct snd_soc_tplg_pcm *)tplg->pos; 184762306a36Sopenharmony_ci size = le32_to_cpu(pcm->size); 184862306a36Sopenharmony_ci if (size > sizeof(struct snd_soc_tplg_pcm) 184962306a36Sopenharmony_ci || size < sizeof(struct snd_soc_tplg_pcm_v4)) { 185062306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid size %d for PCM elems\n", 185162306a36Sopenharmony_ci size); 185262306a36Sopenharmony_ci return -EINVAL; 185362306a36Sopenharmony_ci } 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci if (soc_tplg_check_elem_count(tplg, 185662306a36Sopenharmony_ci size, count, 185762306a36Sopenharmony_ci le32_to_cpu(hdr->payload_size), 185862306a36Sopenharmony_ci "PCM DAI")) 185962306a36Sopenharmony_ci return -EINVAL; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci for (i = 0; i < count; i++) { 186262306a36Sopenharmony_ci pcm = (struct snd_soc_tplg_pcm *)tplg->pos; 186362306a36Sopenharmony_ci size = le32_to_cpu(pcm->size); 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci /* check ABI version by size, create a new version of pcm 186662306a36Sopenharmony_ci * if abi not match. 186762306a36Sopenharmony_ci */ 186862306a36Sopenharmony_ci if (size == sizeof(*pcm)) { 186962306a36Sopenharmony_ci abi_match = true; 187062306a36Sopenharmony_ci _pcm = pcm; 187162306a36Sopenharmony_ci } else { 187262306a36Sopenharmony_ci abi_match = false; 187362306a36Sopenharmony_ci ret = pcm_new_ver(tplg, pcm, &_pcm); 187462306a36Sopenharmony_ci if (ret < 0) 187562306a36Sopenharmony_ci return ret; 187662306a36Sopenharmony_ci } 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci /* create the FE DAIs and DAI links */ 187962306a36Sopenharmony_ci ret = soc_tplg_pcm_create(tplg, _pcm); 188062306a36Sopenharmony_ci if (ret < 0) { 188162306a36Sopenharmony_ci if (!abi_match) 188262306a36Sopenharmony_ci kfree(_pcm); 188362306a36Sopenharmony_ci return ret; 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci /* offset by version-specific struct size and 188762306a36Sopenharmony_ci * real priv data size 188862306a36Sopenharmony_ci */ 188962306a36Sopenharmony_ci tplg->pos += size + le32_to_cpu(_pcm->priv.size); 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci if (!abi_match) 189262306a36Sopenharmony_ci kfree(_pcm); /* free the duplicated one */ 189362306a36Sopenharmony_ci } 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci return 0; 189862306a36Sopenharmony_ci} 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci/** 190162306a36Sopenharmony_ci * set_link_hw_format - Set the HW audio format of the physical DAI link. 190262306a36Sopenharmony_ci * @link: &snd_soc_dai_link which should be updated 190362306a36Sopenharmony_ci * @cfg: physical link configs. 190462306a36Sopenharmony_ci * 190562306a36Sopenharmony_ci * Topology context contains a list of supported HW formats (configs) and 190662306a36Sopenharmony_ci * a default format ID for the physical link. This function will use this 190762306a36Sopenharmony_ci * default ID to choose the HW format to set the link's DAI format for init. 190862306a36Sopenharmony_ci */ 190962306a36Sopenharmony_cistatic void set_link_hw_format(struct snd_soc_dai_link *link, 191062306a36Sopenharmony_ci struct snd_soc_tplg_link_config *cfg) 191162306a36Sopenharmony_ci{ 191262306a36Sopenharmony_ci struct snd_soc_tplg_hw_config *hw_config; 191362306a36Sopenharmony_ci unsigned char bclk_provider, fsync_provider; 191462306a36Sopenharmony_ci unsigned char invert_bclk, invert_fsync; 191562306a36Sopenharmony_ci int i; 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci for (i = 0; i < le32_to_cpu(cfg->num_hw_configs); i++) { 191862306a36Sopenharmony_ci hw_config = &cfg->hw_config[i]; 191962306a36Sopenharmony_ci if (hw_config->id != cfg->default_hw_config_id) 192062306a36Sopenharmony_ci continue; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci link->dai_fmt = le32_to_cpu(hw_config->fmt) & 192362306a36Sopenharmony_ci SND_SOC_DAIFMT_FORMAT_MASK; 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci /* clock gating */ 192662306a36Sopenharmony_ci switch (hw_config->clock_gated) { 192762306a36Sopenharmony_ci case SND_SOC_TPLG_DAI_CLK_GATE_GATED: 192862306a36Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_GATED; 192962306a36Sopenharmony_ci break; 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci case SND_SOC_TPLG_DAI_CLK_GATE_CONT: 193262306a36Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_CONT; 193362306a36Sopenharmony_ci break; 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci default: 193662306a36Sopenharmony_ci /* ignore the value */ 193762306a36Sopenharmony_ci break; 193862306a36Sopenharmony_ci } 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci /* clock signal polarity */ 194162306a36Sopenharmony_ci invert_bclk = hw_config->invert_bclk; 194262306a36Sopenharmony_ci invert_fsync = hw_config->invert_fsync; 194362306a36Sopenharmony_ci if (!invert_bclk && !invert_fsync) 194462306a36Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_NB_NF; 194562306a36Sopenharmony_ci else if (!invert_bclk && invert_fsync) 194662306a36Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_NB_IF; 194762306a36Sopenharmony_ci else if (invert_bclk && !invert_fsync) 194862306a36Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_IB_NF; 194962306a36Sopenharmony_ci else 195062306a36Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_IB_IF; 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci /* clock masters */ 195362306a36Sopenharmony_ci bclk_provider = (hw_config->bclk_provider == 195462306a36Sopenharmony_ci SND_SOC_TPLG_BCLK_CP); 195562306a36Sopenharmony_ci fsync_provider = (hw_config->fsync_provider == 195662306a36Sopenharmony_ci SND_SOC_TPLG_FSYNC_CP); 195762306a36Sopenharmony_ci if (bclk_provider && fsync_provider) 195862306a36Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP; 195962306a36Sopenharmony_ci else if (!bclk_provider && fsync_provider) 196062306a36Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_CBC_CFP; 196162306a36Sopenharmony_ci else if (bclk_provider && !fsync_provider) 196262306a36Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_CBP_CFC; 196362306a36Sopenharmony_ci else 196462306a36Sopenharmony_ci link->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC; 196562306a36Sopenharmony_ci } 196662306a36Sopenharmony_ci} 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci/** 196962306a36Sopenharmony_ci * link_new_ver - Create a new physical link config from the old 197062306a36Sopenharmony_ci * version of source. 197162306a36Sopenharmony_ci * @tplg: topology context 197262306a36Sopenharmony_ci * @src: old version of phyical link config as a source 197362306a36Sopenharmony_ci * @link: latest version of physical link config created from the source 197462306a36Sopenharmony_ci * 197562306a36Sopenharmony_ci * Support from version 4. User need free the returned link config manually. 197662306a36Sopenharmony_ci */ 197762306a36Sopenharmony_cistatic int link_new_ver(struct soc_tplg *tplg, 197862306a36Sopenharmony_ci struct snd_soc_tplg_link_config *src, 197962306a36Sopenharmony_ci struct snd_soc_tplg_link_config **link) 198062306a36Sopenharmony_ci{ 198162306a36Sopenharmony_ci struct snd_soc_tplg_link_config *dest; 198262306a36Sopenharmony_ci struct snd_soc_tplg_link_config_v4 *src_v4; 198362306a36Sopenharmony_ci int i; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci *link = NULL; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci if (le32_to_cpu(src->size) != 198862306a36Sopenharmony_ci sizeof(struct snd_soc_tplg_link_config_v4)) { 198962306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid physical link config size\n"); 199062306a36Sopenharmony_ci return -EINVAL; 199162306a36Sopenharmony_ci } 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci dev_warn(tplg->dev, "ASoC: old version of physical link config\n"); 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci src_v4 = (struct snd_soc_tplg_link_config_v4 *)src; 199662306a36Sopenharmony_ci dest = kzalloc(sizeof(*dest), GFP_KERNEL); 199762306a36Sopenharmony_ci if (!dest) 199862306a36Sopenharmony_ci return -ENOMEM; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci dest->size = cpu_to_le32(sizeof(*dest)); 200162306a36Sopenharmony_ci dest->id = src_v4->id; 200262306a36Sopenharmony_ci dest->num_streams = src_v4->num_streams; 200362306a36Sopenharmony_ci for (i = 0; i < le32_to_cpu(dest->num_streams); i++) 200462306a36Sopenharmony_ci memcpy(&dest->stream[i], &src_v4->stream[i], 200562306a36Sopenharmony_ci sizeof(struct snd_soc_tplg_stream)); 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci *link = dest; 200862306a36Sopenharmony_ci return 0; 200962306a36Sopenharmony_ci} 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci/** 201262306a36Sopenharmony_ci * snd_soc_find_dai_link - Find a DAI link 201362306a36Sopenharmony_ci * 201462306a36Sopenharmony_ci * @card: soc card 201562306a36Sopenharmony_ci * @id: DAI link ID to match 201662306a36Sopenharmony_ci * @name: DAI link name to match, optional 201762306a36Sopenharmony_ci * @stream_name: DAI link stream name to match, optional 201862306a36Sopenharmony_ci * 201962306a36Sopenharmony_ci * This function will search all existing DAI links of the soc card to 202062306a36Sopenharmony_ci * find the link of the same ID. Since DAI links may not have their 202162306a36Sopenharmony_ci * unique ID, so name and stream name should also match if being 202262306a36Sopenharmony_ci * specified. 202362306a36Sopenharmony_ci * 202462306a36Sopenharmony_ci * Return: pointer of DAI link, or NULL if not found. 202562306a36Sopenharmony_ci */ 202662306a36Sopenharmony_cistatic struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card, 202762306a36Sopenharmony_ci int id, const char *name, 202862306a36Sopenharmony_ci const char *stream_name) 202962306a36Sopenharmony_ci{ 203062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci for_each_card_rtds(card, rtd) { 203362306a36Sopenharmony_ci struct snd_soc_dai_link *link = rtd->dai_link; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci if (link->id != id) 203662306a36Sopenharmony_ci continue; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci if (name && (!link->name || !strstr(link->name, name))) 203962306a36Sopenharmony_ci continue; 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci if (stream_name && (!link->stream_name || 204262306a36Sopenharmony_ci !strstr(link->stream_name, stream_name))) 204362306a36Sopenharmony_ci continue; 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci return link; 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci return NULL; 204962306a36Sopenharmony_ci} 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci/* Find and configure an existing physical DAI link */ 205262306a36Sopenharmony_cistatic int soc_tplg_link_config(struct soc_tplg *tplg, 205362306a36Sopenharmony_ci struct snd_soc_tplg_link_config *cfg) 205462306a36Sopenharmony_ci{ 205562306a36Sopenharmony_ci struct snd_soc_dai_link *link; 205662306a36Sopenharmony_ci const char *name, *stream_name; 205762306a36Sopenharmony_ci size_t len; 205862306a36Sopenharmony_ci int ret; 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci len = strnlen(cfg->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 206162306a36Sopenharmony_ci if (len == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 206262306a36Sopenharmony_ci return -EINVAL; 206362306a36Sopenharmony_ci else if (len) 206462306a36Sopenharmony_ci name = cfg->name; 206562306a36Sopenharmony_ci else 206662306a36Sopenharmony_ci name = NULL; 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci len = strnlen(cfg->stream_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 206962306a36Sopenharmony_ci if (len == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 207062306a36Sopenharmony_ci return -EINVAL; 207162306a36Sopenharmony_ci else if (len) 207262306a36Sopenharmony_ci stream_name = cfg->stream_name; 207362306a36Sopenharmony_ci else 207462306a36Sopenharmony_ci stream_name = NULL; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci link = snd_soc_find_dai_link(tplg->comp->card, le32_to_cpu(cfg->id), 207762306a36Sopenharmony_ci name, stream_name); 207862306a36Sopenharmony_ci if (!link) { 207962306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: physical link %s (id %d) not exist\n", 208062306a36Sopenharmony_ci name, cfg->id); 208162306a36Sopenharmony_ci return -EINVAL; 208262306a36Sopenharmony_ci } 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci /* hw format */ 208562306a36Sopenharmony_ci if (cfg->num_hw_configs) 208662306a36Sopenharmony_ci set_link_hw_format(link, cfg); 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci /* flags */ 208962306a36Sopenharmony_ci if (cfg->flag_mask) 209062306a36Sopenharmony_ci set_link_flags(link, 209162306a36Sopenharmony_ci le32_to_cpu(cfg->flag_mask), 209262306a36Sopenharmony_ci le32_to_cpu(cfg->flags)); 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_ci /* pass control to component driver for optional further init */ 209562306a36Sopenharmony_ci ret = soc_tplg_dai_link_load(tplg, link, cfg); 209662306a36Sopenharmony_ci if (ret < 0) { 209762306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: physical link loading failed\n"); 209862306a36Sopenharmony_ci return ret; 209962306a36Sopenharmony_ci } 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci /* for unloading it in snd_soc_tplg_component_remove */ 210262306a36Sopenharmony_ci link->dobj.index = tplg->index; 210362306a36Sopenharmony_ci link->dobj.type = SND_SOC_DOBJ_BACKEND_LINK; 210462306a36Sopenharmony_ci if (tplg->ops) 210562306a36Sopenharmony_ci link->dobj.unload = tplg->ops->link_unload; 210662306a36Sopenharmony_ci list_add(&link->dobj.list, &tplg->comp->dobj_list); 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci return 0; 210962306a36Sopenharmony_ci} 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci/* Load physical link config elements from the topology context */ 211362306a36Sopenharmony_cistatic int soc_tplg_link_elems_load(struct soc_tplg *tplg, 211462306a36Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 211562306a36Sopenharmony_ci{ 211662306a36Sopenharmony_ci struct snd_soc_tplg_link_config *link, *_link; 211762306a36Sopenharmony_ci int count; 211862306a36Sopenharmony_ci int size; 211962306a36Sopenharmony_ci int i, ret; 212062306a36Sopenharmony_ci bool abi_match; 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci count = le32_to_cpu(hdr->count); 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci /* check the element size and count */ 212562306a36Sopenharmony_ci link = (struct snd_soc_tplg_link_config *)tplg->pos; 212662306a36Sopenharmony_ci size = le32_to_cpu(link->size); 212762306a36Sopenharmony_ci if (size > sizeof(struct snd_soc_tplg_link_config) 212862306a36Sopenharmony_ci || size < sizeof(struct snd_soc_tplg_link_config_v4)) { 212962306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid size %d for physical link elems\n", 213062306a36Sopenharmony_ci size); 213162306a36Sopenharmony_ci return -EINVAL; 213262306a36Sopenharmony_ci } 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci if (soc_tplg_check_elem_count(tplg, size, count, 213562306a36Sopenharmony_ci le32_to_cpu(hdr->payload_size), 213662306a36Sopenharmony_ci "physical link config")) 213762306a36Sopenharmony_ci return -EINVAL; 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci /* config physical DAI links */ 214062306a36Sopenharmony_ci for (i = 0; i < count; i++) { 214162306a36Sopenharmony_ci link = (struct snd_soc_tplg_link_config *)tplg->pos; 214262306a36Sopenharmony_ci size = le32_to_cpu(link->size); 214362306a36Sopenharmony_ci if (size == sizeof(*link)) { 214462306a36Sopenharmony_ci abi_match = true; 214562306a36Sopenharmony_ci _link = link; 214662306a36Sopenharmony_ci } else { 214762306a36Sopenharmony_ci abi_match = false; 214862306a36Sopenharmony_ci ret = link_new_ver(tplg, link, &_link); 214962306a36Sopenharmony_ci if (ret < 0) 215062306a36Sopenharmony_ci return ret; 215162306a36Sopenharmony_ci } 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci ret = soc_tplg_link_config(tplg, _link); 215462306a36Sopenharmony_ci if (ret < 0) { 215562306a36Sopenharmony_ci if (!abi_match) 215662306a36Sopenharmony_ci kfree(_link); 215762306a36Sopenharmony_ci return ret; 215862306a36Sopenharmony_ci } 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci /* offset by version-specific struct size and 216162306a36Sopenharmony_ci * real priv data size 216262306a36Sopenharmony_ci */ 216362306a36Sopenharmony_ci tplg->pos += size + le32_to_cpu(_link->priv.size); 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci if (!abi_match) 216662306a36Sopenharmony_ci kfree(_link); /* free the duplicated one */ 216762306a36Sopenharmony_ci } 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci return 0; 217062306a36Sopenharmony_ci} 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci/** 217362306a36Sopenharmony_ci * soc_tplg_dai_config - Find and configure an existing physical DAI. 217462306a36Sopenharmony_ci * @tplg: topology context 217562306a36Sopenharmony_ci * @d: physical DAI configs. 217662306a36Sopenharmony_ci * 217762306a36Sopenharmony_ci * The physical dai should already be registered by the platform driver. 217862306a36Sopenharmony_ci * The platform driver should specify the DAI name and ID for matching. 217962306a36Sopenharmony_ci */ 218062306a36Sopenharmony_cistatic int soc_tplg_dai_config(struct soc_tplg *tplg, 218162306a36Sopenharmony_ci struct snd_soc_tplg_dai *d) 218262306a36Sopenharmony_ci{ 218362306a36Sopenharmony_ci struct snd_soc_dai_link_component dai_component; 218462306a36Sopenharmony_ci struct snd_soc_dai *dai; 218562306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv; 218662306a36Sopenharmony_ci struct snd_soc_pcm_stream *stream; 218762306a36Sopenharmony_ci struct snd_soc_tplg_stream_caps *caps; 218862306a36Sopenharmony_ci int ret; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci memset(&dai_component, 0, sizeof(dai_component)); 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci dai_component.dai_name = d->dai_name; 219362306a36Sopenharmony_ci dai = snd_soc_find_dai(&dai_component); 219462306a36Sopenharmony_ci if (!dai) { 219562306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: physical DAI %s not registered\n", 219662306a36Sopenharmony_ci d->dai_name); 219762306a36Sopenharmony_ci return -EINVAL; 219862306a36Sopenharmony_ci } 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci if (le32_to_cpu(d->dai_id) != dai->id) { 220162306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: physical DAI %s id mismatch\n", 220262306a36Sopenharmony_ci d->dai_name); 220362306a36Sopenharmony_ci return -EINVAL; 220462306a36Sopenharmony_ci } 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci dai_drv = dai->driver; 220762306a36Sopenharmony_ci if (!dai_drv) 220862306a36Sopenharmony_ci return -EINVAL; 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci if (d->playback) { 221162306a36Sopenharmony_ci stream = &dai_drv->playback; 221262306a36Sopenharmony_ci caps = &d->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; 221362306a36Sopenharmony_ci ret = set_stream_info(tplg, stream, caps); 221462306a36Sopenharmony_ci if (ret < 0) 221562306a36Sopenharmony_ci goto err; 221662306a36Sopenharmony_ci } 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci if (d->capture) { 221962306a36Sopenharmony_ci stream = &dai_drv->capture; 222062306a36Sopenharmony_ci caps = &d->caps[SND_SOC_TPLG_STREAM_CAPTURE]; 222162306a36Sopenharmony_ci ret = set_stream_info(tplg, stream, caps); 222262306a36Sopenharmony_ci if (ret < 0) 222362306a36Sopenharmony_ci goto err; 222462306a36Sopenharmony_ci } 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci if (d->flag_mask) 222762306a36Sopenharmony_ci set_dai_flags(dai_drv, 222862306a36Sopenharmony_ci le32_to_cpu(d->flag_mask), 222962306a36Sopenharmony_ci le32_to_cpu(d->flags)); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci /* pass control to component driver for optional further init */ 223262306a36Sopenharmony_ci ret = soc_tplg_dai_load(tplg, dai_drv, NULL, dai); 223362306a36Sopenharmony_ci if (ret < 0) { 223462306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: DAI loading failed\n"); 223562306a36Sopenharmony_ci goto err; 223662306a36Sopenharmony_ci } 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci return 0; 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_cierr: 224162306a36Sopenharmony_ci return ret; 224262306a36Sopenharmony_ci} 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci/* load physical DAI elements */ 224562306a36Sopenharmony_cistatic int soc_tplg_dai_elems_load(struct soc_tplg *tplg, 224662306a36Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 224762306a36Sopenharmony_ci{ 224862306a36Sopenharmony_ci int count; 224962306a36Sopenharmony_ci int i; 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci count = le32_to_cpu(hdr->count); 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci /* config the existing BE DAIs */ 225462306a36Sopenharmony_ci for (i = 0; i < count; i++) { 225562306a36Sopenharmony_ci struct snd_soc_tplg_dai *dai = (struct snd_soc_tplg_dai *)tplg->pos; 225662306a36Sopenharmony_ci int ret; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci if (le32_to_cpu(dai->size) != sizeof(*dai)) { 225962306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: invalid physical DAI size\n"); 226062306a36Sopenharmony_ci return -EINVAL; 226162306a36Sopenharmony_ci } 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci ret = soc_tplg_dai_config(tplg, dai); 226462306a36Sopenharmony_ci if (ret < 0) { 226562306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: failed to configure DAI\n"); 226662306a36Sopenharmony_ci return ret; 226762306a36Sopenharmony_ci } 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci tplg->pos += (sizeof(*dai) + le32_to_cpu(dai->priv.size)); 227062306a36Sopenharmony_ci } 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci dev_dbg(tplg->dev, "ASoC: Configure %d BE DAIs\n", count); 227362306a36Sopenharmony_ci return 0; 227462306a36Sopenharmony_ci} 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci/** 227762306a36Sopenharmony_ci * manifest_new_ver - Create a new version of manifest from the old version 227862306a36Sopenharmony_ci * of source. 227962306a36Sopenharmony_ci * @tplg: topology context 228062306a36Sopenharmony_ci * @src: old version of manifest as a source 228162306a36Sopenharmony_ci * @manifest: latest version of manifest created from the source 228262306a36Sopenharmony_ci * 228362306a36Sopenharmony_ci * Support from version 4. Users need free the returned manifest manually. 228462306a36Sopenharmony_ci */ 228562306a36Sopenharmony_cistatic int manifest_new_ver(struct soc_tplg *tplg, 228662306a36Sopenharmony_ci struct snd_soc_tplg_manifest *src, 228762306a36Sopenharmony_ci struct snd_soc_tplg_manifest **manifest) 228862306a36Sopenharmony_ci{ 228962306a36Sopenharmony_ci struct snd_soc_tplg_manifest *dest; 229062306a36Sopenharmony_ci struct snd_soc_tplg_manifest_v4 *src_v4; 229162306a36Sopenharmony_ci int size; 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci *manifest = NULL; 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci size = le32_to_cpu(src->size); 229662306a36Sopenharmony_ci if (size != sizeof(*src_v4)) { 229762306a36Sopenharmony_ci dev_warn(tplg->dev, "ASoC: invalid manifest size %d\n", 229862306a36Sopenharmony_ci size); 229962306a36Sopenharmony_ci if (size) 230062306a36Sopenharmony_ci return -EINVAL; 230162306a36Sopenharmony_ci src->size = cpu_to_le32(sizeof(*src_v4)); 230262306a36Sopenharmony_ci } 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci dev_warn(tplg->dev, "ASoC: old version of manifest\n"); 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci src_v4 = (struct snd_soc_tplg_manifest_v4 *)src; 230762306a36Sopenharmony_ci dest = kzalloc(sizeof(*dest) + le32_to_cpu(src_v4->priv.size), 230862306a36Sopenharmony_ci GFP_KERNEL); 230962306a36Sopenharmony_ci if (!dest) 231062306a36Sopenharmony_ci return -ENOMEM; 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci dest->size = cpu_to_le32(sizeof(*dest)); /* size of latest abi version */ 231362306a36Sopenharmony_ci dest->control_elems = src_v4->control_elems; 231462306a36Sopenharmony_ci dest->widget_elems = src_v4->widget_elems; 231562306a36Sopenharmony_ci dest->graph_elems = src_v4->graph_elems; 231662306a36Sopenharmony_ci dest->pcm_elems = src_v4->pcm_elems; 231762306a36Sopenharmony_ci dest->dai_link_elems = src_v4->dai_link_elems; 231862306a36Sopenharmony_ci dest->priv.size = src_v4->priv.size; 231962306a36Sopenharmony_ci if (dest->priv.size) 232062306a36Sopenharmony_ci memcpy(dest->priv.data, src_v4->priv.data, 232162306a36Sopenharmony_ci le32_to_cpu(src_v4->priv.size)); 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci *manifest = dest; 232462306a36Sopenharmony_ci return 0; 232562306a36Sopenharmony_ci} 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_cistatic int soc_tplg_manifest_load(struct soc_tplg *tplg, 232862306a36Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 232962306a36Sopenharmony_ci{ 233062306a36Sopenharmony_ci struct snd_soc_tplg_manifest *manifest, *_manifest; 233162306a36Sopenharmony_ci bool abi_match; 233262306a36Sopenharmony_ci int ret = 0; 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci manifest = (struct snd_soc_tplg_manifest *)tplg->pos; 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci /* check ABI version by size, create a new manifest if abi not match */ 233762306a36Sopenharmony_ci if (le32_to_cpu(manifest->size) == sizeof(*manifest)) { 233862306a36Sopenharmony_ci abi_match = true; 233962306a36Sopenharmony_ci _manifest = manifest; 234062306a36Sopenharmony_ci } else { 234162306a36Sopenharmony_ci abi_match = false; 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci ret = manifest_new_ver(tplg, manifest, &_manifest); 234462306a36Sopenharmony_ci if (ret < 0) 234562306a36Sopenharmony_ci return ret; 234662306a36Sopenharmony_ci } 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci /* pass control to component driver for optional further init */ 234962306a36Sopenharmony_ci if (tplg->ops && tplg->ops->manifest) 235062306a36Sopenharmony_ci ret = tplg->ops->manifest(tplg->comp, tplg->index, _manifest); 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci if (!abi_match) /* free the duplicated one */ 235362306a36Sopenharmony_ci kfree(_manifest); 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci return ret; 235662306a36Sopenharmony_ci} 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci/* validate header magic, size and type */ 235962306a36Sopenharmony_cistatic int soc_tplg_valid_header(struct soc_tplg *tplg, 236062306a36Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 236162306a36Sopenharmony_ci{ 236262306a36Sopenharmony_ci if (le32_to_cpu(hdr->size) != sizeof(*hdr)) { 236362306a36Sopenharmony_ci dev_err(tplg->dev, 236462306a36Sopenharmony_ci "ASoC: invalid header size for type %d at offset 0x%lx size 0x%zx.\n", 236562306a36Sopenharmony_ci le32_to_cpu(hdr->type), soc_tplg_get_hdr_offset(tplg), 236662306a36Sopenharmony_ci tplg->fw->size); 236762306a36Sopenharmony_ci return -EINVAL; 236862306a36Sopenharmony_ci } 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci if (soc_tplg_get_hdr_offset(tplg) + le32_to_cpu(hdr->payload_size) >= tplg->fw->size) { 237162306a36Sopenharmony_ci dev_err(tplg->dev, 237262306a36Sopenharmony_ci "ASoC: invalid header of type %d at offset %ld payload_size %d\n", 237362306a36Sopenharmony_ci le32_to_cpu(hdr->type), soc_tplg_get_hdr_offset(tplg), 237462306a36Sopenharmony_ci hdr->payload_size); 237562306a36Sopenharmony_ci return -EINVAL; 237662306a36Sopenharmony_ci } 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci /* big endian firmware objects not supported atm */ 237962306a36Sopenharmony_ci if (le32_to_cpu(hdr->magic) == SOC_TPLG_MAGIC_BIG_ENDIAN) { 238062306a36Sopenharmony_ci dev_err(tplg->dev, 238162306a36Sopenharmony_ci "ASoC: pass %d big endian not supported header got %x at offset 0x%lx size 0x%zx.\n", 238262306a36Sopenharmony_ci tplg->pass, hdr->magic, 238362306a36Sopenharmony_ci soc_tplg_get_hdr_offset(tplg), tplg->fw->size); 238462306a36Sopenharmony_ci return -EINVAL; 238562306a36Sopenharmony_ci } 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci if (le32_to_cpu(hdr->magic) != SND_SOC_TPLG_MAGIC) { 238862306a36Sopenharmony_ci dev_err(tplg->dev, 238962306a36Sopenharmony_ci "ASoC: pass %d does not have a valid header got %x at offset 0x%lx size 0x%zx.\n", 239062306a36Sopenharmony_ci tplg->pass, hdr->magic, 239162306a36Sopenharmony_ci soc_tplg_get_hdr_offset(tplg), tplg->fw->size); 239262306a36Sopenharmony_ci return -EINVAL; 239362306a36Sopenharmony_ci } 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci /* Support ABI from version 4 */ 239662306a36Sopenharmony_ci if (le32_to_cpu(hdr->abi) > SND_SOC_TPLG_ABI_VERSION || 239762306a36Sopenharmony_ci le32_to_cpu(hdr->abi) < SND_SOC_TPLG_ABI_VERSION_MIN) { 239862306a36Sopenharmony_ci dev_err(tplg->dev, 239962306a36Sopenharmony_ci "ASoC: pass %d invalid ABI version got 0x%x need 0x%x at offset 0x%lx size 0x%zx.\n", 240062306a36Sopenharmony_ci tplg->pass, hdr->abi, 240162306a36Sopenharmony_ci SND_SOC_TPLG_ABI_VERSION, soc_tplg_get_hdr_offset(tplg), 240262306a36Sopenharmony_ci tplg->fw->size); 240362306a36Sopenharmony_ci return -EINVAL; 240462306a36Sopenharmony_ci } 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_ci if (hdr->payload_size == 0) { 240762306a36Sopenharmony_ci dev_err(tplg->dev, "ASoC: header has 0 size at offset 0x%lx.\n", 240862306a36Sopenharmony_ci soc_tplg_get_hdr_offset(tplg)); 240962306a36Sopenharmony_ci return -EINVAL; 241062306a36Sopenharmony_ci } 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci return 0; 241362306a36Sopenharmony_ci} 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci/* check header type and call appropriate handler */ 241662306a36Sopenharmony_cistatic int soc_tplg_load_header(struct soc_tplg *tplg, 241762306a36Sopenharmony_ci struct snd_soc_tplg_hdr *hdr) 241862306a36Sopenharmony_ci{ 241962306a36Sopenharmony_ci int (*elem_load)(struct soc_tplg *tplg, 242062306a36Sopenharmony_ci struct snd_soc_tplg_hdr *hdr); 242162306a36Sopenharmony_ci unsigned int hdr_pass; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci tplg->pos = tplg->hdr_pos + sizeof(struct snd_soc_tplg_hdr); 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci tplg->index = le32_to_cpu(hdr->index); 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci switch (le32_to_cpu(hdr->type)) { 242862306a36Sopenharmony_ci case SND_SOC_TPLG_TYPE_MIXER: 242962306a36Sopenharmony_ci case SND_SOC_TPLG_TYPE_ENUM: 243062306a36Sopenharmony_ci case SND_SOC_TPLG_TYPE_BYTES: 243162306a36Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_CONTROL; 243262306a36Sopenharmony_ci elem_load = soc_tplg_kcontrol_elems_load; 243362306a36Sopenharmony_ci break; 243462306a36Sopenharmony_ci case SND_SOC_TPLG_TYPE_DAPM_GRAPH: 243562306a36Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_GRAPH; 243662306a36Sopenharmony_ci elem_load = soc_tplg_dapm_graph_elems_load; 243762306a36Sopenharmony_ci break; 243862306a36Sopenharmony_ci case SND_SOC_TPLG_TYPE_DAPM_WIDGET: 243962306a36Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_WIDGET; 244062306a36Sopenharmony_ci elem_load = soc_tplg_dapm_widget_elems_load; 244162306a36Sopenharmony_ci break; 244262306a36Sopenharmony_ci case SND_SOC_TPLG_TYPE_PCM: 244362306a36Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_PCM_DAI; 244462306a36Sopenharmony_ci elem_load = soc_tplg_pcm_elems_load; 244562306a36Sopenharmony_ci break; 244662306a36Sopenharmony_ci case SND_SOC_TPLG_TYPE_DAI: 244762306a36Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_BE_DAI; 244862306a36Sopenharmony_ci elem_load = soc_tplg_dai_elems_load; 244962306a36Sopenharmony_ci break; 245062306a36Sopenharmony_ci case SND_SOC_TPLG_TYPE_DAI_LINK: 245162306a36Sopenharmony_ci case SND_SOC_TPLG_TYPE_BACKEND_LINK: 245262306a36Sopenharmony_ci /* physical link configurations */ 245362306a36Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_LINK; 245462306a36Sopenharmony_ci elem_load = soc_tplg_link_elems_load; 245562306a36Sopenharmony_ci break; 245662306a36Sopenharmony_ci case SND_SOC_TPLG_TYPE_MANIFEST: 245762306a36Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_MANIFEST; 245862306a36Sopenharmony_ci elem_load = soc_tplg_manifest_load; 245962306a36Sopenharmony_ci break; 246062306a36Sopenharmony_ci default: 246162306a36Sopenharmony_ci /* bespoke vendor data object */ 246262306a36Sopenharmony_ci hdr_pass = SOC_TPLG_PASS_VENDOR; 246362306a36Sopenharmony_ci elem_load = soc_tplg_vendor_load; 246462306a36Sopenharmony_ci break; 246562306a36Sopenharmony_ci } 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci if (tplg->pass == hdr_pass) { 246862306a36Sopenharmony_ci dev_dbg(tplg->dev, 246962306a36Sopenharmony_ci "ASoC: Got 0x%x bytes of type %d version %d vendor %d at pass %d\n", 247062306a36Sopenharmony_ci hdr->payload_size, hdr->type, hdr->version, 247162306a36Sopenharmony_ci hdr->vendor_type, tplg->pass); 247262306a36Sopenharmony_ci return elem_load(tplg, hdr); 247362306a36Sopenharmony_ci } 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci return 0; 247662306a36Sopenharmony_ci} 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci/* process the topology file headers */ 247962306a36Sopenharmony_cistatic int soc_tplg_process_headers(struct soc_tplg *tplg) 248062306a36Sopenharmony_ci{ 248162306a36Sopenharmony_ci int ret; 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci /* process the header types from start to end */ 248462306a36Sopenharmony_ci for (tplg->pass = SOC_TPLG_PASS_START; tplg->pass <= SOC_TPLG_PASS_END; tplg->pass++) { 248562306a36Sopenharmony_ci struct snd_soc_tplg_hdr *hdr; 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci tplg->hdr_pos = tplg->fw->data; 248862306a36Sopenharmony_ci hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos; 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci while (!soc_tplg_is_eof(tplg)) { 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci /* make sure header is valid before loading */ 249362306a36Sopenharmony_ci ret = soc_tplg_valid_header(tplg, hdr); 249462306a36Sopenharmony_ci if (ret < 0) 249562306a36Sopenharmony_ci return ret; 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_ci /* load the header object */ 249862306a36Sopenharmony_ci ret = soc_tplg_load_header(tplg, hdr); 249962306a36Sopenharmony_ci if (ret < 0) { 250062306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) { 250162306a36Sopenharmony_ci dev_err(tplg->dev, 250262306a36Sopenharmony_ci "ASoC: topology: could not load header: %d\n", 250362306a36Sopenharmony_ci ret); 250462306a36Sopenharmony_ci } 250562306a36Sopenharmony_ci return ret; 250662306a36Sopenharmony_ci } 250762306a36Sopenharmony_ci 250862306a36Sopenharmony_ci /* goto next header */ 250962306a36Sopenharmony_ci tplg->hdr_pos += le32_to_cpu(hdr->payload_size) + 251062306a36Sopenharmony_ci sizeof(struct snd_soc_tplg_hdr); 251162306a36Sopenharmony_ci hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos; 251262306a36Sopenharmony_ci } 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci } 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci /* signal DAPM we are complete */ 251762306a36Sopenharmony_ci ret = soc_tplg_dapm_complete(tplg); 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci return ret; 252062306a36Sopenharmony_ci} 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_cistatic int soc_tplg_load(struct soc_tplg *tplg) 252362306a36Sopenharmony_ci{ 252462306a36Sopenharmony_ci int ret; 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci ret = soc_tplg_process_headers(tplg); 252762306a36Sopenharmony_ci if (ret == 0) 252862306a36Sopenharmony_ci return soc_tplg_complete(tplg); 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci return ret; 253162306a36Sopenharmony_ci} 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci/* load audio component topology from "firmware" file */ 253462306a36Sopenharmony_ciint snd_soc_tplg_component_load(struct snd_soc_component *comp, 253562306a36Sopenharmony_ci struct snd_soc_tplg_ops *ops, const struct firmware *fw) 253662306a36Sopenharmony_ci{ 253762306a36Sopenharmony_ci struct soc_tplg tplg; 253862306a36Sopenharmony_ci int ret; 253962306a36Sopenharmony_ci 254062306a36Sopenharmony_ci /* 254162306a36Sopenharmony_ci * check if we have sane parameters: 254262306a36Sopenharmony_ci * comp - needs to exist to keep and reference data while parsing 254362306a36Sopenharmony_ci * comp->card - used for setting card related parameters 254462306a36Sopenharmony_ci * comp->card->dev - used for resource management and prints 254562306a36Sopenharmony_ci * fw - we need it, as it is the very thing we parse 254662306a36Sopenharmony_ci */ 254762306a36Sopenharmony_ci if (!comp || !comp->card || !comp->card->dev || !fw) 254862306a36Sopenharmony_ci return -EINVAL; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci /* setup parsing context */ 255162306a36Sopenharmony_ci memset(&tplg, 0, sizeof(tplg)); 255262306a36Sopenharmony_ci tplg.fw = fw; 255362306a36Sopenharmony_ci tplg.dev = comp->card->dev; 255462306a36Sopenharmony_ci tplg.comp = comp; 255562306a36Sopenharmony_ci if (ops) { 255662306a36Sopenharmony_ci tplg.ops = ops; 255762306a36Sopenharmony_ci tplg.io_ops = ops->io_ops; 255862306a36Sopenharmony_ci tplg.io_ops_count = ops->io_ops_count; 255962306a36Sopenharmony_ci tplg.bytes_ext_ops = ops->bytes_ext_ops; 256062306a36Sopenharmony_ci tplg.bytes_ext_ops_count = ops->bytes_ext_ops_count; 256162306a36Sopenharmony_ci } 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci ret = soc_tplg_load(&tplg); 256462306a36Sopenharmony_ci /* free the created components if fail to load topology */ 256562306a36Sopenharmony_ci if (ret) 256662306a36Sopenharmony_ci snd_soc_tplg_component_remove(comp); 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_ci return ret; 256962306a36Sopenharmony_ci} 257062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_tplg_component_load); 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci/* remove dynamic controls from the component driver */ 257362306a36Sopenharmony_ciint snd_soc_tplg_component_remove(struct snd_soc_component *comp) 257462306a36Sopenharmony_ci{ 257562306a36Sopenharmony_ci struct snd_soc_dobj *dobj, *next_dobj; 257662306a36Sopenharmony_ci int pass; 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci /* process the header types from end to start */ 257962306a36Sopenharmony_ci for (pass = SOC_TPLG_PASS_END; pass >= SOC_TPLG_PASS_START; pass--) { 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci /* remove mixer controls */ 258262306a36Sopenharmony_ci list_for_each_entry_safe(dobj, next_dobj, &comp->dobj_list, 258362306a36Sopenharmony_ci list) { 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ci switch (dobj->type) { 258662306a36Sopenharmony_ci case SND_SOC_DOBJ_BYTES: 258762306a36Sopenharmony_ci case SND_SOC_DOBJ_ENUM: 258862306a36Sopenharmony_ci case SND_SOC_DOBJ_MIXER: 258962306a36Sopenharmony_ci soc_tplg_remove_kcontrol(comp, dobj, pass); 259062306a36Sopenharmony_ci break; 259162306a36Sopenharmony_ci case SND_SOC_DOBJ_GRAPH: 259262306a36Sopenharmony_ci soc_tplg_remove_route(comp, dobj, pass); 259362306a36Sopenharmony_ci break; 259462306a36Sopenharmony_ci case SND_SOC_DOBJ_WIDGET: 259562306a36Sopenharmony_ci soc_tplg_remove_widget(comp, dobj, pass); 259662306a36Sopenharmony_ci break; 259762306a36Sopenharmony_ci case SND_SOC_DOBJ_PCM: 259862306a36Sopenharmony_ci soc_tplg_remove_dai(comp, dobj, pass); 259962306a36Sopenharmony_ci break; 260062306a36Sopenharmony_ci case SND_SOC_DOBJ_DAI_LINK: 260162306a36Sopenharmony_ci soc_tplg_remove_link(comp, dobj, pass); 260262306a36Sopenharmony_ci break; 260362306a36Sopenharmony_ci case SND_SOC_DOBJ_BACKEND_LINK: 260462306a36Sopenharmony_ci /* 260562306a36Sopenharmony_ci * call link_unload ops if extra 260662306a36Sopenharmony_ci * deinitialization is needed. 260762306a36Sopenharmony_ci */ 260862306a36Sopenharmony_ci remove_backend_link(comp, dobj, pass); 260962306a36Sopenharmony_ci break; 261062306a36Sopenharmony_ci default: 261162306a36Sopenharmony_ci dev_err(comp->dev, "ASoC: invalid component type %d for removal\n", 261262306a36Sopenharmony_ci dobj->type); 261362306a36Sopenharmony_ci break; 261462306a36Sopenharmony_ci } 261562306a36Sopenharmony_ci } 261662306a36Sopenharmony_ci } 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci /* let caller know if FW can be freed when no objects are left */ 261962306a36Sopenharmony_ci return !list_empty(&comp->dobj_list); 262062306a36Sopenharmony_ci} 262162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_soc_tplg_component_remove); 2622