18a978234SLiam Girdwood /* 28a978234SLiam Girdwood * soc-topology.c -- ALSA SoC Topology 38a978234SLiam Girdwood * 48a978234SLiam Girdwood * Copyright (C) 2012 Texas Instruments Inc. 58a978234SLiam Girdwood * Copyright (C) 2015 Intel Corporation. 68a978234SLiam Girdwood * 78a978234SLiam Girdwood * Authors: Liam Girdwood <liam.r.girdwood@linux.intel.com> 88a978234SLiam Girdwood * K, Mythri P <mythri.p.k@intel.com> 98a978234SLiam Girdwood * Prusty, Subhransu S <subhransu.s.prusty@intel.com> 108a978234SLiam Girdwood * B, Jayachandran <jayachandran.b@intel.com> 118a978234SLiam Girdwood * Abdullah, Omair M <omair.m.abdullah@intel.com> 128a978234SLiam Girdwood * Jin, Yao <yao.jin@intel.com> 138a978234SLiam Girdwood * Lin, Mengdong <mengdong.lin@intel.com> 148a978234SLiam Girdwood * 158a978234SLiam Girdwood * This program is free software; you can redistribute it and/or modify it 168a978234SLiam Girdwood * under the terms of the GNU General Public License as published by the 178a978234SLiam Girdwood * Free Software Foundation; either version 2 of the License, or (at your 188a978234SLiam Girdwood * option) any later version. 198a978234SLiam Girdwood * 208a978234SLiam Girdwood * Add support to read audio firmware topology alongside firmware text. The 218a978234SLiam Girdwood * topology data can contain kcontrols, DAPM graphs, widgets, DAIs, DAI links, 228a978234SLiam Girdwood * equalizers, firmware, coefficients etc. 238a978234SLiam Girdwood * 248a978234SLiam Girdwood * This file only manages the core ALSA and ASoC components, all other bespoke 258a978234SLiam Girdwood * firmware topology data is passed to component drivers for bespoke handling. 268a978234SLiam Girdwood */ 278a978234SLiam Girdwood 288a978234SLiam Girdwood #include <linux/kernel.h> 298a978234SLiam Girdwood #include <linux/export.h> 308a978234SLiam Girdwood #include <linux/list.h> 318a978234SLiam Girdwood #include <linux/firmware.h> 328a978234SLiam Girdwood #include <linux/slab.h> 338a978234SLiam Girdwood #include <sound/soc.h> 348a978234SLiam Girdwood #include <sound/soc-dapm.h> 358a978234SLiam Girdwood #include <sound/soc-topology.h> 3628a87eebSMengdong Lin #include <sound/tlv.h> 378a978234SLiam Girdwood 388a978234SLiam Girdwood /* 398a978234SLiam Girdwood * We make several passes over the data (since it wont necessarily be ordered) 408a978234SLiam Girdwood * and process objects in the following order. This guarantees the component 418a978234SLiam Girdwood * drivers will be ready with any vendor data before the mixers and DAPM objects 428a978234SLiam Girdwood * are loaded (that may make use of the vendor data). 438a978234SLiam Girdwood */ 448a978234SLiam Girdwood #define SOC_TPLG_PASS_MANIFEST 0 458a978234SLiam Girdwood #define SOC_TPLG_PASS_VENDOR 1 468a978234SLiam Girdwood #define SOC_TPLG_PASS_MIXER 2 478a978234SLiam Girdwood #define SOC_TPLG_PASS_WIDGET 3 481a8e7fabSMengdong Lin #define SOC_TPLG_PASS_PCM_DAI 4 491a8e7fabSMengdong Lin #define SOC_TPLG_PASS_GRAPH 5 501a8e7fabSMengdong Lin #define SOC_TPLG_PASS_PINS 6 510038be9aSMengdong Lin #define SOC_TPLG_PASS_BE_DAI 7 52593d9e52SMengdong Lin #define SOC_TPLG_PASS_LINK 8 538a978234SLiam Girdwood 548a978234SLiam Girdwood #define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST 55593d9e52SMengdong Lin #define SOC_TPLG_PASS_END SOC_TPLG_PASS_LINK 568a978234SLiam Girdwood 57583958faSMengdong Lin /* 58583958faSMengdong Lin * Old version of ABI structs, supported for backward compatibility. 59583958faSMengdong Lin */ 60583958faSMengdong Lin 61583958faSMengdong Lin /* Manifest v4 */ 62583958faSMengdong Lin struct snd_soc_tplg_manifest_v4 { 63583958faSMengdong Lin __le32 size; /* in bytes of this structure */ 64583958faSMengdong Lin __le32 control_elems; /* number of control elements */ 65583958faSMengdong Lin __le32 widget_elems; /* number of widget elements */ 66583958faSMengdong Lin __le32 graph_elems; /* number of graph elements */ 67583958faSMengdong Lin __le32 pcm_elems; /* number of PCM elements */ 68583958faSMengdong Lin __le32 dai_link_elems; /* number of DAI link elements */ 69583958faSMengdong Lin struct snd_soc_tplg_private priv; 70583958faSMengdong Lin } __packed; 71583958faSMengdong Lin 7255726dc9SMengdong Lin /* Stream Capabilities v4 */ 7355726dc9SMengdong Lin struct snd_soc_tplg_stream_caps_v4 { 7455726dc9SMengdong Lin __le32 size; /* in bytes of this structure */ 7555726dc9SMengdong Lin char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 7655726dc9SMengdong Lin __le64 formats; /* supported formats SNDRV_PCM_FMTBIT_* */ 7755726dc9SMengdong Lin __le32 rates; /* supported rates SNDRV_PCM_RATE_* */ 7855726dc9SMengdong Lin __le32 rate_min; /* min rate */ 7955726dc9SMengdong Lin __le32 rate_max; /* max rate */ 8055726dc9SMengdong Lin __le32 channels_min; /* min channels */ 8155726dc9SMengdong Lin __le32 channels_max; /* max channels */ 8255726dc9SMengdong Lin __le32 periods_min; /* min number of periods */ 8355726dc9SMengdong Lin __le32 periods_max; /* max number of periods */ 8455726dc9SMengdong Lin __le32 period_size_min; /* min period size bytes */ 8555726dc9SMengdong Lin __le32 period_size_max; /* max period size bytes */ 8655726dc9SMengdong Lin __le32 buffer_size_min; /* min buffer size bytes */ 8755726dc9SMengdong Lin __le32 buffer_size_max; /* max buffer size bytes */ 8855726dc9SMengdong Lin } __packed; 8955726dc9SMengdong Lin 9055726dc9SMengdong Lin /* PCM v4 */ 9155726dc9SMengdong Lin struct snd_soc_tplg_pcm_v4 { 9255726dc9SMengdong Lin __le32 size; /* in bytes of this structure */ 9355726dc9SMengdong Lin char pcm_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 9455726dc9SMengdong Lin char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 9555726dc9SMengdong Lin __le32 pcm_id; /* unique ID - used to match with DAI link */ 9655726dc9SMengdong Lin __le32 dai_id; /* unique ID - used to match */ 9755726dc9SMengdong Lin __le32 playback; /* supports playback mode */ 9855726dc9SMengdong Lin __le32 capture; /* supports capture mode */ 9955726dc9SMengdong Lin __le32 compress; /* 1 = compressed; 0 = PCM */ 10055726dc9SMengdong Lin struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* for DAI link */ 10155726dc9SMengdong Lin __le32 num_streams; /* number of streams */ 10255726dc9SMengdong Lin struct snd_soc_tplg_stream_caps_v4 caps[2]; /* playback and capture for DAI */ 10355726dc9SMengdong Lin } __packed; 10455726dc9SMengdong Lin 105593d9e52SMengdong Lin /* Physical link config v4 */ 106593d9e52SMengdong Lin struct snd_soc_tplg_link_config_v4 { 107593d9e52SMengdong Lin __le32 size; /* in bytes of this structure */ 108593d9e52SMengdong Lin __le32 id; /* unique ID - used to match */ 109593d9e52SMengdong Lin struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* supported configs playback and captrure */ 110593d9e52SMengdong Lin __le32 num_streams; /* number of streams */ 111593d9e52SMengdong Lin } __packed; 112593d9e52SMengdong Lin 113583958faSMengdong Lin /* topology context */ 1148a978234SLiam Girdwood struct soc_tplg { 1158a978234SLiam Girdwood const struct firmware *fw; 1168a978234SLiam Girdwood 1178a978234SLiam Girdwood /* runtime FW parsing */ 1188a978234SLiam Girdwood const u8 *pos; /* read postion */ 1198a978234SLiam Girdwood const u8 *hdr_pos; /* header position */ 1208a978234SLiam Girdwood unsigned int pass; /* pass number */ 1218a978234SLiam Girdwood 1228a978234SLiam Girdwood /* component caller */ 1238a978234SLiam Girdwood struct device *dev; 1248a978234SLiam Girdwood struct snd_soc_component *comp; 1258a978234SLiam Girdwood u32 index; /* current block index */ 1268a978234SLiam Girdwood u32 req_index; /* required index, only loaded/free matching blocks */ 1278a978234SLiam Girdwood 12888a17d8fSMengdong Lin /* vendor specific kcontrol operations */ 1298a978234SLiam Girdwood const struct snd_soc_tplg_kcontrol_ops *io_ops; 1308a978234SLiam Girdwood int io_ops_count; 1318a978234SLiam Girdwood 1321a3232d2SMengdong Lin /* vendor specific bytes ext handlers, for TLV bytes controls */ 1331a3232d2SMengdong Lin const struct snd_soc_tplg_bytes_ext_ops *bytes_ext_ops; 1341a3232d2SMengdong Lin int bytes_ext_ops_count; 1351a3232d2SMengdong Lin 1368a978234SLiam Girdwood /* optional fw loading callbacks to component drivers */ 1378a978234SLiam Girdwood struct snd_soc_tplg_ops *ops; 1388a978234SLiam Girdwood }; 1398a978234SLiam Girdwood 1408a978234SLiam Girdwood static int soc_tplg_process_headers(struct soc_tplg *tplg); 1418a978234SLiam Girdwood static void soc_tplg_complete(struct soc_tplg *tplg); 1428a978234SLiam Girdwood struct snd_soc_dapm_widget * 1438a978234SLiam Girdwood snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, 1448a978234SLiam Girdwood const struct snd_soc_dapm_widget *widget); 1458a978234SLiam Girdwood struct snd_soc_dapm_widget * 1468a978234SLiam Girdwood snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, 1478a978234SLiam Girdwood const struct snd_soc_dapm_widget *widget); 1488a978234SLiam Girdwood 1498a978234SLiam Girdwood /* check we dont overflow the data for this control chunk */ 1508a978234SLiam Girdwood static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size, 1518a978234SLiam Girdwood unsigned int count, size_t bytes, const char *elem_type) 1528a978234SLiam Girdwood { 1538a978234SLiam Girdwood const u8 *end = tplg->pos + elem_size * count; 1548a978234SLiam Girdwood 1558a978234SLiam Girdwood if (end > tplg->fw->data + tplg->fw->size) { 1568a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: %s overflow end of data\n", 1578a978234SLiam Girdwood elem_type); 1588a978234SLiam Girdwood return -EINVAL; 1598a978234SLiam Girdwood } 1608a978234SLiam Girdwood 1618a978234SLiam Girdwood /* check there is enough room in chunk for control. 1628a978234SLiam Girdwood extra bytes at the end of control are for vendor data here */ 1638a978234SLiam Girdwood if (elem_size * count > bytes) { 1648a978234SLiam Girdwood dev_err(tplg->dev, 1658a978234SLiam Girdwood "ASoC: %s count %d of size %zu is bigger than chunk %zu\n", 1668a978234SLiam Girdwood elem_type, count, elem_size, bytes); 1678a978234SLiam Girdwood return -EINVAL; 1688a978234SLiam Girdwood } 1698a978234SLiam Girdwood 1708a978234SLiam Girdwood return 0; 1718a978234SLiam Girdwood } 1728a978234SLiam Girdwood 1738a978234SLiam Girdwood static inline int soc_tplg_is_eof(struct soc_tplg *tplg) 1748a978234SLiam Girdwood { 1758a978234SLiam Girdwood const u8 *end = tplg->hdr_pos; 1768a978234SLiam Girdwood 1778a978234SLiam Girdwood if (end >= tplg->fw->data + tplg->fw->size) 1788a978234SLiam Girdwood return 1; 1798a978234SLiam Girdwood return 0; 1808a978234SLiam Girdwood } 1818a978234SLiam Girdwood 1828a978234SLiam Girdwood static inline unsigned long soc_tplg_get_hdr_offset(struct soc_tplg *tplg) 1838a978234SLiam Girdwood { 1848a978234SLiam Girdwood return (unsigned long)(tplg->hdr_pos - tplg->fw->data); 1858a978234SLiam Girdwood } 1868a978234SLiam Girdwood 1878a978234SLiam Girdwood static inline unsigned long soc_tplg_get_offset(struct soc_tplg *tplg) 1888a978234SLiam Girdwood { 1898a978234SLiam Girdwood return (unsigned long)(tplg->pos - tplg->fw->data); 1908a978234SLiam Girdwood } 1918a978234SLiam Girdwood 1928a978234SLiam Girdwood /* mapping of Kcontrol types and associated operations. */ 1938a978234SLiam Girdwood static const struct snd_soc_tplg_kcontrol_ops io_ops[] = { 1948a978234SLiam Girdwood {SND_SOC_TPLG_CTL_VOLSW, snd_soc_get_volsw, 1958a978234SLiam Girdwood snd_soc_put_volsw, snd_soc_info_volsw}, 1968a978234SLiam Girdwood {SND_SOC_TPLG_CTL_VOLSW_SX, snd_soc_get_volsw_sx, 1978a978234SLiam Girdwood snd_soc_put_volsw_sx, NULL}, 1988a978234SLiam Girdwood {SND_SOC_TPLG_CTL_ENUM, snd_soc_get_enum_double, 1998a978234SLiam Girdwood snd_soc_put_enum_double, snd_soc_info_enum_double}, 2008a978234SLiam Girdwood {SND_SOC_TPLG_CTL_ENUM_VALUE, snd_soc_get_enum_double, 2018a978234SLiam Girdwood snd_soc_put_enum_double, NULL}, 2028a978234SLiam Girdwood {SND_SOC_TPLG_CTL_BYTES, snd_soc_bytes_get, 2038a978234SLiam Girdwood snd_soc_bytes_put, snd_soc_bytes_info}, 2048a978234SLiam Girdwood {SND_SOC_TPLG_CTL_RANGE, snd_soc_get_volsw_range, 2058a978234SLiam Girdwood snd_soc_put_volsw_range, snd_soc_info_volsw_range}, 2068a978234SLiam Girdwood {SND_SOC_TPLG_CTL_VOLSW_XR_SX, snd_soc_get_xr_sx, 2078a978234SLiam Girdwood snd_soc_put_xr_sx, snd_soc_info_xr_sx}, 2088a978234SLiam Girdwood {SND_SOC_TPLG_CTL_STROBE, snd_soc_get_strobe, 2098a978234SLiam Girdwood snd_soc_put_strobe, NULL}, 2108a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_CTL_VOLSW, snd_soc_dapm_get_volsw, 2112c57d478SJeeja KP snd_soc_dapm_put_volsw, snd_soc_info_volsw}, 2128a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE, snd_soc_dapm_get_enum_double, 2138a978234SLiam Girdwood snd_soc_dapm_put_enum_double, snd_soc_info_enum_double}, 2148a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT, snd_soc_dapm_get_enum_double, 2158a978234SLiam Girdwood snd_soc_dapm_put_enum_double, NULL}, 2168a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE, snd_soc_dapm_get_enum_double, 2178a978234SLiam Girdwood snd_soc_dapm_put_enum_double, NULL}, 2188a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_CTL_PIN, snd_soc_dapm_get_pin_switch, 2198a978234SLiam Girdwood snd_soc_dapm_put_pin_switch, snd_soc_dapm_info_pin_switch}, 2208a978234SLiam Girdwood }; 2218a978234SLiam Girdwood 2228a978234SLiam Girdwood struct soc_tplg_map { 2238a978234SLiam Girdwood int uid; 2248a978234SLiam Girdwood int kid; 2258a978234SLiam Girdwood }; 2268a978234SLiam Girdwood 2278a978234SLiam Girdwood /* mapping of widget types from UAPI IDs to kernel IDs */ 2288a978234SLiam Girdwood static const struct soc_tplg_map dapm_map[] = { 2298a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_INPUT, snd_soc_dapm_input}, 2308a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_OUTPUT, snd_soc_dapm_output}, 2318a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_MUX, snd_soc_dapm_mux}, 2328a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_MIXER, snd_soc_dapm_mixer}, 2338a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_PGA, snd_soc_dapm_pga}, 2348a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_OUT_DRV, snd_soc_dapm_out_drv}, 2358a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_ADC, snd_soc_dapm_adc}, 2368a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_DAC, snd_soc_dapm_dac}, 2378a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_SWITCH, snd_soc_dapm_switch}, 2388a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_PRE, snd_soc_dapm_pre}, 2398a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_POST, snd_soc_dapm_post}, 2408a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_AIF_IN, snd_soc_dapm_aif_in}, 2418a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_AIF_OUT, snd_soc_dapm_aif_out}, 2428a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_DAI_IN, snd_soc_dapm_dai_in}, 2438a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_DAI_OUT, snd_soc_dapm_dai_out}, 2448a978234SLiam Girdwood {SND_SOC_TPLG_DAPM_DAI_LINK, snd_soc_dapm_dai_link}, 2458a70b454SLiam Girdwood {SND_SOC_TPLG_DAPM_BUFFER, snd_soc_dapm_buffer}, 2468a70b454SLiam Girdwood {SND_SOC_TPLG_DAPM_SCHEDULER, snd_soc_dapm_scheduler}, 2478a70b454SLiam Girdwood {SND_SOC_TPLG_DAPM_EFFECT, snd_soc_dapm_effect}, 2488a70b454SLiam Girdwood {SND_SOC_TPLG_DAPM_SIGGEN, snd_soc_dapm_siggen}, 2498a70b454SLiam Girdwood {SND_SOC_TPLG_DAPM_SRC, snd_soc_dapm_src}, 2508a70b454SLiam Girdwood {SND_SOC_TPLG_DAPM_ASRC, snd_soc_dapm_asrc}, 2518a70b454SLiam Girdwood {SND_SOC_TPLG_DAPM_ENCODER, snd_soc_dapm_encoder}, 2528a70b454SLiam Girdwood {SND_SOC_TPLG_DAPM_DECODER, snd_soc_dapm_decoder}, 2538a978234SLiam Girdwood }; 2548a978234SLiam Girdwood 2558a978234SLiam Girdwood static int tplc_chan_get_reg(struct soc_tplg *tplg, 2568a978234SLiam Girdwood struct snd_soc_tplg_channel *chan, int map) 2578a978234SLiam Girdwood { 2588a978234SLiam Girdwood int i; 2598a978234SLiam Girdwood 2608a978234SLiam Girdwood for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) { 2618a978234SLiam Girdwood if (chan[i].id == map) 2628a978234SLiam Girdwood return chan[i].reg; 2638a978234SLiam Girdwood } 2648a978234SLiam Girdwood 2658a978234SLiam Girdwood return -EINVAL; 2668a978234SLiam Girdwood } 2678a978234SLiam Girdwood 2688a978234SLiam Girdwood static int tplc_chan_get_shift(struct soc_tplg *tplg, 2698a978234SLiam Girdwood struct snd_soc_tplg_channel *chan, int map) 2708a978234SLiam Girdwood { 2718a978234SLiam Girdwood int i; 2728a978234SLiam Girdwood 2738a978234SLiam Girdwood for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) { 2748a978234SLiam Girdwood if (chan[i].id == map) 2758a978234SLiam Girdwood return chan[i].shift; 2768a978234SLiam Girdwood } 2778a978234SLiam Girdwood 2788a978234SLiam Girdwood return -EINVAL; 2798a978234SLiam Girdwood } 2808a978234SLiam Girdwood 2818a978234SLiam Girdwood static int get_widget_id(int tplg_type) 2828a978234SLiam Girdwood { 2838a978234SLiam Girdwood int i; 2848a978234SLiam Girdwood 2858a978234SLiam Girdwood for (i = 0; i < ARRAY_SIZE(dapm_map); i++) { 2868a978234SLiam Girdwood if (tplg_type == dapm_map[i].uid) 2878a978234SLiam Girdwood return dapm_map[i].kid; 2888a978234SLiam Girdwood } 2898a978234SLiam Girdwood 2908a978234SLiam Girdwood return -EINVAL; 2918a978234SLiam Girdwood } 2928a978234SLiam Girdwood 2938a978234SLiam Girdwood static inline void soc_bind_err(struct soc_tplg *tplg, 2948a978234SLiam Girdwood struct snd_soc_tplg_ctl_hdr *hdr, int index) 2958a978234SLiam Girdwood { 2968a978234SLiam Girdwood dev_err(tplg->dev, 2978a978234SLiam Girdwood "ASoC: invalid control type (g,p,i) %d:%d:%d index %d at 0x%lx\n", 2988a978234SLiam Girdwood hdr->ops.get, hdr->ops.put, hdr->ops.info, index, 2998a978234SLiam Girdwood soc_tplg_get_offset(tplg)); 3008a978234SLiam Girdwood } 3018a978234SLiam Girdwood 3028a978234SLiam Girdwood static inline void soc_control_err(struct soc_tplg *tplg, 3038a978234SLiam Girdwood struct snd_soc_tplg_ctl_hdr *hdr, const char *name) 3048a978234SLiam Girdwood { 3058a978234SLiam Girdwood dev_err(tplg->dev, 3068a978234SLiam Girdwood "ASoC: no complete mixer IO handler for %s type (g,p,i) %d:%d:%d at 0x%lx\n", 3078a978234SLiam Girdwood name, hdr->ops.get, hdr->ops.put, hdr->ops.info, 3088a978234SLiam Girdwood soc_tplg_get_offset(tplg)); 3098a978234SLiam Girdwood } 3108a978234SLiam Girdwood 3118a978234SLiam Girdwood /* pass vendor data to component driver for processing */ 3128a978234SLiam Girdwood static int soc_tplg_vendor_load_(struct soc_tplg *tplg, 3138a978234SLiam Girdwood struct snd_soc_tplg_hdr *hdr) 3148a978234SLiam Girdwood { 3158a978234SLiam Girdwood int ret = 0; 3168a978234SLiam Girdwood 3178a978234SLiam Girdwood if (tplg->comp && tplg->ops && tplg->ops->vendor_load) 3188a978234SLiam Girdwood ret = tplg->ops->vendor_load(tplg->comp, hdr); 3198a978234SLiam Girdwood else { 3208a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: no vendor load callback for ID %d\n", 3218a978234SLiam Girdwood hdr->vendor_type); 3228a978234SLiam Girdwood return -EINVAL; 3238a978234SLiam Girdwood } 3248a978234SLiam Girdwood 3258a978234SLiam Girdwood if (ret < 0) 3268a978234SLiam Girdwood dev_err(tplg->dev, 3278a978234SLiam Girdwood "ASoC: vendor load failed at hdr offset %ld/0x%lx for type %d:%d\n", 3288a978234SLiam Girdwood soc_tplg_get_hdr_offset(tplg), 3298a978234SLiam Girdwood soc_tplg_get_hdr_offset(tplg), 3308a978234SLiam Girdwood hdr->type, hdr->vendor_type); 3318a978234SLiam Girdwood return ret; 3328a978234SLiam Girdwood } 3338a978234SLiam Girdwood 3348a978234SLiam Girdwood /* pass vendor data to component driver for processing */ 3358a978234SLiam Girdwood static int soc_tplg_vendor_load(struct soc_tplg *tplg, 3368a978234SLiam Girdwood struct snd_soc_tplg_hdr *hdr) 3378a978234SLiam Girdwood { 3388a978234SLiam Girdwood if (tplg->pass != SOC_TPLG_PASS_VENDOR) 3398a978234SLiam Girdwood return 0; 3408a978234SLiam Girdwood 3418a978234SLiam Girdwood return soc_tplg_vendor_load_(tplg, hdr); 3428a978234SLiam Girdwood } 3438a978234SLiam Girdwood 3448a978234SLiam Girdwood /* optionally pass new dynamic widget to component driver. This is mainly for 3458a978234SLiam Girdwood * external widgets where we can assign private data/ops */ 3468a978234SLiam Girdwood static int soc_tplg_widget_load(struct soc_tplg *tplg, 3478a978234SLiam Girdwood struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) 3488a978234SLiam Girdwood { 3498a978234SLiam Girdwood if (tplg->comp && tplg->ops && tplg->ops->widget_load) 3508a978234SLiam Girdwood return tplg->ops->widget_load(tplg->comp, w, tplg_w); 3518a978234SLiam Girdwood 3528a978234SLiam Girdwood return 0; 3538a978234SLiam Girdwood } 3548a978234SLiam Girdwood 355ebd259d3SLiam Girdwood /* optionally pass new dynamic widget to component driver. This is mainly for 356ebd259d3SLiam Girdwood * external widgets where we can assign private data/ops */ 357ebd259d3SLiam Girdwood static int soc_tplg_widget_ready(struct soc_tplg *tplg, 358ebd259d3SLiam Girdwood struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tplg_w) 359ebd259d3SLiam Girdwood { 360ebd259d3SLiam Girdwood if (tplg->comp && tplg->ops && tplg->ops->widget_ready) 361ebd259d3SLiam Girdwood return tplg->ops->widget_ready(tplg->comp, w, tplg_w); 362ebd259d3SLiam Girdwood 363ebd259d3SLiam Girdwood return 0; 364ebd259d3SLiam Girdwood } 365ebd259d3SLiam Girdwood 366183b8021SMasahiro Yamada /* pass DAI configurations to component driver for extra initialization */ 36764527e8aSMengdong Lin static int soc_tplg_dai_load(struct soc_tplg *tplg, 36864527e8aSMengdong Lin struct snd_soc_dai_driver *dai_drv) 3698a978234SLiam Girdwood { 37064527e8aSMengdong Lin if (tplg->comp && tplg->ops && tplg->ops->dai_load) 37164527e8aSMengdong Lin return tplg->ops->dai_load(tplg->comp, dai_drv); 3728a978234SLiam Girdwood 3738a978234SLiam Girdwood return 0; 3748a978234SLiam Girdwood } 3758a978234SLiam Girdwood 376183b8021SMasahiro Yamada /* pass link configurations to component driver for extra initialization */ 377acfc7d46SMengdong Lin static int soc_tplg_dai_link_load(struct soc_tplg *tplg, 378acfc7d46SMengdong Lin struct snd_soc_dai_link *link) 379acfc7d46SMengdong Lin { 380acfc7d46SMengdong Lin if (tplg->comp && tplg->ops && tplg->ops->link_load) 381acfc7d46SMengdong Lin return tplg->ops->link_load(tplg->comp, link); 382acfc7d46SMengdong Lin 383acfc7d46SMengdong Lin return 0; 384acfc7d46SMengdong Lin } 385acfc7d46SMengdong Lin 3868a978234SLiam Girdwood /* tell the component driver that all firmware has been loaded in this request */ 3878a978234SLiam Girdwood static void soc_tplg_complete(struct soc_tplg *tplg) 3888a978234SLiam Girdwood { 3898a978234SLiam Girdwood if (tplg->comp && tplg->ops && tplg->ops->complete) 3908a978234SLiam Girdwood tplg->ops->complete(tplg->comp); 3918a978234SLiam Girdwood } 3928a978234SLiam Girdwood 3938a978234SLiam Girdwood /* add a dynamic kcontrol */ 3948a978234SLiam Girdwood static int soc_tplg_add_dcontrol(struct snd_card *card, struct device *dev, 3958a978234SLiam Girdwood const struct snd_kcontrol_new *control_new, const char *prefix, 3968a978234SLiam Girdwood void *data, struct snd_kcontrol **kcontrol) 3978a978234SLiam Girdwood { 3988a978234SLiam Girdwood int err; 3998a978234SLiam Girdwood 4008a978234SLiam Girdwood *kcontrol = snd_soc_cnew(control_new, data, control_new->name, prefix); 4018a978234SLiam Girdwood if (*kcontrol == NULL) { 4028a978234SLiam Girdwood dev_err(dev, "ASoC: Failed to create new kcontrol %s\n", 4038a978234SLiam Girdwood control_new->name); 4048a978234SLiam Girdwood return -ENOMEM; 4058a978234SLiam Girdwood } 4068a978234SLiam Girdwood 4078a978234SLiam Girdwood err = snd_ctl_add(card, *kcontrol); 4088a978234SLiam Girdwood if (err < 0) { 4098a978234SLiam Girdwood dev_err(dev, "ASoC: Failed to add %s: %d\n", 4108a978234SLiam Girdwood control_new->name, err); 4118a978234SLiam Girdwood return err; 4128a978234SLiam Girdwood } 4138a978234SLiam Girdwood 4148a978234SLiam Girdwood return 0; 4158a978234SLiam Girdwood } 4168a978234SLiam Girdwood 4178a978234SLiam Girdwood /* add a dynamic kcontrol for component driver */ 4188a978234SLiam Girdwood static int soc_tplg_add_kcontrol(struct soc_tplg *tplg, 4198a978234SLiam Girdwood struct snd_kcontrol_new *k, struct snd_kcontrol **kcontrol) 4208a978234SLiam Girdwood { 4218a978234SLiam Girdwood struct snd_soc_component *comp = tplg->comp; 4228a978234SLiam Girdwood 4238a978234SLiam Girdwood return soc_tplg_add_dcontrol(comp->card->snd_card, 4248a978234SLiam Girdwood comp->dev, k, NULL, comp, kcontrol); 4258a978234SLiam Girdwood } 4268a978234SLiam Girdwood 4278a978234SLiam Girdwood /* remove a mixer kcontrol */ 4288a978234SLiam Girdwood static void remove_mixer(struct snd_soc_component *comp, 4298a978234SLiam Girdwood struct snd_soc_dobj *dobj, int pass) 4308a978234SLiam Girdwood { 4318a978234SLiam Girdwood struct snd_card *card = comp->card->snd_card; 4328a978234SLiam Girdwood struct soc_mixer_control *sm = 4338a978234SLiam Girdwood container_of(dobj, struct soc_mixer_control, dobj); 4348a978234SLiam Girdwood const unsigned int *p = NULL; 4358a978234SLiam Girdwood 4368a978234SLiam Girdwood if (pass != SOC_TPLG_PASS_MIXER) 4378a978234SLiam Girdwood return; 4388a978234SLiam Girdwood 4398a978234SLiam Girdwood if (dobj->ops && dobj->ops->control_unload) 4408a978234SLiam Girdwood dobj->ops->control_unload(comp, dobj); 4418a978234SLiam Girdwood 4428a978234SLiam Girdwood if (sm->dobj.control.kcontrol->tlv.p) 4438a978234SLiam Girdwood p = sm->dobj.control.kcontrol->tlv.p; 4448a978234SLiam Girdwood snd_ctl_remove(card, sm->dobj.control.kcontrol); 4458a978234SLiam Girdwood list_del(&sm->dobj.list); 4468a978234SLiam Girdwood kfree(sm); 4478a978234SLiam Girdwood kfree(p); 4488a978234SLiam Girdwood } 4498a978234SLiam Girdwood 4508a978234SLiam Girdwood /* remove an enum kcontrol */ 4518a978234SLiam Girdwood static void remove_enum(struct snd_soc_component *comp, 4528a978234SLiam Girdwood struct snd_soc_dobj *dobj, int pass) 4538a978234SLiam Girdwood { 4548a978234SLiam Girdwood struct snd_card *card = comp->card->snd_card; 4558a978234SLiam Girdwood struct soc_enum *se = container_of(dobj, struct soc_enum, dobj); 4568a978234SLiam Girdwood int i; 4578a978234SLiam Girdwood 4588a978234SLiam Girdwood if (pass != SOC_TPLG_PASS_MIXER) 4598a978234SLiam Girdwood return; 4608a978234SLiam Girdwood 4618a978234SLiam Girdwood if (dobj->ops && dobj->ops->control_unload) 4628a978234SLiam Girdwood dobj->ops->control_unload(comp, dobj); 4638a978234SLiam Girdwood 4648a978234SLiam Girdwood snd_ctl_remove(card, se->dobj.control.kcontrol); 4658a978234SLiam Girdwood list_del(&se->dobj.list); 4668a978234SLiam Girdwood 4678a978234SLiam Girdwood kfree(se->dobj.control.dvalues); 4688a978234SLiam Girdwood for (i = 0; i < se->items; i++) 4698a978234SLiam Girdwood kfree(se->dobj.control.dtexts[i]); 4708a978234SLiam Girdwood kfree(se); 4718a978234SLiam Girdwood } 4728a978234SLiam Girdwood 4738a978234SLiam Girdwood /* remove a byte kcontrol */ 4748a978234SLiam Girdwood static void remove_bytes(struct snd_soc_component *comp, 4758a978234SLiam Girdwood struct snd_soc_dobj *dobj, int pass) 4768a978234SLiam Girdwood { 4778a978234SLiam Girdwood struct snd_card *card = comp->card->snd_card; 4788a978234SLiam Girdwood struct soc_bytes_ext *sb = 4798a978234SLiam Girdwood container_of(dobj, struct soc_bytes_ext, dobj); 4808a978234SLiam Girdwood 4818a978234SLiam Girdwood if (pass != SOC_TPLG_PASS_MIXER) 4828a978234SLiam Girdwood return; 4838a978234SLiam Girdwood 4848a978234SLiam Girdwood if (dobj->ops && dobj->ops->control_unload) 4858a978234SLiam Girdwood dobj->ops->control_unload(comp, dobj); 4868a978234SLiam Girdwood 4878a978234SLiam Girdwood snd_ctl_remove(card, sb->dobj.control.kcontrol); 4888a978234SLiam Girdwood list_del(&sb->dobj.list); 4898a978234SLiam Girdwood kfree(sb); 4908a978234SLiam Girdwood } 4918a978234SLiam Girdwood 4928a978234SLiam Girdwood /* remove a widget and it's kcontrols - routes must be removed first */ 4938a978234SLiam Girdwood static void remove_widget(struct snd_soc_component *comp, 4948a978234SLiam Girdwood struct snd_soc_dobj *dobj, int pass) 4958a978234SLiam Girdwood { 4968a978234SLiam Girdwood struct snd_card *card = comp->card->snd_card; 4978a978234SLiam Girdwood struct snd_soc_dapm_widget *w = 4988a978234SLiam Girdwood container_of(dobj, struct snd_soc_dapm_widget, dobj); 4998a978234SLiam Girdwood int i; 5008a978234SLiam Girdwood 5018a978234SLiam Girdwood if (pass != SOC_TPLG_PASS_WIDGET) 5028a978234SLiam Girdwood return; 5038a978234SLiam Girdwood 5048a978234SLiam Girdwood if (dobj->ops && dobj->ops->widget_unload) 5058a978234SLiam Girdwood dobj->ops->widget_unload(comp, dobj); 5068a978234SLiam Girdwood 50705bdcf12SLiam Girdwood if (!w->kcontrols) 50805bdcf12SLiam Girdwood goto free_news; 50905bdcf12SLiam Girdwood 5108a978234SLiam Girdwood /* 5111a7dd6e2SMengdong Lin * Dynamic Widgets either have 1..N enum kcontrols or mixers. 5128a978234SLiam Girdwood * The enum may either have an array of values or strings. 5138a978234SLiam Girdwood */ 514eea3dd4fSMengdong Lin if (dobj->widget.kcontrol_type == SND_SOC_TPLG_TYPE_ENUM) { 5158a978234SLiam Girdwood /* enumerated widget mixer */ 5161a7dd6e2SMengdong Lin for (i = 0; i < w->num_kcontrols; i++) { 5171a7dd6e2SMengdong Lin struct snd_kcontrol *kcontrol = w->kcontrols[i]; 5188a978234SLiam Girdwood struct soc_enum *se = 5191a7dd6e2SMengdong Lin (struct soc_enum *)kcontrol->private_value; 520d7766aa5SColin Ian King int j; 5218a978234SLiam Girdwood 5221a7dd6e2SMengdong Lin snd_ctl_remove(card, kcontrol); 5238a978234SLiam Girdwood 5248a978234SLiam Girdwood kfree(se->dobj.control.dvalues); 525d7766aa5SColin Ian King for (j = 0; j < se->items; j++) 526d7766aa5SColin Ian King kfree(se->dobj.control.dtexts[j]); 5278a978234SLiam Girdwood 5288a978234SLiam Girdwood kfree(se); 529267e2c6fSLiam Girdwood kfree(w->kcontrol_news[i].name); 5301a7dd6e2SMengdong Lin } 5318a978234SLiam Girdwood } else { 532eea3dd4fSMengdong Lin /* volume mixer or bytes controls */ 5338a978234SLiam Girdwood for (i = 0; i < w->num_kcontrols; i++) { 5348a978234SLiam Girdwood struct snd_kcontrol *kcontrol = w->kcontrols[i]; 5358a978234SLiam Girdwood 536eea3dd4fSMengdong Lin if (dobj->widget.kcontrol_type 537eea3dd4fSMengdong Lin == SND_SOC_TPLG_TYPE_MIXER) 538eea3dd4fSMengdong Lin kfree(kcontrol->tlv.p); 5398a978234SLiam Girdwood 540eea3dd4fSMengdong Lin /* Private value is used as struct soc_mixer_control 541eea3dd4fSMengdong Lin * for volume mixers or soc_bytes_ext for bytes 542eea3dd4fSMengdong Lin * controls. 543eea3dd4fSMengdong Lin */ 544eea3dd4fSMengdong Lin kfree((void *)kcontrol->private_value); 545c2b36129SColin Ian King snd_ctl_remove(card, kcontrol); 546267e2c6fSLiam Girdwood kfree(w->kcontrol_news[i].name); 5478a978234SLiam Girdwood } 5488a978234SLiam Girdwood } 54905bdcf12SLiam Girdwood 55005bdcf12SLiam Girdwood free_news: 5518a978234SLiam Girdwood kfree(w->kcontrol_news); 55205bdcf12SLiam Girdwood 5538a978234SLiam Girdwood /* widget w is freed by soc-dapm.c */ 5548a978234SLiam Girdwood } 5558a978234SLiam Girdwood 55664527e8aSMengdong Lin /* remove DAI configurations */ 55764527e8aSMengdong Lin static void remove_dai(struct snd_soc_component *comp, 5588a978234SLiam Girdwood struct snd_soc_dobj *dobj, int pass) 5598a978234SLiam Girdwood { 56064527e8aSMengdong Lin struct snd_soc_dai_driver *dai_drv = 56164527e8aSMengdong Lin container_of(dobj, struct snd_soc_dai_driver, dobj); 56264527e8aSMengdong Lin 5638a978234SLiam Girdwood if (pass != SOC_TPLG_PASS_PCM_DAI) 5648a978234SLiam Girdwood return; 5658a978234SLiam Girdwood 56664527e8aSMengdong Lin if (dobj->ops && dobj->ops->dai_unload) 56764527e8aSMengdong Lin dobj->ops->dai_unload(comp, dobj); 5688a978234SLiam Girdwood 5698f27c4abSMengdong Lin kfree(dai_drv->name); 5708a978234SLiam Girdwood list_del(&dobj->list); 57164527e8aSMengdong Lin kfree(dai_drv); 5728a978234SLiam Girdwood } 5738a978234SLiam Girdwood 574acfc7d46SMengdong Lin /* remove link configurations */ 575acfc7d46SMengdong Lin static void remove_link(struct snd_soc_component *comp, 576acfc7d46SMengdong Lin struct snd_soc_dobj *dobj, int pass) 577acfc7d46SMengdong Lin { 578acfc7d46SMengdong Lin struct snd_soc_dai_link *link = 579acfc7d46SMengdong Lin container_of(dobj, struct snd_soc_dai_link, dobj); 580acfc7d46SMengdong Lin 581acfc7d46SMengdong Lin if (pass != SOC_TPLG_PASS_PCM_DAI) 582acfc7d46SMengdong Lin return; 583acfc7d46SMengdong Lin 584acfc7d46SMengdong Lin if (dobj->ops && dobj->ops->link_unload) 585acfc7d46SMengdong Lin dobj->ops->link_unload(comp, dobj); 586acfc7d46SMengdong Lin 5878f27c4abSMengdong Lin kfree(link->name); 5888f27c4abSMengdong Lin kfree(link->stream_name); 5898f27c4abSMengdong Lin kfree(link->cpu_dai_name); 5908f27c4abSMengdong Lin 591acfc7d46SMengdong Lin list_del(&dobj->list); 592acfc7d46SMengdong Lin snd_soc_remove_dai_link(comp->card, link); 593acfc7d46SMengdong Lin kfree(link); 594acfc7d46SMengdong Lin } 595acfc7d46SMengdong Lin 5968a978234SLiam Girdwood /* bind a kcontrol to it's IO handlers */ 5978a978234SLiam Girdwood static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, 5988a978234SLiam Girdwood struct snd_kcontrol_new *k, 5992b5cdb91SMengdong Lin const struct soc_tplg *tplg) 6008a978234SLiam Girdwood { 6012b5cdb91SMengdong Lin const struct snd_soc_tplg_kcontrol_ops *ops; 6021a3232d2SMengdong Lin const struct snd_soc_tplg_bytes_ext_ops *ext_ops; 6032b5cdb91SMengdong Lin int num_ops, i; 6048a978234SLiam Girdwood 6051a3232d2SMengdong Lin if (hdr->ops.info == SND_SOC_TPLG_CTL_BYTES 6061a3232d2SMengdong Lin && k->iface & SNDRV_CTL_ELEM_IFACE_MIXER 6071a3232d2SMengdong Lin && k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE 6081a3232d2SMengdong Lin && k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { 6091a3232d2SMengdong Lin struct soc_bytes_ext *sbe; 6101a3232d2SMengdong Lin struct snd_soc_tplg_bytes_control *be; 6111a3232d2SMengdong Lin 6121a3232d2SMengdong Lin sbe = (struct soc_bytes_ext *)k->private_value; 6131a3232d2SMengdong Lin be = container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); 6141a3232d2SMengdong Lin 6151a3232d2SMengdong Lin /* TLV bytes controls need standard kcontrol info handler, 6161a3232d2SMengdong Lin * TLV callback and extended put/get handlers. 6171a3232d2SMengdong Lin */ 618f4be978bSOmair M Abdullah k->info = snd_soc_bytes_info_ext; 6191a3232d2SMengdong Lin k->tlv.c = snd_soc_bytes_tlv_callback; 6201a3232d2SMengdong Lin 6211a3232d2SMengdong Lin ext_ops = tplg->bytes_ext_ops; 6221a3232d2SMengdong Lin num_ops = tplg->bytes_ext_ops_count; 6231a3232d2SMengdong Lin for (i = 0; i < num_ops; i++) { 6241a3232d2SMengdong Lin if (!sbe->put && ext_ops[i].id == be->ext_ops.put) 6251a3232d2SMengdong Lin sbe->put = ext_ops[i].put; 6261a3232d2SMengdong Lin if (!sbe->get && ext_ops[i].id == be->ext_ops.get) 6271a3232d2SMengdong Lin sbe->get = ext_ops[i].get; 6281a3232d2SMengdong Lin } 6291a3232d2SMengdong Lin 6301a3232d2SMengdong Lin if (sbe->put && sbe->get) 6311a3232d2SMengdong Lin return 0; 6321a3232d2SMengdong Lin else 6331a3232d2SMengdong Lin return -EINVAL; 6341a3232d2SMengdong Lin } 6351a3232d2SMengdong Lin 63688a17d8fSMengdong Lin /* try and map vendor specific kcontrol handlers first */ 6372b5cdb91SMengdong Lin ops = tplg->io_ops; 6382b5cdb91SMengdong Lin num_ops = tplg->io_ops_count; 6398a978234SLiam Girdwood for (i = 0; i < num_ops; i++) { 6408a978234SLiam Girdwood 6412b5cdb91SMengdong Lin if (k->put == NULL && ops[i].id == hdr->ops.put) 6428a978234SLiam Girdwood k->put = ops[i].put; 6432b5cdb91SMengdong Lin if (k->get == NULL && ops[i].id == hdr->ops.get) 6448a978234SLiam Girdwood k->get = ops[i].get; 6452b5cdb91SMengdong Lin if (k->info == NULL && ops[i].id == hdr->ops.info) 6462b5cdb91SMengdong Lin k->info = ops[i].info; 6478a978234SLiam Girdwood } 6488a978234SLiam Girdwood 64988a17d8fSMengdong Lin /* vendor specific handlers found ? */ 65088a17d8fSMengdong Lin if (k->put && k->get && k->info) 65188a17d8fSMengdong Lin return 0; 65288a17d8fSMengdong Lin 65388a17d8fSMengdong Lin /* none found so try standard kcontrol handlers */ 6542b5cdb91SMengdong Lin ops = io_ops; 6552b5cdb91SMengdong Lin num_ops = ARRAY_SIZE(io_ops); 65688a17d8fSMengdong Lin for (i = 0; i < num_ops; i++) { 65788a17d8fSMengdong Lin 65888a17d8fSMengdong Lin if (k->put == NULL && ops[i].id == hdr->ops.put) 65988a17d8fSMengdong Lin k->put = ops[i].put; 66088a17d8fSMengdong Lin if (k->get == NULL && ops[i].id == hdr->ops.get) 66188a17d8fSMengdong Lin k->get = ops[i].get; 66288a17d8fSMengdong Lin if (k->info == NULL && ops[i].id == hdr->ops.info) 6638a978234SLiam Girdwood k->info = ops[i].info; 6648a978234SLiam Girdwood } 6658a978234SLiam Girdwood 6668a978234SLiam Girdwood /* standard handlers found ? */ 6678a978234SLiam Girdwood if (k->put && k->get && k->info) 6688a978234SLiam Girdwood return 0; 6698a978234SLiam Girdwood 6708a978234SLiam Girdwood /* nothing to bind */ 6718a978234SLiam Girdwood return -EINVAL; 6728a978234SLiam Girdwood } 6738a978234SLiam Girdwood 6748a978234SLiam Girdwood /* bind a widgets to it's evnt handlers */ 6758a978234SLiam Girdwood int snd_soc_tplg_widget_bind_event(struct snd_soc_dapm_widget *w, 6768a978234SLiam Girdwood const struct snd_soc_tplg_widget_events *events, 6778a978234SLiam Girdwood int num_events, u16 event_type) 6788a978234SLiam Girdwood { 6798a978234SLiam Girdwood int i; 6808a978234SLiam Girdwood 6818a978234SLiam Girdwood w->event = NULL; 6828a978234SLiam Girdwood 6838a978234SLiam Girdwood for (i = 0; i < num_events; i++) { 6848a978234SLiam Girdwood if (event_type == events[i].type) { 6858a978234SLiam Girdwood 6868a978234SLiam Girdwood /* found - so assign event */ 6878a978234SLiam Girdwood w->event = events[i].event_handler; 6888a978234SLiam Girdwood return 0; 6898a978234SLiam Girdwood } 6908a978234SLiam Girdwood } 6918a978234SLiam Girdwood 6928a978234SLiam Girdwood /* not found */ 6938a978234SLiam Girdwood return -EINVAL; 6948a978234SLiam Girdwood } 6958a978234SLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_bind_event); 6968a978234SLiam Girdwood 6978a978234SLiam Girdwood /* optionally pass new dynamic kcontrol to component driver. */ 6988a978234SLiam Girdwood static int soc_tplg_init_kcontrol(struct soc_tplg *tplg, 6998a978234SLiam Girdwood struct snd_kcontrol_new *k, struct snd_soc_tplg_ctl_hdr *hdr) 7008a978234SLiam Girdwood { 7018a978234SLiam Girdwood if (tplg->comp && tplg->ops && tplg->ops->control_load) 7028a978234SLiam Girdwood return tplg->ops->control_load(tplg->comp, k, hdr); 7038a978234SLiam Girdwood 7048a978234SLiam Girdwood return 0; 7058a978234SLiam Girdwood } 7068a978234SLiam Girdwood 70728a87eebSMengdong Lin 70828a87eebSMengdong Lin static int soc_tplg_create_tlv_db_scale(struct soc_tplg *tplg, 70928a87eebSMengdong Lin struct snd_kcontrol_new *kc, struct snd_soc_tplg_tlv_dbscale *scale) 7108a978234SLiam Girdwood { 71128a87eebSMengdong Lin unsigned int item_len = 2 * sizeof(unsigned int); 71228a87eebSMengdong Lin unsigned int *p; 7138a978234SLiam Girdwood 71428a87eebSMengdong Lin p = kzalloc(item_len + 2 * sizeof(unsigned int), GFP_KERNEL); 71528a87eebSMengdong Lin if (!p) 7168a978234SLiam Girdwood return -ENOMEM; 7178a978234SLiam Girdwood 71828a87eebSMengdong Lin p[0] = SNDRV_CTL_TLVT_DB_SCALE; 71928a87eebSMengdong Lin p[1] = item_len; 72028a87eebSMengdong Lin p[2] = scale->min; 72128a87eebSMengdong Lin p[3] = (scale->step & TLV_DB_SCALE_MASK) 72228a87eebSMengdong Lin | (scale->mute ? TLV_DB_SCALE_MUTE : 0); 7238a978234SLiam Girdwood 72428a87eebSMengdong Lin kc->tlv.p = (void *)p; 72528a87eebSMengdong Lin return 0; 72628a87eebSMengdong Lin } 72728a87eebSMengdong Lin 72828a87eebSMengdong Lin static int soc_tplg_create_tlv(struct soc_tplg *tplg, 72928a87eebSMengdong Lin struct snd_kcontrol_new *kc, struct snd_soc_tplg_ctl_hdr *tc) 73028a87eebSMengdong Lin { 73128a87eebSMengdong Lin struct snd_soc_tplg_ctl_tlv *tplg_tlv; 73228a87eebSMengdong Lin 73328a87eebSMengdong Lin if (!(tc->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)) 73428a87eebSMengdong Lin return 0; 73528a87eebSMengdong Lin 7361a3232d2SMengdong Lin if (!(tc->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) { 73728a87eebSMengdong Lin tplg_tlv = &tc->tlv; 73828a87eebSMengdong Lin switch (tplg_tlv->type) { 73928a87eebSMengdong Lin case SNDRV_CTL_TLVT_DB_SCALE: 74028a87eebSMengdong Lin return soc_tplg_create_tlv_db_scale(tplg, kc, 74128a87eebSMengdong Lin &tplg_tlv->scale); 74228a87eebSMengdong Lin 74328a87eebSMengdong Lin /* TODO: add support for other TLV types */ 74428a87eebSMengdong Lin default: 74528a87eebSMengdong Lin dev_dbg(tplg->dev, "Unsupported TLV type %d\n", 74628a87eebSMengdong Lin tplg_tlv->type); 74728a87eebSMengdong Lin return -EINVAL; 74828a87eebSMengdong Lin } 74928a87eebSMengdong Lin } 7508a978234SLiam Girdwood 7518a978234SLiam Girdwood return 0; 7528a978234SLiam Girdwood } 7538a978234SLiam Girdwood 7548a978234SLiam Girdwood static inline void soc_tplg_free_tlv(struct soc_tplg *tplg, 7558a978234SLiam Girdwood struct snd_kcontrol_new *kc) 7568a978234SLiam Girdwood { 7578a978234SLiam Girdwood kfree(kc->tlv.p); 7588a978234SLiam Girdwood } 7598a978234SLiam Girdwood 7608a978234SLiam Girdwood static int soc_tplg_dbytes_create(struct soc_tplg *tplg, unsigned int count, 7618a978234SLiam Girdwood size_t size) 7628a978234SLiam Girdwood { 7638a978234SLiam Girdwood struct snd_soc_tplg_bytes_control *be; 7648a978234SLiam Girdwood struct soc_bytes_ext *sbe; 7658a978234SLiam Girdwood struct snd_kcontrol_new kc; 7668a978234SLiam Girdwood int i, err; 7678a978234SLiam Girdwood 7688a978234SLiam Girdwood if (soc_tplg_check_elem_count(tplg, 7698a978234SLiam Girdwood sizeof(struct snd_soc_tplg_bytes_control), count, 7708a978234SLiam Girdwood size, "mixer bytes")) { 7718a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: Invalid count %d for byte control\n", 7728a978234SLiam Girdwood count); 7738a978234SLiam Girdwood return -EINVAL; 7748a978234SLiam Girdwood } 7758a978234SLiam Girdwood 7768a978234SLiam Girdwood for (i = 0; i < count; i++) { 7778a978234SLiam Girdwood be = (struct snd_soc_tplg_bytes_control *)tplg->pos; 7788a978234SLiam Girdwood 7798a978234SLiam Girdwood /* validate kcontrol */ 7808a978234SLiam Girdwood if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 7818a978234SLiam Girdwood SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 7828a978234SLiam Girdwood return -EINVAL; 7838a978234SLiam Girdwood 7848a978234SLiam Girdwood sbe = kzalloc(sizeof(*sbe), GFP_KERNEL); 7858a978234SLiam Girdwood if (sbe == NULL) 7868a978234SLiam Girdwood return -ENOMEM; 7878a978234SLiam Girdwood 7888a978234SLiam Girdwood tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + 7898a978234SLiam Girdwood be->priv.size); 7908a978234SLiam Girdwood 7918a978234SLiam Girdwood dev_dbg(tplg->dev, 7928a978234SLiam Girdwood "ASoC: adding bytes kcontrol %s with access 0x%x\n", 7938a978234SLiam Girdwood be->hdr.name, be->hdr.access); 7948a978234SLiam Girdwood 7958a978234SLiam Girdwood memset(&kc, 0, sizeof(kc)); 7968a978234SLiam Girdwood kc.name = be->hdr.name; 7978a978234SLiam Girdwood kc.private_value = (long)sbe; 7988a978234SLiam Girdwood kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 7998a978234SLiam Girdwood kc.access = be->hdr.access; 8008a978234SLiam Girdwood 8018a978234SLiam Girdwood sbe->max = be->max; 8028a978234SLiam Girdwood sbe->dobj.type = SND_SOC_DOBJ_BYTES; 8038a978234SLiam Girdwood sbe->dobj.ops = tplg->ops; 8048a978234SLiam Girdwood INIT_LIST_HEAD(&sbe->dobj.list); 8058a978234SLiam Girdwood 8068a978234SLiam Girdwood /* map io handlers */ 8072b5cdb91SMengdong Lin err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc, tplg); 8088a978234SLiam Girdwood if (err) { 8098a978234SLiam Girdwood soc_control_err(tplg, &be->hdr, be->hdr.name); 8108a978234SLiam Girdwood kfree(sbe); 8118a978234SLiam Girdwood continue; 8128a978234SLiam Girdwood } 8138a978234SLiam Girdwood 8148a978234SLiam Girdwood /* pass control to driver for optional further init */ 8158a978234SLiam Girdwood err = soc_tplg_init_kcontrol(tplg, &kc, 8168a978234SLiam Girdwood (struct snd_soc_tplg_ctl_hdr *)be); 8178a978234SLiam Girdwood if (err < 0) { 8188a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: failed to init %s\n", 8198a978234SLiam Girdwood be->hdr.name); 8208a978234SLiam Girdwood kfree(sbe); 8218a978234SLiam Girdwood continue; 8228a978234SLiam Girdwood } 8238a978234SLiam Girdwood 8248a978234SLiam Girdwood /* register control here */ 8258a978234SLiam Girdwood err = soc_tplg_add_kcontrol(tplg, &kc, 8268a978234SLiam Girdwood &sbe->dobj.control.kcontrol); 8278a978234SLiam Girdwood if (err < 0) { 8288a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: failed to add %s\n", 8298a978234SLiam Girdwood be->hdr.name); 8308a978234SLiam Girdwood kfree(sbe); 8318a978234SLiam Girdwood continue; 8328a978234SLiam Girdwood } 8338a978234SLiam Girdwood 8348a978234SLiam Girdwood list_add(&sbe->dobj.list, &tplg->comp->dobj_list); 8358a978234SLiam Girdwood } 8368a978234SLiam Girdwood return 0; 8378a978234SLiam Girdwood 8388a978234SLiam Girdwood } 8398a978234SLiam Girdwood 8408a978234SLiam Girdwood static int soc_tplg_dmixer_create(struct soc_tplg *tplg, unsigned int count, 8418a978234SLiam Girdwood size_t size) 8428a978234SLiam Girdwood { 8438a978234SLiam Girdwood struct snd_soc_tplg_mixer_control *mc; 8448a978234SLiam Girdwood struct soc_mixer_control *sm; 8458a978234SLiam Girdwood struct snd_kcontrol_new kc; 8468a978234SLiam Girdwood int i, err; 8478a978234SLiam Girdwood 8488a978234SLiam Girdwood if (soc_tplg_check_elem_count(tplg, 8498a978234SLiam Girdwood sizeof(struct snd_soc_tplg_mixer_control), 8508a978234SLiam Girdwood count, size, "mixers")) { 8518a978234SLiam Girdwood 8528a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: invalid count %d for controls\n", 8538a978234SLiam Girdwood count); 8548a978234SLiam Girdwood return -EINVAL; 8558a978234SLiam Girdwood } 8568a978234SLiam Girdwood 8578a978234SLiam Girdwood for (i = 0; i < count; i++) { 8588a978234SLiam Girdwood mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; 8598a978234SLiam Girdwood 8608a978234SLiam Girdwood /* validate kcontrol */ 8618a978234SLiam Girdwood if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 8628a978234SLiam Girdwood SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 8638a978234SLiam Girdwood return -EINVAL; 8648a978234SLiam Girdwood 8658a978234SLiam Girdwood sm = kzalloc(sizeof(*sm), GFP_KERNEL); 8668a978234SLiam Girdwood if (sm == NULL) 8678a978234SLiam Girdwood return -ENOMEM; 8688a978234SLiam Girdwood tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) + 8698a978234SLiam Girdwood mc->priv.size); 8708a978234SLiam Girdwood 8718a978234SLiam Girdwood dev_dbg(tplg->dev, 8728a978234SLiam Girdwood "ASoC: adding mixer kcontrol %s with access 0x%x\n", 8738a978234SLiam Girdwood mc->hdr.name, mc->hdr.access); 8748a978234SLiam Girdwood 8758a978234SLiam Girdwood memset(&kc, 0, sizeof(kc)); 8768a978234SLiam Girdwood kc.name = mc->hdr.name; 8778a978234SLiam Girdwood kc.private_value = (long)sm; 8788a978234SLiam Girdwood kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 8798a978234SLiam Girdwood kc.access = mc->hdr.access; 8808a978234SLiam Girdwood 8818a978234SLiam Girdwood /* we only support FL/FR channel mapping atm */ 8828a978234SLiam Girdwood sm->reg = tplc_chan_get_reg(tplg, mc->channel, 8838a978234SLiam Girdwood SNDRV_CHMAP_FL); 8848a978234SLiam Girdwood sm->rreg = tplc_chan_get_reg(tplg, mc->channel, 8858a978234SLiam Girdwood SNDRV_CHMAP_FR); 8868a978234SLiam Girdwood sm->shift = tplc_chan_get_shift(tplg, mc->channel, 8878a978234SLiam Girdwood SNDRV_CHMAP_FL); 8888a978234SLiam Girdwood sm->rshift = tplc_chan_get_shift(tplg, mc->channel, 8898a978234SLiam Girdwood SNDRV_CHMAP_FR); 8908a978234SLiam Girdwood 8918a978234SLiam Girdwood sm->max = mc->max; 8928a978234SLiam Girdwood sm->min = mc->min; 8938a978234SLiam Girdwood sm->invert = mc->invert; 8948a978234SLiam Girdwood sm->platform_max = mc->platform_max; 8958a978234SLiam Girdwood sm->dobj.index = tplg->index; 8968a978234SLiam Girdwood sm->dobj.ops = tplg->ops; 8978a978234SLiam Girdwood sm->dobj.type = SND_SOC_DOBJ_MIXER; 8988a978234SLiam Girdwood INIT_LIST_HEAD(&sm->dobj.list); 8998a978234SLiam Girdwood 9008a978234SLiam Girdwood /* map io handlers */ 9012b5cdb91SMengdong Lin err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc, tplg); 9028a978234SLiam Girdwood if (err) { 9038a978234SLiam Girdwood soc_control_err(tplg, &mc->hdr, mc->hdr.name); 9048a978234SLiam Girdwood kfree(sm); 9058a978234SLiam Girdwood continue; 9068a978234SLiam Girdwood } 9078a978234SLiam Girdwood 9088a978234SLiam Girdwood /* pass control to driver for optional further init */ 9098a978234SLiam Girdwood err = soc_tplg_init_kcontrol(tplg, &kc, 9108a978234SLiam Girdwood (struct snd_soc_tplg_ctl_hdr *) mc); 9118a978234SLiam Girdwood if (err < 0) { 9128a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: failed to init %s\n", 9138a978234SLiam Girdwood mc->hdr.name); 9148a978234SLiam Girdwood kfree(sm); 9158a978234SLiam Girdwood continue; 9168a978234SLiam Girdwood } 9178a978234SLiam Girdwood 9188a978234SLiam Girdwood /* create any TLV data */ 91928a87eebSMengdong Lin soc_tplg_create_tlv(tplg, &kc, &mc->hdr); 9208a978234SLiam Girdwood 9218a978234SLiam Girdwood /* register control here */ 9228a978234SLiam Girdwood err = soc_tplg_add_kcontrol(tplg, &kc, 9238a978234SLiam Girdwood &sm->dobj.control.kcontrol); 9248a978234SLiam Girdwood if (err < 0) { 9258a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: failed to add %s\n", 9268a978234SLiam Girdwood mc->hdr.name); 9278a978234SLiam Girdwood soc_tplg_free_tlv(tplg, &kc); 9288a978234SLiam Girdwood kfree(sm); 9298a978234SLiam Girdwood continue; 9308a978234SLiam Girdwood } 9318a978234SLiam Girdwood 9328a978234SLiam Girdwood list_add(&sm->dobj.list, &tplg->comp->dobj_list); 9338a978234SLiam Girdwood } 9348a978234SLiam Girdwood 9358a978234SLiam Girdwood return 0; 9368a978234SLiam Girdwood } 9378a978234SLiam Girdwood 9388a978234SLiam Girdwood static int soc_tplg_denum_create_texts(struct soc_enum *se, 9398a978234SLiam Girdwood struct snd_soc_tplg_enum_control *ec) 9408a978234SLiam Girdwood { 9418a978234SLiam Girdwood int i, ret; 9428a978234SLiam Girdwood 9438a978234SLiam Girdwood se->dobj.control.dtexts = 9448a978234SLiam Girdwood kzalloc(sizeof(char *) * ec->items, GFP_KERNEL); 9458a978234SLiam Girdwood if (se->dobj.control.dtexts == NULL) 9468a978234SLiam Girdwood return -ENOMEM; 9478a978234SLiam Girdwood 9488a978234SLiam Girdwood for (i = 0; i < ec->items; i++) { 9498a978234SLiam Girdwood 9508a978234SLiam Girdwood if (strnlen(ec->texts[i], SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 9518a978234SLiam Girdwood SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { 9528a978234SLiam Girdwood ret = -EINVAL; 9538a978234SLiam Girdwood goto err; 9548a978234SLiam Girdwood } 9558a978234SLiam Girdwood 9568a978234SLiam Girdwood se->dobj.control.dtexts[i] = kstrdup(ec->texts[i], GFP_KERNEL); 9578a978234SLiam Girdwood if (!se->dobj.control.dtexts[i]) { 9588a978234SLiam Girdwood ret = -ENOMEM; 9598a978234SLiam Girdwood goto err; 9608a978234SLiam Girdwood } 9618a978234SLiam Girdwood } 9628a978234SLiam Girdwood 963b6e38b29SMousumi Jana se->texts = (const char * const *)se->dobj.control.dtexts; 9648a978234SLiam Girdwood return 0; 9658a978234SLiam Girdwood 9668a978234SLiam Girdwood err: 9678a978234SLiam Girdwood for (--i; i >= 0; i--) 9688a978234SLiam Girdwood kfree(se->dobj.control.dtexts[i]); 9698a978234SLiam Girdwood kfree(se->dobj.control.dtexts); 9708a978234SLiam Girdwood return ret; 9718a978234SLiam Girdwood } 9728a978234SLiam Girdwood 9738a978234SLiam Girdwood static int soc_tplg_denum_create_values(struct soc_enum *se, 9748a978234SLiam Girdwood struct snd_soc_tplg_enum_control *ec) 9758a978234SLiam Girdwood { 9768a978234SLiam Girdwood if (ec->items > sizeof(*ec->values)) 9778a978234SLiam Girdwood return -EINVAL; 9788a978234SLiam Girdwood 979376c0afeSAndrzej Hajda se->dobj.control.dvalues = kmemdup(ec->values, 980376c0afeSAndrzej Hajda ec->items * sizeof(u32), 981376c0afeSAndrzej Hajda GFP_KERNEL); 9828a978234SLiam Girdwood if (!se->dobj.control.dvalues) 9838a978234SLiam Girdwood return -ENOMEM; 9848a978234SLiam Girdwood 9858a978234SLiam Girdwood return 0; 9868a978234SLiam Girdwood } 9878a978234SLiam Girdwood 9888a978234SLiam Girdwood static int soc_tplg_denum_create(struct soc_tplg *tplg, unsigned int count, 9898a978234SLiam Girdwood size_t size) 9908a978234SLiam Girdwood { 9918a978234SLiam Girdwood struct snd_soc_tplg_enum_control *ec; 9928a978234SLiam Girdwood struct soc_enum *se; 9938a978234SLiam Girdwood struct snd_kcontrol_new kc; 9948a978234SLiam Girdwood int i, ret, err; 9958a978234SLiam Girdwood 9968a978234SLiam Girdwood if (soc_tplg_check_elem_count(tplg, 9978a978234SLiam Girdwood sizeof(struct snd_soc_tplg_enum_control), 9988a978234SLiam Girdwood count, size, "enums")) { 9998a978234SLiam Girdwood 10008a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: invalid count %d for enum controls\n", 10018a978234SLiam Girdwood count); 10028a978234SLiam Girdwood return -EINVAL; 10038a978234SLiam Girdwood } 10048a978234SLiam Girdwood 10058a978234SLiam Girdwood for (i = 0; i < count; i++) { 10068a978234SLiam Girdwood ec = (struct snd_soc_tplg_enum_control *)tplg->pos; 10078a978234SLiam Girdwood tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + 10088a978234SLiam Girdwood ec->priv.size); 10098a978234SLiam Girdwood 10108a978234SLiam Girdwood /* validate kcontrol */ 10118a978234SLiam Girdwood if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 10128a978234SLiam Girdwood SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 10138a978234SLiam Girdwood return -EINVAL; 10148a978234SLiam Girdwood 10158a978234SLiam Girdwood se = kzalloc((sizeof(*se)), GFP_KERNEL); 10168a978234SLiam Girdwood if (se == NULL) 10178a978234SLiam Girdwood return -ENOMEM; 10188a978234SLiam Girdwood 10198a978234SLiam Girdwood dev_dbg(tplg->dev, "ASoC: adding enum kcontrol %s size %d\n", 10208a978234SLiam Girdwood ec->hdr.name, ec->items); 10218a978234SLiam Girdwood 10228a978234SLiam Girdwood memset(&kc, 0, sizeof(kc)); 10238a978234SLiam Girdwood kc.name = ec->hdr.name; 10248a978234SLiam Girdwood kc.private_value = (long)se; 10258a978234SLiam Girdwood kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 10268a978234SLiam Girdwood kc.access = ec->hdr.access; 10278a978234SLiam Girdwood 10288a978234SLiam Girdwood se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); 10298a978234SLiam Girdwood se->shift_l = tplc_chan_get_shift(tplg, ec->channel, 10308a978234SLiam Girdwood SNDRV_CHMAP_FL); 10318a978234SLiam Girdwood se->shift_r = tplc_chan_get_shift(tplg, ec->channel, 10328a978234SLiam Girdwood SNDRV_CHMAP_FL); 10338a978234SLiam Girdwood 10348a978234SLiam Girdwood se->items = ec->items; 10358a978234SLiam Girdwood se->mask = ec->mask; 10368a978234SLiam Girdwood se->dobj.index = tplg->index; 10378a978234SLiam Girdwood se->dobj.type = SND_SOC_DOBJ_ENUM; 10388a978234SLiam Girdwood se->dobj.ops = tplg->ops; 10398a978234SLiam Girdwood INIT_LIST_HEAD(&se->dobj.list); 10408a978234SLiam Girdwood 10418a978234SLiam Girdwood switch (ec->hdr.ops.info) { 10428a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: 10438a978234SLiam Girdwood case SND_SOC_TPLG_CTL_ENUM_VALUE: 10448a978234SLiam Girdwood err = soc_tplg_denum_create_values(se, ec); 10458a978234SLiam Girdwood if (err < 0) { 10468a978234SLiam Girdwood dev_err(tplg->dev, 10478a978234SLiam Girdwood "ASoC: could not create values for %s\n", 10488a978234SLiam Girdwood ec->hdr.name); 10498a978234SLiam Girdwood kfree(se); 10508a978234SLiam Girdwood continue; 10518a978234SLiam Girdwood } 10528a978234SLiam Girdwood /* fall through and create texts */ 10538a978234SLiam Girdwood case SND_SOC_TPLG_CTL_ENUM: 10548a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: 10558a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: 10568a978234SLiam Girdwood err = soc_tplg_denum_create_texts(se, ec); 10578a978234SLiam Girdwood if (err < 0) { 10588a978234SLiam Girdwood dev_err(tplg->dev, 10598a978234SLiam Girdwood "ASoC: could not create texts for %s\n", 10608a978234SLiam Girdwood ec->hdr.name); 10618a978234SLiam Girdwood kfree(se); 10628a978234SLiam Girdwood continue; 10638a978234SLiam Girdwood } 10648a978234SLiam Girdwood break; 10658a978234SLiam Girdwood default: 10668a978234SLiam Girdwood dev_err(tplg->dev, 10678a978234SLiam Girdwood "ASoC: invalid enum control type %d for %s\n", 10688a978234SLiam Girdwood ec->hdr.ops.info, ec->hdr.name); 10698a978234SLiam Girdwood kfree(se); 10708a978234SLiam Girdwood continue; 10718a978234SLiam Girdwood } 10728a978234SLiam Girdwood 10738a978234SLiam Girdwood /* map io handlers */ 10742b5cdb91SMengdong Lin err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc, tplg); 10758a978234SLiam Girdwood if (err) { 10768a978234SLiam Girdwood soc_control_err(tplg, &ec->hdr, ec->hdr.name); 10778a978234SLiam Girdwood kfree(se); 10788a978234SLiam Girdwood continue; 10798a978234SLiam Girdwood } 10808a978234SLiam Girdwood 10818a978234SLiam Girdwood /* pass control to driver for optional further init */ 10828a978234SLiam Girdwood err = soc_tplg_init_kcontrol(tplg, &kc, 10838a978234SLiam Girdwood (struct snd_soc_tplg_ctl_hdr *) ec); 10848a978234SLiam Girdwood if (err < 0) { 10858a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: failed to init %s\n", 10868a978234SLiam Girdwood ec->hdr.name); 10878a978234SLiam Girdwood kfree(se); 10888a978234SLiam Girdwood continue; 10898a978234SLiam Girdwood } 10908a978234SLiam Girdwood 10918a978234SLiam Girdwood /* register control here */ 10928a978234SLiam Girdwood ret = soc_tplg_add_kcontrol(tplg, 10938a978234SLiam Girdwood &kc, &se->dobj.control.kcontrol); 10948a978234SLiam Girdwood if (ret < 0) { 10958a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: could not add kcontrol %s\n", 10968a978234SLiam Girdwood ec->hdr.name); 10978a978234SLiam Girdwood kfree(se); 10988a978234SLiam Girdwood continue; 10998a978234SLiam Girdwood } 11008a978234SLiam Girdwood 11018a978234SLiam Girdwood list_add(&se->dobj.list, &tplg->comp->dobj_list); 11028a978234SLiam Girdwood } 11038a978234SLiam Girdwood 11048a978234SLiam Girdwood return 0; 11058a978234SLiam Girdwood } 11068a978234SLiam Girdwood 11078a978234SLiam Girdwood static int soc_tplg_kcontrol_elems_load(struct soc_tplg *tplg, 11088a978234SLiam Girdwood struct snd_soc_tplg_hdr *hdr) 11098a978234SLiam Girdwood { 11108a978234SLiam Girdwood struct snd_soc_tplg_ctl_hdr *control_hdr; 11118a978234SLiam Girdwood int i; 11128a978234SLiam Girdwood 11138a978234SLiam Girdwood if (tplg->pass != SOC_TPLG_PASS_MIXER) { 11148a978234SLiam Girdwood tplg->pos += hdr->size + hdr->payload_size; 11158a978234SLiam Girdwood return 0; 11168a978234SLiam Girdwood } 11178a978234SLiam Girdwood 11188a978234SLiam Girdwood dev_dbg(tplg->dev, "ASoC: adding %d kcontrols at 0x%lx\n", hdr->count, 11198a978234SLiam Girdwood soc_tplg_get_offset(tplg)); 11208a978234SLiam Girdwood 11218a978234SLiam Girdwood for (i = 0; i < hdr->count; i++) { 11228a978234SLiam Girdwood 11238a978234SLiam Girdwood control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos; 11248a978234SLiam Girdwood 112506eb49f7SMengdong Lin if (control_hdr->size != sizeof(*control_hdr)) { 112606eb49f7SMengdong Lin dev_err(tplg->dev, "ASoC: invalid control size\n"); 112706eb49f7SMengdong Lin return -EINVAL; 112806eb49f7SMengdong Lin } 112906eb49f7SMengdong Lin 11308a978234SLiam Girdwood switch (control_hdr->ops.info) { 11318a978234SLiam Girdwood case SND_SOC_TPLG_CTL_VOLSW: 11328a978234SLiam Girdwood case SND_SOC_TPLG_CTL_STROBE: 11338a978234SLiam Girdwood case SND_SOC_TPLG_CTL_VOLSW_SX: 11348a978234SLiam Girdwood case SND_SOC_TPLG_CTL_VOLSW_XR_SX: 11358a978234SLiam Girdwood case SND_SOC_TPLG_CTL_RANGE: 11368a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_VOLSW: 11378a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_PIN: 11388a978234SLiam Girdwood soc_tplg_dmixer_create(tplg, 1, hdr->payload_size); 11398a978234SLiam Girdwood break; 11408a978234SLiam Girdwood case SND_SOC_TPLG_CTL_ENUM: 11418a978234SLiam Girdwood case SND_SOC_TPLG_CTL_ENUM_VALUE: 11428a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: 11438a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: 11448a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: 11458a978234SLiam Girdwood soc_tplg_denum_create(tplg, 1, hdr->payload_size); 11468a978234SLiam Girdwood break; 11478a978234SLiam Girdwood case SND_SOC_TPLG_CTL_BYTES: 11488a978234SLiam Girdwood soc_tplg_dbytes_create(tplg, 1, hdr->payload_size); 11498a978234SLiam Girdwood break; 11508a978234SLiam Girdwood default: 11518a978234SLiam Girdwood soc_bind_err(tplg, control_hdr, i); 11528a978234SLiam Girdwood return -EINVAL; 11538a978234SLiam Girdwood } 11548a978234SLiam Girdwood } 11558a978234SLiam Girdwood 11568a978234SLiam Girdwood return 0; 11578a978234SLiam Girdwood } 11588a978234SLiam Girdwood 11598a978234SLiam Girdwood static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg, 11608a978234SLiam Girdwood struct snd_soc_tplg_hdr *hdr) 11618a978234SLiam Girdwood { 11628a978234SLiam Girdwood struct snd_soc_dapm_context *dapm = &tplg->comp->dapm; 11638a978234SLiam Girdwood struct snd_soc_dapm_route route; 11648a978234SLiam Girdwood struct snd_soc_tplg_dapm_graph_elem *elem; 11658a978234SLiam Girdwood int count = hdr->count, i; 11668a978234SLiam Girdwood 11678a978234SLiam Girdwood if (tplg->pass != SOC_TPLG_PASS_GRAPH) { 11688a978234SLiam Girdwood tplg->pos += hdr->size + hdr->payload_size; 11698a978234SLiam Girdwood return 0; 11708a978234SLiam Girdwood } 11718a978234SLiam Girdwood 11728a978234SLiam Girdwood if (soc_tplg_check_elem_count(tplg, 11738a978234SLiam Girdwood sizeof(struct snd_soc_tplg_dapm_graph_elem), 11748a978234SLiam Girdwood count, hdr->payload_size, "graph")) { 11758a978234SLiam Girdwood 11768a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: invalid count %d for DAPM routes\n", 11778a978234SLiam Girdwood count); 11788a978234SLiam Girdwood return -EINVAL; 11798a978234SLiam Girdwood } 11808a978234SLiam Girdwood 1181b75a6511SLiam Girdwood dev_dbg(tplg->dev, "ASoC: adding %d DAPM routes for index %d\n", count, 1182b75a6511SLiam Girdwood hdr->index); 11838a978234SLiam Girdwood 11848a978234SLiam Girdwood for (i = 0; i < count; i++) { 11858a978234SLiam Girdwood elem = (struct snd_soc_tplg_dapm_graph_elem *)tplg->pos; 11868a978234SLiam Girdwood tplg->pos += sizeof(struct snd_soc_tplg_dapm_graph_elem); 11878a978234SLiam Girdwood 11888a978234SLiam Girdwood /* validate routes */ 11898a978234SLiam Girdwood if (strnlen(elem->source, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 11908a978234SLiam Girdwood SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 11918a978234SLiam Girdwood return -EINVAL; 11928a978234SLiam Girdwood if (strnlen(elem->sink, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 11938a978234SLiam Girdwood SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 11948a978234SLiam Girdwood return -EINVAL; 11958a978234SLiam Girdwood if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 11968a978234SLiam Girdwood SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 11978a978234SLiam Girdwood return -EINVAL; 11988a978234SLiam Girdwood 11998a978234SLiam Girdwood route.source = elem->source; 12008a978234SLiam Girdwood route.sink = elem->sink; 12018a978234SLiam Girdwood route.connected = NULL; /* set to NULL atm for tplg users */ 12028a978234SLiam Girdwood if (strnlen(elem->control, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0) 12038a978234SLiam Girdwood route.control = NULL; 12048a978234SLiam Girdwood else 12058a978234SLiam Girdwood route.control = elem->control; 12068a978234SLiam Girdwood 12078a978234SLiam Girdwood /* add route, but keep going if some fail */ 12088a978234SLiam Girdwood snd_soc_dapm_add_routes(dapm, &route, 1); 12098a978234SLiam Girdwood } 12108a978234SLiam Girdwood 12118a978234SLiam Girdwood return 0; 12128a978234SLiam Girdwood } 12138a978234SLiam Girdwood 12148a978234SLiam Girdwood static struct snd_kcontrol_new *soc_tplg_dapm_widget_dmixer_create( 12158a978234SLiam Girdwood struct soc_tplg *tplg, int num_kcontrols) 12168a978234SLiam Girdwood { 12178a978234SLiam Girdwood struct snd_kcontrol_new *kc; 12188a978234SLiam Girdwood struct soc_mixer_control *sm; 12198a978234SLiam Girdwood struct snd_soc_tplg_mixer_control *mc; 12208a978234SLiam Girdwood int i, err; 12218a978234SLiam Girdwood 12224ca7deb1SAxel Lin kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL); 12238a978234SLiam Girdwood if (kc == NULL) 12248a978234SLiam Girdwood return NULL; 12258a978234SLiam Girdwood 12268a978234SLiam Girdwood for (i = 0; i < num_kcontrols; i++) { 12278a978234SLiam Girdwood mc = (struct snd_soc_tplg_mixer_control *)tplg->pos; 12288a978234SLiam Girdwood sm = kzalloc(sizeof(*sm), GFP_KERNEL); 12298a978234SLiam Girdwood if (sm == NULL) 12308a978234SLiam Girdwood goto err; 12318a978234SLiam Girdwood 12328a978234SLiam Girdwood tplg->pos += (sizeof(struct snd_soc_tplg_mixer_control) + 12338a978234SLiam Girdwood mc->priv.size); 12348a978234SLiam Girdwood 12358a978234SLiam Girdwood /* validate kcontrol */ 12368a978234SLiam Girdwood if (strnlen(mc->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 12378a978234SLiam Girdwood SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 12388a978234SLiam Girdwood goto err_str; 12398a978234SLiam Girdwood 12408a978234SLiam Girdwood dev_dbg(tplg->dev, " adding DAPM widget mixer control %s at %d\n", 12418a978234SLiam Girdwood mc->hdr.name, i); 12428a978234SLiam Girdwood 1243267e2c6fSLiam Girdwood kc[i].name = kstrdup(mc->hdr.name, GFP_KERNEL); 1244267e2c6fSLiam Girdwood if (kc[i].name == NULL) 1245267e2c6fSLiam Girdwood goto err_str; 12468a978234SLiam Girdwood kc[i].private_value = (long)sm; 12478a978234SLiam Girdwood kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; 12488a978234SLiam Girdwood kc[i].access = mc->hdr.access; 12498a978234SLiam Girdwood 12508a978234SLiam Girdwood /* we only support FL/FR channel mapping atm */ 12518a978234SLiam Girdwood sm->reg = tplc_chan_get_reg(tplg, mc->channel, 12528a978234SLiam Girdwood SNDRV_CHMAP_FL); 12538a978234SLiam Girdwood sm->rreg = tplc_chan_get_reg(tplg, mc->channel, 12548a978234SLiam Girdwood SNDRV_CHMAP_FR); 12558a978234SLiam Girdwood sm->shift = tplc_chan_get_shift(tplg, mc->channel, 12568a978234SLiam Girdwood SNDRV_CHMAP_FL); 12578a978234SLiam Girdwood sm->rshift = tplc_chan_get_shift(tplg, mc->channel, 12588a978234SLiam Girdwood SNDRV_CHMAP_FR); 12598a978234SLiam Girdwood 12608a978234SLiam Girdwood sm->max = mc->max; 12618a978234SLiam Girdwood sm->min = mc->min; 12628a978234SLiam Girdwood sm->invert = mc->invert; 12638a978234SLiam Girdwood sm->platform_max = mc->platform_max; 12648a978234SLiam Girdwood sm->dobj.index = tplg->index; 12658a978234SLiam Girdwood INIT_LIST_HEAD(&sm->dobj.list); 12668a978234SLiam Girdwood 12678a978234SLiam Girdwood /* map io handlers */ 12682b5cdb91SMengdong Lin err = soc_tplg_kcontrol_bind_io(&mc->hdr, &kc[i], tplg); 12698a978234SLiam Girdwood if (err) { 12708a978234SLiam Girdwood soc_control_err(tplg, &mc->hdr, mc->hdr.name); 12718a978234SLiam Girdwood kfree(sm); 12728a978234SLiam Girdwood continue; 12738a978234SLiam Girdwood } 12748a978234SLiam Girdwood 12758a978234SLiam Girdwood /* pass control to driver for optional further init */ 12768a978234SLiam Girdwood err = soc_tplg_init_kcontrol(tplg, &kc[i], 12778a978234SLiam Girdwood (struct snd_soc_tplg_ctl_hdr *)mc); 12788a978234SLiam Girdwood if (err < 0) { 12798a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: failed to init %s\n", 12808a978234SLiam Girdwood mc->hdr.name); 12818a978234SLiam Girdwood kfree(sm); 12828a978234SLiam Girdwood continue; 12838a978234SLiam Girdwood } 1284bde8b388SRanjani Sridharan 1285bde8b388SRanjani Sridharan /* create any TLV data */ 1286bde8b388SRanjani Sridharan soc_tplg_create_tlv(tplg, &kc[i], &mc->hdr); 12878a978234SLiam Girdwood } 12888a978234SLiam Girdwood return kc; 12898a978234SLiam Girdwood 12908a978234SLiam Girdwood err_str: 12918a978234SLiam Girdwood kfree(sm); 12928a978234SLiam Girdwood err: 1293267e2c6fSLiam Girdwood for (--i; i >= 0; i--) { 12948a978234SLiam Girdwood kfree((void *)kc[i].private_value); 1295267e2c6fSLiam Girdwood kfree(kc[i].name); 1296267e2c6fSLiam Girdwood } 12978a978234SLiam Girdwood kfree(kc); 12988a978234SLiam Girdwood return NULL; 12998a978234SLiam Girdwood } 13008a978234SLiam Girdwood 13018a978234SLiam Girdwood static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create( 13021a7dd6e2SMengdong Lin struct soc_tplg *tplg, int num_kcontrols) 13038a978234SLiam Girdwood { 13048a978234SLiam Girdwood struct snd_kcontrol_new *kc; 13058a978234SLiam Girdwood struct snd_soc_tplg_enum_control *ec; 13068a978234SLiam Girdwood struct soc_enum *se; 13071a7dd6e2SMengdong Lin int i, j, err; 13088a978234SLiam Girdwood 13091a7dd6e2SMengdong Lin kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL); 13101a7dd6e2SMengdong Lin if (kc == NULL) 13111a7dd6e2SMengdong Lin return NULL; 13121a7dd6e2SMengdong Lin 13131a7dd6e2SMengdong Lin for (i = 0; i < num_kcontrols; i++) { 13148a978234SLiam Girdwood ec = (struct snd_soc_tplg_enum_control *)tplg->pos; 13158a978234SLiam Girdwood /* validate kcontrol */ 13168a978234SLiam Girdwood if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 13178a978234SLiam Girdwood SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 1318f3ee9096SChristophe JAILLET goto err; 13198a978234SLiam Girdwood 13208a978234SLiam Girdwood se = kzalloc(sizeof(*se), GFP_KERNEL); 13218a978234SLiam Girdwood if (se == NULL) 13228a978234SLiam Girdwood goto err; 13238a978234SLiam Girdwood 13248a978234SLiam Girdwood dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n", 13258a978234SLiam Girdwood ec->hdr.name); 13268a978234SLiam Girdwood 1327267e2c6fSLiam Girdwood kc[i].name = kstrdup(ec->hdr.name, GFP_KERNEL); 132865030ff3SDan Carpenter if (kc[i].name == NULL) { 132965030ff3SDan Carpenter kfree(se); 1330267e2c6fSLiam Girdwood goto err_se; 133165030ff3SDan Carpenter } 13321a7dd6e2SMengdong Lin kc[i].private_value = (long)se; 13331a7dd6e2SMengdong Lin kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; 13341a7dd6e2SMengdong Lin kc[i].access = ec->hdr.access; 13358a978234SLiam Girdwood 13368a978234SLiam Girdwood /* we only support FL/FR channel mapping atm */ 13378a978234SLiam Girdwood se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL); 13381a7dd6e2SMengdong Lin se->shift_l = tplc_chan_get_shift(tplg, ec->channel, 13391a7dd6e2SMengdong Lin SNDRV_CHMAP_FL); 13401a7dd6e2SMengdong Lin se->shift_r = tplc_chan_get_shift(tplg, ec->channel, 13411a7dd6e2SMengdong Lin SNDRV_CHMAP_FR); 13428a978234SLiam Girdwood 13438a978234SLiam Girdwood se->items = ec->items; 13448a978234SLiam Girdwood se->mask = ec->mask; 13458a978234SLiam Girdwood se->dobj.index = tplg->index; 13468a978234SLiam Girdwood 13478a978234SLiam Girdwood switch (ec->hdr.ops.info) { 13488a978234SLiam Girdwood case SND_SOC_TPLG_CTL_ENUM_VALUE: 13498a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: 13508a978234SLiam Girdwood err = soc_tplg_denum_create_values(se, ec); 13518a978234SLiam Girdwood if (err < 0) { 13528a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: could not create values for %s\n", 13538a978234SLiam Girdwood ec->hdr.name); 13548a978234SLiam Girdwood goto err_se; 13558a978234SLiam Girdwood } 13568a978234SLiam Girdwood /* fall through to create texts */ 13578a978234SLiam Girdwood case SND_SOC_TPLG_CTL_ENUM: 13588a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: 13598a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: 13608a978234SLiam Girdwood err = soc_tplg_denum_create_texts(se, ec); 13618a978234SLiam Girdwood if (err < 0) { 13628a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: could not create texts for %s\n", 13638a978234SLiam Girdwood ec->hdr.name); 13648a978234SLiam Girdwood goto err_se; 13658a978234SLiam Girdwood } 13668a978234SLiam Girdwood break; 13678a978234SLiam Girdwood default: 13688a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n", 13698a978234SLiam Girdwood ec->hdr.ops.info, ec->hdr.name); 13708a978234SLiam Girdwood goto err_se; 13718a978234SLiam Girdwood } 13728a978234SLiam Girdwood 13738a978234SLiam Girdwood /* map io handlers */ 13741a7dd6e2SMengdong Lin err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc[i], tplg); 13758a978234SLiam Girdwood if (err) { 13768a978234SLiam Girdwood soc_control_err(tplg, &ec->hdr, ec->hdr.name); 13778a978234SLiam Girdwood goto err_se; 13788a978234SLiam Girdwood } 13798a978234SLiam Girdwood 13808a978234SLiam Girdwood /* pass control to driver for optional further init */ 13811a7dd6e2SMengdong Lin err = soc_tplg_init_kcontrol(tplg, &kc[i], 13828a978234SLiam Girdwood (struct snd_soc_tplg_ctl_hdr *)ec); 13838a978234SLiam Girdwood if (err < 0) { 13848a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: failed to init %s\n", 13858a978234SLiam Girdwood ec->hdr.name); 13868a978234SLiam Girdwood goto err_se; 13878a978234SLiam Girdwood } 13888a978234SLiam Girdwood 13891a7dd6e2SMengdong Lin tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) + 13901a7dd6e2SMengdong Lin ec->priv.size); 13911a7dd6e2SMengdong Lin } 13921a7dd6e2SMengdong Lin 13938a978234SLiam Girdwood return kc; 13948a978234SLiam Girdwood 13958a978234SLiam Girdwood err_se: 13961a7dd6e2SMengdong Lin for (; i >= 0; i--) { 13978a978234SLiam Girdwood /* free values and texts */ 13981a7dd6e2SMengdong Lin se = (struct soc_enum *)kc[i].private_value; 13996d5574edSChristophe JAILLET if (!se) 14006d5574edSChristophe JAILLET continue; 14016d5574edSChristophe JAILLET 14028a978234SLiam Girdwood kfree(se->dobj.control.dvalues); 14031a7dd6e2SMengdong Lin for (j = 0; j < ec->items; j++) 14041a7dd6e2SMengdong Lin kfree(se->dobj.control.dtexts[j]); 14058a978234SLiam Girdwood 14068a978234SLiam Girdwood kfree(se); 1407267e2c6fSLiam Girdwood kfree(kc[i].name); 14081a7dd6e2SMengdong Lin } 14098a978234SLiam Girdwood err: 14108a978234SLiam Girdwood kfree(kc); 14118a978234SLiam Girdwood 14128a978234SLiam Girdwood return NULL; 14138a978234SLiam Girdwood } 14148a978234SLiam Girdwood 14158a978234SLiam Girdwood static struct snd_kcontrol_new *soc_tplg_dapm_widget_dbytes_create( 14168a978234SLiam Girdwood struct soc_tplg *tplg, int count) 14178a978234SLiam Girdwood { 14188a978234SLiam Girdwood struct snd_soc_tplg_bytes_control *be; 14198a978234SLiam Girdwood struct soc_bytes_ext *sbe; 14208a978234SLiam Girdwood struct snd_kcontrol_new *kc; 14218a978234SLiam Girdwood int i, err; 14228a978234SLiam Girdwood 14234ca7deb1SAxel Lin kc = kcalloc(count, sizeof(*kc), GFP_KERNEL); 14248a978234SLiam Girdwood if (!kc) 14258a978234SLiam Girdwood return NULL; 14268a978234SLiam Girdwood 14278a978234SLiam Girdwood for (i = 0; i < count; i++) { 14288a978234SLiam Girdwood be = (struct snd_soc_tplg_bytes_control *)tplg->pos; 14298a978234SLiam Girdwood 14308a978234SLiam Girdwood /* validate kcontrol */ 14318a978234SLiam Girdwood if (strnlen(be->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 14328a978234SLiam Girdwood SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 14338a978234SLiam Girdwood goto err; 14348a978234SLiam Girdwood 14358a978234SLiam Girdwood sbe = kzalloc(sizeof(*sbe), GFP_KERNEL); 14368a978234SLiam Girdwood if (sbe == NULL) 14378a978234SLiam Girdwood goto err; 14388a978234SLiam Girdwood 14398a978234SLiam Girdwood tplg->pos += (sizeof(struct snd_soc_tplg_bytes_control) + 14408a978234SLiam Girdwood be->priv.size); 14418a978234SLiam Girdwood 14428a978234SLiam Girdwood dev_dbg(tplg->dev, 14438a978234SLiam Girdwood "ASoC: adding bytes kcontrol %s with access 0x%x\n", 14448a978234SLiam Girdwood be->hdr.name, be->hdr.access); 14458a978234SLiam Girdwood 1446267e2c6fSLiam Girdwood kc[i].name = kstrdup(be->hdr.name, GFP_KERNEL); 144765030ff3SDan Carpenter if (kc[i].name == NULL) { 144865030ff3SDan Carpenter kfree(sbe); 1449267e2c6fSLiam Girdwood goto err; 145065030ff3SDan Carpenter } 14518a978234SLiam Girdwood kc[i].private_value = (long)sbe; 14528a978234SLiam Girdwood kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER; 14538a978234SLiam Girdwood kc[i].access = be->hdr.access; 14548a978234SLiam Girdwood 14558a978234SLiam Girdwood sbe->max = be->max; 14568a978234SLiam Girdwood INIT_LIST_HEAD(&sbe->dobj.list); 14578a978234SLiam Girdwood 14588a978234SLiam Girdwood /* map standard io handlers and check for external handlers */ 14592b5cdb91SMengdong Lin err = soc_tplg_kcontrol_bind_io(&be->hdr, &kc[i], tplg); 14608a978234SLiam Girdwood if (err) { 14618a978234SLiam Girdwood soc_control_err(tplg, &be->hdr, be->hdr.name); 14628a978234SLiam Girdwood kfree(sbe); 14638a978234SLiam Girdwood continue; 14648a978234SLiam Girdwood } 14658a978234SLiam Girdwood 14668a978234SLiam Girdwood /* pass control to driver for optional further init */ 14678a978234SLiam Girdwood err = soc_tplg_init_kcontrol(tplg, &kc[i], 14688a978234SLiam Girdwood (struct snd_soc_tplg_ctl_hdr *)be); 14698a978234SLiam Girdwood if (err < 0) { 14708a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: failed to init %s\n", 14718a978234SLiam Girdwood be->hdr.name); 14728a978234SLiam Girdwood kfree(sbe); 14738a978234SLiam Girdwood continue; 14748a978234SLiam Girdwood } 14758a978234SLiam Girdwood } 14768a978234SLiam Girdwood 14778a978234SLiam Girdwood return kc; 14788a978234SLiam Girdwood 14798a978234SLiam Girdwood err: 1480267e2c6fSLiam Girdwood for (--i; i >= 0; i--) { 14818a978234SLiam Girdwood kfree((void *)kc[i].private_value); 1482267e2c6fSLiam Girdwood kfree(kc[i].name); 1483267e2c6fSLiam Girdwood } 14848a978234SLiam Girdwood 14858a978234SLiam Girdwood kfree(kc); 14868a978234SLiam Girdwood return NULL; 14878a978234SLiam Girdwood } 14888a978234SLiam Girdwood 14898a978234SLiam Girdwood static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg, 14908a978234SLiam Girdwood struct snd_soc_tplg_dapm_widget *w) 14918a978234SLiam Girdwood { 14928a978234SLiam Girdwood struct snd_soc_dapm_context *dapm = &tplg->comp->dapm; 14938a978234SLiam Girdwood struct snd_soc_dapm_widget template, *widget; 14948a978234SLiam Girdwood struct snd_soc_tplg_ctl_hdr *control_hdr; 14958a978234SLiam Girdwood struct snd_soc_card *card = tplg->comp->card; 1496eea3dd4fSMengdong Lin unsigned int kcontrol_type; 14978a978234SLiam Girdwood int ret = 0; 14988a978234SLiam Girdwood 14998a978234SLiam Girdwood if (strnlen(w->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 15008a978234SLiam Girdwood SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 15018a978234SLiam Girdwood return -EINVAL; 15028a978234SLiam Girdwood if (strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 15038a978234SLiam Girdwood SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 15048a978234SLiam Girdwood return -EINVAL; 15058a978234SLiam Girdwood 15068a978234SLiam Girdwood dev_dbg(tplg->dev, "ASoC: creating DAPM widget %s id %d\n", 15078a978234SLiam Girdwood w->name, w->id); 15088a978234SLiam Girdwood 15098a978234SLiam Girdwood memset(&template, 0, sizeof(template)); 15108a978234SLiam Girdwood 15118a978234SLiam Girdwood /* map user to kernel widget ID */ 15128a978234SLiam Girdwood template.id = get_widget_id(w->id); 15138a978234SLiam Girdwood if (template.id < 0) 15148a978234SLiam Girdwood return template.id; 15158a978234SLiam Girdwood 1516c3421a6aSLiam Girdwood /* strings are allocated here, but used and freed by the widget */ 15178a978234SLiam Girdwood template.name = kstrdup(w->name, GFP_KERNEL); 15188a978234SLiam Girdwood if (!template.name) 15198a978234SLiam Girdwood return -ENOMEM; 15208a978234SLiam Girdwood template.sname = kstrdup(w->sname, GFP_KERNEL); 15218a978234SLiam Girdwood if (!template.sname) { 15228a978234SLiam Girdwood ret = -ENOMEM; 15238a978234SLiam Girdwood goto err; 15248a978234SLiam Girdwood } 15258a978234SLiam Girdwood template.reg = w->reg; 15268a978234SLiam Girdwood template.shift = w->shift; 15278a978234SLiam Girdwood template.mask = w->mask; 15286dc6db79SSubhransu S. Prusty template.subseq = w->subseq; 15298a978234SLiam Girdwood template.on_val = w->invert ? 0 : 1; 15308a978234SLiam Girdwood template.off_val = w->invert ? 1 : 0; 15318a978234SLiam Girdwood template.ignore_suspend = w->ignore_suspend; 15328a978234SLiam Girdwood template.event_flags = w->event_flags; 15338a978234SLiam Girdwood template.dobj.index = tplg->index; 15348a978234SLiam Girdwood 15358a978234SLiam Girdwood tplg->pos += 15368a978234SLiam Girdwood (sizeof(struct snd_soc_tplg_dapm_widget) + w->priv.size); 15378a978234SLiam Girdwood if (w->num_kcontrols == 0) { 1538dd5abb74SArnd Bergmann kcontrol_type = 0; 15398a978234SLiam Girdwood template.num_kcontrols = 0; 15408a978234SLiam Girdwood goto widget; 15418a978234SLiam Girdwood } 15428a978234SLiam Girdwood 15438a978234SLiam Girdwood control_hdr = (struct snd_soc_tplg_ctl_hdr *)tplg->pos; 15448a978234SLiam Girdwood dev_dbg(tplg->dev, "ASoC: template %s has %d controls of type %x\n", 15458a978234SLiam Girdwood w->name, w->num_kcontrols, control_hdr->type); 15468a978234SLiam Girdwood 15478a978234SLiam Girdwood switch (control_hdr->ops.info) { 15488a978234SLiam Girdwood case SND_SOC_TPLG_CTL_VOLSW: 15498a978234SLiam Girdwood case SND_SOC_TPLG_CTL_STROBE: 15508a978234SLiam Girdwood case SND_SOC_TPLG_CTL_VOLSW_SX: 15518a978234SLiam Girdwood case SND_SOC_TPLG_CTL_VOLSW_XR_SX: 15528a978234SLiam Girdwood case SND_SOC_TPLG_CTL_RANGE: 15538a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_VOLSW: 1554eea3dd4fSMengdong Lin kcontrol_type = SND_SOC_TPLG_TYPE_MIXER; /* volume mixer */ 15558a978234SLiam Girdwood template.num_kcontrols = w->num_kcontrols; 15568a978234SLiam Girdwood template.kcontrol_news = 15578a978234SLiam Girdwood soc_tplg_dapm_widget_dmixer_create(tplg, 15588a978234SLiam Girdwood template.num_kcontrols); 15598a978234SLiam Girdwood if (!template.kcontrol_news) { 15608a978234SLiam Girdwood ret = -ENOMEM; 15618a978234SLiam Girdwood goto hdr_err; 15628a978234SLiam Girdwood } 15638a978234SLiam Girdwood break; 15648a978234SLiam Girdwood case SND_SOC_TPLG_CTL_ENUM: 15658a978234SLiam Girdwood case SND_SOC_TPLG_CTL_ENUM_VALUE: 15668a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: 15678a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: 15688a978234SLiam Girdwood case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: 1569eea3dd4fSMengdong Lin kcontrol_type = SND_SOC_TPLG_TYPE_ENUM; /* enumerated mixer */ 15701a7dd6e2SMengdong Lin template.num_kcontrols = w->num_kcontrols; 15718a978234SLiam Girdwood template.kcontrol_news = 15721a7dd6e2SMengdong Lin soc_tplg_dapm_widget_denum_create(tplg, 15731a7dd6e2SMengdong Lin template.num_kcontrols); 15748a978234SLiam Girdwood if (!template.kcontrol_news) { 15758a978234SLiam Girdwood ret = -ENOMEM; 15768a978234SLiam Girdwood goto hdr_err; 15778a978234SLiam Girdwood } 15788a978234SLiam Girdwood break; 15798a978234SLiam Girdwood case SND_SOC_TPLG_CTL_BYTES: 1580eea3dd4fSMengdong Lin kcontrol_type = SND_SOC_TPLG_TYPE_BYTES; /* bytes control */ 15818a978234SLiam Girdwood template.num_kcontrols = w->num_kcontrols; 15828a978234SLiam Girdwood template.kcontrol_news = 15838a978234SLiam Girdwood soc_tplg_dapm_widget_dbytes_create(tplg, 15848a978234SLiam Girdwood template.num_kcontrols); 15858a978234SLiam Girdwood if (!template.kcontrol_news) { 15868a978234SLiam Girdwood ret = -ENOMEM; 15878a978234SLiam Girdwood goto hdr_err; 15888a978234SLiam Girdwood } 15898a978234SLiam Girdwood break; 15908a978234SLiam Girdwood default: 15918a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: invalid widget control type %d:%d:%d\n", 15928a978234SLiam Girdwood control_hdr->ops.get, control_hdr->ops.put, 15938a978234SLiam Girdwood control_hdr->ops.info); 15948a978234SLiam Girdwood ret = -EINVAL; 15958a978234SLiam Girdwood goto hdr_err; 15968a978234SLiam Girdwood } 15978a978234SLiam Girdwood 15988a978234SLiam Girdwood widget: 15998a978234SLiam Girdwood ret = soc_tplg_widget_load(tplg, &template, w); 16008a978234SLiam Girdwood if (ret < 0) 16018a978234SLiam Girdwood goto hdr_err; 16028a978234SLiam Girdwood 16038a978234SLiam Girdwood /* card dapm mutex is held by the core if we are loading topology 16048a978234SLiam Girdwood * data during sound card init. */ 16058a978234SLiam Girdwood if (card->instantiated) 16068a978234SLiam Girdwood widget = snd_soc_dapm_new_control(dapm, &template); 16078a978234SLiam Girdwood else 16088a978234SLiam Girdwood widget = snd_soc_dapm_new_control_unlocked(dapm, &template); 160937e1df8cSLinus Walleij if (IS_ERR(widget)) { 161037e1df8cSLinus Walleij ret = PTR_ERR(widget); 161137e1df8cSLinus Walleij /* Do not nag about probe deferrals */ 161237e1df8cSLinus Walleij if (ret != -EPROBE_DEFER) 161337e1df8cSLinus Walleij dev_err(tplg->dev, 161437e1df8cSLinus Walleij "ASoC: failed to create widget %s controls (%d)\n", 161537e1df8cSLinus Walleij w->name, ret); 161637e1df8cSLinus Walleij goto hdr_err; 161737e1df8cSLinus Walleij } 16188a978234SLiam Girdwood if (widget == NULL) { 16198a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n", 16208a978234SLiam Girdwood w->name); 16218ae3ea48SWei Yongjun ret = -ENOMEM; 16228a978234SLiam Girdwood goto hdr_err; 16238a978234SLiam Girdwood } 16248a978234SLiam Girdwood 16258a978234SLiam Girdwood widget->dobj.type = SND_SOC_DOBJ_WIDGET; 1626eea3dd4fSMengdong Lin widget->dobj.widget.kcontrol_type = kcontrol_type; 16278a978234SLiam Girdwood widget->dobj.ops = tplg->ops; 16288a978234SLiam Girdwood widget->dobj.index = tplg->index; 16298a978234SLiam Girdwood list_add(&widget->dobj.list, &tplg->comp->dobj_list); 1630ebd259d3SLiam Girdwood 1631ebd259d3SLiam Girdwood ret = soc_tplg_widget_ready(tplg, widget, w); 1632ebd259d3SLiam Girdwood if (ret < 0) 1633ebd259d3SLiam Girdwood goto ready_err; 1634ebd259d3SLiam Girdwood 16358a978234SLiam Girdwood return 0; 16368a978234SLiam Girdwood 1637ebd259d3SLiam Girdwood ready_err: 1638ebd259d3SLiam Girdwood snd_soc_tplg_widget_remove(widget); 1639ebd259d3SLiam Girdwood snd_soc_dapm_free_widget(widget); 16408a978234SLiam Girdwood hdr_err: 16418a978234SLiam Girdwood kfree(template.sname); 16428a978234SLiam Girdwood err: 16438a978234SLiam Girdwood kfree(template.name); 16448a978234SLiam Girdwood return ret; 16458a978234SLiam Girdwood } 16468a978234SLiam Girdwood 16478a978234SLiam Girdwood static int soc_tplg_dapm_widget_elems_load(struct soc_tplg *tplg, 16488a978234SLiam Girdwood struct snd_soc_tplg_hdr *hdr) 16498a978234SLiam Girdwood { 16508a978234SLiam Girdwood struct snd_soc_tplg_dapm_widget *widget; 16518a978234SLiam Girdwood int ret, count = hdr->count, i; 16528a978234SLiam Girdwood 16538a978234SLiam Girdwood if (tplg->pass != SOC_TPLG_PASS_WIDGET) 16548a978234SLiam Girdwood return 0; 16558a978234SLiam Girdwood 16568a978234SLiam Girdwood dev_dbg(tplg->dev, "ASoC: adding %d DAPM widgets\n", count); 16578a978234SLiam Girdwood 16588a978234SLiam Girdwood for (i = 0; i < count; i++) { 16598a978234SLiam Girdwood widget = (struct snd_soc_tplg_dapm_widget *) tplg->pos; 166006eb49f7SMengdong Lin if (widget->size != sizeof(*widget)) { 166106eb49f7SMengdong Lin dev_err(tplg->dev, "ASoC: invalid widget size\n"); 166206eb49f7SMengdong Lin return -EINVAL; 166306eb49f7SMengdong Lin } 166406eb49f7SMengdong Lin 16658a978234SLiam Girdwood ret = soc_tplg_dapm_widget_create(tplg, widget); 16667de76b62SMengdong Lin if (ret < 0) { 16678a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: failed to load widget %s\n", 16688a978234SLiam Girdwood widget->name); 16697de76b62SMengdong Lin return ret; 16707de76b62SMengdong Lin } 16718a978234SLiam Girdwood } 16728a978234SLiam Girdwood 16738a978234SLiam Girdwood return 0; 16748a978234SLiam Girdwood } 16758a978234SLiam Girdwood 16768a978234SLiam Girdwood static int soc_tplg_dapm_complete(struct soc_tplg *tplg) 16778a978234SLiam Girdwood { 16788a978234SLiam Girdwood struct snd_soc_card *card = tplg->comp->card; 16798a978234SLiam Girdwood int ret; 16808a978234SLiam Girdwood 16818a978234SLiam Girdwood /* Card might not have been registered at this point. 16828a978234SLiam Girdwood * If so, just return success. 16838a978234SLiam Girdwood */ 16848a978234SLiam Girdwood if (!card || !card->instantiated) { 16858a978234SLiam Girdwood dev_warn(tplg->dev, "ASoC: Parent card not yet available," 1686cc9d4714SLiam Girdwood " widget card binding deferred\n"); 16878a978234SLiam Girdwood return 0; 16888a978234SLiam Girdwood } 16898a978234SLiam Girdwood 16908a978234SLiam Girdwood ret = snd_soc_dapm_new_widgets(card); 16918a978234SLiam Girdwood if (ret < 0) 16928a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n", 16938a978234SLiam Girdwood ret); 16948a978234SLiam Girdwood 16958a978234SLiam Girdwood return 0; 16968a978234SLiam Girdwood } 16978a978234SLiam Girdwood 1698b6b6e4d6SMengdong Lin static void set_stream_info(struct snd_soc_pcm_stream *stream, 1699b6b6e4d6SMengdong Lin struct snd_soc_tplg_stream_caps *caps) 1700b6b6e4d6SMengdong Lin { 1701b6b6e4d6SMengdong Lin stream->stream_name = kstrdup(caps->name, GFP_KERNEL); 1702b6b6e4d6SMengdong Lin stream->channels_min = caps->channels_min; 1703b6b6e4d6SMengdong Lin stream->channels_max = caps->channels_max; 1704b6b6e4d6SMengdong Lin stream->rates = caps->rates; 1705b6b6e4d6SMengdong Lin stream->rate_min = caps->rate_min; 1706b6b6e4d6SMengdong Lin stream->rate_max = caps->rate_max; 1707b6b6e4d6SMengdong Lin stream->formats = caps->formats; 1708f918e169SMengdong Lin stream->sig_bits = caps->sig_bits; 1709b6b6e4d6SMengdong Lin } 1710b6b6e4d6SMengdong Lin 17110038be9aSMengdong Lin static void set_dai_flags(struct snd_soc_dai_driver *dai_drv, 17120038be9aSMengdong Lin unsigned int flag_mask, unsigned int flags) 17130038be9aSMengdong Lin { 17140038be9aSMengdong Lin if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES) 17150038be9aSMengdong Lin dai_drv->symmetric_rates = 17160038be9aSMengdong Lin flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES ? 1 : 0; 17170038be9aSMengdong Lin 17180038be9aSMengdong Lin if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS) 17190038be9aSMengdong Lin dai_drv->symmetric_channels = 17200038be9aSMengdong Lin flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS ? 17210038be9aSMengdong Lin 1 : 0; 17220038be9aSMengdong Lin 17230038be9aSMengdong Lin if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS) 17240038be9aSMengdong Lin dai_drv->symmetric_samplebits = 17250038be9aSMengdong Lin flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS ? 17260038be9aSMengdong Lin 1 : 0; 17270038be9aSMengdong Lin } 17280038be9aSMengdong Lin 172964527e8aSMengdong Lin static int soc_tplg_dai_create(struct soc_tplg *tplg, 173064527e8aSMengdong Lin struct snd_soc_tplg_pcm *pcm) 173164527e8aSMengdong Lin { 173264527e8aSMengdong Lin struct snd_soc_dai_driver *dai_drv; 173364527e8aSMengdong Lin struct snd_soc_pcm_stream *stream; 173464527e8aSMengdong Lin struct snd_soc_tplg_stream_caps *caps; 173564527e8aSMengdong Lin int ret; 173664527e8aSMengdong Lin 173764527e8aSMengdong Lin dai_drv = kzalloc(sizeof(struct snd_soc_dai_driver), GFP_KERNEL); 173864527e8aSMengdong Lin if (dai_drv == NULL) 173964527e8aSMengdong Lin return -ENOMEM; 174064527e8aSMengdong Lin 17418f27c4abSMengdong Lin if (strlen(pcm->dai_name)) 17428f27c4abSMengdong Lin dai_drv->name = kstrdup(pcm->dai_name, GFP_KERNEL); 174364527e8aSMengdong Lin dai_drv->id = pcm->dai_id; 174464527e8aSMengdong Lin 174564527e8aSMengdong Lin if (pcm->playback) { 174664527e8aSMengdong Lin stream = &dai_drv->playback; 174764527e8aSMengdong Lin caps = &pcm->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; 1748b6b6e4d6SMengdong Lin set_stream_info(stream, caps); 174964527e8aSMengdong Lin } 175064527e8aSMengdong Lin 175164527e8aSMengdong Lin if (pcm->capture) { 175264527e8aSMengdong Lin stream = &dai_drv->capture; 175364527e8aSMengdong Lin caps = &pcm->caps[SND_SOC_TPLG_STREAM_CAPTURE]; 1754b6b6e4d6SMengdong Lin set_stream_info(stream, caps); 175564527e8aSMengdong Lin } 175664527e8aSMengdong Lin 175764527e8aSMengdong Lin /* pass control to component driver for optional further init */ 175864527e8aSMengdong Lin ret = soc_tplg_dai_load(tplg, dai_drv); 175964527e8aSMengdong Lin if (ret < 0) { 176064527e8aSMengdong Lin dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); 176164527e8aSMengdong Lin kfree(dai_drv); 176264527e8aSMengdong Lin return ret; 176364527e8aSMengdong Lin } 176464527e8aSMengdong Lin 176564527e8aSMengdong Lin dai_drv->dobj.index = tplg->index; 176664527e8aSMengdong Lin dai_drv->dobj.ops = tplg->ops; 176764527e8aSMengdong Lin dai_drv->dobj.type = SND_SOC_DOBJ_PCM; 176864527e8aSMengdong Lin list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list); 176964527e8aSMengdong Lin 177064527e8aSMengdong Lin /* register the DAI to the component */ 177164527e8aSMengdong Lin return snd_soc_register_dai(tplg->comp, dai_drv); 177264527e8aSMengdong Lin } 177364527e8aSMengdong Lin 1774717a8e72SMengdong Lin static void set_link_flags(struct snd_soc_dai_link *link, 1775717a8e72SMengdong Lin unsigned int flag_mask, unsigned int flags) 1776717a8e72SMengdong Lin { 1777717a8e72SMengdong Lin if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES) 1778717a8e72SMengdong Lin link->symmetric_rates = 1779717a8e72SMengdong Lin flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES ? 1 : 0; 1780717a8e72SMengdong Lin 1781717a8e72SMengdong Lin if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS) 1782717a8e72SMengdong Lin link->symmetric_channels = 1783717a8e72SMengdong Lin flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS ? 1784717a8e72SMengdong Lin 1 : 0; 1785717a8e72SMengdong Lin 1786717a8e72SMengdong Lin if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS) 1787717a8e72SMengdong Lin link->symmetric_samplebits = 1788717a8e72SMengdong Lin flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS ? 1789717a8e72SMengdong Lin 1 : 0; 17906ff67ccaSMengdong Lin 17916ff67ccaSMengdong Lin if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP) 17926ff67ccaSMengdong Lin link->ignore_suspend = 17936ff67ccaSMengdong Lin flags & SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP ? 17946ff67ccaSMengdong Lin 1 : 0; 1795717a8e72SMengdong Lin } 1796717a8e72SMengdong Lin 179767d1c21eSGuneshwor Singh /* create the FE DAI link */ 1798ab4bc5eeSMengdong Lin static int soc_tplg_fe_link_create(struct soc_tplg *tplg, 1799acfc7d46SMengdong Lin struct snd_soc_tplg_pcm *pcm) 1800acfc7d46SMengdong Lin { 1801acfc7d46SMengdong Lin struct snd_soc_dai_link *link; 1802acfc7d46SMengdong Lin int ret; 1803acfc7d46SMengdong Lin 1804acfc7d46SMengdong Lin link = kzalloc(sizeof(struct snd_soc_dai_link), GFP_KERNEL); 1805acfc7d46SMengdong Lin if (link == NULL) 1806acfc7d46SMengdong Lin return -ENOMEM; 1807acfc7d46SMengdong Lin 18088f27c4abSMengdong Lin if (strlen(pcm->pcm_name)) { 18098f27c4abSMengdong Lin link->name = kstrdup(pcm->pcm_name, GFP_KERNEL); 18108f27c4abSMengdong Lin link->stream_name = kstrdup(pcm->pcm_name, GFP_KERNEL); 18118f27c4abSMengdong Lin } 1812b84fff5aSMengdong Lin link->id = pcm->pcm_id; 1813acfc7d46SMengdong Lin 18148f27c4abSMengdong Lin if (strlen(pcm->dai_name)) 18158f27c4abSMengdong Lin link->cpu_dai_name = kstrdup(pcm->dai_name, GFP_KERNEL); 18168f27c4abSMengdong Lin 181767d1c21eSGuneshwor Singh link->codec_name = "snd-soc-dummy"; 181867d1c21eSGuneshwor Singh link->codec_dai_name = "snd-soc-dummy-dai"; 181967d1c21eSGuneshwor Singh 182067d1c21eSGuneshwor Singh /* enable DPCM */ 182167d1c21eSGuneshwor Singh link->dynamic = 1; 182267d1c21eSGuneshwor Singh link->dpcm_playback = pcm->playback; 182367d1c21eSGuneshwor Singh link->dpcm_capture = pcm->capture; 1824717a8e72SMengdong Lin if (pcm->flag_mask) 1825717a8e72SMengdong Lin set_link_flags(link, pcm->flag_mask, pcm->flags); 182667d1c21eSGuneshwor Singh 1827acfc7d46SMengdong Lin /* pass control to component driver for optional further init */ 1828acfc7d46SMengdong Lin ret = soc_tplg_dai_link_load(tplg, link); 1829acfc7d46SMengdong Lin if (ret < 0) { 1830acfc7d46SMengdong Lin dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n"); 1831acfc7d46SMengdong Lin kfree(link); 1832acfc7d46SMengdong Lin return ret; 1833acfc7d46SMengdong Lin } 1834acfc7d46SMengdong Lin 1835acfc7d46SMengdong Lin link->dobj.index = tplg->index; 1836acfc7d46SMengdong Lin link->dobj.ops = tplg->ops; 1837acfc7d46SMengdong Lin link->dobj.type = SND_SOC_DOBJ_DAI_LINK; 1838acfc7d46SMengdong Lin list_add(&link->dobj.list, &tplg->comp->dobj_list); 1839acfc7d46SMengdong Lin 1840acfc7d46SMengdong Lin snd_soc_add_dai_link(tplg->comp->card, link); 1841acfc7d46SMengdong Lin return 0; 1842acfc7d46SMengdong Lin } 1843acfc7d46SMengdong Lin 1844acfc7d46SMengdong Lin /* create a FE DAI and DAI link from the PCM object */ 184564527e8aSMengdong Lin static int soc_tplg_pcm_create(struct soc_tplg *tplg, 184664527e8aSMengdong Lin struct snd_soc_tplg_pcm *pcm) 184764527e8aSMengdong Lin { 1848acfc7d46SMengdong Lin int ret; 1849acfc7d46SMengdong Lin 1850acfc7d46SMengdong Lin ret = soc_tplg_dai_create(tplg, pcm); 1851acfc7d46SMengdong Lin if (ret < 0) 1852acfc7d46SMengdong Lin return ret; 1853acfc7d46SMengdong Lin 1854ab4bc5eeSMengdong Lin return soc_tplg_fe_link_create(tplg, pcm); 185564527e8aSMengdong Lin } 185664527e8aSMengdong Lin 185755726dc9SMengdong Lin /* copy stream caps from the old version 4 of source */ 185855726dc9SMengdong Lin static void stream_caps_new_ver(struct snd_soc_tplg_stream_caps *dest, 185955726dc9SMengdong Lin struct snd_soc_tplg_stream_caps_v4 *src) 186055726dc9SMengdong Lin { 186155726dc9SMengdong Lin dest->size = sizeof(*dest); 186255726dc9SMengdong Lin memcpy(dest->name, src->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 186355726dc9SMengdong Lin dest->formats = src->formats; 186455726dc9SMengdong Lin dest->rates = src->rates; 186555726dc9SMengdong Lin dest->rate_min = src->rate_min; 186655726dc9SMengdong Lin dest->rate_max = src->rate_max; 186755726dc9SMengdong Lin dest->channels_min = src->channels_min; 186855726dc9SMengdong Lin dest->channels_max = src->channels_max; 186955726dc9SMengdong Lin dest->periods_min = src->periods_min; 187055726dc9SMengdong Lin dest->periods_max = src->periods_max; 187155726dc9SMengdong Lin dest->period_size_min = src->period_size_min; 187255726dc9SMengdong Lin dest->period_size_max = src->period_size_max; 187355726dc9SMengdong Lin dest->buffer_size_min = src->buffer_size_min; 187455726dc9SMengdong Lin dest->buffer_size_max = src->buffer_size_max; 187555726dc9SMengdong Lin } 187655726dc9SMengdong Lin 187755726dc9SMengdong Lin /** 187855726dc9SMengdong Lin * pcm_new_ver - Create the new version of PCM from the old version. 187955726dc9SMengdong Lin * @tplg: topology context 188055726dc9SMengdong Lin * @src: older version of pcm as a source 188155726dc9SMengdong Lin * @pcm: latest version of pcm created from the source 188255726dc9SMengdong Lin * 188355726dc9SMengdong Lin * Support from vesion 4. User should free the returned pcm manually. 188455726dc9SMengdong Lin */ 188555726dc9SMengdong Lin static int pcm_new_ver(struct soc_tplg *tplg, 188655726dc9SMengdong Lin struct snd_soc_tplg_pcm *src, 188755726dc9SMengdong Lin struct snd_soc_tplg_pcm **pcm) 188855726dc9SMengdong Lin { 188955726dc9SMengdong Lin struct snd_soc_tplg_pcm *dest; 189055726dc9SMengdong Lin struct snd_soc_tplg_pcm_v4 *src_v4; 189155726dc9SMengdong Lin int i; 189255726dc9SMengdong Lin 189355726dc9SMengdong Lin *pcm = NULL; 189455726dc9SMengdong Lin 189555726dc9SMengdong Lin if (src->size != sizeof(*src_v4)) { 189655726dc9SMengdong Lin dev_err(tplg->dev, "ASoC: invalid PCM size\n"); 189755726dc9SMengdong Lin return -EINVAL; 189855726dc9SMengdong Lin } 189955726dc9SMengdong Lin 190055726dc9SMengdong Lin dev_warn(tplg->dev, "ASoC: old version of PCM\n"); 190155726dc9SMengdong Lin src_v4 = (struct snd_soc_tplg_pcm_v4 *)src; 190255726dc9SMengdong Lin dest = kzalloc(sizeof(*dest), GFP_KERNEL); 190355726dc9SMengdong Lin if (!dest) 190455726dc9SMengdong Lin return -ENOMEM; 190555726dc9SMengdong Lin 190655726dc9SMengdong Lin dest->size = sizeof(*dest); /* size of latest abi version */ 190755726dc9SMengdong Lin memcpy(dest->pcm_name, src_v4->pcm_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 190855726dc9SMengdong Lin memcpy(dest->dai_name, src_v4->dai_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 190955726dc9SMengdong Lin dest->pcm_id = src_v4->pcm_id; 191055726dc9SMengdong Lin dest->dai_id = src_v4->dai_id; 191155726dc9SMengdong Lin dest->playback = src_v4->playback; 191255726dc9SMengdong Lin dest->capture = src_v4->capture; 191355726dc9SMengdong Lin dest->compress = src_v4->compress; 191455726dc9SMengdong Lin dest->num_streams = src_v4->num_streams; 191555726dc9SMengdong Lin for (i = 0; i < dest->num_streams; i++) 191655726dc9SMengdong Lin memcpy(&dest->stream[i], &src_v4->stream[i], 191755726dc9SMengdong Lin sizeof(struct snd_soc_tplg_stream)); 191855726dc9SMengdong Lin 191955726dc9SMengdong Lin for (i = 0; i < 2; i++) 192055726dc9SMengdong Lin stream_caps_new_ver(&dest->caps[i], &src_v4->caps[i]); 192155726dc9SMengdong Lin 192255726dc9SMengdong Lin *pcm = dest; 192355726dc9SMengdong Lin return 0; 192455726dc9SMengdong Lin } 192555726dc9SMengdong Lin 192664527e8aSMengdong Lin static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg, 19278a978234SLiam Girdwood struct snd_soc_tplg_hdr *hdr) 19288a978234SLiam Girdwood { 192955726dc9SMengdong Lin struct snd_soc_tplg_pcm *pcm, *_pcm; 19308a978234SLiam Girdwood int count = hdr->count; 1931fd340455SVinod Koul int i; 193255726dc9SMengdong Lin bool abi_match; 19338a978234SLiam Girdwood 19348a978234SLiam Girdwood if (tplg->pass != SOC_TPLG_PASS_PCM_DAI) 19358a978234SLiam Girdwood return 0; 19368a978234SLiam Girdwood 193755726dc9SMengdong Lin /* check the element size and count */ 193855726dc9SMengdong Lin pcm = (struct snd_soc_tplg_pcm *)tplg->pos; 193955726dc9SMengdong Lin if (pcm->size > sizeof(struct snd_soc_tplg_pcm) 194055726dc9SMengdong Lin || pcm->size < sizeof(struct snd_soc_tplg_pcm_v4)) { 194155726dc9SMengdong Lin dev_err(tplg->dev, "ASoC: invalid size %d for PCM elems\n", 194255726dc9SMengdong Lin pcm->size); 194355726dc9SMengdong Lin return -EINVAL; 194455726dc9SMengdong Lin } 194555726dc9SMengdong Lin 19468a978234SLiam Girdwood if (soc_tplg_check_elem_count(tplg, 194755726dc9SMengdong Lin pcm->size, count, 19488a978234SLiam Girdwood hdr->payload_size, "PCM DAI")) { 19498a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n", 19508a978234SLiam Girdwood count); 19518a978234SLiam Girdwood return -EINVAL; 19528a978234SLiam Girdwood } 19538a978234SLiam Girdwood 195464527e8aSMengdong Lin for (i = 0; i < count; i++) { 195555726dc9SMengdong Lin pcm = (struct snd_soc_tplg_pcm *)tplg->pos; 195655726dc9SMengdong Lin 195755726dc9SMengdong Lin /* check ABI version by size, create a new version of pcm 195855726dc9SMengdong Lin * if abi not match. 195955726dc9SMengdong Lin */ 196055726dc9SMengdong Lin if (pcm->size == sizeof(*pcm)) { 196155726dc9SMengdong Lin abi_match = true; 196255726dc9SMengdong Lin _pcm = pcm; 196355726dc9SMengdong Lin } else { 196455726dc9SMengdong Lin abi_match = false; 1965fd340455SVinod Koul pcm_new_ver(tplg, pcm, &_pcm); 196606eb49f7SMengdong Lin } 196706eb49f7SMengdong Lin 196855726dc9SMengdong Lin /* create the FE DAIs and DAI links */ 196955726dc9SMengdong Lin soc_tplg_pcm_create(tplg, _pcm); 197055726dc9SMengdong Lin 1971717a8e72SMengdong Lin /* offset by version-specific struct size and 1972717a8e72SMengdong Lin * real priv data size 1973717a8e72SMengdong Lin */ 1974717a8e72SMengdong Lin tplg->pos += pcm->size + _pcm->priv.size; 1975717a8e72SMengdong Lin 197655726dc9SMengdong Lin if (!abi_match) 197755726dc9SMengdong Lin kfree(_pcm); /* free the duplicated one */ 197864527e8aSMengdong Lin } 197964527e8aSMengdong Lin 19808a978234SLiam Girdwood dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count); 19818a978234SLiam Girdwood 19828a978234SLiam Girdwood return 0; 19838a978234SLiam Girdwood } 19848a978234SLiam Girdwood 19850038be9aSMengdong Lin /** 1986593d9e52SMengdong Lin * set_link_hw_format - Set the HW audio format of the physical DAI link. 19878abab35fSCharles Keepax * @link: &snd_soc_dai_link which should be updated 1988593d9e52SMengdong Lin * @cfg: physical link configs. 1989593d9e52SMengdong Lin * 1990593d9e52SMengdong Lin * Topology context contains a list of supported HW formats (configs) and 1991593d9e52SMengdong Lin * a default format ID for the physical link. This function will use this 1992593d9e52SMengdong Lin * default ID to choose the HW format to set the link's DAI format for init. 1993593d9e52SMengdong Lin */ 1994593d9e52SMengdong Lin static void set_link_hw_format(struct snd_soc_dai_link *link, 1995593d9e52SMengdong Lin struct snd_soc_tplg_link_config *cfg) 1996593d9e52SMengdong Lin { 1997593d9e52SMengdong Lin struct snd_soc_tplg_hw_config *hw_config; 1998593d9e52SMengdong Lin unsigned char bclk_master, fsync_master; 1999593d9e52SMengdong Lin unsigned char invert_bclk, invert_fsync; 2000593d9e52SMengdong Lin int i; 2001593d9e52SMengdong Lin 2002593d9e52SMengdong Lin for (i = 0; i < cfg->num_hw_configs; i++) { 2003593d9e52SMengdong Lin hw_config = &cfg->hw_config[i]; 2004593d9e52SMengdong Lin if (hw_config->id != cfg->default_hw_config_id) 2005593d9e52SMengdong Lin continue; 2006593d9e52SMengdong Lin 2007593d9e52SMengdong Lin link->dai_fmt = hw_config->fmt & SND_SOC_DAIFMT_FORMAT_MASK; 2008593d9e52SMengdong Lin 2009593d9e52SMengdong Lin /* clock signal polarity */ 2010593d9e52SMengdong Lin invert_bclk = hw_config->invert_bclk; 2011593d9e52SMengdong Lin invert_fsync = hw_config->invert_fsync; 2012593d9e52SMengdong Lin if (!invert_bclk && !invert_fsync) 2013593d9e52SMengdong Lin link->dai_fmt |= SND_SOC_DAIFMT_NB_NF; 2014593d9e52SMengdong Lin else if (!invert_bclk && invert_fsync) 2015593d9e52SMengdong Lin link->dai_fmt |= SND_SOC_DAIFMT_NB_IF; 2016593d9e52SMengdong Lin else if (invert_bclk && !invert_fsync) 2017593d9e52SMengdong Lin link->dai_fmt |= SND_SOC_DAIFMT_IB_NF; 2018593d9e52SMengdong Lin else 2019593d9e52SMengdong Lin link->dai_fmt |= SND_SOC_DAIFMT_IB_IF; 2020593d9e52SMengdong Lin 2021593d9e52SMengdong Lin /* clock masters */ 2022593d9e52SMengdong Lin bclk_master = hw_config->bclk_master; 2023593d9e52SMengdong Lin fsync_master = hw_config->fsync_master; 2024593d9e52SMengdong Lin if (!bclk_master && !fsync_master) 2025593d9e52SMengdong Lin link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; 2026593d9e52SMengdong Lin else if (bclk_master && !fsync_master) 2027593d9e52SMengdong Lin link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; 2028593d9e52SMengdong Lin else if (!bclk_master && fsync_master) 2029593d9e52SMengdong Lin link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; 2030593d9e52SMengdong Lin else 2031593d9e52SMengdong Lin link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; 2032593d9e52SMengdong Lin } 2033593d9e52SMengdong Lin } 2034593d9e52SMengdong Lin 2035593d9e52SMengdong Lin /** 2036593d9e52SMengdong Lin * link_new_ver - Create a new physical link config from the old 2037593d9e52SMengdong Lin * version of source. 20388abab35fSCharles Keepax * @tplg: topology context 2039593d9e52SMengdong Lin * @src: old version of phyical link config as a source 2040593d9e52SMengdong Lin * @link: latest version of physical link config created from the source 2041593d9e52SMengdong Lin * 2042593d9e52SMengdong Lin * Support from vesion 4. User need free the returned link config manually. 2043593d9e52SMengdong Lin */ 2044593d9e52SMengdong Lin static int link_new_ver(struct soc_tplg *tplg, 2045593d9e52SMengdong Lin struct snd_soc_tplg_link_config *src, 2046593d9e52SMengdong Lin struct snd_soc_tplg_link_config **link) 2047593d9e52SMengdong Lin { 2048593d9e52SMengdong Lin struct snd_soc_tplg_link_config *dest; 2049593d9e52SMengdong Lin struct snd_soc_tplg_link_config_v4 *src_v4; 2050593d9e52SMengdong Lin int i; 2051593d9e52SMengdong Lin 2052593d9e52SMengdong Lin *link = NULL; 2053593d9e52SMengdong Lin 2054593d9e52SMengdong Lin if (src->size != sizeof(struct snd_soc_tplg_link_config_v4)) { 2055593d9e52SMengdong Lin dev_err(tplg->dev, "ASoC: invalid physical link config size\n"); 2056593d9e52SMengdong Lin return -EINVAL; 2057593d9e52SMengdong Lin } 2058593d9e52SMengdong Lin 2059593d9e52SMengdong Lin dev_warn(tplg->dev, "ASoC: old version of physical link config\n"); 2060593d9e52SMengdong Lin 2061593d9e52SMengdong Lin src_v4 = (struct snd_soc_tplg_link_config_v4 *)src; 2062593d9e52SMengdong Lin dest = kzalloc(sizeof(*dest), GFP_KERNEL); 2063593d9e52SMengdong Lin if (!dest) 2064593d9e52SMengdong Lin return -ENOMEM; 2065593d9e52SMengdong Lin 2066593d9e52SMengdong Lin dest->size = sizeof(*dest); 2067593d9e52SMengdong Lin dest->id = src_v4->id; 2068593d9e52SMengdong Lin dest->num_streams = src_v4->num_streams; 2069593d9e52SMengdong Lin for (i = 0; i < dest->num_streams; i++) 2070593d9e52SMengdong Lin memcpy(&dest->stream[i], &src_v4->stream[i], 2071593d9e52SMengdong Lin sizeof(struct snd_soc_tplg_stream)); 2072593d9e52SMengdong Lin 2073593d9e52SMengdong Lin *link = dest; 2074593d9e52SMengdong Lin return 0; 2075593d9e52SMengdong Lin } 2076593d9e52SMengdong Lin 2077593d9e52SMengdong Lin /* Find and configure an existing physical DAI link */ 2078593d9e52SMengdong Lin static int soc_tplg_link_config(struct soc_tplg *tplg, 2079593d9e52SMengdong Lin struct snd_soc_tplg_link_config *cfg) 2080593d9e52SMengdong Lin { 2081593d9e52SMengdong Lin struct snd_soc_dai_link *link; 2082593d9e52SMengdong Lin const char *name, *stream_name; 2083dbab1cb8SMengdong Lin size_t len; 2084593d9e52SMengdong Lin int ret; 2085593d9e52SMengdong Lin 2086dbab1cb8SMengdong Lin len = strnlen(cfg->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 2087dbab1cb8SMengdong Lin if (len == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 2088dbab1cb8SMengdong Lin return -EINVAL; 2089dbab1cb8SMengdong Lin else if (len) 2090dbab1cb8SMengdong Lin name = cfg->name; 2091dbab1cb8SMengdong Lin else 2092dbab1cb8SMengdong Lin name = NULL; 2093dbab1cb8SMengdong Lin 2094dbab1cb8SMengdong Lin len = strnlen(cfg->stream_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); 2095dbab1cb8SMengdong Lin if (len == SNDRV_CTL_ELEM_ID_NAME_MAXLEN) 2096dbab1cb8SMengdong Lin return -EINVAL; 2097dbab1cb8SMengdong Lin else if (len) 2098dbab1cb8SMengdong Lin stream_name = cfg->stream_name; 2099dbab1cb8SMengdong Lin else 2100dbab1cb8SMengdong Lin stream_name = NULL; 2101593d9e52SMengdong Lin 2102593d9e52SMengdong Lin link = snd_soc_find_dai_link(tplg->comp->card, cfg->id, 2103593d9e52SMengdong Lin name, stream_name); 2104593d9e52SMengdong Lin if (!link) { 2105593d9e52SMengdong Lin dev_err(tplg->dev, "ASoC: physical link %s (id %d) not exist\n", 2106593d9e52SMengdong Lin name, cfg->id); 2107593d9e52SMengdong Lin return -EINVAL; 2108593d9e52SMengdong Lin } 2109593d9e52SMengdong Lin 2110593d9e52SMengdong Lin /* hw format */ 2111593d9e52SMengdong Lin if (cfg->num_hw_configs) 2112593d9e52SMengdong Lin set_link_hw_format(link, cfg); 2113593d9e52SMengdong Lin 2114593d9e52SMengdong Lin /* flags */ 2115593d9e52SMengdong Lin if (cfg->flag_mask) 2116593d9e52SMengdong Lin set_link_flags(link, cfg->flag_mask, cfg->flags); 2117593d9e52SMengdong Lin 2118593d9e52SMengdong Lin /* pass control to component driver for optional further init */ 2119593d9e52SMengdong Lin ret = soc_tplg_dai_link_load(tplg, link); 2120593d9e52SMengdong Lin if (ret < 0) { 2121593d9e52SMengdong Lin dev_err(tplg->dev, "ASoC: physical link loading failed\n"); 2122593d9e52SMengdong Lin return ret; 2123593d9e52SMengdong Lin } 2124593d9e52SMengdong Lin 2125593d9e52SMengdong Lin return 0; 2126593d9e52SMengdong Lin } 2127593d9e52SMengdong Lin 2128593d9e52SMengdong Lin 2129593d9e52SMengdong Lin /* Load physical link config elements from the topology context */ 2130593d9e52SMengdong Lin static int soc_tplg_link_elems_load(struct soc_tplg *tplg, 2131593d9e52SMengdong Lin struct snd_soc_tplg_hdr *hdr) 2132593d9e52SMengdong Lin { 2133593d9e52SMengdong Lin struct snd_soc_tplg_link_config *link, *_link; 2134593d9e52SMengdong Lin int count = hdr->count; 2135593d9e52SMengdong Lin int i, ret; 2136593d9e52SMengdong Lin bool abi_match; 2137593d9e52SMengdong Lin 2138593d9e52SMengdong Lin if (tplg->pass != SOC_TPLG_PASS_LINK) { 2139593d9e52SMengdong Lin tplg->pos += hdr->size + hdr->payload_size; 2140593d9e52SMengdong Lin return 0; 2141593d9e52SMengdong Lin }; 2142593d9e52SMengdong Lin 2143593d9e52SMengdong Lin /* check the element size and count */ 2144593d9e52SMengdong Lin link = (struct snd_soc_tplg_link_config *)tplg->pos; 2145593d9e52SMengdong Lin if (link->size > sizeof(struct snd_soc_tplg_link_config) 2146593d9e52SMengdong Lin || link->size < sizeof(struct snd_soc_tplg_link_config_v4)) { 2147593d9e52SMengdong Lin dev_err(tplg->dev, "ASoC: invalid size %d for physical link elems\n", 2148593d9e52SMengdong Lin link->size); 2149593d9e52SMengdong Lin return -EINVAL; 2150593d9e52SMengdong Lin } 2151593d9e52SMengdong Lin 2152593d9e52SMengdong Lin if (soc_tplg_check_elem_count(tplg, 2153593d9e52SMengdong Lin link->size, count, 2154593d9e52SMengdong Lin hdr->payload_size, "physical link config")) { 2155593d9e52SMengdong Lin dev_err(tplg->dev, "ASoC: invalid count %d for physical link elems\n", 2156593d9e52SMengdong Lin count); 2157593d9e52SMengdong Lin return -EINVAL; 2158593d9e52SMengdong Lin } 2159593d9e52SMengdong Lin 2160593d9e52SMengdong Lin /* config physical DAI links */ 2161593d9e52SMengdong Lin for (i = 0; i < count; i++) { 2162593d9e52SMengdong Lin link = (struct snd_soc_tplg_link_config *)tplg->pos; 2163593d9e52SMengdong Lin if (link->size == sizeof(*link)) { 2164593d9e52SMengdong Lin abi_match = true; 2165593d9e52SMengdong Lin _link = link; 2166593d9e52SMengdong Lin } else { 2167593d9e52SMengdong Lin abi_match = false; 2168593d9e52SMengdong Lin ret = link_new_ver(tplg, link, &_link); 2169593d9e52SMengdong Lin if (ret < 0) 2170593d9e52SMengdong Lin return ret; 2171593d9e52SMengdong Lin } 2172593d9e52SMengdong Lin 2173593d9e52SMengdong Lin ret = soc_tplg_link_config(tplg, _link); 2174593d9e52SMengdong Lin if (ret < 0) 2175593d9e52SMengdong Lin return ret; 2176593d9e52SMengdong Lin 2177593d9e52SMengdong Lin /* offset by version-specific struct size and 2178593d9e52SMengdong Lin * real priv data size 2179593d9e52SMengdong Lin */ 2180593d9e52SMengdong Lin tplg->pos += link->size + _link->priv.size; 2181593d9e52SMengdong Lin 2182593d9e52SMengdong Lin if (!abi_match) 2183593d9e52SMengdong Lin kfree(_link); /* free the duplicated one */ 2184593d9e52SMengdong Lin } 2185593d9e52SMengdong Lin 2186593d9e52SMengdong Lin return 0; 2187593d9e52SMengdong Lin } 2188593d9e52SMengdong Lin 2189593d9e52SMengdong Lin /** 21909aa3f034SMengdong Lin * soc_tplg_dai_config - Find and configure an existing physical DAI. 21910038be9aSMengdong Lin * @tplg: topology context 21929aa3f034SMengdong Lin * @d: physical DAI configs. 21930038be9aSMengdong Lin * 21949aa3f034SMengdong Lin * The physical dai should already be registered by the platform driver. 21959aa3f034SMengdong Lin * The platform driver should specify the DAI name and ID for matching. 21960038be9aSMengdong Lin */ 21979aa3f034SMengdong Lin static int soc_tplg_dai_config(struct soc_tplg *tplg, 21989aa3f034SMengdong Lin struct snd_soc_tplg_dai *d) 21990038be9aSMengdong Lin { 22000038be9aSMengdong Lin struct snd_soc_dai_link_component dai_component = {0}; 22010038be9aSMengdong Lin struct snd_soc_dai *dai; 22020038be9aSMengdong Lin struct snd_soc_dai_driver *dai_drv; 22030038be9aSMengdong Lin struct snd_soc_pcm_stream *stream; 22040038be9aSMengdong Lin struct snd_soc_tplg_stream_caps *caps; 22050038be9aSMengdong Lin int ret; 22060038be9aSMengdong Lin 22079aa3f034SMengdong Lin dai_component.dai_name = d->dai_name; 22080038be9aSMengdong Lin dai = snd_soc_find_dai(&dai_component); 22090038be9aSMengdong Lin if (!dai) { 22109aa3f034SMengdong Lin dev_err(tplg->dev, "ASoC: physical DAI %s not registered\n", 22119aa3f034SMengdong Lin d->dai_name); 22120038be9aSMengdong Lin return -EINVAL; 22130038be9aSMengdong Lin } 22140038be9aSMengdong Lin 22159aa3f034SMengdong Lin if (d->dai_id != dai->id) { 22169aa3f034SMengdong Lin dev_err(tplg->dev, "ASoC: physical DAI %s id mismatch\n", 22179aa3f034SMengdong Lin d->dai_name); 22180038be9aSMengdong Lin return -EINVAL; 22190038be9aSMengdong Lin } 22200038be9aSMengdong Lin 22210038be9aSMengdong Lin dai_drv = dai->driver; 22220038be9aSMengdong Lin if (!dai_drv) 22230038be9aSMengdong Lin return -EINVAL; 22240038be9aSMengdong Lin 22259aa3f034SMengdong Lin if (d->playback) { 22260038be9aSMengdong Lin stream = &dai_drv->playback; 22279aa3f034SMengdong Lin caps = &d->caps[SND_SOC_TPLG_STREAM_PLAYBACK]; 22280038be9aSMengdong Lin set_stream_info(stream, caps); 22290038be9aSMengdong Lin } 22300038be9aSMengdong Lin 22319aa3f034SMengdong Lin if (d->capture) { 22320038be9aSMengdong Lin stream = &dai_drv->capture; 22339aa3f034SMengdong Lin caps = &d->caps[SND_SOC_TPLG_STREAM_CAPTURE]; 22340038be9aSMengdong Lin set_stream_info(stream, caps); 22350038be9aSMengdong Lin } 22360038be9aSMengdong Lin 22379aa3f034SMengdong Lin if (d->flag_mask) 22389aa3f034SMengdong Lin set_dai_flags(dai_drv, d->flag_mask, d->flags); 22390038be9aSMengdong Lin 22400038be9aSMengdong Lin /* pass control to component driver for optional further init */ 22410038be9aSMengdong Lin ret = soc_tplg_dai_load(tplg, dai_drv); 22420038be9aSMengdong Lin if (ret < 0) { 22430038be9aSMengdong Lin dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n"); 22440038be9aSMengdong Lin return ret; 22450038be9aSMengdong Lin } 22460038be9aSMengdong Lin 22470038be9aSMengdong Lin return 0; 22480038be9aSMengdong Lin } 22490038be9aSMengdong Lin 22509aa3f034SMengdong Lin /* load physical DAI elements */ 22519aa3f034SMengdong Lin static int soc_tplg_dai_elems_load(struct soc_tplg *tplg, 22520038be9aSMengdong Lin struct snd_soc_tplg_hdr *hdr) 22530038be9aSMengdong Lin { 22549aa3f034SMengdong Lin struct snd_soc_tplg_dai *dai; 22550038be9aSMengdong Lin int count = hdr->count; 22560038be9aSMengdong Lin int i; 22570038be9aSMengdong Lin 22580038be9aSMengdong Lin if (tplg->pass != SOC_TPLG_PASS_BE_DAI) 22590038be9aSMengdong Lin return 0; 22600038be9aSMengdong Lin 22610038be9aSMengdong Lin /* config the existing BE DAIs */ 22620038be9aSMengdong Lin for (i = 0; i < count; i++) { 22639aa3f034SMengdong Lin dai = (struct snd_soc_tplg_dai *)tplg->pos; 22649aa3f034SMengdong Lin if (dai->size != sizeof(*dai)) { 22659aa3f034SMengdong Lin dev_err(tplg->dev, "ASoC: invalid physical DAI size\n"); 22660038be9aSMengdong Lin return -EINVAL; 22670038be9aSMengdong Lin } 22680038be9aSMengdong Lin 22699aa3f034SMengdong Lin soc_tplg_dai_config(tplg, dai); 22709aa3f034SMengdong Lin tplg->pos += (sizeof(*dai) + dai->priv.size); 22710038be9aSMengdong Lin } 22720038be9aSMengdong Lin 22730038be9aSMengdong Lin dev_dbg(tplg->dev, "ASoC: Configure %d BE DAIs\n", count); 22740038be9aSMengdong Lin return 0; 22750038be9aSMengdong Lin } 22760038be9aSMengdong Lin 2277583958faSMengdong Lin /** 2278583958faSMengdong Lin * manifest_new_ver - Create a new version of manifest from the old version 2279583958faSMengdong Lin * of source. 22808abab35fSCharles Keepax * @tplg: topology context 2281583958faSMengdong Lin * @src: old version of manifest as a source 2282583958faSMengdong Lin * @manifest: latest version of manifest created from the source 2283583958faSMengdong Lin * 2284583958faSMengdong Lin * Support from vesion 4. Users need free the returned manifest manually. 2285583958faSMengdong Lin */ 2286583958faSMengdong Lin static int manifest_new_ver(struct soc_tplg *tplg, 2287583958faSMengdong Lin struct snd_soc_tplg_manifest *src, 2288583958faSMengdong Lin struct snd_soc_tplg_manifest **manifest) 2289583958faSMengdong Lin { 2290583958faSMengdong Lin struct snd_soc_tplg_manifest *dest; 2291583958faSMengdong Lin struct snd_soc_tplg_manifest_v4 *src_v4; 2292583958faSMengdong Lin 2293583958faSMengdong Lin *manifest = NULL; 2294583958faSMengdong Lin 2295583958faSMengdong Lin if (src->size != sizeof(*src_v4)) { 2296583958faSMengdong Lin dev_err(tplg->dev, "ASoC: invalid manifest size\n"); 2297583958faSMengdong Lin return -EINVAL; 2298583958faSMengdong Lin } 2299583958faSMengdong Lin 2300583958faSMengdong Lin dev_warn(tplg->dev, "ASoC: old version of manifest\n"); 2301583958faSMengdong Lin 2302583958faSMengdong Lin src_v4 = (struct snd_soc_tplg_manifest_v4 *)src; 2303583958faSMengdong Lin dest = kzalloc(sizeof(*dest) + src_v4->priv.size, GFP_KERNEL); 2304583958faSMengdong Lin if (!dest) 2305583958faSMengdong Lin return -ENOMEM; 2306583958faSMengdong Lin 2307583958faSMengdong Lin dest->size = sizeof(*dest); /* size of latest abi version */ 2308583958faSMengdong Lin dest->control_elems = src_v4->control_elems; 2309583958faSMengdong Lin dest->widget_elems = src_v4->widget_elems; 2310583958faSMengdong Lin dest->graph_elems = src_v4->graph_elems; 2311583958faSMengdong Lin dest->pcm_elems = src_v4->pcm_elems; 2312583958faSMengdong Lin dest->dai_link_elems = src_v4->dai_link_elems; 2313583958faSMengdong Lin dest->priv.size = src_v4->priv.size; 2314583958faSMengdong Lin if (dest->priv.size) 2315583958faSMengdong Lin memcpy(dest->priv.data, src_v4->priv.data, 2316583958faSMengdong Lin src_v4->priv.size); 2317583958faSMengdong Lin 2318583958faSMengdong Lin *manifest = dest; 2319583958faSMengdong Lin return 0; 2320583958faSMengdong Lin } 23210038be9aSMengdong Lin 23228a978234SLiam Girdwood static int soc_tplg_manifest_load(struct soc_tplg *tplg, 23238a978234SLiam Girdwood struct snd_soc_tplg_hdr *hdr) 23248a978234SLiam Girdwood { 2325583958faSMengdong Lin struct snd_soc_tplg_manifest *manifest, *_manifest; 2326583958faSMengdong Lin bool abi_match; 2327583958faSMengdong Lin int err; 23288a978234SLiam Girdwood 23298a978234SLiam Girdwood if (tplg->pass != SOC_TPLG_PASS_MANIFEST) 23308a978234SLiam Girdwood return 0; 23318a978234SLiam Girdwood 23328a978234SLiam Girdwood manifest = (struct snd_soc_tplg_manifest *)tplg->pos; 2333583958faSMengdong Lin 2334583958faSMengdong Lin /* check ABI version by size, create a new manifest if abi not match */ 2335583958faSMengdong Lin if (manifest->size == sizeof(*manifest)) { 2336583958faSMengdong Lin abi_match = true; 2337583958faSMengdong Lin _manifest = manifest; 2338583958faSMengdong Lin } else { 2339583958faSMengdong Lin abi_match = false; 2340583958faSMengdong Lin err = manifest_new_ver(tplg, manifest, &_manifest); 2341583958faSMengdong Lin if (err < 0) 2342583958faSMengdong Lin return err; 234306eb49f7SMengdong Lin } 234406eb49f7SMengdong Lin 2345583958faSMengdong Lin /* pass control to component driver for optional further init */ 23468a978234SLiam Girdwood if (tplg->comp && tplg->ops && tplg->ops->manifest) 2347583958faSMengdong Lin return tplg->ops->manifest(tplg->comp, _manifest); 23488a978234SLiam Girdwood 2349583958faSMengdong Lin if (!abi_match) /* free the duplicated one */ 2350583958faSMengdong Lin kfree(_manifest); 2351583958faSMengdong Lin 23528a978234SLiam Girdwood return 0; 23538a978234SLiam Girdwood } 23548a978234SLiam Girdwood 23558a978234SLiam Girdwood /* validate header magic, size and type */ 23568a978234SLiam Girdwood static int soc_valid_header(struct soc_tplg *tplg, 23578a978234SLiam Girdwood struct snd_soc_tplg_hdr *hdr) 23588a978234SLiam Girdwood { 23598a978234SLiam Girdwood if (soc_tplg_get_hdr_offset(tplg) >= tplg->fw->size) 23608a978234SLiam Girdwood return 0; 23618a978234SLiam Girdwood 236206eb49f7SMengdong Lin if (hdr->size != sizeof(*hdr)) { 236306eb49f7SMengdong Lin dev_err(tplg->dev, 236406eb49f7SMengdong Lin "ASoC: invalid header size for type %d at offset 0x%lx size 0x%zx.\n", 236506eb49f7SMengdong Lin hdr->type, soc_tplg_get_hdr_offset(tplg), 236606eb49f7SMengdong Lin tplg->fw->size); 236706eb49f7SMengdong Lin return -EINVAL; 236806eb49f7SMengdong Lin } 236906eb49f7SMengdong Lin 23708a978234SLiam Girdwood /* big endian firmware objects not supported atm */ 23718a978234SLiam Girdwood if (hdr->magic == cpu_to_be32(SND_SOC_TPLG_MAGIC)) { 23728a978234SLiam Girdwood dev_err(tplg->dev, 23738a978234SLiam Girdwood "ASoC: pass %d big endian not supported header got %x at offset 0x%lx size 0x%zx.\n", 23748a978234SLiam Girdwood tplg->pass, hdr->magic, 23758a978234SLiam Girdwood soc_tplg_get_hdr_offset(tplg), tplg->fw->size); 23768a978234SLiam Girdwood return -EINVAL; 23778a978234SLiam Girdwood } 23788a978234SLiam Girdwood 23798a978234SLiam Girdwood if (hdr->magic != SND_SOC_TPLG_MAGIC) { 23808a978234SLiam Girdwood dev_err(tplg->dev, 23818a978234SLiam Girdwood "ASoC: pass %d does not have a valid header got %x at offset 0x%lx size 0x%zx.\n", 23828a978234SLiam Girdwood tplg->pass, hdr->magic, 23838a978234SLiam Girdwood soc_tplg_get_hdr_offset(tplg), tplg->fw->size); 23848a978234SLiam Girdwood return -EINVAL; 23858a978234SLiam Girdwood } 23868a978234SLiam Girdwood 2387288b8da7SMengdong Lin /* Support ABI from version 4 */ 2388288b8da7SMengdong Lin if (hdr->abi > SND_SOC_TPLG_ABI_VERSION 2389288b8da7SMengdong Lin || hdr->abi < SND_SOC_TPLG_ABI_VERSION_MIN) { 23908a978234SLiam Girdwood dev_err(tplg->dev, 23918a978234SLiam Girdwood "ASoC: pass %d invalid ABI version got 0x%x need 0x%x at offset 0x%lx size 0x%zx.\n", 23928a978234SLiam Girdwood tplg->pass, hdr->abi, 23938a978234SLiam Girdwood SND_SOC_TPLG_ABI_VERSION, soc_tplg_get_hdr_offset(tplg), 23948a978234SLiam Girdwood tplg->fw->size); 23958a978234SLiam Girdwood return -EINVAL; 23968a978234SLiam Girdwood } 23978a978234SLiam Girdwood 23988a978234SLiam Girdwood if (hdr->payload_size == 0) { 23998a978234SLiam Girdwood dev_err(tplg->dev, "ASoC: header has 0 size at offset 0x%lx.\n", 24008a978234SLiam Girdwood soc_tplg_get_hdr_offset(tplg)); 24018a978234SLiam Girdwood return -EINVAL; 24028a978234SLiam Girdwood } 24038a978234SLiam Girdwood 24048a978234SLiam Girdwood if (tplg->pass == hdr->type) 24058a978234SLiam Girdwood dev_dbg(tplg->dev, 24068a978234SLiam Girdwood "ASoC: Got 0x%x bytes of type %d version %d vendor %d at pass %d\n", 24078a978234SLiam Girdwood hdr->payload_size, hdr->type, hdr->version, 24088a978234SLiam Girdwood hdr->vendor_type, tplg->pass); 24098a978234SLiam Girdwood 24108a978234SLiam Girdwood return 1; 24118a978234SLiam Girdwood } 24128a978234SLiam Girdwood 24138a978234SLiam Girdwood /* check header type and call appropriate handler */ 24148a978234SLiam Girdwood static int soc_tplg_load_header(struct soc_tplg *tplg, 24158a978234SLiam Girdwood struct snd_soc_tplg_hdr *hdr) 24168a978234SLiam Girdwood { 24178a978234SLiam Girdwood tplg->pos = tplg->hdr_pos + sizeof(struct snd_soc_tplg_hdr); 24188a978234SLiam Girdwood 24198a978234SLiam Girdwood /* check for matching ID */ 24208a978234SLiam Girdwood if (hdr->index != tplg->req_index && 2421bb97142bSLiam Girdwood tplg->req_index != SND_SOC_TPLG_INDEX_ALL) 24228a978234SLiam Girdwood return 0; 24238a978234SLiam Girdwood 24248a978234SLiam Girdwood tplg->index = hdr->index; 24258a978234SLiam Girdwood 24268a978234SLiam Girdwood switch (hdr->type) { 24278a978234SLiam Girdwood case SND_SOC_TPLG_TYPE_MIXER: 24288a978234SLiam Girdwood case SND_SOC_TPLG_TYPE_ENUM: 24298a978234SLiam Girdwood case SND_SOC_TPLG_TYPE_BYTES: 24308a978234SLiam Girdwood return soc_tplg_kcontrol_elems_load(tplg, hdr); 24318a978234SLiam Girdwood case SND_SOC_TPLG_TYPE_DAPM_GRAPH: 24328a978234SLiam Girdwood return soc_tplg_dapm_graph_elems_load(tplg, hdr); 24338a978234SLiam Girdwood case SND_SOC_TPLG_TYPE_DAPM_WIDGET: 24348a978234SLiam Girdwood return soc_tplg_dapm_widget_elems_load(tplg, hdr); 24358a978234SLiam Girdwood case SND_SOC_TPLG_TYPE_PCM: 243664527e8aSMengdong Lin return soc_tplg_pcm_elems_load(tplg, hdr); 24373fbf7935SMengdong Lin case SND_SOC_TPLG_TYPE_DAI: 24389aa3f034SMengdong Lin return soc_tplg_dai_elems_load(tplg, hdr); 2439593d9e52SMengdong Lin case SND_SOC_TPLG_TYPE_DAI_LINK: 2440593d9e52SMengdong Lin case SND_SOC_TPLG_TYPE_BACKEND_LINK: 2441593d9e52SMengdong Lin /* physical link configurations */ 2442593d9e52SMengdong Lin return soc_tplg_link_elems_load(tplg, hdr); 24438a978234SLiam Girdwood case SND_SOC_TPLG_TYPE_MANIFEST: 24448a978234SLiam Girdwood return soc_tplg_manifest_load(tplg, hdr); 24458a978234SLiam Girdwood default: 24468a978234SLiam Girdwood /* bespoke vendor data object */ 24478a978234SLiam Girdwood return soc_tplg_vendor_load(tplg, hdr); 24488a978234SLiam Girdwood } 24498a978234SLiam Girdwood 24508a978234SLiam Girdwood return 0; 24518a978234SLiam Girdwood } 24528a978234SLiam Girdwood 24538a978234SLiam Girdwood /* process the topology file headers */ 24548a978234SLiam Girdwood static int soc_tplg_process_headers(struct soc_tplg *tplg) 24558a978234SLiam Girdwood { 24568a978234SLiam Girdwood struct snd_soc_tplg_hdr *hdr; 24578a978234SLiam Girdwood int ret; 24588a978234SLiam Girdwood 24598a978234SLiam Girdwood tplg->pass = SOC_TPLG_PASS_START; 24608a978234SLiam Girdwood 24618a978234SLiam Girdwood /* process the header types from start to end */ 24628a978234SLiam Girdwood while (tplg->pass <= SOC_TPLG_PASS_END) { 24638a978234SLiam Girdwood 24648a978234SLiam Girdwood tplg->hdr_pos = tplg->fw->data; 24658a978234SLiam Girdwood hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos; 24668a978234SLiam Girdwood 24678a978234SLiam Girdwood while (!soc_tplg_is_eof(tplg)) { 24688a978234SLiam Girdwood 24698a978234SLiam Girdwood /* make sure header is valid before loading */ 24708a978234SLiam Girdwood ret = soc_valid_header(tplg, hdr); 24718a978234SLiam Girdwood if (ret < 0) 24728a978234SLiam Girdwood return ret; 24738a978234SLiam Girdwood else if (ret == 0) 24748a978234SLiam Girdwood break; 24758a978234SLiam Girdwood 24768a978234SLiam Girdwood /* load the header object */ 24778a978234SLiam Girdwood ret = soc_tplg_load_header(tplg, hdr); 24788a978234SLiam Girdwood if (ret < 0) 24798a978234SLiam Girdwood return ret; 24808a978234SLiam Girdwood 24818a978234SLiam Girdwood /* goto next header */ 24828a978234SLiam Girdwood tplg->hdr_pos += hdr->payload_size + 24838a978234SLiam Girdwood sizeof(struct snd_soc_tplg_hdr); 24848a978234SLiam Girdwood hdr = (struct snd_soc_tplg_hdr *)tplg->hdr_pos; 24858a978234SLiam Girdwood } 24868a978234SLiam Girdwood 24878a978234SLiam Girdwood /* next data type pass */ 24888a978234SLiam Girdwood tplg->pass++; 24898a978234SLiam Girdwood } 24908a978234SLiam Girdwood 24918a978234SLiam Girdwood /* signal DAPM we are complete */ 24928a978234SLiam Girdwood ret = soc_tplg_dapm_complete(tplg); 24938a978234SLiam Girdwood if (ret < 0) 24948a978234SLiam Girdwood dev_err(tplg->dev, 24958a978234SLiam Girdwood "ASoC: failed to initialise DAPM from Firmware\n"); 24968a978234SLiam Girdwood 24978a978234SLiam Girdwood return ret; 24988a978234SLiam Girdwood } 24998a978234SLiam Girdwood 25008a978234SLiam Girdwood static int soc_tplg_load(struct soc_tplg *tplg) 25018a978234SLiam Girdwood { 25028a978234SLiam Girdwood int ret; 25038a978234SLiam Girdwood 25048a978234SLiam Girdwood ret = soc_tplg_process_headers(tplg); 25058a978234SLiam Girdwood if (ret == 0) 25068a978234SLiam Girdwood soc_tplg_complete(tplg); 25078a978234SLiam Girdwood 25088a978234SLiam Girdwood return ret; 25098a978234SLiam Girdwood } 25108a978234SLiam Girdwood 25118a978234SLiam Girdwood /* load audio component topology from "firmware" file */ 25128a978234SLiam Girdwood int snd_soc_tplg_component_load(struct snd_soc_component *comp, 25138a978234SLiam Girdwood struct snd_soc_tplg_ops *ops, const struct firmware *fw, u32 id) 25148a978234SLiam Girdwood { 25158a978234SLiam Girdwood struct soc_tplg tplg; 25168a978234SLiam Girdwood 25178a978234SLiam Girdwood /* setup parsing context */ 25188a978234SLiam Girdwood memset(&tplg, 0, sizeof(tplg)); 25198a978234SLiam Girdwood tplg.fw = fw; 25208a978234SLiam Girdwood tplg.dev = comp->dev; 25218a978234SLiam Girdwood tplg.comp = comp; 25228a978234SLiam Girdwood tplg.ops = ops; 25238a978234SLiam Girdwood tplg.req_index = id; 25248a978234SLiam Girdwood tplg.io_ops = ops->io_ops; 25258a978234SLiam Girdwood tplg.io_ops_count = ops->io_ops_count; 25261a3232d2SMengdong Lin tplg.bytes_ext_ops = ops->bytes_ext_ops; 25271a3232d2SMengdong Lin tplg.bytes_ext_ops_count = ops->bytes_ext_ops_count; 25288a978234SLiam Girdwood 25298a978234SLiam Girdwood return soc_tplg_load(&tplg); 25308a978234SLiam Girdwood } 25318a978234SLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_tplg_component_load); 25328a978234SLiam Girdwood 25338a978234SLiam Girdwood /* remove this dynamic widget */ 25348a978234SLiam Girdwood void snd_soc_tplg_widget_remove(struct snd_soc_dapm_widget *w) 25358a978234SLiam Girdwood { 25368a978234SLiam Girdwood /* make sure we are a widget */ 25378a978234SLiam Girdwood if (w->dobj.type != SND_SOC_DOBJ_WIDGET) 25388a978234SLiam Girdwood return; 25398a978234SLiam Girdwood 25408a978234SLiam Girdwood remove_widget(w->dapm->component, &w->dobj, SOC_TPLG_PASS_WIDGET); 25418a978234SLiam Girdwood } 25428a978234SLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove); 25438a978234SLiam Girdwood 25448a978234SLiam Girdwood /* remove all dynamic widgets from this DAPM context */ 25458a978234SLiam Girdwood void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, 25468a978234SLiam Girdwood u32 index) 25478a978234SLiam Girdwood { 25488a978234SLiam Girdwood struct snd_soc_dapm_widget *w, *next_w; 25498a978234SLiam Girdwood 25508a978234SLiam Girdwood list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { 25518a978234SLiam Girdwood 25528a978234SLiam Girdwood /* make sure we are a widget with correct context */ 25538a978234SLiam Girdwood if (w->dobj.type != SND_SOC_DOBJ_WIDGET || w->dapm != dapm) 25548a978234SLiam Girdwood continue; 25558a978234SLiam Girdwood 25568a978234SLiam Girdwood /* match ID */ 25578a978234SLiam Girdwood if (w->dobj.index != index && 25588a978234SLiam Girdwood w->dobj.index != SND_SOC_TPLG_INDEX_ALL) 25598a978234SLiam Girdwood continue; 25608a978234SLiam Girdwood /* check and free and dynamic widget kcontrols */ 25618a978234SLiam Girdwood snd_soc_tplg_widget_remove(w); 2562b97e2698SLars-Peter Clausen snd_soc_dapm_free_widget(w); 25638a978234SLiam Girdwood } 2564fd589a1bSJyri Sarha snd_soc_dapm_reset_cache(dapm); 25658a978234SLiam Girdwood } 25668a978234SLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_tplg_widget_remove_all); 25678a978234SLiam Girdwood 25688a978234SLiam Girdwood /* remove dynamic controls from the component driver */ 25698a978234SLiam Girdwood int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index) 25708a978234SLiam Girdwood { 25718a978234SLiam Girdwood struct snd_soc_dobj *dobj, *next_dobj; 25728a978234SLiam Girdwood int pass = SOC_TPLG_PASS_END; 25738a978234SLiam Girdwood 25748a978234SLiam Girdwood /* process the header types from end to start */ 25758a978234SLiam Girdwood while (pass >= SOC_TPLG_PASS_START) { 25768a978234SLiam Girdwood 25778a978234SLiam Girdwood /* remove mixer controls */ 25788a978234SLiam Girdwood list_for_each_entry_safe(dobj, next_dobj, &comp->dobj_list, 25798a978234SLiam Girdwood list) { 25808a978234SLiam Girdwood 25818a978234SLiam Girdwood /* match index */ 25828a978234SLiam Girdwood if (dobj->index != index && 2583*feb12f0cSYan Wang index != SND_SOC_TPLG_INDEX_ALL) 25848a978234SLiam Girdwood continue; 25858a978234SLiam Girdwood 25868a978234SLiam Girdwood switch (dobj->type) { 25878a978234SLiam Girdwood case SND_SOC_DOBJ_MIXER: 25888a978234SLiam Girdwood remove_mixer(comp, dobj, pass); 25898a978234SLiam Girdwood break; 25908a978234SLiam Girdwood case SND_SOC_DOBJ_ENUM: 25918a978234SLiam Girdwood remove_enum(comp, dobj, pass); 25928a978234SLiam Girdwood break; 25938a978234SLiam Girdwood case SND_SOC_DOBJ_BYTES: 25948a978234SLiam Girdwood remove_bytes(comp, dobj, pass); 25958a978234SLiam Girdwood break; 25968a978234SLiam Girdwood case SND_SOC_DOBJ_WIDGET: 25978a978234SLiam Girdwood remove_widget(comp, dobj, pass); 25988a978234SLiam Girdwood break; 25998a978234SLiam Girdwood case SND_SOC_DOBJ_PCM: 260064527e8aSMengdong Lin remove_dai(comp, dobj, pass); 26018a978234SLiam Girdwood break; 2602acfc7d46SMengdong Lin case SND_SOC_DOBJ_DAI_LINK: 2603acfc7d46SMengdong Lin remove_link(comp, dobj, pass); 2604acfc7d46SMengdong Lin break; 26058a978234SLiam Girdwood default: 26068a978234SLiam Girdwood dev_err(comp->dev, "ASoC: invalid component type %d for removal\n", 26078a978234SLiam Girdwood dobj->type); 26088a978234SLiam Girdwood break; 26098a978234SLiam Girdwood } 26108a978234SLiam Girdwood } 26118a978234SLiam Girdwood pass--; 26128a978234SLiam Girdwood } 26138a978234SLiam Girdwood 26148a978234SLiam Girdwood /* let caller know if FW can be freed when no objects are left */ 26158a978234SLiam Girdwood return !list_empty(&comp->dobj_list); 26168a978234SLiam Girdwood } 26178a978234SLiam Girdwood EXPORT_SYMBOL_GPL(snd_soc_tplg_component_remove); 2618