162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 962306a36Sopenharmony_ci// 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/bits.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/firmware.h> 1562306a36Sopenharmony_ci#include <linux/workqueue.h> 1662306a36Sopenharmony_ci#include <sound/tlv.h> 1762306a36Sopenharmony_ci#include <uapi/sound/sof/tokens.h> 1862306a36Sopenharmony_ci#include "sof-priv.h" 1962306a36Sopenharmony_ci#include "sof-audio.h" 2062306a36Sopenharmony_ci#include "ops.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define COMP_ID_UNASSIGNED 0xffffffff 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * Constants used in the computation of linear volume gain 2562306a36Sopenharmony_ci * from dB gain 20th root of 10 in Q1.16 fixed-point notation 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci#define VOL_TWENTIETH_ROOT_OF_TEN 73533 2862306a36Sopenharmony_ci/* 40th root of 10 in Q1.16 fixed-point notation*/ 2962306a36Sopenharmony_ci#define VOL_FORTIETH_ROOT_OF_TEN 69419 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* 0.5 dB step value in topology TLV */ 3262306a36Sopenharmony_ci#define VOL_HALF_DB_STEP 50 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* TLV data items */ 3562306a36Sopenharmony_ci#define TLV_MIN 0 3662306a36Sopenharmony_ci#define TLV_STEP 1 3762306a36Sopenharmony_ci#define TLV_MUTE 2 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/** 4062306a36Sopenharmony_ci * sof_update_ipc_object - Parse multiple sets of tokens within the token array associated with the 4162306a36Sopenharmony_ci * token ID. 4262306a36Sopenharmony_ci * @scomp: pointer to SOC component 4362306a36Sopenharmony_ci * @object: target IPC struct to save the parsed values 4462306a36Sopenharmony_ci * @token_id: token ID for the token array to be searched 4562306a36Sopenharmony_ci * @tuples: pointer to the tuples array 4662306a36Sopenharmony_ci * @num_tuples: number of tuples in the tuples array 4762306a36Sopenharmony_ci * @object_size: size of the object 4862306a36Sopenharmony_ci * @token_instance_num: number of times the same @token_id needs to be parsed i.e. the function 4962306a36Sopenharmony_ci * looks for @token_instance_num of each token in the token array associated 5062306a36Sopenharmony_ci * with the @token_id 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ciint sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id, 5362306a36Sopenharmony_ci struct snd_sof_tuple *tuples, int num_tuples, 5462306a36Sopenharmony_ci size_t object_size, int token_instance_num) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 5762306a36Sopenharmony_ci const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 5862306a36Sopenharmony_ci const struct sof_token_info *token_list; 5962306a36Sopenharmony_ci const struct sof_topology_token *tokens; 6062306a36Sopenharmony_ci int i, j; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci token_list = tplg_ops ? tplg_ops->token_list : NULL; 6362306a36Sopenharmony_ci /* nothing to do if token_list is NULL */ 6462306a36Sopenharmony_ci if (!token_list) 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (token_list[token_id].count < 0) { 6862306a36Sopenharmony_ci dev_err(scomp->dev, "Invalid token count for token ID: %d\n", token_id); 6962306a36Sopenharmony_ci return -EINVAL; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* No tokens to match */ 7362306a36Sopenharmony_ci if (!token_list[token_id].count) 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci tokens = token_list[token_id].tokens; 7762306a36Sopenharmony_ci if (!tokens) { 7862306a36Sopenharmony_ci dev_err(scomp->dev, "Invalid tokens for token id: %d\n", token_id); 7962306a36Sopenharmony_ci return -EINVAL; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci for (i = 0; i < token_list[token_id].count; i++) { 8362306a36Sopenharmony_ci int offset = 0; 8462306a36Sopenharmony_ci int num_tokens_matched = 0; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci for (j = 0; j < num_tuples; j++) { 8762306a36Sopenharmony_ci if (tokens[i].token == tuples[j].token) { 8862306a36Sopenharmony_ci switch (tokens[i].type) { 8962306a36Sopenharmony_ci case SND_SOC_TPLG_TUPLE_TYPE_WORD: 9062306a36Sopenharmony_ci { 9162306a36Sopenharmony_ci u32 *val = (u32 *)((u8 *)object + tokens[i].offset + 9262306a36Sopenharmony_ci offset); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci *val = tuples[j].value.v; 9562306a36Sopenharmony_ci break; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci case SND_SOC_TPLG_TUPLE_TYPE_SHORT: 9862306a36Sopenharmony_ci case SND_SOC_TPLG_TUPLE_TYPE_BOOL: 9962306a36Sopenharmony_ci { 10062306a36Sopenharmony_ci u16 *val = (u16 *)((u8 *)object + tokens[i].offset + 10162306a36Sopenharmony_ci offset); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci *val = (u16)tuples[j].value.v; 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci case SND_SOC_TPLG_TUPLE_TYPE_STRING: 10762306a36Sopenharmony_ci { 10862306a36Sopenharmony_ci if (!tokens[i].get_token) { 10962306a36Sopenharmony_ci dev_err(scomp->dev, 11062306a36Sopenharmony_ci "get_token not defined for token %d in %s\n", 11162306a36Sopenharmony_ci tokens[i].token, token_list[token_id].name); 11262306a36Sopenharmony_ci return -EINVAL; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci tokens[i].get_token((void *)tuples[j].value.s, object, 11662306a36Sopenharmony_ci tokens[i].offset + offset); 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci default: 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci num_tokens_matched++; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* found all required sets of current token. Move to the next one */ 12662306a36Sopenharmony_ci if (!(num_tokens_matched % token_instance_num)) 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /* move to the next object */ 13062306a36Sopenharmony_ci offset += object_size; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic inline int get_tlv_data(const int *p, int tlv[SOF_TLV_ITEMS]) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci /* we only support dB scale TLV type at the moment */ 14162306a36Sopenharmony_ci if ((int)p[SNDRV_CTL_TLVO_TYPE] != SNDRV_CTL_TLVT_DB_SCALE) 14262306a36Sopenharmony_ci return -EINVAL; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* min value in topology tlv data is multiplied by 100 */ 14562306a36Sopenharmony_ci tlv[TLV_MIN] = (int)p[SNDRV_CTL_TLVO_DB_SCALE_MIN] / 100; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* volume steps */ 14862306a36Sopenharmony_ci tlv[TLV_STEP] = (int)(p[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & 14962306a36Sopenharmony_ci TLV_DB_SCALE_MASK); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* mute ON/OFF */ 15262306a36Sopenharmony_ci if ((p[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] & 15362306a36Sopenharmony_ci TLV_DB_SCALE_MUTE) == 0) 15462306a36Sopenharmony_ci tlv[TLV_MUTE] = 0; 15562306a36Sopenharmony_ci else 15662306a36Sopenharmony_ci tlv[TLV_MUTE] = 1; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* 16262306a36Sopenharmony_ci * Function to truncate an unsigned 64-bit number 16362306a36Sopenharmony_ci * by x bits and return 32-bit unsigned number. This 16462306a36Sopenharmony_ci * function also takes care of rounding while truncating 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_cistatic inline u32 vol_shift_64(u64 i, u32 x) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci /* do not truncate more than 32 bits */ 16962306a36Sopenharmony_ci if (x > 32) 17062306a36Sopenharmony_ci x = 32; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (x == 0) 17362306a36Sopenharmony_ci return (u32)i; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return (u32)(((i >> (x - 1)) + 1) >> 1); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* 17962306a36Sopenharmony_ci * Function to compute a ^ exp where, 18062306a36Sopenharmony_ci * a is a fractional number represented by a fixed-point 18162306a36Sopenharmony_ci * integer with a fractional world length of "fwl" 18262306a36Sopenharmony_ci * exp is an integer 18362306a36Sopenharmony_ci * fwl is the fractional word length 18462306a36Sopenharmony_ci * Return value is a fractional number represented by a 18562306a36Sopenharmony_ci * fixed-point integer with a fractional word length of "fwl" 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_cistatic u32 vol_pow32(u32 a, int exp, u32 fwl) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci int i, iter; 19062306a36Sopenharmony_ci u32 power = 1 << fwl; 19162306a36Sopenharmony_ci u64 numerator; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* if exponent is 0, return 1 */ 19462306a36Sopenharmony_ci if (exp == 0) 19562306a36Sopenharmony_ci return power; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* determine the number of iterations based on the exponent */ 19862306a36Sopenharmony_ci if (exp < 0) 19962306a36Sopenharmony_ci iter = exp * -1; 20062306a36Sopenharmony_ci else 20162306a36Sopenharmony_ci iter = exp; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* mutiply a "iter" times to compute power */ 20462306a36Sopenharmony_ci for (i = 0; i < iter; i++) { 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * Product of 2 Qx.fwl fixed-point numbers yields a Q2*x.2*fwl 20762306a36Sopenharmony_ci * Truncate product back to fwl fractional bits with rounding 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci power = vol_shift_64((u64)power * a, fwl); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (exp > 0) { 21362306a36Sopenharmony_ci /* if exp is positive, return the result */ 21462306a36Sopenharmony_ci return power; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* if exp is negative, return the multiplicative inverse */ 21862306a36Sopenharmony_ci numerator = (u64)1 << (fwl << 1); 21962306a36Sopenharmony_ci do_div(numerator, power); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return (u32)numerator; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* 22562306a36Sopenharmony_ci * Function to calculate volume gain from TLV data. 22662306a36Sopenharmony_ci * This function can only handle gain steps that are multiples of 0.5 dB 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ciu32 vol_compute_gain(u32 value, int *tlv) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci int dB_gain; 23162306a36Sopenharmony_ci u32 linear_gain; 23262306a36Sopenharmony_ci int f_step; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* mute volume */ 23562306a36Sopenharmony_ci if (value == 0 && tlv[TLV_MUTE]) 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* 23962306a36Sopenharmony_ci * compute dB gain from tlv. tlv_step 24062306a36Sopenharmony_ci * in topology is multiplied by 100 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci dB_gain = tlv[TLV_MIN] + (value * tlv[TLV_STEP]) / 100; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* 24562306a36Sopenharmony_ci * compute linear gain represented by fixed-point 24662306a36Sopenharmony_ci * int with VOLUME_FWL fractional bits 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci linear_gain = vol_pow32(VOL_TWENTIETH_ROOT_OF_TEN, dB_gain, VOLUME_FWL); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* extract the fractional part of volume step */ 25162306a36Sopenharmony_ci f_step = tlv[TLV_STEP] - (tlv[TLV_STEP] / 100); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* if volume step is an odd multiple of 0.5 dB */ 25462306a36Sopenharmony_ci if (f_step == VOL_HALF_DB_STEP && (value & 1)) 25562306a36Sopenharmony_ci linear_gain = vol_shift_64((u64)linear_gain * 25662306a36Sopenharmony_ci VOL_FORTIETH_ROOT_OF_TEN, 25762306a36Sopenharmony_ci VOLUME_FWL); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return linear_gain; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* 26362306a36Sopenharmony_ci * Set up volume table for kcontrols from tlv data 26462306a36Sopenharmony_ci * "size" specifies the number of entries in the table 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_cistatic int set_up_volume_table(struct snd_sof_control *scontrol, 26762306a36Sopenharmony_ci int tlv[SOF_TLV_ITEMS], int size) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct snd_soc_component *scomp = scontrol->scomp; 27062306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 27162306a36Sopenharmony_ci const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (tplg_ops && tplg_ops->control && tplg_ops->control->set_up_volume_table) 27462306a36Sopenharmony_ci return tplg_ops->control->set_up_volume_table(scontrol, tlv, size); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci dev_err(scomp->dev, "Mandatory op %s not set\n", __func__); 27762306a36Sopenharmony_ci return -EINVAL; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistruct sof_dai_types { 28162306a36Sopenharmony_ci const char *name; 28262306a36Sopenharmony_ci enum sof_ipc_dai_type type; 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic const struct sof_dai_types sof_dais[] = { 28662306a36Sopenharmony_ci {"SSP", SOF_DAI_INTEL_SSP}, 28762306a36Sopenharmony_ci {"HDA", SOF_DAI_INTEL_HDA}, 28862306a36Sopenharmony_ci {"DMIC", SOF_DAI_INTEL_DMIC}, 28962306a36Sopenharmony_ci {"ALH", SOF_DAI_INTEL_ALH}, 29062306a36Sopenharmony_ci {"SAI", SOF_DAI_IMX_SAI}, 29162306a36Sopenharmony_ci {"ESAI", SOF_DAI_IMX_ESAI}, 29262306a36Sopenharmony_ci {"ACP", SOF_DAI_AMD_BT}, 29362306a36Sopenharmony_ci {"ACPSP", SOF_DAI_AMD_SP}, 29462306a36Sopenharmony_ci {"ACPDMIC", SOF_DAI_AMD_DMIC}, 29562306a36Sopenharmony_ci {"ACPHS", SOF_DAI_AMD_HS}, 29662306a36Sopenharmony_ci {"AFE", SOF_DAI_MEDIATEK_AFE}, 29762306a36Sopenharmony_ci {"ACPSP_VIRTUAL", SOF_DAI_AMD_SP_VIRTUAL}, 29862306a36Sopenharmony_ci {"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL}, 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci}; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic enum sof_ipc_dai_type find_dai(const char *name) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci int i; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sof_dais); i++) { 30762306a36Sopenharmony_ci if (strcmp(name, sof_dais[i].name) == 0) 30862306a36Sopenharmony_ci return sof_dais[i].type; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return SOF_DAI_INTEL_NONE; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci/* 31562306a36Sopenharmony_ci * Supported Frame format types and lookup, add new ones to end of list. 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistruct sof_frame_types { 31962306a36Sopenharmony_ci const char *name; 32062306a36Sopenharmony_ci enum sof_ipc_frame frame; 32162306a36Sopenharmony_ci}; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic const struct sof_frame_types sof_frames[] = { 32462306a36Sopenharmony_ci {"s16le", SOF_IPC_FRAME_S16_LE}, 32562306a36Sopenharmony_ci {"s24le", SOF_IPC_FRAME_S24_4LE}, 32662306a36Sopenharmony_ci {"s32le", SOF_IPC_FRAME_S32_LE}, 32762306a36Sopenharmony_ci {"float", SOF_IPC_FRAME_FLOAT}, 32862306a36Sopenharmony_ci}; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic enum sof_ipc_frame find_format(const char *name) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci int i; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sof_frames); i++) { 33562306a36Sopenharmony_ci if (strcmp(name, sof_frames[i].name) == 0) 33662306a36Sopenharmony_ci return sof_frames[i].frame; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* use s32le if nothing is specified */ 34062306a36Sopenharmony_ci return SOF_IPC_FRAME_S32_LE; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ciint get_token_u32(void *elem, void *object, u32 offset) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct snd_soc_tplg_vendor_value_elem *velem = elem; 34662306a36Sopenharmony_ci u32 *val = (u32 *)((u8 *)object + offset); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci *val = le32_to_cpu(velem->value); 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ciint get_token_u16(void *elem, void *object, u32 offset) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct snd_soc_tplg_vendor_value_elem *velem = elem; 35562306a36Sopenharmony_ci u16 *val = (u16 *)((u8 *)object + offset); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci *val = (u16)le32_to_cpu(velem->value); 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ciint get_token_uuid(void *elem, void *object, u32 offset) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct snd_soc_tplg_vendor_uuid_elem *velem = elem; 36462306a36Sopenharmony_ci u8 *dst = (u8 *)object + offset; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci memcpy(dst, velem->uuid, UUID_SIZE); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/* 37262306a36Sopenharmony_ci * The string gets from topology will be stored in heap, the owner only 37362306a36Sopenharmony_ci * holds a char* member point to the heap. 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ciint get_token_string(void *elem, void *object, u32 offset) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci /* "dst" here points to the char* member of the owner */ 37862306a36Sopenharmony_ci char **dst = (char **)((u8 *)object + offset); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci *dst = kstrdup(elem, GFP_KERNEL); 38162306a36Sopenharmony_ci if (!*dst) 38262306a36Sopenharmony_ci return -ENOMEM; 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci}; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ciint get_token_comp_format(void *elem, void *object, u32 offset) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci u32 *val = (u32 *)((u8 *)object + offset); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci *val = find_format((const char *)elem); 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ciint get_token_dai_type(void *elem, void *object, u32 offset) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci u32 *val = (u32 *)((u8 *)object + offset); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci *val = find_dai((const char *)elem); 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci/* PCM */ 40362306a36Sopenharmony_cistatic const struct sof_topology_token stream_tokens[] = { 40462306a36Sopenharmony_ci {SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, 40562306a36Sopenharmony_ci offsetof(struct snd_sof_pcm, stream[0].d0i3_compatible)}, 40662306a36Sopenharmony_ci {SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, 40762306a36Sopenharmony_ci offsetof(struct snd_sof_pcm, stream[1].d0i3_compatible)}, 40862306a36Sopenharmony_ci}; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* Leds */ 41162306a36Sopenharmony_cistatic const struct sof_topology_token led_tokens[] = { 41262306a36Sopenharmony_ci {SOF_TKN_MUTE_LED_USE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 41362306a36Sopenharmony_ci offsetof(struct snd_sof_led_control, use_led)}, 41462306a36Sopenharmony_ci {SOF_TKN_MUTE_LED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 41562306a36Sopenharmony_ci offsetof(struct snd_sof_led_control, direction)}, 41662306a36Sopenharmony_ci}; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic const struct sof_topology_token comp_pin_tokens[] = { 41962306a36Sopenharmony_ci {SOF_TKN_COMP_NUM_INPUT_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 42062306a36Sopenharmony_ci offsetof(struct snd_sof_widget, num_input_pins)}, 42162306a36Sopenharmony_ci {SOF_TKN_COMP_NUM_OUTPUT_PINS, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 42262306a36Sopenharmony_ci offsetof(struct snd_sof_widget, num_output_pins)}, 42362306a36Sopenharmony_ci}; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic const struct sof_topology_token comp_input_pin_binding_tokens[] = { 42662306a36Sopenharmony_ci {SOF_TKN_COMP_INPUT_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING, 42762306a36Sopenharmony_ci get_token_string, 0}, 42862306a36Sopenharmony_ci}; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic const struct sof_topology_token comp_output_pin_binding_tokens[] = { 43162306a36Sopenharmony_ci {SOF_TKN_COMP_OUTPUT_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING, 43262306a36Sopenharmony_ci get_token_string, 0}, 43362306a36Sopenharmony_ci}; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/** 43662306a36Sopenharmony_ci * sof_parse_uuid_tokens - Parse multiple sets of UUID tokens 43762306a36Sopenharmony_ci * @scomp: pointer to soc component 43862306a36Sopenharmony_ci * @object: target ipc struct for parsed values 43962306a36Sopenharmony_ci * @offset: offset within the object pointer 44062306a36Sopenharmony_ci * @tokens: array of struct sof_topology_token containing the tokens to be matched 44162306a36Sopenharmony_ci * @num_tokens: number of tokens in tokens array 44262306a36Sopenharmony_ci * @array: source pointer to consecutive vendor arrays in topology 44362306a36Sopenharmony_ci * 44462306a36Sopenharmony_ci * This function parses multiple sets of string type tokens in vendor arrays 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_cistatic int sof_parse_uuid_tokens(struct snd_soc_component *scomp, 44762306a36Sopenharmony_ci void *object, size_t offset, 44862306a36Sopenharmony_ci const struct sof_topology_token *tokens, int num_tokens, 44962306a36Sopenharmony_ci struct snd_soc_tplg_vendor_array *array) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct snd_soc_tplg_vendor_uuid_elem *elem; 45262306a36Sopenharmony_ci int found = 0; 45362306a36Sopenharmony_ci int i, j; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* parse element by element */ 45662306a36Sopenharmony_ci for (i = 0; i < le32_to_cpu(array->num_elems); i++) { 45762306a36Sopenharmony_ci elem = &array->uuid[i]; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* search for token */ 46062306a36Sopenharmony_ci for (j = 0; j < num_tokens; j++) { 46162306a36Sopenharmony_ci /* match token type */ 46262306a36Sopenharmony_ci if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID) 46362306a36Sopenharmony_ci continue; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* match token id */ 46662306a36Sopenharmony_ci if (tokens[j].token != le32_to_cpu(elem->token)) 46762306a36Sopenharmony_ci continue; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* matched - now load token */ 47062306a36Sopenharmony_ci tokens[j].get_token(elem, object, 47162306a36Sopenharmony_ci offset + tokens[j].offset); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci found++; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return found; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci/** 48162306a36Sopenharmony_ci * sof_copy_tuples - Parse tokens and copy them to the @tuples array 48262306a36Sopenharmony_ci * @sdev: pointer to struct snd_sof_dev 48362306a36Sopenharmony_ci * @array: source pointer to consecutive vendor arrays in topology 48462306a36Sopenharmony_ci * @array_size: size of @array 48562306a36Sopenharmony_ci * @token_id: Token ID associated with a token array 48662306a36Sopenharmony_ci * @token_instance_num: number of times the same @token_id needs to be parsed i.e. the function 48762306a36Sopenharmony_ci * looks for @token_instance_num of each token in the token array associated 48862306a36Sopenharmony_ci * with the @token_id 48962306a36Sopenharmony_ci * @tuples: tuples array to copy the matched tuples to 49062306a36Sopenharmony_ci * @tuples_size: size of @tuples 49162306a36Sopenharmony_ci * @num_copied_tuples: pointer to the number of copied tuples in the tuples array 49262306a36Sopenharmony_ci * 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_cistatic int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_array *array, 49562306a36Sopenharmony_ci int array_size, u32 token_id, int token_instance_num, 49662306a36Sopenharmony_ci struct snd_sof_tuple *tuples, int tuples_size, int *num_copied_tuples) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 49962306a36Sopenharmony_ci const struct sof_token_info *token_list; 50062306a36Sopenharmony_ci const struct sof_topology_token *tokens; 50162306a36Sopenharmony_ci int found = 0; 50262306a36Sopenharmony_ci int num_tokens, asize; 50362306a36Sopenharmony_ci int i, j; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci token_list = tplg_ops ? tplg_ops->token_list : NULL; 50662306a36Sopenharmony_ci /* nothing to do if token_list is NULL */ 50762306a36Sopenharmony_ci if (!token_list) 50862306a36Sopenharmony_ci return 0; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (!tuples || !num_copied_tuples) { 51162306a36Sopenharmony_ci dev_err(sdev->dev, "Invalid tuples array\n"); 51262306a36Sopenharmony_ci return -EINVAL; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci tokens = token_list[token_id].tokens; 51662306a36Sopenharmony_ci num_tokens = token_list[token_id].count; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (!tokens) { 51962306a36Sopenharmony_ci dev_err(sdev->dev, "No token array defined for token ID: %d\n", token_id); 52062306a36Sopenharmony_ci return -EINVAL; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* check if there's space in the tuples array for new tokens */ 52462306a36Sopenharmony_ci if (*num_copied_tuples >= tuples_size) { 52562306a36Sopenharmony_ci dev_err(sdev->dev, "No space in tuples array for new tokens from %s", 52662306a36Sopenharmony_ci token_list[token_id].name); 52762306a36Sopenharmony_ci return -EINVAL; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci while (array_size > 0 && found < num_tokens * token_instance_num) { 53162306a36Sopenharmony_ci asize = le32_to_cpu(array->size); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* validate asize */ 53462306a36Sopenharmony_ci if (asize < 0) { 53562306a36Sopenharmony_ci dev_err(sdev->dev, "Invalid array size 0x%x\n", asize); 53662306a36Sopenharmony_ci return -EINVAL; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* make sure there is enough data before parsing */ 54062306a36Sopenharmony_ci array_size -= asize; 54162306a36Sopenharmony_ci if (array_size < 0) { 54262306a36Sopenharmony_ci dev_err(sdev->dev, "Invalid array size 0x%x\n", asize); 54362306a36Sopenharmony_ci return -EINVAL; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* parse element by element */ 54762306a36Sopenharmony_ci for (i = 0; i < le32_to_cpu(array->num_elems); i++) { 54862306a36Sopenharmony_ci /* search for token */ 54962306a36Sopenharmony_ci for (j = 0; j < num_tokens; j++) { 55062306a36Sopenharmony_ci /* match token type */ 55162306a36Sopenharmony_ci if (!(tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD || 55262306a36Sopenharmony_ci tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT || 55362306a36Sopenharmony_ci tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BYTE || 55462306a36Sopenharmony_ci tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BOOL || 55562306a36Sopenharmony_ci tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_STRING)) 55662306a36Sopenharmony_ci continue; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_STRING) { 55962306a36Sopenharmony_ci struct snd_soc_tplg_vendor_string_elem *elem; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci elem = &array->string[i]; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* match token id */ 56462306a36Sopenharmony_ci if (tokens[j].token != le32_to_cpu(elem->token)) 56562306a36Sopenharmony_ci continue; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci tuples[*num_copied_tuples].token = tokens[j].token; 56862306a36Sopenharmony_ci tuples[*num_copied_tuples].value.s = elem->string; 56962306a36Sopenharmony_ci } else { 57062306a36Sopenharmony_ci struct snd_soc_tplg_vendor_value_elem *elem; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci elem = &array->value[i]; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* match token id */ 57562306a36Sopenharmony_ci if (tokens[j].token != le32_to_cpu(elem->token)) 57662306a36Sopenharmony_ci continue; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci tuples[*num_copied_tuples].token = tokens[j].token; 57962306a36Sopenharmony_ci tuples[*num_copied_tuples].value.v = 58062306a36Sopenharmony_ci le32_to_cpu(elem->value); 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci found++; 58362306a36Sopenharmony_ci (*num_copied_tuples)++; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* stop if there's no space for any more new tuples */ 58662306a36Sopenharmony_ci if (*num_copied_tuples == tuples_size) 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* stop when we've found the required token instances */ 59162306a36Sopenharmony_ci if (found == num_tokens * token_instance_num) 59262306a36Sopenharmony_ci return 0; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* next array */ 59662306a36Sopenharmony_ci array = (struct snd_soc_tplg_vendor_array *)((u8 *)array + asize); 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci/** 60362306a36Sopenharmony_ci * sof_parse_string_tokens - Parse multiple sets of tokens 60462306a36Sopenharmony_ci * @scomp: pointer to soc component 60562306a36Sopenharmony_ci * @object: target ipc struct for parsed values 60662306a36Sopenharmony_ci * @offset: offset within the object pointer 60762306a36Sopenharmony_ci * @tokens: array of struct sof_topology_token containing the tokens to be matched 60862306a36Sopenharmony_ci * @num_tokens: number of tokens in tokens array 60962306a36Sopenharmony_ci * @array: source pointer to consecutive vendor arrays in topology 61062306a36Sopenharmony_ci * 61162306a36Sopenharmony_ci * This function parses multiple sets of string type tokens in vendor arrays 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_cistatic int sof_parse_string_tokens(struct snd_soc_component *scomp, 61462306a36Sopenharmony_ci void *object, int offset, 61562306a36Sopenharmony_ci const struct sof_topology_token *tokens, int num_tokens, 61662306a36Sopenharmony_ci struct snd_soc_tplg_vendor_array *array) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct snd_soc_tplg_vendor_string_elem *elem; 61962306a36Sopenharmony_ci int found = 0; 62062306a36Sopenharmony_ci int i, j, ret; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* parse element by element */ 62362306a36Sopenharmony_ci for (i = 0; i < le32_to_cpu(array->num_elems); i++) { 62462306a36Sopenharmony_ci elem = &array->string[i]; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* search for token */ 62762306a36Sopenharmony_ci for (j = 0; j < num_tokens; j++) { 62862306a36Sopenharmony_ci /* match token type */ 62962306a36Sopenharmony_ci if (tokens[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING) 63062306a36Sopenharmony_ci continue; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* match token id */ 63362306a36Sopenharmony_ci if (tokens[j].token != le32_to_cpu(elem->token)) 63462306a36Sopenharmony_ci continue; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* matched - now load token */ 63762306a36Sopenharmony_ci ret = tokens[j].get_token(elem->string, object, offset + tokens[j].offset); 63862306a36Sopenharmony_ci if (ret < 0) 63962306a36Sopenharmony_ci return ret; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci found++; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci return found; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci/** 64962306a36Sopenharmony_ci * sof_parse_word_tokens - Parse multiple sets of tokens 65062306a36Sopenharmony_ci * @scomp: pointer to soc component 65162306a36Sopenharmony_ci * @object: target ipc struct for parsed values 65262306a36Sopenharmony_ci * @offset: offset within the object pointer 65362306a36Sopenharmony_ci * @tokens: array of struct sof_topology_token containing the tokens to be matched 65462306a36Sopenharmony_ci * @num_tokens: number of tokens in tokens array 65562306a36Sopenharmony_ci * @array: source pointer to consecutive vendor arrays in topology 65662306a36Sopenharmony_ci * 65762306a36Sopenharmony_ci * This function parses multiple sets of word type tokens in vendor arrays 65862306a36Sopenharmony_ci */ 65962306a36Sopenharmony_cistatic int sof_parse_word_tokens(struct snd_soc_component *scomp, 66062306a36Sopenharmony_ci void *object, int offset, 66162306a36Sopenharmony_ci const struct sof_topology_token *tokens, int num_tokens, 66262306a36Sopenharmony_ci struct snd_soc_tplg_vendor_array *array) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct snd_soc_tplg_vendor_value_elem *elem; 66562306a36Sopenharmony_ci int found = 0; 66662306a36Sopenharmony_ci int i, j; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* parse element by element */ 66962306a36Sopenharmony_ci for (i = 0; i < le32_to_cpu(array->num_elems); i++) { 67062306a36Sopenharmony_ci elem = &array->value[i]; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* search for token */ 67362306a36Sopenharmony_ci for (j = 0; j < num_tokens; j++) { 67462306a36Sopenharmony_ci /* match token type */ 67562306a36Sopenharmony_ci if (!(tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD || 67662306a36Sopenharmony_ci tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT || 67762306a36Sopenharmony_ci tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BYTE || 67862306a36Sopenharmony_ci tokens[j].type == SND_SOC_TPLG_TUPLE_TYPE_BOOL)) 67962306a36Sopenharmony_ci continue; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* match token id */ 68262306a36Sopenharmony_ci if (tokens[j].token != le32_to_cpu(elem->token)) 68362306a36Sopenharmony_ci continue; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* load token */ 68662306a36Sopenharmony_ci tokens[j].get_token(elem, object, offset + tokens[j].offset); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci found++; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return found; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci/** 69662306a36Sopenharmony_ci * sof_parse_token_sets - Parse multiple sets of tokens 69762306a36Sopenharmony_ci * @scomp: pointer to soc component 69862306a36Sopenharmony_ci * @object: target ipc struct for parsed values 69962306a36Sopenharmony_ci * @tokens: token definition array describing what tokens to parse 70062306a36Sopenharmony_ci * @count: number of tokens in definition array 70162306a36Sopenharmony_ci * @array: source pointer to consecutive vendor arrays in topology 70262306a36Sopenharmony_ci * @array_size: total size of @array 70362306a36Sopenharmony_ci * @token_instance_num: number of times the same tokens needs to be parsed i.e. the function 70462306a36Sopenharmony_ci * looks for @token_instance_num of each token in the @tokens 70562306a36Sopenharmony_ci * @object_size: offset to next target ipc struct with multiple sets 70662306a36Sopenharmony_ci * 70762306a36Sopenharmony_ci * This function parses multiple sets of tokens in vendor arrays into 70862306a36Sopenharmony_ci * consecutive ipc structs. 70962306a36Sopenharmony_ci */ 71062306a36Sopenharmony_cistatic int sof_parse_token_sets(struct snd_soc_component *scomp, 71162306a36Sopenharmony_ci void *object, const struct sof_topology_token *tokens, 71262306a36Sopenharmony_ci int count, struct snd_soc_tplg_vendor_array *array, 71362306a36Sopenharmony_ci int array_size, int token_instance_num, size_t object_size) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci size_t offset = 0; 71662306a36Sopenharmony_ci int found = 0; 71762306a36Sopenharmony_ci int total = 0; 71862306a36Sopenharmony_ci int asize; 71962306a36Sopenharmony_ci int ret; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci while (array_size > 0 && total < count * token_instance_num) { 72262306a36Sopenharmony_ci asize = le32_to_cpu(array->size); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* validate asize */ 72562306a36Sopenharmony_ci if (asize < 0) { /* FIXME: A zero-size array makes no sense */ 72662306a36Sopenharmony_ci dev_err(scomp->dev, "error: invalid array size 0x%x\n", 72762306a36Sopenharmony_ci asize); 72862306a36Sopenharmony_ci return -EINVAL; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* make sure there is enough data before parsing */ 73262306a36Sopenharmony_ci array_size -= asize; 73362306a36Sopenharmony_ci if (array_size < 0) { 73462306a36Sopenharmony_ci dev_err(scomp->dev, "error: invalid array size 0x%x\n", 73562306a36Sopenharmony_ci asize); 73662306a36Sopenharmony_ci return -EINVAL; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* call correct parser depending on type */ 74062306a36Sopenharmony_ci switch (le32_to_cpu(array->type)) { 74162306a36Sopenharmony_ci case SND_SOC_TPLG_TUPLE_TYPE_UUID: 74262306a36Sopenharmony_ci found += sof_parse_uuid_tokens(scomp, object, offset, tokens, count, 74362306a36Sopenharmony_ci array); 74462306a36Sopenharmony_ci break; 74562306a36Sopenharmony_ci case SND_SOC_TPLG_TUPLE_TYPE_STRING: 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci ret = sof_parse_string_tokens(scomp, object, offset, tokens, count, 74862306a36Sopenharmony_ci array); 74962306a36Sopenharmony_ci if (ret < 0) { 75062306a36Sopenharmony_ci dev_err(scomp->dev, "error: no memory to copy string token\n"); 75162306a36Sopenharmony_ci return ret; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci found += ret; 75562306a36Sopenharmony_ci break; 75662306a36Sopenharmony_ci case SND_SOC_TPLG_TUPLE_TYPE_BOOL: 75762306a36Sopenharmony_ci case SND_SOC_TPLG_TUPLE_TYPE_BYTE: 75862306a36Sopenharmony_ci case SND_SOC_TPLG_TUPLE_TYPE_WORD: 75962306a36Sopenharmony_ci case SND_SOC_TPLG_TUPLE_TYPE_SHORT: 76062306a36Sopenharmony_ci found += sof_parse_word_tokens(scomp, object, offset, tokens, count, 76162306a36Sopenharmony_ci array); 76262306a36Sopenharmony_ci break; 76362306a36Sopenharmony_ci default: 76462306a36Sopenharmony_ci dev_err(scomp->dev, "error: unknown token type %d\n", 76562306a36Sopenharmony_ci array->type); 76662306a36Sopenharmony_ci return -EINVAL; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* next array */ 77062306a36Sopenharmony_ci array = (struct snd_soc_tplg_vendor_array *)((u8 *)array 77162306a36Sopenharmony_ci + asize); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /* move to next target struct */ 77462306a36Sopenharmony_ci if (found >= count) { 77562306a36Sopenharmony_ci offset += object_size; 77662306a36Sopenharmony_ci total += found; 77762306a36Sopenharmony_ci found = 0; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci return 0; 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci/** 78562306a36Sopenharmony_ci * sof_parse_tokens - Parse one set of tokens 78662306a36Sopenharmony_ci * @scomp: pointer to soc component 78762306a36Sopenharmony_ci * @object: target ipc struct for parsed values 78862306a36Sopenharmony_ci * @tokens: token definition array describing what tokens to parse 78962306a36Sopenharmony_ci * @num_tokens: number of tokens in definition array 79062306a36Sopenharmony_ci * @array: source pointer to consecutive vendor arrays in topology 79162306a36Sopenharmony_ci * @array_size: total size of @array 79262306a36Sopenharmony_ci * 79362306a36Sopenharmony_ci * This function parses a single set of tokens in vendor arrays into 79462306a36Sopenharmony_ci * consecutive ipc structs. 79562306a36Sopenharmony_ci */ 79662306a36Sopenharmony_cistatic int sof_parse_tokens(struct snd_soc_component *scomp, void *object, 79762306a36Sopenharmony_ci const struct sof_topology_token *tokens, int num_tokens, 79862306a36Sopenharmony_ci struct snd_soc_tplg_vendor_array *array, 79962306a36Sopenharmony_ci int array_size) 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci /* 80362306a36Sopenharmony_ci * sof_parse_tokens is used when topology contains only a single set of 80462306a36Sopenharmony_ci * identical tuples arrays. So additional parameters to 80562306a36Sopenharmony_ci * sof_parse_token_sets are sets = 1 (only 1 set) and 80662306a36Sopenharmony_ci * object_size = 0 (irrelevant). 80762306a36Sopenharmony_ci */ 80862306a36Sopenharmony_ci return sof_parse_token_sets(scomp, object, tokens, num_tokens, array, 80962306a36Sopenharmony_ci array_size, 1, 0); 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci/* 81362306a36Sopenharmony_ci * Standard Kcontrols. 81462306a36Sopenharmony_ci */ 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic int sof_control_load_volume(struct snd_soc_component *scomp, 81762306a36Sopenharmony_ci struct snd_sof_control *scontrol, 81862306a36Sopenharmony_ci struct snd_kcontrol_new *kc, 81962306a36Sopenharmony_ci struct snd_soc_tplg_ctl_hdr *hdr) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 82262306a36Sopenharmony_ci struct snd_soc_tplg_mixer_control *mc = 82362306a36Sopenharmony_ci container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); 82462306a36Sopenharmony_ci int tlv[SOF_TLV_ITEMS]; 82562306a36Sopenharmony_ci unsigned int mask; 82662306a36Sopenharmony_ci int ret; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* validate topology data */ 82962306a36Sopenharmony_ci if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) 83062306a36Sopenharmony_ci return -EINVAL; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /* 83362306a36Sopenharmony_ci * If control has more than 2 channels we need to override the info. This is because even if 83462306a36Sopenharmony_ci * ASoC layer has defined topology's max channel count to SND_SOC_TPLG_MAX_CHAN = 8, the 83562306a36Sopenharmony_ci * pre-defined dapm control types (and related functions) creating the actual control 83662306a36Sopenharmony_ci * restrict the channels only to mono or stereo. 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_ci if (le32_to_cpu(mc->num_channels) > 2) 83962306a36Sopenharmony_ci kc->info = snd_sof_volume_info; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci scontrol->comp_id = sdev->next_comp_id; 84262306a36Sopenharmony_ci scontrol->min_volume_step = le32_to_cpu(mc->min); 84362306a36Sopenharmony_ci scontrol->max_volume_step = le32_to_cpu(mc->max); 84462306a36Sopenharmony_ci scontrol->num_channels = le32_to_cpu(mc->num_channels); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci scontrol->max = le32_to_cpu(mc->max); 84762306a36Sopenharmony_ci if (le32_to_cpu(mc->max) == 1) 84862306a36Sopenharmony_ci goto skip; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* extract tlv data */ 85162306a36Sopenharmony_ci if (!kc->tlv.p || get_tlv_data(kc->tlv.p, tlv) < 0) { 85262306a36Sopenharmony_ci dev_err(scomp->dev, "error: invalid TLV data\n"); 85362306a36Sopenharmony_ci return -EINVAL; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* set up volume table */ 85762306a36Sopenharmony_ci ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1); 85862306a36Sopenharmony_ci if (ret < 0) { 85962306a36Sopenharmony_ci dev_err(scomp->dev, "error: setting up volume table\n"); 86062306a36Sopenharmony_ci return ret; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ciskip: 86462306a36Sopenharmony_ci /* set up possible led control from mixer private data */ 86562306a36Sopenharmony_ci ret = sof_parse_tokens(scomp, &scontrol->led_ctl, led_tokens, 86662306a36Sopenharmony_ci ARRAY_SIZE(led_tokens), mc->priv.array, 86762306a36Sopenharmony_ci le32_to_cpu(mc->priv.size)); 86862306a36Sopenharmony_ci if (ret != 0) { 86962306a36Sopenharmony_ci dev_err(scomp->dev, "error: parse led tokens failed %d\n", 87062306a36Sopenharmony_ci le32_to_cpu(mc->priv.size)); 87162306a36Sopenharmony_ci goto err; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (scontrol->led_ctl.use_led) { 87562306a36Sopenharmony_ci mask = scontrol->led_ctl.direction ? SNDRV_CTL_ELEM_ACCESS_MIC_LED : 87662306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_SPK_LED; 87762306a36Sopenharmony_ci scontrol->access &= ~SNDRV_CTL_ELEM_ACCESS_LED_MASK; 87862306a36Sopenharmony_ci scontrol->access |= mask; 87962306a36Sopenharmony_ci kc->access &= ~SNDRV_CTL_ELEM_ACCESS_LED_MASK; 88062306a36Sopenharmony_ci kc->access |= mask; 88162306a36Sopenharmony_ci sdev->led_present = true; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", 88562306a36Sopenharmony_ci scontrol->comp_id, scontrol->num_channels); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_cierr: 89062306a36Sopenharmony_ci if (le32_to_cpu(mc->max) > 1) 89162306a36Sopenharmony_ci kfree(scontrol->volume_table); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci return ret; 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic int sof_control_load_enum(struct snd_soc_component *scomp, 89762306a36Sopenharmony_ci struct snd_sof_control *scontrol, 89862306a36Sopenharmony_ci struct snd_kcontrol_new *kc, 89962306a36Sopenharmony_ci struct snd_soc_tplg_ctl_hdr *hdr) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 90262306a36Sopenharmony_ci struct snd_soc_tplg_enum_control *ec = 90362306a36Sopenharmony_ci container_of(hdr, struct snd_soc_tplg_enum_control, hdr); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci /* validate topology data */ 90662306a36Sopenharmony_ci if (le32_to_cpu(ec->num_channels) > SND_SOC_TPLG_MAX_CHAN) 90762306a36Sopenharmony_ci return -EINVAL; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci scontrol->comp_id = sdev->next_comp_id; 91062306a36Sopenharmony_ci scontrol->num_channels = le32_to_cpu(ec->num_channels); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", 91362306a36Sopenharmony_ci scontrol->comp_id, scontrol->num_channels, scontrol->comp_id); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci return 0; 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic int sof_control_load_bytes(struct snd_soc_component *scomp, 91962306a36Sopenharmony_ci struct snd_sof_control *scontrol, 92062306a36Sopenharmony_ci struct snd_kcontrol_new *kc, 92162306a36Sopenharmony_ci struct snd_soc_tplg_ctl_hdr *hdr) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 92462306a36Sopenharmony_ci struct snd_soc_tplg_bytes_control *control = 92562306a36Sopenharmony_ci container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); 92662306a36Sopenharmony_ci struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value; 92762306a36Sopenharmony_ci size_t priv_size = le32_to_cpu(control->priv.size); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci scontrol->max_size = sbe->max; 93062306a36Sopenharmony_ci scontrol->comp_id = sdev->next_comp_id; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci dev_dbg(scomp->dev, "tplg: load kcontrol index %d\n", scontrol->comp_id); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci /* copy the private data */ 93562306a36Sopenharmony_ci if (priv_size > 0) { 93662306a36Sopenharmony_ci scontrol->priv = kmemdup(control->priv.data, priv_size, GFP_KERNEL); 93762306a36Sopenharmony_ci if (!scontrol->priv) 93862306a36Sopenharmony_ci return -ENOMEM; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci scontrol->priv_size = priv_size; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci return 0; 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci/* external kcontrol init - used for any driver specific init */ 94762306a36Sopenharmony_cistatic int sof_control_load(struct snd_soc_component *scomp, int index, 94862306a36Sopenharmony_ci struct snd_kcontrol_new *kc, 94962306a36Sopenharmony_ci struct snd_soc_tplg_ctl_hdr *hdr) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci struct soc_mixer_control *sm; 95262306a36Sopenharmony_ci struct soc_bytes_ext *sbe; 95362306a36Sopenharmony_ci struct soc_enum *se; 95462306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 95562306a36Sopenharmony_ci struct snd_soc_dobj *dobj; 95662306a36Sopenharmony_ci struct snd_sof_control *scontrol; 95762306a36Sopenharmony_ci int ret; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci dev_dbg(scomp->dev, "tplg: load control type %d name : %s\n", 96062306a36Sopenharmony_ci hdr->type, hdr->name); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL); 96362306a36Sopenharmony_ci if (!scontrol) 96462306a36Sopenharmony_ci return -ENOMEM; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci scontrol->name = kstrdup(hdr->name, GFP_KERNEL); 96762306a36Sopenharmony_ci if (!scontrol->name) { 96862306a36Sopenharmony_ci kfree(scontrol); 96962306a36Sopenharmony_ci return -ENOMEM; 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci scontrol->scomp = scomp; 97362306a36Sopenharmony_ci scontrol->access = kc->access; 97462306a36Sopenharmony_ci scontrol->info_type = le32_to_cpu(hdr->ops.info); 97562306a36Sopenharmony_ci scontrol->index = kc->index; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci switch (le32_to_cpu(hdr->ops.info)) { 97862306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW: 97962306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW_SX: 98062306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_VOLSW_XR_SX: 98162306a36Sopenharmony_ci sm = (struct soc_mixer_control *)kc->private_value; 98262306a36Sopenharmony_ci dobj = &sm->dobj; 98362306a36Sopenharmony_ci ret = sof_control_load_volume(scomp, scontrol, kc, hdr); 98462306a36Sopenharmony_ci break; 98562306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_BYTES: 98662306a36Sopenharmony_ci sbe = (struct soc_bytes_ext *)kc->private_value; 98762306a36Sopenharmony_ci dobj = &sbe->dobj; 98862306a36Sopenharmony_ci ret = sof_control_load_bytes(scomp, scontrol, kc, hdr); 98962306a36Sopenharmony_ci break; 99062306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM: 99162306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_ENUM_VALUE: 99262306a36Sopenharmony_ci se = (struct soc_enum *)kc->private_value; 99362306a36Sopenharmony_ci dobj = &se->dobj; 99462306a36Sopenharmony_ci ret = sof_control_load_enum(scomp, scontrol, kc, hdr); 99562306a36Sopenharmony_ci break; 99662306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_RANGE: 99762306a36Sopenharmony_ci case SND_SOC_TPLG_CTL_STROBE: 99862306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_VOLSW: 99962306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE: 100062306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT: 100162306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE: 100262306a36Sopenharmony_ci case SND_SOC_TPLG_DAPM_CTL_PIN: 100362306a36Sopenharmony_ci default: 100462306a36Sopenharmony_ci dev_warn(scomp->dev, "control type not supported %d:%d:%d\n", 100562306a36Sopenharmony_ci hdr->ops.get, hdr->ops.put, hdr->ops.info); 100662306a36Sopenharmony_ci kfree(scontrol->name); 100762306a36Sopenharmony_ci kfree(scontrol); 100862306a36Sopenharmony_ci return 0; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci if (ret < 0) { 101262306a36Sopenharmony_ci kfree(scontrol->name); 101362306a36Sopenharmony_ci kfree(scontrol); 101462306a36Sopenharmony_ci return ret; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci scontrol->led_ctl.led_value = -1; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci dobj->private = scontrol; 102062306a36Sopenharmony_ci list_add(&scontrol->list, &sdev->kcontrol_list); 102162306a36Sopenharmony_ci return 0; 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cistatic int sof_control_unload(struct snd_soc_component *scomp, 102562306a36Sopenharmony_ci struct snd_soc_dobj *dobj) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 102862306a36Sopenharmony_ci const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 102962306a36Sopenharmony_ci struct snd_sof_control *scontrol = dobj->private; 103062306a36Sopenharmony_ci int ret = 0; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scontrol->name); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (tplg_ops && tplg_ops->control_free) { 103562306a36Sopenharmony_ci ret = tplg_ops->control_free(sdev, scontrol); 103662306a36Sopenharmony_ci if (ret < 0) 103762306a36Sopenharmony_ci dev_err(scomp->dev, "failed to free control: %s\n", scontrol->name); 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci /* free all data before returning in case of error too */ 104162306a36Sopenharmony_ci kfree(scontrol->ipc_control_data); 104262306a36Sopenharmony_ci kfree(scontrol->priv); 104362306a36Sopenharmony_ci kfree(scontrol->name); 104462306a36Sopenharmony_ci list_del(&scontrol->list); 104562306a36Sopenharmony_ci kfree(scontrol); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci return ret; 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci/* 105162306a36Sopenharmony_ci * DAI Topology 105262306a36Sopenharmony_ci */ 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic int sof_connect_dai_widget(struct snd_soc_component *scomp, 105562306a36Sopenharmony_ci struct snd_soc_dapm_widget *w, 105662306a36Sopenharmony_ci struct snd_soc_tplg_dapm_widget *tw, 105762306a36Sopenharmony_ci struct snd_sof_dai *dai) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci struct snd_soc_card *card = scomp->card; 106062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 106162306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai; 106262306a36Sopenharmony_ci int stream; 106362306a36Sopenharmony_ci int i; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (!w->sname) { 106662306a36Sopenharmony_ci dev_err(scomp->dev, "Widget %s does not have stream\n", w->name); 106762306a36Sopenharmony_ci return -EINVAL; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (w->id == snd_soc_dapm_dai_out) 107162306a36Sopenharmony_ci stream = SNDRV_PCM_STREAM_CAPTURE; 107262306a36Sopenharmony_ci else if (w->id == snd_soc_dapm_dai_in) 107362306a36Sopenharmony_ci stream = SNDRV_PCM_STREAM_PLAYBACK; 107462306a36Sopenharmony_ci else 107562306a36Sopenharmony_ci goto end; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci list_for_each_entry(rtd, &card->rtd_list, list) { 107862306a36Sopenharmony_ci /* does stream match DAI link ? */ 107962306a36Sopenharmony_ci if (!rtd->dai_link->stream_name || 108062306a36Sopenharmony_ci !strstr(rtd->dai_link->stream_name, w->sname)) 108162306a36Sopenharmony_ci continue; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) { 108462306a36Sopenharmony_ci /* 108562306a36Sopenharmony_ci * Please create DAI widget in the right order 108662306a36Sopenharmony_ci * to ensure BE will connect to the right DAI 108762306a36Sopenharmony_ci * widget. 108862306a36Sopenharmony_ci */ 108962306a36Sopenharmony_ci if (!snd_soc_dai_get_widget(cpu_dai, stream)) { 109062306a36Sopenharmony_ci snd_soc_dai_set_widget(cpu_dai, stream, w); 109162306a36Sopenharmony_ci break; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci if (i == rtd->dai_link->num_cpus) { 109562306a36Sopenharmony_ci dev_err(scomp->dev, "error: can't find BE for DAI %s\n", w->name); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci return -EINVAL; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci dai->name = rtd->dai_link->name; 110162306a36Sopenharmony_ci dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n", 110262306a36Sopenharmony_ci w->name, rtd->dai_link->name); 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ciend: 110562306a36Sopenharmony_ci /* check we have a connection */ 110662306a36Sopenharmony_ci if (!dai->name) { 110762306a36Sopenharmony_ci dev_err(scomp->dev, "error: can't connect DAI %s stream %s\n", 110862306a36Sopenharmony_ci w->name, w->sname); 110962306a36Sopenharmony_ci return -EINVAL; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci return 0; 111362306a36Sopenharmony_ci} 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic void sof_disconnect_dai_widget(struct snd_soc_component *scomp, 111662306a36Sopenharmony_ci struct snd_soc_dapm_widget *w) 111762306a36Sopenharmony_ci{ 111862306a36Sopenharmony_ci struct snd_soc_card *card = scomp->card; 111962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd; 112062306a36Sopenharmony_ci const char *sname = w->sname; 112162306a36Sopenharmony_ci struct snd_soc_dai *cpu_dai; 112262306a36Sopenharmony_ci int i, stream; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci if (!sname) 112562306a36Sopenharmony_ci return; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (w->id == snd_soc_dapm_dai_out) 112862306a36Sopenharmony_ci stream = SNDRV_PCM_STREAM_CAPTURE; 112962306a36Sopenharmony_ci else if (w->id == snd_soc_dapm_dai_in) 113062306a36Sopenharmony_ci stream = SNDRV_PCM_STREAM_PLAYBACK; 113162306a36Sopenharmony_ci else 113262306a36Sopenharmony_ci return; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci list_for_each_entry(rtd, &card->rtd_list, list) { 113562306a36Sopenharmony_ci /* does stream match DAI link ? */ 113662306a36Sopenharmony_ci if (!rtd->dai_link->stream_name || 113762306a36Sopenharmony_ci !strstr(rtd->dai_link->stream_name, sname)) 113862306a36Sopenharmony_ci continue; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci for_each_rtd_cpu_dais(rtd, i, cpu_dai) 114162306a36Sopenharmony_ci if (snd_soc_dai_get_widget(cpu_dai, stream) == w) { 114262306a36Sopenharmony_ci snd_soc_dai_set_widget(cpu_dai, stream, NULL); 114362306a36Sopenharmony_ci break; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci} 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci/* bind PCM ID to host component ID */ 114962306a36Sopenharmony_cistatic int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, 115062306a36Sopenharmony_ci int dir) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 115362306a36Sopenharmony_ci struct snd_sof_widget *host_widget; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci if (sdev->dspless_mode_selected) 115662306a36Sopenharmony_ci return 0; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci host_widget = snd_sof_find_swidget_sname(scomp, 115962306a36Sopenharmony_ci spcm->pcm.caps[dir].name, 116062306a36Sopenharmony_ci dir); 116162306a36Sopenharmony_ci if (!host_widget) { 116262306a36Sopenharmony_ci dev_err(scomp->dev, "can't find host comp to bind pcm\n"); 116362306a36Sopenharmony_ci return -EINVAL; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci spcm->stream[dir].comp_id = host_widget->comp_id; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci return 0; 116962306a36Sopenharmony_ci} 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_cistatic int sof_get_token_value(u32 token_id, struct snd_sof_tuple *tuples, int num_tuples) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci int i; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if (!tuples) 117662306a36Sopenharmony_ci return -EINVAL; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci for (i = 0; i < num_tuples; i++) { 117962306a36Sopenharmony_ci if (tuples[i].token == token_id) 118062306a36Sopenharmony_ci return tuples[i].value.v; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci return -EINVAL; 118462306a36Sopenharmony_ci} 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_cistatic int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_sof_widget *swidget, 118762306a36Sopenharmony_ci struct snd_soc_tplg_dapm_widget *tw, 118862306a36Sopenharmony_ci enum sof_tokens *object_token_list, int count) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 119162306a36Sopenharmony_ci const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 119262306a36Sopenharmony_ci struct snd_soc_tplg_private *private = &tw->priv; 119362306a36Sopenharmony_ci const struct sof_token_info *token_list; 119462306a36Sopenharmony_ci int num_tuples = 0; 119562306a36Sopenharmony_ci int ret, i; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci token_list = tplg_ops ? tplg_ops->token_list : NULL; 119862306a36Sopenharmony_ci /* nothing to do if token_list is NULL */ 119962306a36Sopenharmony_ci if (!token_list) 120062306a36Sopenharmony_ci return 0; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci if (count > 0 && !object_token_list) { 120362306a36Sopenharmony_ci dev_err(scomp->dev, "No token list for widget %s\n", swidget->widget->name); 120462306a36Sopenharmony_ci return -EINVAL; 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci /* calculate max size of tuples array */ 120862306a36Sopenharmony_ci for (i = 0; i < count; i++) 120962306a36Sopenharmony_ci num_tuples += token_list[object_token_list[i]].count; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci /* allocate memory for tuples array */ 121262306a36Sopenharmony_ci swidget->tuples = kcalloc(num_tuples, sizeof(*swidget->tuples), GFP_KERNEL); 121362306a36Sopenharmony_ci if (!swidget->tuples) 121462306a36Sopenharmony_ci return -ENOMEM; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci /* parse token list for widget */ 121762306a36Sopenharmony_ci for (i = 0; i < count; i++) { 121862306a36Sopenharmony_ci int num_sets = 1; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci if (object_token_list[i] >= SOF_TOKEN_COUNT) { 122162306a36Sopenharmony_ci dev_err(scomp->dev, "Invalid token id %d for widget %s\n", 122262306a36Sopenharmony_ci object_token_list[i], swidget->widget->name); 122362306a36Sopenharmony_ci ret = -EINVAL; 122462306a36Sopenharmony_ci goto err; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci switch (object_token_list[i]) { 122862306a36Sopenharmony_ci case SOF_COMP_EXT_TOKENS: 122962306a36Sopenharmony_ci /* parse and save UUID in swidget */ 123062306a36Sopenharmony_ci ret = sof_parse_tokens(scomp, swidget, 123162306a36Sopenharmony_ci token_list[object_token_list[i]].tokens, 123262306a36Sopenharmony_ci token_list[object_token_list[i]].count, 123362306a36Sopenharmony_ci private->array, le32_to_cpu(private->size)); 123462306a36Sopenharmony_ci if (ret < 0) { 123562306a36Sopenharmony_ci dev_err(scomp->dev, "Failed parsing %s for widget %s\n", 123662306a36Sopenharmony_ci token_list[object_token_list[i]].name, 123762306a36Sopenharmony_ci swidget->widget->name); 123862306a36Sopenharmony_ci goto err; 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci continue; 124262306a36Sopenharmony_ci case SOF_IN_AUDIO_FORMAT_TOKENS: 124362306a36Sopenharmony_ci num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS, 124462306a36Sopenharmony_ci swidget->tuples, swidget->num_tuples); 124562306a36Sopenharmony_ci if (num_sets < 0) { 124662306a36Sopenharmony_ci dev_err(sdev->dev, "Invalid input audio format count for %s\n", 124762306a36Sopenharmony_ci swidget->widget->name); 124862306a36Sopenharmony_ci ret = num_sets; 124962306a36Sopenharmony_ci goto err; 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci break; 125262306a36Sopenharmony_ci case SOF_OUT_AUDIO_FORMAT_TOKENS: 125362306a36Sopenharmony_ci num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS, 125462306a36Sopenharmony_ci swidget->tuples, swidget->num_tuples); 125562306a36Sopenharmony_ci if (num_sets < 0) { 125662306a36Sopenharmony_ci dev_err(sdev->dev, "Invalid output audio format count for %s\n", 125762306a36Sopenharmony_ci swidget->widget->name); 125862306a36Sopenharmony_ci ret = num_sets; 125962306a36Sopenharmony_ci goto err; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci break; 126262306a36Sopenharmony_ci default: 126362306a36Sopenharmony_ci break; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci if (num_sets > 1) { 126762306a36Sopenharmony_ci struct snd_sof_tuple *new_tuples; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci num_tuples += token_list[object_token_list[i]].count * (num_sets - 1); 127062306a36Sopenharmony_ci new_tuples = krealloc(swidget->tuples, 127162306a36Sopenharmony_ci sizeof(*new_tuples) * num_tuples, GFP_KERNEL); 127262306a36Sopenharmony_ci if (!new_tuples) { 127362306a36Sopenharmony_ci ret = -ENOMEM; 127462306a36Sopenharmony_ci goto err; 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci swidget->tuples = new_tuples; 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci /* copy one set of tuples per token ID into swidget->tuples */ 128162306a36Sopenharmony_ci ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), 128262306a36Sopenharmony_ci object_token_list[i], num_sets, swidget->tuples, 128362306a36Sopenharmony_ci num_tuples, &swidget->num_tuples); 128462306a36Sopenharmony_ci if (ret < 0) { 128562306a36Sopenharmony_ci dev_err(scomp->dev, "Failed parsing %s for widget %s err: %d\n", 128662306a36Sopenharmony_ci token_list[object_token_list[i]].name, swidget->widget->name, ret); 128762306a36Sopenharmony_ci goto err; 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci return 0; 129262306a36Sopenharmony_cierr: 129362306a36Sopenharmony_ci kfree(swidget->tuples); 129462306a36Sopenharmony_ci return ret; 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic void sof_free_pin_binding(struct snd_sof_widget *swidget, 129862306a36Sopenharmony_ci bool pin_type) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci char **pin_binding; 130162306a36Sopenharmony_ci u32 num_pins; 130262306a36Sopenharmony_ci int i; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci if (pin_type == SOF_PIN_TYPE_INPUT) { 130562306a36Sopenharmony_ci pin_binding = swidget->input_pin_binding; 130662306a36Sopenharmony_ci num_pins = swidget->num_input_pins; 130762306a36Sopenharmony_ci } else { 130862306a36Sopenharmony_ci pin_binding = swidget->output_pin_binding; 130962306a36Sopenharmony_ci num_pins = swidget->num_output_pins; 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci if (pin_binding) { 131362306a36Sopenharmony_ci for (i = 0; i < num_pins; i++) 131462306a36Sopenharmony_ci kfree(pin_binding[i]); 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci kfree(pin_binding); 131862306a36Sopenharmony_ci} 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_cistatic int sof_parse_pin_binding(struct snd_sof_widget *swidget, 132162306a36Sopenharmony_ci struct snd_soc_tplg_private *priv, bool pin_type) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci const struct sof_topology_token *pin_binding_token; 132462306a36Sopenharmony_ci char *pin_binding[SOF_WIDGET_MAX_NUM_PINS]; 132562306a36Sopenharmony_ci int token_count; 132662306a36Sopenharmony_ci u32 num_pins; 132762306a36Sopenharmony_ci char **pb; 132862306a36Sopenharmony_ci int ret; 132962306a36Sopenharmony_ci int i; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci if (pin_type == SOF_PIN_TYPE_INPUT) { 133262306a36Sopenharmony_ci num_pins = swidget->num_input_pins; 133362306a36Sopenharmony_ci pin_binding_token = comp_input_pin_binding_tokens; 133462306a36Sopenharmony_ci token_count = ARRAY_SIZE(comp_input_pin_binding_tokens); 133562306a36Sopenharmony_ci } else { 133662306a36Sopenharmony_ci num_pins = swidget->num_output_pins; 133762306a36Sopenharmony_ci pin_binding_token = comp_output_pin_binding_tokens; 133862306a36Sopenharmony_ci token_count = ARRAY_SIZE(comp_output_pin_binding_tokens); 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci memset(pin_binding, 0, SOF_WIDGET_MAX_NUM_PINS * sizeof(char *)); 134262306a36Sopenharmony_ci ret = sof_parse_token_sets(swidget->scomp, pin_binding, pin_binding_token, 134362306a36Sopenharmony_ci token_count, priv->array, le32_to_cpu(priv->size), 134462306a36Sopenharmony_ci num_pins, sizeof(char *)); 134562306a36Sopenharmony_ci if (ret < 0) 134662306a36Sopenharmony_ci goto err; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* copy pin binding array to swidget only if it is defined in topology */ 134962306a36Sopenharmony_ci if (pin_binding[0]) { 135062306a36Sopenharmony_ci pb = kmemdup(pin_binding, num_pins * sizeof(char *), GFP_KERNEL); 135162306a36Sopenharmony_ci if (!pb) { 135262306a36Sopenharmony_ci ret = -ENOMEM; 135362306a36Sopenharmony_ci goto err; 135462306a36Sopenharmony_ci } 135562306a36Sopenharmony_ci if (pin_type == SOF_PIN_TYPE_INPUT) 135662306a36Sopenharmony_ci swidget->input_pin_binding = pb; 135762306a36Sopenharmony_ci else 135862306a36Sopenharmony_ci swidget->output_pin_binding = pb; 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci return 0; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cierr: 136462306a36Sopenharmony_ci for (i = 0; i < num_pins; i++) 136562306a36Sopenharmony_ci kfree(pin_binding[i]); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci return ret; 136862306a36Sopenharmony_ci} 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_cistatic int get_w_no_wname_in_long_name(void *elem, void *object, u32 offset) 137162306a36Sopenharmony_ci{ 137262306a36Sopenharmony_ci struct snd_soc_tplg_vendor_value_elem *velem = elem; 137362306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = object; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci w->no_wname_in_kcontrol_name = !!le32_to_cpu(velem->value); 137662306a36Sopenharmony_ci return 0; 137762306a36Sopenharmony_ci} 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_cistatic const struct sof_topology_token dapm_widget_tokens[] = { 138062306a36Sopenharmony_ci {SOF_TKN_COMP_NO_WNAME_IN_KCONTROL_NAME, SND_SOC_TPLG_TUPLE_TYPE_BOOL, 138162306a36Sopenharmony_ci get_w_no_wname_in_long_name, 0} 138262306a36Sopenharmony_ci}; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci/* external widget init - used for any driver specific init */ 138562306a36Sopenharmony_cistatic int sof_widget_ready(struct snd_soc_component *scomp, int index, 138662306a36Sopenharmony_ci struct snd_soc_dapm_widget *w, 138762306a36Sopenharmony_ci struct snd_soc_tplg_dapm_widget *tw) 138862306a36Sopenharmony_ci{ 138962306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 139062306a36Sopenharmony_ci const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 139162306a36Sopenharmony_ci const struct sof_ipc_tplg_widget_ops *widget_ops; 139262306a36Sopenharmony_ci struct snd_soc_tplg_private *priv = &tw->priv; 139362306a36Sopenharmony_ci enum sof_tokens *token_list = NULL; 139462306a36Sopenharmony_ci struct snd_sof_widget *swidget; 139562306a36Sopenharmony_ci struct snd_sof_dai *dai; 139662306a36Sopenharmony_ci int token_list_size = 0; 139762306a36Sopenharmony_ci int ret = 0; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); 140062306a36Sopenharmony_ci if (!swidget) 140162306a36Sopenharmony_ci return -ENOMEM; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci swidget->scomp = scomp; 140462306a36Sopenharmony_ci swidget->widget = w; 140562306a36Sopenharmony_ci swidget->comp_id = sdev->next_comp_id++; 140662306a36Sopenharmony_ci swidget->id = w->id; 140762306a36Sopenharmony_ci swidget->pipeline_id = index; 140862306a36Sopenharmony_ci swidget->private = NULL; 140962306a36Sopenharmony_ci mutex_init(&swidget->setup_mutex); 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci ida_init(&swidget->output_queue_ida); 141262306a36Sopenharmony_ci ida_init(&swidget->input_queue_ida); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci ret = sof_parse_tokens(scomp, w, dapm_widget_tokens, ARRAY_SIZE(dapm_widget_tokens), 141562306a36Sopenharmony_ci priv->array, le32_to_cpu(priv->size)); 141662306a36Sopenharmony_ci if (ret < 0) { 141762306a36Sopenharmony_ci dev_err(scomp->dev, "failed to parse dapm widget tokens for %s\n", 141862306a36Sopenharmony_ci w->name); 141962306a36Sopenharmony_ci goto widget_free; 142062306a36Sopenharmony_ci } 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci ret = sof_parse_tokens(scomp, swidget, comp_pin_tokens, 142362306a36Sopenharmony_ci ARRAY_SIZE(comp_pin_tokens), priv->array, 142462306a36Sopenharmony_ci le32_to_cpu(priv->size)); 142562306a36Sopenharmony_ci if (ret < 0) { 142662306a36Sopenharmony_ci dev_err(scomp->dev, "failed to parse component pin tokens for %s\n", 142762306a36Sopenharmony_ci w->name); 142862306a36Sopenharmony_ci goto widget_free; 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci if (swidget->num_input_pins > SOF_WIDGET_MAX_NUM_PINS || 143262306a36Sopenharmony_ci swidget->num_output_pins > SOF_WIDGET_MAX_NUM_PINS) { 143362306a36Sopenharmony_ci dev_err(scomp->dev, "invalid pins for %s: [input: %d, output: %d]\n", 143462306a36Sopenharmony_ci swidget->widget->name, swidget->num_input_pins, swidget->num_output_pins); 143562306a36Sopenharmony_ci ret = -EINVAL; 143662306a36Sopenharmony_ci goto widget_free; 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci if (swidget->num_input_pins > 1) { 144062306a36Sopenharmony_ci ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_INPUT); 144162306a36Sopenharmony_ci /* on parsing error, pin binding is not allocated, nothing to free. */ 144262306a36Sopenharmony_ci if (ret < 0) { 144362306a36Sopenharmony_ci dev_err(scomp->dev, "failed to parse input pin binding for %s\n", 144462306a36Sopenharmony_ci w->name); 144562306a36Sopenharmony_ci goto widget_free; 144662306a36Sopenharmony_ci } 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci if (swidget->num_output_pins > 1) { 145062306a36Sopenharmony_ci ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_OUTPUT); 145162306a36Sopenharmony_ci /* on parsing error, pin binding is not allocated, nothing to free. */ 145262306a36Sopenharmony_ci if (ret < 0) { 145362306a36Sopenharmony_ci dev_err(scomp->dev, "failed to parse output pin binding for %s\n", 145462306a36Sopenharmony_ci w->name); 145562306a36Sopenharmony_ci goto widget_free; 145662306a36Sopenharmony_ci } 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci dev_dbg(scomp->dev, 146062306a36Sopenharmony_ci "tplg: widget %d (%s) is ready [type: %d, pipe: %d, pins: %d / %d, stream: %s]\n", 146162306a36Sopenharmony_ci swidget->comp_id, w->name, swidget->id, index, 146262306a36Sopenharmony_ci swidget->num_input_pins, swidget->num_output_pins, 146362306a36Sopenharmony_ci strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 ? w->sname : "none"); 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci widget_ops = tplg_ops ? tplg_ops->widget : NULL; 146662306a36Sopenharmony_ci if (widget_ops) { 146762306a36Sopenharmony_ci token_list = widget_ops[w->id].token_list; 146862306a36Sopenharmony_ci token_list_size = widget_ops[w->id].token_list_size; 146962306a36Sopenharmony_ci } 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci /* handle any special case widgets */ 147262306a36Sopenharmony_ci switch (w->id) { 147362306a36Sopenharmony_ci case snd_soc_dapm_dai_in: 147462306a36Sopenharmony_ci case snd_soc_dapm_dai_out: 147562306a36Sopenharmony_ci dai = kzalloc(sizeof(*dai), GFP_KERNEL); 147662306a36Sopenharmony_ci if (!dai) { 147762306a36Sopenharmony_ci ret = -ENOMEM; 147862306a36Sopenharmony_ci goto widget_free; 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); 148262306a36Sopenharmony_ci if (!ret) 148362306a36Sopenharmony_ci ret = sof_connect_dai_widget(scomp, w, tw, dai); 148462306a36Sopenharmony_ci if (ret < 0) { 148562306a36Sopenharmony_ci kfree(dai); 148662306a36Sopenharmony_ci break; 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci list_add(&dai->list, &sdev->dai_list); 148962306a36Sopenharmony_ci swidget->private = dai; 149062306a36Sopenharmony_ci break; 149162306a36Sopenharmony_ci case snd_soc_dapm_effect: 149262306a36Sopenharmony_ci /* check we have some tokens - we need at least process type */ 149362306a36Sopenharmony_ci if (le32_to_cpu(tw->priv.size) == 0) { 149462306a36Sopenharmony_ci dev_err(scomp->dev, "error: process tokens not found\n"); 149562306a36Sopenharmony_ci ret = -EINVAL; 149662306a36Sopenharmony_ci break; 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); 149962306a36Sopenharmony_ci break; 150062306a36Sopenharmony_ci case snd_soc_dapm_pga: 150162306a36Sopenharmony_ci if (!le32_to_cpu(tw->num_kcontrols)) { 150262306a36Sopenharmony_ci dev_err(scomp->dev, "invalid kcontrol count %d for volume\n", 150362306a36Sopenharmony_ci tw->num_kcontrols); 150462306a36Sopenharmony_ci ret = -EINVAL; 150562306a36Sopenharmony_ci break; 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci fallthrough; 150962306a36Sopenharmony_ci case snd_soc_dapm_mixer: 151062306a36Sopenharmony_ci case snd_soc_dapm_buffer: 151162306a36Sopenharmony_ci case snd_soc_dapm_scheduler: 151262306a36Sopenharmony_ci case snd_soc_dapm_aif_out: 151362306a36Sopenharmony_ci case snd_soc_dapm_aif_in: 151462306a36Sopenharmony_ci case snd_soc_dapm_src: 151562306a36Sopenharmony_ci case snd_soc_dapm_asrc: 151662306a36Sopenharmony_ci case snd_soc_dapm_siggen: 151762306a36Sopenharmony_ci case snd_soc_dapm_mux: 151862306a36Sopenharmony_ci case snd_soc_dapm_demux: 151962306a36Sopenharmony_ci ret = sof_widget_parse_tokens(scomp, swidget, tw, token_list, token_list_size); 152062306a36Sopenharmony_ci break; 152162306a36Sopenharmony_ci case snd_soc_dapm_switch: 152262306a36Sopenharmony_ci case snd_soc_dapm_dai_link: 152362306a36Sopenharmony_ci case snd_soc_dapm_kcontrol: 152462306a36Sopenharmony_ci default: 152562306a36Sopenharmony_ci dev_dbg(scomp->dev, "widget type %d name %s not handled\n", swidget->id, tw->name); 152662306a36Sopenharmony_ci break; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci /* check token parsing reply */ 153062306a36Sopenharmony_ci if (ret < 0) { 153162306a36Sopenharmony_ci dev_err(scomp->dev, 153262306a36Sopenharmony_ci "error: failed to add widget id %d type %d name : %s stream %s\n", 153362306a36Sopenharmony_ci tw->shift, swidget->id, tw->name, 153462306a36Sopenharmony_ci strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 153562306a36Sopenharmony_ci ? tw->sname : "none"); 153662306a36Sopenharmony_ci goto widget_free; 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci if (sof_debug_check_flag(SOF_DBG_DISABLE_MULTICORE)) { 154062306a36Sopenharmony_ci swidget->core = SOF_DSP_PRIMARY_CORE; 154162306a36Sopenharmony_ci } else { 154262306a36Sopenharmony_ci int core = sof_get_token_value(SOF_TKN_COMP_CORE_ID, swidget->tuples, 154362306a36Sopenharmony_ci swidget->num_tuples); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci if (core >= 0) 154662306a36Sopenharmony_ci swidget->core = core; 154762306a36Sopenharmony_ci } 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci /* bind widget to external event */ 155062306a36Sopenharmony_ci if (tw->event_type) { 155162306a36Sopenharmony_ci if (widget_ops && widget_ops[w->id].bind_event) { 155262306a36Sopenharmony_ci ret = widget_ops[w->id].bind_event(scomp, swidget, 155362306a36Sopenharmony_ci le16_to_cpu(tw->event_type)); 155462306a36Sopenharmony_ci if (ret) { 155562306a36Sopenharmony_ci dev_err(scomp->dev, "widget event binding failed for %s\n", 155662306a36Sopenharmony_ci swidget->widget->name); 155762306a36Sopenharmony_ci goto free; 155862306a36Sopenharmony_ci } 155962306a36Sopenharmony_ci } 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci /* create and add pipeline for scheduler type widgets */ 156362306a36Sopenharmony_ci if (w->id == snd_soc_dapm_scheduler) { 156462306a36Sopenharmony_ci struct snd_sof_pipeline *spipe; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci spipe = kzalloc(sizeof(*spipe), GFP_KERNEL); 156762306a36Sopenharmony_ci if (!spipe) { 156862306a36Sopenharmony_ci ret = -ENOMEM; 156962306a36Sopenharmony_ci goto free; 157062306a36Sopenharmony_ci } 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci spipe->pipe_widget = swidget; 157362306a36Sopenharmony_ci swidget->spipe = spipe; 157462306a36Sopenharmony_ci list_add(&spipe->list, &sdev->pipeline_list); 157562306a36Sopenharmony_ci } 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci w->dobj.private = swidget; 157862306a36Sopenharmony_ci list_add(&swidget->list, &sdev->widget_list); 157962306a36Sopenharmony_ci return ret; 158062306a36Sopenharmony_cifree: 158162306a36Sopenharmony_ci kfree(swidget->private); 158262306a36Sopenharmony_ci kfree(swidget->tuples); 158362306a36Sopenharmony_ciwidget_free: 158462306a36Sopenharmony_ci kfree(swidget); 158562306a36Sopenharmony_ci return ret; 158662306a36Sopenharmony_ci} 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_cistatic int sof_route_unload(struct snd_soc_component *scomp, 158962306a36Sopenharmony_ci struct snd_soc_dobj *dobj) 159062306a36Sopenharmony_ci{ 159162306a36Sopenharmony_ci struct snd_sof_route *sroute; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci sroute = dobj->private; 159462306a36Sopenharmony_ci if (!sroute) 159562306a36Sopenharmony_ci return 0; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci /* free sroute and its private data */ 159862306a36Sopenharmony_ci kfree(sroute->private); 159962306a36Sopenharmony_ci list_del(&sroute->list); 160062306a36Sopenharmony_ci kfree(sroute); 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci return 0; 160362306a36Sopenharmony_ci} 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_cistatic int sof_widget_unload(struct snd_soc_component *scomp, 160662306a36Sopenharmony_ci struct snd_soc_dobj *dobj) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 160962306a36Sopenharmony_ci const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 161062306a36Sopenharmony_ci const struct sof_ipc_tplg_widget_ops *widget_ops; 161162306a36Sopenharmony_ci const struct snd_kcontrol_new *kc; 161262306a36Sopenharmony_ci struct snd_soc_dapm_widget *widget; 161362306a36Sopenharmony_ci struct snd_sof_control *scontrol; 161462306a36Sopenharmony_ci struct snd_sof_widget *swidget; 161562306a36Sopenharmony_ci struct soc_mixer_control *sm; 161662306a36Sopenharmony_ci struct soc_bytes_ext *sbe; 161762306a36Sopenharmony_ci struct snd_sof_dai *dai; 161862306a36Sopenharmony_ci struct soc_enum *se; 161962306a36Sopenharmony_ci int i; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci swidget = dobj->private; 162262306a36Sopenharmony_ci if (!swidget) 162362306a36Sopenharmony_ci return 0; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci widget = swidget->widget; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci switch (swidget->id) { 162862306a36Sopenharmony_ci case snd_soc_dapm_dai_in: 162962306a36Sopenharmony_ci case snd_soc_dapm_dai_out: 163062306a36Sopenharmony_ci dai = swidget->private; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci if (dai) 163362306a36Sopenharmony_ci list_del(&dai->list); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci sof_disconnect_dai_widget(scomp, widget); 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci break; 163862306a36Sopenharmony_ci case snd_soc_dapm_scheduler: 163962306a36Sopenharmony_ci { 164062306a36Sopenharmony_ci struct snd_sof_pipeline *spipe = swidget->spipe; 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci list_del(&spipe->list); 164362306a36Sopenharmony_ci kfree(spipe); 164462306a36Sopenharmony_ci swidget->spipe = NULL; 164562306a36Sopenharmony_ci break; 164662306a36Sopenharmony_ci } 164762306a36Sopenharmony_ci default: 164862306a36Sopenharmony_ci break; 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci for (i = 0; i < widget->num_kcontrols; i++) { 165162306a36Sopenharmony_ci kc = &widget->kcontrol_news[i]; 165262306a36Sopenharmony_ci switch (widget->dobj.widget.kcontrol_type[i]) { 165362306a36Sopenharmony_ci case SND_SOC_TPLG_TYPE_MIXER: 165462306a36Sopenharmony_ci sm = (struct soc_mixer_control *)kc->private_value; 165562306a36Sopenharmony_ci scontrol = sm->dobj.private; 165662306a36Sopenharmony_ci if (sm->max > 1) 165762306a36Sopenharmony_ci kfree(scontrol->volume_table); 165862306a36Sopenharmony_ci break; 165962306a36Sopenharmony_ci case SND_SOC_TPLG_TYPE_ENUM: 166062306a36Sopenharmony_ci se = (struct soc_enum *)kc->private_value; 166162306a36Sopenharmony_ci scontrol = se->dobj.private; 166262306a36Sopenharmony_ci break; 166362306a36Sopenharmony_ci case SND_SOC_TPLG_TYPE_BYTES: 166462306a36Sopenharmony_ci sbe = (struct soc_bytes_ext *)kc->private_value; 166562306a36Sopenharmony_ci scontrol = sbe->dobj.private; 166662306a36Sopenharmony_ci break; 166762306a36Sopenharmony_ci default: 166862306a36Sopenharmony_ci dev_warn(scomp->dev, "unsupported kcontrol_type\n"); 166962306a36Sopenharmony_ci goto out; 167062306a36Sopenharmony_ci } 167162306a36Sopenharmony_ci kfree(scontrol->ipc_control_data); 167262306a36Sopenharmony_ci list_del(&scontrol->list); 167362306a36Sopenharmony_ci kfree(scontrol->name); 167462306a36Sopenharmony_ci kfree(scontrol); 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ciout: 167862306a36Sopenharmony_ci /* free IPC related data */ 167962306a36Sopenharmony_ci widget_ops = tplg_ops ? tplg_ops->widget : NULL; 168062306a36Sopenharmony_ci if (widget_ops && widget_ops[swidget->id].ipc_free) 168162306a36Sopenharmony_ci widget_ops[swidget->id].ipc_free(swidget); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci ida_destroy(&swidget->output_queue_ida); 168462306a36Sopenharmony_ci ida_destroy(&swidget->input_queue_ida); 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci sof_free_pin_binding(swidget, SOF_PIN_TYPE_INPUT); 168762306a36Sopenharmony_ci sof_free_pin_binding(swidget, SOF_PIN_TYPE_OUTPUT); 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci kfree(swidget->tuples); 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci /* remove and free swidget object */ 169262306a36Sopenharmony_ci list_del(&swidget->list); 169362306a36Sopenharmony_ci kfree(swidget); 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci return 0; 169662306a36Sopenharmony_ci} 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci/* 169962306a36Sopenharmony_ci * DAI HW configuration. 170062306a36Sopenharmony_ci */ 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci/* FE DAI - used for any driver specific init */ 170362306a36Sopenharmony_cistatic int sof_dai_load(struct snd_soc_component *scomp, int index, 170462306a36Sopenharmony_ci struct snd_soc_dai_driver *dai_drv, 170562306a36Sopenharmony_ci struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) 170662306a36Sopenharmony_ci{ 170762306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 170862306a36Sopenharmony_ci const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm); 170962306a36Sopenharmony_ci struct snd_soc_tplg_stream_caps *caps; 171062306a36Sopenharmony_ci struct snd_soc_tplg_private *private = &pcm->priv; 171162306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 171262306a36Sopenharmony_ci int stream; 171362306a36Sopenharmony_ci int ret; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci /* nothing to do for BEs atm */ 171662306a36Sopenharmony_ci if (!pcm) 171762306a36Sopenharmony_ci return 0; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci spcm = kzalloc(sizeof(*spcm), GFP_KERNEL); 172062306a36Sopenharmony_ci if (!spcm) 172162306a36Sopenharmony_ci return -ENOMEM; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci spcm->scomp = scomp; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci for_each_pcm_streams(stream) { 172662306a36Sopenharmony_ci spcm->stream[stream].comp_id = COMP_ID_UNASSIGNED; 172762306a36Sopenharmony_ci if (pcm->compress) 172862306a36Sopenharmony_ci snd_sof_compr_init_elapsed_work(&spcm->stream[stream].period_elapsed_work); 172962306a36Sopenharmony_ci else 173062306a36Sopenharmony_ci snd_sof_pcm_init_elapsed_work(&spcm->stream[stream].period_elapsed_work); 173162306a36Sopenharmony_ci } 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci spcm->pcm = *pcm; 173462306a36Sopenharmony_ci dev_dbg(scomp->dev, "tplg: load pcm %s\n", pcm->dai_name); 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci /* perform pcm set op */ 173762306a36Sopenharmony_ci if (ipc_pcm_ops && ipc_pcm_ops->pcm_setup) { 173862306a36Sopenharmony_ci ret = ipc_pcm_ops->pcm_setup(sdev, spcm); 173962306a36Sopenharmony_ci if (ret < 0) { 174062306a36Sopenharmony_ci kfree(spcm); 174162306a36Sopenharmony_ci return ret; 174262306a36Sopenharmony_ci } 174362306a36Sopenharmony_ci } 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci dai_drv->dobj.private = spcm; 174662306a36Sopenharmony_ci list_add(&spcm->list, &sdev->pcm_list); 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci ret = sof_parse_tokens(scomp, spcm, stream_tokens, 174962306a36Sopenharmony_ci ARRAY_SIZE(stream_tokens), private->array, 175062306a36Sopenharmony_ci le32_to_cpu(private->size)); 175162306a36Sopenharmony_ci if (ret) { 175262306a36Sopenharmony_ci dev_err(scomp->dev, "error: parse stream tokens failed %d\n", 175362306a36Sopenharmony_ci le32_to_cpu(private->size)); 175462306a36Sopenharmony_ci return ret; 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci /* do we need to allocate playback PCM DMA pages */ 175862306a36Sopenharmony_ci if (!spcm->pcm.playback) 175962306a36Sopenharmony_ci goto capture; 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci stream = SNDRV_PCM_STREAM_PLAYBACK; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci caps = &spcm->pcm.caps[stream]; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci /* allocate playback page table buffer */ 176662306a36Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, 176762306a36Sopenharmony_ci PAGE_SIZE, &spcm->stream[stream].page_table); 176862306a36Sopenharmony_ci if (ret < 0) { 176962306a36Sopenharmony_ci dev_err(scomp->dev, "error: can't alloc page table for %s %d\n", 177062306a36Sopenharmony_ci caps->name, ret); 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci return ret; 177362306a36Sopenharmony_ci } 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci /* bind pcm to host comp */ 177662306a36Sopenharmony_ci ret = spcm_bind(scomp, spcm, stream); 177762306a36Sopenharmony_ci if (ret) { 177862306a36Sopenharmony_ci dev_err(scomp->dev, 177962306a36Sopenharmony_ci "error: can't bind pcm to host\n"); 178062306a36Sopenharmony_ci goto free_playback_tables; 178162306a36Sopenharmony_ci } 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_cicapture: 178462306a36Sopenharmony_ci stream = SNDRV_PCM_STREAM_CAPTURE; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci /* do we need to allocate capture PCM DMA pages */ 178762306a36Sopenharmony_ci if (!spcm->pcm.capture) 178862306a36Sopenharmony_ci return ret; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci caps = &spcm->pcm.caps[stream]; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci /* allocate capture page table buffer */ 179362306a36Sopenharmony_ci ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, 179462306a36Sopenharmony_ci PAGE_SIZE, &spcm->stream[stream].page_table); 179562306a36Sopenharmony_ci if (ret < 0) { 179662306a36Sopenharmony_ci dev_err(scomp->dev, "error: can't alloc page table for %s %d\n", 179762306a36Sopenharmony_ci caps->name, ret); 179862306a36Sopenharmony_ci goto free_playback_tables; 179962306a36Sopenharmony_ci } 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci /* bind pcm to host comp */ 180262306a36Sopenharmony_ci ret = spcm_bind(scomp, spcm, stream); 180362306a36Sopenharmony_ci if (ret) { 180462306a36Sopenharmony_ci dev_err(scomp->dev, 180562306a36Sopenharmony_ci "error: can't bind pcm to host\n"); 180662306a36Sopenharmony_ci snd_dma_free_pages(&spcm->stream[stream].page_table); 180762306a36Sopenharmony_ci goto free_playback_tables; 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci return ret; 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_cifree_playback_tables: 181362306a36Sopenharmony_ci if (spcm->pcm.playback) 181462306a36Sopenharmony_ci snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].page_table); 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci return ret; 181762306a36Sopenharmony_ci} 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_cistatic int sof_dai_unload(struct snd_soc_component *scomp, 182062306a36Sopenharmony_ci struct snd_soc_dobj *dobj) 182162306a36Sopenharmony_ci{ 182262306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 182362306a36Sopenharmony_ci const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm); 182462306a36Sopenharmony_ci struct snd_sof_pcm *spcm = dobj->private; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci /* free PCM DMA pages */ 182762306a36Sopenharmony_ci if (spcm->pcm.playback) 182862306a36Sopenharmony_ci snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].page_table); 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci if (spcm->pcm.capture) 183162306a36Sopenharmony_ci snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_CAPTURE].page_table); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci /* perform pcm free op */ 183462306a36Sopenharmony_ci if (ipc_pcm_ops && ipc_pcm_ops->pcm_free) 183562306a36Sopenharmony_ci ipc_pcm_ops->pcm_free(sdev, spcm); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci /* remove from list and free spcm */ 183862306a36Sopenharmony_ci list_del(&spcm->list); 183962306a36Sopenharmony_ci kfree(spcm); 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci return 0; 184262306a36Sopenharmony_ci} 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_cistatic const struct sof_topology_token common_dai_link_tokens[] = { 184562306a36Sopenharmony_ci {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, 184662306a36Sopenharmony_ci offsetof(struct snd_sof_dai_link, type)}, 184762306a36Sopenharmony_ci}; 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci/* DAI link - used for any driver specific init */ 185062306a36Sopenharmony_cistatic int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_soc_dai_link *link, 185162306a36Sopenharmony_ci struct snd_soc_tplg_link_config *cfg) 185262306a36Sopenharmony_ci{ 185362306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 185462306a36Sopenharmony_ci const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 185562306a36Sopenharmony_ci struct snd_soc_tplg_private *private = &cfg->priv; 185662306a36Sopenharmony_ci const struct sof_token_info *token_list; 185762306a36Sopenharmony_ci struct snd_sof_dai_link *slink; 185862306a36Sopenharmony_ci u32 token_id = 0; 185962306a36Sopenharmony_ci int num_tuples = 0; 186062306a36Sopenharmony_ci int ret, num_sets; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci if (!link->platforms) { 186362306a36Sopenharmony_ci dev_err(scomp->dev, "error: no platforms\n"); 186462306a36Sopenharmony_ci return -EINVAL; 186562306a36Sopenharmony_ci } 186662306a36Sopenharmony_ci link->platforms->name = dev_name(scomp->dev); 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci if (tplg_ops && tplg_ops->link_setup) { 186962306a36Sopenharmony_ci ret = tplg_ops->link_setup(sdev, link); 187062306a36Sopenharmony_ci if (ret < 0) 187162306a36Sopenharmony_ci return ret; 187262306a36Sopenharmony_ci } 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci /* Set nonatomic property for FE dai links as their trigger action involves IPC's */ 187562306a36Sopenharmony_ci if (!link->no_pcm) { 187662306a36Sopenharmony_ci link->nonatomic = true; 187762306a36Sopenharmony_ci return 0; 187862306a36Sopenharmony_ci } 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci /* check we have some tokens - we need at least DAI type */ 188162306a36Sopenharmony_ci if (le32_to_cpu(private->size) == 0) { 188262306a36Sopenharmony_ci dev_err(scomp->dev, "error: expected tokens for DAI, none found\n"); 188362306a36Sopenharmony_ci return -EINVAL; 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci slink = kzalloc(sizeof(*slink), GFP_KERNEL); 188762306a36Sopenharmony_ci if (!slink) 188862306a36Sopenharmony_ci return -ENOMEM; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci slink->num_hw_configs = le32_to_cpu(cfg->num_hw_configs); 189162306a36Sopenharmony_ci slink->hw_configs = kmemdup(cfg->hw_config, 189262306a36Sopenharmony_ci sizeof(*slink->hw_configs) * slink->num_hw_configs, 189362306a36Sopenharmony_ci GFP_KERNEL); 189462306a36Sopenharmony_ci if (!slink->hw_configs) { 189562306a36Sopenharmony_ci kfree(slink); 189662306a36Sopenharmony_ci return -ENOMEM; 189762306a36Sopenharmony_ci } 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci slink->default_hw_cfg_id = le32_to_cpu(cfg->default_hw_config_id); 190062306a36Sopenharmony_ci slink->link = link; 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci dev_dbg(scomp->dev, "tplg: %d hw_configs found, default id: %d for dai link %s!\n", 190362306a36Sopenharmony_ci slink->num_hw_configs, slink->default_hw_cfg_id, link->name); 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci ret = sof_parse_tokens(scomp, slink, common_dai_link_tokens, 190662306a36Sopenharmony_ci ARRAY_SIZE(common_dai_link_tokens), 190762306a36Sopenharmony_ci private->array, le32_to_cpu(private->size)); 190862306a36Sopenharmony_ci if (ret < 0) { 190962306a36Sopenharmony_ci dev_err(scomp->dev, "Failed tp parse common DAI link tokens\n"); 191062306a36Sopenharmony_ci kfree(slink->hw_configs); 191162306a36Sopenharmony_ci kfree(slink); 191262306a36Sopenharmony_ci return ret; 191362306a36Sopenharmony_ci } 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci token_list = tplg_ops ? tplg_ops->token_list : NULL; 191662306a36Sopenharmony_ci if (!token_list) 191762306a36Sopenharmony_ci goto out; 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci /* calculate size of tuples array */ 192062306a36Sopenharmony_ci num_tuples += token_list[SOF_DAI_LINK_TOKENS].count; 192162306a36Sopenharmony_ci num_sets = slink->num_hw_configs; 192262306a36Sopenharmony_ci switch (slink->type) { 192362306a36Sopenharmony_ci case SOF_DAI_INTEL_SSP: 192462306a36Sopenharmony_ci token_id = SOF_SSP_TOKENS; 192562306a36Sopenharmony_ci num_tuples += token_list[SOF_SSP_TOKENS].count * slink->num_hw_configs; 192662306a36Sopenharmony_ci break; 192762306a36Sopenharmony_ci case SOF_DAI_INTEL_DMIC: 192862306a36Sopenharmony_ci token_id = SOF_DMIC_TOKENS; 192962306a36Sopenharmony_ci num_tuples += token_list[SOF_DMIC_TOKENS].count; 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci /* Allocate memory for max PDM controllers */ 193262306a36Sopenharmony_ci num_tuples += token_list[SOF_DMIC_PDM_TOKENS].count * SOF_DAI_INTEL_DMIC_NUM_CTRL; 193362306a36Sopenharmony_ci break; 193462306a36Sopenharmony_ci case SOF_DAI_INTEL_HDA: 193562306a36Sopenharmony_ci token_id = SOF_HDA_TOKENS; 193662306a36Sopenharmony_ci num_tuples += token_list[SOF_HDA_TOKENS].count; 193762306a36Sopenharmony_ci break; 193862306a36Sopenharmony_ci case SOF_DAI_INTEL_ALH: 193962306a36Sopenharmony_ci token_id = SOF_ALH_TOKENS; 194062306a36Sopenharmony_ci num_tuples += token_list[SOF_ALH_TOKENS].count; 194162306a36Sopenharmony_ci break; 194262306a36Sopenharmony_ci case SOF_DAI_IMX_SAI: 194362306a36Sopenharmony_ci token_id = SOF_SAI_TOKENS; 194462306a36Sopenharmony_ci num_tuples += token_list[SOF_SAI_TOKENS].count; 194562306a36Sopenharmony_ci break; 194662306a36Sopenharmony_ci case SOF_DAI_IMX_ESAI: 194762306a36Sopenharmony_ci token_id = SOF_ESAI_TOKENS; 194862306a36Sopenharmony_ci num_tuples += token_list[SOF_ESAI_TOKENS].count; 194962306a36Sopenharmony_ci break; 195062306a36Sopenharmony_ci case SOF_DAI_MEDIATEK_AFE: 195162306a36Sopenharmony_ci token_id = SOF_AFE_TOKENS; 195262306a36Sopenharmony_ci num_tuples += token_list[SOF_AFE_TOKENS].count; 195362306a36Sopenharmony_ci break; 195462306a36Sopenharmony_ci case SOF_DAI_AMD_DMIC: 195562306a36Sopenharmony_ci token_id = SOF_ACPDMIC_TOKENS; 195662306a36Sopenharmony_ci num_tuples += token_list[SOF_ACPDMIC_TOKENS].count; 195762306a36Sopenharmony_ci break; 195862306a36Sopenharmony_ci case SOF_DAI_AMD_SP: 195962306a36Sopenharmony_ci case SOF_DAI_AMD_HS: 196062306a36Sopenharmony_ci case SOF_DAI_AMD_SP_VIRTUAL: 196162306a36Sopenharmony_ci case SOF_DAI_AMD_HS_VIRTUAL: 196262306a36Sopenharmony_ci token_id = SOF_ACPI2S_TOKENS; 196362306a36Sopenharmony_ci num_tuples += token_list[SOF_ACPI2S_TOKENS].count; 196462306a36Sopenharmony_ci break; 196562306a36Sopenharmony_ci default: 196662306a36Sopenharmony_ci break; 196762306a36Sopenharmony_ci } 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci /* allocate memory for tuples array */ 197062306a36Sopenharmony_ci slink->tuples = kcalloc(num_tuples, sizeof(*slink->tuples), GFP_KERNEL); 197162306a36Sopenharmony_ci if (!slink->tuples) { 197262306a36Sopenharmony_ci kfree(slink->hw_configs); 197362306a36Sopenharmony_ci kfree(slink); 197462306a36Sopenharmony_ci return -ENOMEM; 197562306a36Sopenharmony_ci } 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci if (token_list[SOF_DAI_LINK_TOKENS].tokens) { 197862306a36Sopenharmony_ci /* parse one set of DAI link tokens */ 197962306a36Sopenharmony_ci ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), 198062306a36Sopenharmony_ci SOF_DAI_LINK_TOKENS, 1, slink->tuples, 198162306a36Sopenharmony_ci num_tuples, &slink->num_tuples); 198262306a36Sopenharmony_ci if (ret < 0) { 198362306a36Sopenharmony_ci dev_err(scomp->dev, "failed to parse %s for dai link %s\n", 198462306a36Sopenharmony_ci token_list[SOF_DAI_LINK_TOKENS].name, link->name); 198562306a36Sopenharmony_ci goto err; 198662306a36Sopenharmony_ci } 198762306a36Sopenharmony_ci } 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci /* nothing more to do if there are no DAI type-specific tokens defined */ 199062306a36Sopenharmony_ci if (!token_id || !token_list[token_id].tokens) 199162306a36Sopenharmony_ci goto out; 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci /* parse "num_sets" sets of DAI-specific tokens */ 199462306a36Sopenharmony_ci ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), 199562306a36Sopenharmony_ci token_id, num_sets, slink->tuples, num_tuples, &slink->num_tuples); 199662306a36Sopenharmony_ci if (ret < 0) { 199762306a36Sopenharmony_ci dev_err(scomp->dev, "failed to parse %s for dai link %s\n", 199862306a36Sopenharmony_ci token_list[token_id].name, link->name); 199962306a36Sopenharmony_ci goto err; 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci /* for DMIC, also parse all sets of DMIC PDM tokens based on active PDM count */ 200362306a36Sopenharmony_ci if (token_id == SOF_DMIC_TOKENS) { 200462306a36Sopenharmony_ci num_sets = sof_get_token_value(SOF_TKN_INTEL_DMIC_NUM_PDM_ACTIVE, 200562306a36Sopenharmony_ci slink->tuples, slink->num_tuples); 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci if (num_sets < 0) { 200862306a36Sopenharmony_ci dev_err(sdev->dev, "Invalid active PDM count for %s\n", link->name); 200962306a36Sopenharmony_ci ret = num_sets; 201062306a36Sopenharmony_ci goto err; 201162306a36Sopenharmony_ci } 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), 201462306a36Sopenharmony_ci SOF_DMIC_PDM_TOKENS, num_sets, slink->tuples, 201562306a36Sopenharmony_ci num_tuples, &slink->num_tuples); 201662306a36Sopenharmony_ci if (ret < 0) { 201762306a36Sopenharmony_ci dev_err(scomp->dev, "failed to parse %s for dai link %s\n", 201862306a36Sopenharmony_ci token_list[SOF_DMIC_PDM_TOKENS].name, link->name); 201962306a36Sopenharmony_ci goto err; 202062306a36Sopenharmony_ci } 202162306a36Sopenharmony_ci } 202262306a36Sopenharmony_ciout: 202362306a36Sopenharmony_ci link->dobj.private = slink; 202462306a36Sopenharmony_ci list_add(&slink->list, &sdev->dai_link_list); 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci return 0; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_cierr: 202962306a36Sopenharmony_ci kfree(slink->tuples); 203062306a36Sopenharmony_ci kfree(slink->hw_configs); 203162306a36Sopenharmony_ci kfree(slink); 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci return ret; 203462306a36Sopenharmony_ci} 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_cistatic int sof_link_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) 203762306a36Sopenharmony_ci{ 203862306a36Sopenharmony_ci struct snd_sof_dai_link *slink = dobj->private; 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci if (!slink) 204162306a36Sopenharmony_ci return 0; 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci kfree(slink->tuples); 204462306a36Sopenharmony_ci list_del(&slink->list); 204562306a36Sopenharmony_ci kfree(slink->hw_configs); 204662306a36Sopenharmony_ci kfree(slink); 204762306a36Sopenharmony_ci dobj->private = NULL; 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci return 0; 205062306a36Sopenharmony_ci} 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci/* DAI link - used for any driver specific init */ 205362306a36Sopenharmony_cistatic int sof_route_load(struct snd_soc_component *scomp, int index, 205462306a36Sopenharmony_ci struct snd_soc_dapm_route *route) 205562306a36Sopenharmony_ci{ 205662306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 205762306a36Sopenharmony_ci struct snd_sof_widget *source_swidget, *sink_swidget; 205862306a36Sopenharmony_ci struct snd_soc_dobj *dobj = &route->dobj; 205962306a36Sopenharmony_ci struct snd_sof_route *sroute; 206062306a36Sopenharmony_ci int ret = 0; 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci /* allocate memory for sroute and connect */ 206362306a36Sopenharmony_ci sroute = kzalloc(sizeof(*sroute), GFP_KERNEL); 206462306a36Sopenharmony_ci if (!sroute) 206562306a36Sopenharmony_ci return -ENOMEM; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci sroute->scomp = scomp; 206862306a36Sopenharmony_ci dev_dbg(scomp->dev, "sink %s control %s source %s\n", 206962306a36Sopenharmony_ci route->sink, route->control ? route->control : "none", 207062306a36Sopenharmony_ci route->source); 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci /* source component */ 207362306a36Sopenharmony_ci source_swidget = snd_sof_find_swidget(scomp, (char *)route->source); 207462306a36Sopenharmony_ci if (!source_swidget) { 207562306a36Sopenharmony_ci dev_err(scomp->dev, "error: source %s not found\n", 207662306a36Sopenharmony_ci route->source); 207762306a36Sopenharmony_ci ret = -EINVAL; 207862306a36Sopenharmony_ci goto err; 207962306a36Sopenharmony_ci } 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci /* 208262306a36Sopenharmony_ci * Virtual widgets of type output/out_drv may be added in topology 208362306a36Sopenharmony_ci * for compatibility. These are not handled by the FW. 208462306a36Sopenharmony_ci * So, don't send routes whose source/sink widget is of such types 208562306a36Sopenharmony_ci * to the DSP. 208662306a36Sopenharmony_ci */ 208762306a36Sopenharmony_ci if (source_swidget->id == snd_soc_dapm_out_drv || 208862306a36Sopenharmony_ci source_swidget->id == snd_soc_dapm_output) 208962306a36Sopenharmony_ci goto err; 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci /* sink component */ 209262306a36Sopenharmony_ci sink_swidget = snd_sof_find_swidget(scomp, (char *)route->sink); 209362306a36Sopenharmony_ci if (!sink_swidget) { 209462306a36Sopenharmony_ci dev_err(scomp->dev, "error: sink %s not found\n", 209562306a36Sopenharmony_ci route->sink); 209662306a36Sopenharmony_ci ret = -EINVAL; 209762306a36Sopenharmony_ci goto err; 209862306a36Sopenharmony_ci } 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci /* 210162306a36Sopenharmony_ci * Don't send routes whose sink widget is of type 210262306a36Sopenharmony_ci * output or out_drv to the DSP 210362306a36Sopenharmony_ci */ 210462306a36Sopenharmony_ci if (sink_swidget->id == snd_soc_dapm_out_drv || 210562306a36Sopenharmony_ci sink_swidget->id == snd_soc_dapm_output) 210662306a36Sopenharmony_ci goto err; 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci sroute->route = route; 210962306a36Sopenharmony_ci dobj->private = sroute; 211062306a36Sopenharmony_ci sroute->src_widget = source_swidget; 211162306a36Sopenharmony_ci sroute->sink_widget = sink_swidget; 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci /* add route to route list */ 211462306a36Sopenharmony_ci list_add(&sroute->list, &sdev->route_list); 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci return 0; 211762306a36Sopenharmony_cierr: 211862306a36Sopenharmony_ci kfree(sroute); 211962306a36Sopenharmony_ci return ret; 212062306a36Sopenharmony_ci} 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci/** 212362306a36Sopenharmony_ci * sof_set_widget_pipeline - Set pipeline for a component 212462306a36Sopenharmony_ci * @sdev: pointer to struct snd_sof_dev 212562306a36Sopenharmony_ci * @spipe: pointer to struct snd_sof_pipeline 212662306a36Sopenharmony_ci * @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget 212762306a36Sopenharmony_ci * 212862306a36Sopenharmony_ci * Return: 0 if successful, -EINVAL on error. 212962306a36Sopenharmony_ci * The function checks if @swidget is associated with any volatile controls. If so, setting 213062306a36Sopenharmony_ci * the dynamic_pipeline_widget is disallowed. 213162306a36Sopenharmony_ci */ 213262306a36Sopenharmony_cistatic int sof_set_widget_pipeline(struct snd_sof_dev *sdev, struct snd_sof_pipeline *spipe, 213362306a36Sopenharmony_ci struct snd_sof_widget *swidget) 213462306a36Sopenharmony_ci{ 213562306a36Sopenharmony_ci struct snd_sof_widget *pipe_widget = spipe->pipe_widget; 213662306a36Sopenharmony_ci struct snd_sof_control *scontrol; 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci if (pipe_widget->dynamic_pipeline_widget) { 213962306a36Sopenharmony_ci /* dynamic widgets cannot have volatile kcontrols */ 214062306a36Sopenharmony_ci list_for_each_entry(scontrol, &sdev->kcontrol_list, list) 214162306a36Sopenharmony_ci if (scontrol->comp_id == swidget->comp_id && 214262306a36Sopenharmony_ci (scontrol->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE)) { 214362306a36Sopenharmony_ci dev_err(sdev->dev, 214462306a36Sopenharmony_ci "error: volatile control found for dynamic widget %s\n", 214562306a36Sopenharmony_ci swidget->widget->name); 214662306a36Sopenharmony_ci return -EINVAL; 214762306a36Sopenharmony_ci } 214862306a36Sopenharmony_ci } 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci /* set the pipeline and apply the dynamic_pipeline_widget_flag */ 215162306a36Sopenharmony_ci swidget->spipe = spipe; 215262306a36Sopenharmony_ci swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget; 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci return 0; 215562306a36Sopenharmony_ci} 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci/* completion - called at completion of firmware loading */ 215862306a36Sopenharmony_cistatic int sof_complete(struct snd_soc_component *scomp) 215962306a36Sopenharmony_ci{ 216062306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 216162306a36Sopenharmony_ci const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 216262306a36Sopenharmony_ci const struct sof_ipc_tplg_widget_ops *widget_ops; 216362306a36Sopenharmony_ci struct snd_sof_control *scontrol; 216462306a36Sopenharmony_ci struct snd_sof_pipeline *spipe; 216562306a36Sopenharmony_ci int ret; 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci widget_ops = tplg_ops ? tplg_ops->widget : NULL; 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci /* first update all control IPC structures based on the IPC version */ 217062306a36Sopenharmony_ci if (tplg_ops && tplg_ops->control_setup) 217162306a36Sopenharmony_ci list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { 217262306a36Sopenharmony_ci ret = tplg_ops->control_setup(sdev, scontrol); 217362306a36Sopenharmony_ci if (ret < 0) { 217462306a36Sopenharmony_ci dev_err(sdev->dev, "failed updating IPC struct for control %s\n", 217562306a36Sopenharmony_ci scontrol->name); 217662306a36Sopenharmony_ci return ret; 217762306a36Sopenharmony_ci } 217862306a36Sopenharmony_ci } 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci /* set up the IPC structures for the pipeline widgets */ 218162306a36Sopenharmony_ci list_for_each_entry(spipe, &sdev->pipeline_list, list) { 218262306a36Sopenharmony_ci struct snd_sof_widget *pipe_widget = spipe->pipe_widget; 218362306a36Sopenharmony_ci struct snd_sof_widget *swidget; 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci pipe_widget->instance_id = -EINVAL; 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci /* Update the scheduler widget's IPC structure */ 218862306a36Sopenharmony_ci if (widget_ops && widget_ops[pipe_widget->id].ipc_setup) { 218962306a36Sopenharmony_ci ret = widget_ops[pipe_widget->id].ipc_setup(pipe_widget); 219062306a36Sopenharmony_ci if (ret < 0) { 219162306a36Sopenharmony_ci dev_err(sdev->dev, "failed updating IPC struct for %s\n", 219262306a36Sopenharmony_ci pipe_widget->widget->name); 219362306a36Sopenharmony_ci return ret; 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci } 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci /* set the pipeline and update the IPC structure for the non scheduler widgets */ 219862306a36Sopenharmony_ci list_for_each_entry(swidget, &sdev->widget_list, list) 219962306a36Sopenharmony_ci if (swidget->widget->id != snd_soc_dapm_scheduler && 220062306a36Sopenharmony_ci swidget->pipeline_id == pipe_widget->pipeline_id) { 220162306a36Sopenharmony_ci ret = sof_set_widget_pipeline(sdev, spipe, swidget); 220262306a36Sopenharmony_ci if (ret < 0) 220362306a36Sopenharmony_ci return ret; 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci if (widget_ops && widget_ops[swidget->id].ipc_setup) { 220662306a36Sopenharmony_ci ret = widget_ops[swidget->id].ipc_setup(swidget); 220762306a36Sopenharmony_ci if (ret < 0) { 220862306a36Sopenharmony_ci dev_err(sdev->dev, 220962306a36Sopenharmony_ci "failed updating IPC struct for %s\n", 221062306a36Sopenharmony_ci swidget->widget->name); 221162306a36Sopenharmony_ci return ret; 221262306a36Sopenharmony_ci } 221362306a36Sopenharmony_ci } 221462306a36Sopenharmony_ci } 221562306a36Sopenharmony_ci } 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci /* verify topology components loading including dynamic pipelines */ 221862306a36Sopenharmony_ci if (sof_debug_check_flag(SOF_DBG_VERIFY_TPLG)) { 221962306a36Sopenharmony_ci if (tplg_ops && tplg_ops->set_up_all_pipelines && 222062306a36Sopenharmony_ci tplg_ops->tear_down_all_pipelines) { 222162306a36Sopenharmony_ci ret = tplg_ops->set_up_all_pipelines(sdev, true); 222262306a36Sopenharmony_ci if (ret < 0) { 222362306a36Sopenharmony_ci dev_err(sdev->dev, "Failed to set up all topology pipelines: %d\n", 222462306a36Sopenharmony_ci ret); 222562306a36Sopenharmony_ci return ret; 222662306a36Sopenharmony_ci } 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci ret = tplg_ops->tear_down_all_pipelines(sdev, true); 222962306a36Sopenharmony_ci if (ret < 0) { 223062306a36Sopenharmony_ci dev_err(sdev->dev, "Failed to tear down topology pipelines: %d\n", 223162306a36Sopenharmony_ci ret); 223262306a36Sopenharmony_ci return ret; 223362306a36Sopenharmony_ci } 223462306a36Sopenharmony_ci } 223562306a36Sopenharmony_ci } 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci /* set up static pipelines */ 223862306a36Sopenharmony_ci if (tplg_ops && tplg_ops->set_up_all_pipelines) 223962306a36Sopenharmony_ci return tplg_ops->set_up_all_pipelines(sdev, false); 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci return 0; 224262306a36Sopenharmony_ci} 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci/* manifest - optional to inform component of manifest */ 224562306a36Sopenharmony_cistatic int sof_manifest(struct snd_soc_component *scomp, int index, 224662306a36Sopenharmony_ci struct snd_soc_tplg_manifest *man) 224762306a36Sopenharmony_ci{ 224862306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 224962306a36Sopenharmony_ci const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci if (tplg_ops && tplg_ops->parse_manifest) 225262306a36Sopenharmony_ci return tplg_ops->parse_manifest(scomp, index, man); 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci return 0; 225562306a36Sopenharmony_ci} 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci/* vendor specific kcontrol handlers available for binding */ 225862306a36Sopenharmony_cistatic const struct snd_soc_tplg_kcontrol_ops sof_io_ops[] = { 225962306a36Sopenharmony_ci {SOF_TPLG_KCTL_VOL_ID, snd_sof_volume_get, snd_sof_volume_put}, 226062306a36Sopenharmony_ci {SOF_TPLG_KCTL_BYTES_ID, snd_sof_bytes_get, snd_sof_bytes_put}, 226162306a36Sopenharmony_ci {SOF_TPLG_KCTL_ENUM_ID, snd_sof_enum_get, snd_sof_enum_put}, 226262306a36Sopenharmony_ci {SOF_TPLG_KCTL_SWITCH_ID, snd_sof_switch_get, snd_sof_switch_put}, 226362306a36Sopenharmony_ci}; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci/* vendor specific bytes ext handlers available for binding */ 226662306a36Sopenharmony_cistatic const struct snd_soc_tplg_bytes_ext_ops sof_bytes_ext_ops[] = { 226762306a36Sopenharmony_ci {SOF_TPLG_KCTL_BYTES_ID, snd_sof_bytes_ext_get, snd_sof_bytes_ext_put}, 226862306a36Sopenharmony_ci {SOF_TPLG_KCTL_BYTES_VOLATILE_RO, snd_sof_bytes_ext_volatile_get}, 226962306a36Sopenharmony_ci}; 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_cistatic struct snd_soc_tplg_ops sof_tplg_ops = { 227262306a36Sopenharmony_ci /* external kcontrol init - used for any driver specific init */ 227362306a36Sopenharmony_ci .control_load = sof_control_load, 227462306a36Sopenharmony_ci .control_unload = sof_control_unload, 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci /* external kcontrol init - used for any driver specific init */ 227762306a36Sopenharmony_ci .dapm_route_load = sof_route_load, 227862306a36Sopenharmony_ci .dapm_route_unload = sof_route_unload, 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci /* external widget init - used for any driver specific init */ 228162306a36Sopenharmony_ci /* .widget_load is not currently used */ 228262306a36Sopenharmony_ci .widget_ready = sof_widget_ready, 228362306a36Sopenharmony_ci .widget_unload = sof_widget_unload, 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci /* FE DAI - used for any driver specific init */ 228662306a36Sopenharmony_ci .dai_load = sof_dai_load, 228762306a36Sopenharmony_ci .dai_unload = sof_dai_unload, 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci /* DAI link - used for any driver specific init */ 229062306a36Sopenharmony_ci .link_load = sof_link_load, 229162306a36Sopenharmony_ci .link_unload = sof_link_unload, 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci /* completion - called at completion of firmware loading */ 229462306a36Sopenharmony_ci .complete = sof_complete, 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci /* manifest - optional to inform component of manifest */ 229762306a36Sopenharmony_ci .manifest = sof_manifest, 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci /* vendor specific kcontrol handlers available for binding */ 230062306a36Sopenharmony_ci .io_ops = sof_io_ops, 230162306a36Sopenharmony_ci .io_ops_count = ARRAY_SIZE(sof_io_ops), 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci /* vendor specific bytes ext handlers available for binding */ 230462306a36Sopenharmony_ci .bytes_ext_ops = sof_bytes_ext_ops, 230562306a36Sopenharmony_ci .bytes_ext_ops_count = ARRAY_SIZE(sof_bytes_ext_ops), 230662306a36Sopenharmony_ci}; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_cistatic int snd_sof_dspless_kcontrol(struct snd_kcontrol *kcontrol, 230962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 231062306a36Sopenharmony_ci{ 231162306a36Sopenharmony_ci return 0; 231262306a36Sopenharmony_ci} 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_cistatic const struct snd_soc_tplg_kcontrol_ops sof_dspless_io_ops[] = { 231562306a36Sopenharmony_ci {SOF_TPLG_KCTL_VOL_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol}, 231662306a36Sopenharmony_ci {SOF_TPLG_KCTL_BYTES_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol}, 231762306a36Sopenharmony_ci {SOF_TPLG_KCTL_ENUM_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol}, 231862306a36Sopenharmony_ci {SOF_TPLG_KCTL_SWITCH_ID, snd_sof_dspless_kcontrol, snd_sof_dspless_kcontrol}, 231962306a36Sopenharmony_ci}; 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_cistatic int snd_sof_dspless_bytes_ext_get(struct snd_kcontrol *kcontrol, 232262306a36Sopenharmony_ci unsigned int __user *binary_data, 232362306a36Sopenharmony_ci unsigned int size) 232462306a36Sopenharmony_ci{ 232562306a36Sopenharmony_ci return 0; 232662306a36Sopenharmony_ci} 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_cistatic int snd_sof_dspless_bytes_ext_put(struct snd_kcontrol *kcontrol, 232962306a36Sopenharmony_ci const unsigned int __user *binary_data, 233062306a36Sopenharmony_ci unsigned int size) 233162306a36Sopenharmony_ci{ 233262306a36Sopenharmony_ci return 0; 233362306a36Sopenharmony_ci} 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_cistatic const struct snd_soc_tplg_bytes_ext_ops sof_dspless_bytes_ext_ops[] = { 233662306a36Sopenharmony_ci {SOF_TPLG_KCTL_BYTES_ID, snd_sof_dspless_bytes_ext_get, snd_sof_dspless_bytes_ext_put}, 233762306a36Sopenharmony_ci {SOF_TPLG_KCTL_BYTES_VOLATILE_RO, snd_sof_dspless_bytes_ext_get}, 233862306a36Sopenharmony_ci}; 233962306a36Sopenharmony_ci 234062306a36Sopenharmony_ci/* external widget init - used for any driver specific init */ 234162306a36Sopenharmony_cistatic int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index, 234262306a36Sopenharmony_ci struct snd_soc_dapm_widget *w, 234362306a36Sopenharmony_ci struct snd_soc_tplg_dapm_widget *tw) 234462306a36Sopenharmony_ci{ 234562306a36Sopenharmony_ci if (WIDGET_IS_DAI(w->id)) { 234662306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 234762306a36Sopenharmony_ci struct snd_sof_widget *swidget; 234862306a36Sopenharmony_ci struct snd_sof_dai dai; 234962306a36Sopenharmony_ci int ret; 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); 235262306a36Sopenharmony_ci if (!swidget) 235362306a36Sopenharmony_ci return -ENOMEM; 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci memset(&dai, 0, sizeof(dai)); 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci ret = sof_connect_dai_widget(scomp, w, tw, &dai); 235862306a36Sopenharmony_ci if (ret) { 235962306a36Sopenharmony_ci kfree(swidget); 236062306a36Sopenharmony_ci return ret; 236162306a36Sopenharmony_ci } 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci swidget->scomp = scomp; 236462306a36Sopenharmony_ci swidget->widget = w; 236562306a36Sopenharmony_ci mutex_init(&swidget->setup_mutex); 236662306a36Sopenharmony_ci w->dobj.private = swidget; 236762306a36Sopenharmony_ci list_add(&swidget->list, &sdev->widget_list); 236862306a36Sopenharmony_ci } 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci return 0; 237162306a36Sopenharmony_ci} 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_cistatic int sof_dspless_widget_unload(struct snd_soc_component *scomp, 237462306a36Sopenharmony_ci struct snd_soc_dobj *dobj) 237562306a36Sopenharmony_ci{ 237662306a36Sopenharmony_ci struct snd_soc_dapm_widget *w = container_of(dobj, struct snd_soc_dapm_widget, dobj); 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci if (WIDGET_IS_DAI(w->id)) { 237962306a36Sopenharmony_ci struct snd_sof_widget *swidget = dobj->private; 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci sof_disconnect_dai_widget(scomp, w); 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci if (!swidget) 238462306a36Sopenharmony_ci return 0; 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci /* remove and free swidget object */ 238762306a36Sopenharmony_ci list_del(&swidget->list); 238862306a36Sopenharmony_ci kfree(swidget); 238962306a36Sopenharmony_ci } 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci return 0; 239262306a36Sopenharmony_ci} 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_cistatic int sof_dspless_link_load(struct snd_soc_component *scomp, int index, 239562306a36Sopenharmony_ci struct snd_soc_dai_link *link, 239662306a36Sopenharmony_ci struct snd_soc_tplg_link_config *cfg) 239762306a36Sopenharmony_ci{ 239862306a36Sopenharmony_ci link->platforms->name = dev_name(scomp->dev); 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci /* Set nonatomic property for FE dai links for FE-BE compatibility */ 240162306a36Sopenharmony_ci if (!link->no_pcm) 240262306a36Sopenharmony_ci link->nonatomic = true; 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci return 0; 240562306a36Sopenharmony_ci} 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_cistatic struct snd_soc_tplg_ops sof_dspless_tplg_ops = { 240862306a36Sopenharmony_ci /* external widget init - used for any driver specific init */ 240962306a36Sopenharmony_ci .widget_ready = sof_dspless_widget_ready, 241062306a36Sopenharmony_ci .widget_unload = sof_dspless_widget_unload, 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci /* FE DAI - used for any driver specific init */ 241362306a36Sopenharmony_ci .dai_load = sof_dai_load, 241462306a36Sopenharmony_ci .dai_unload = sof_dai_unload, 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci /* DAI link - used for any driver specific init */ 241762306a36Sopenharmony_ci .link_load = sof_dspless_link_load, 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci /* vendor specific kcontrol handlers available for binding */ 242062306a36Sopenharmony_ci .io_ops = sof_dspless_io_ops, 242162306a36Sopenharmony_ci .io_ops_count = ARRAY_SIZE(sof_dspless_io_ops), 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci /* vendor specific bytes ext handlers available for binding */ 242462306a36Sopenharmony_ci .bytes_ext_ops = sof_dspless_bytes_ext_ops, 242562306a36Sopenharmony_ci .bytes_ext_ops_count = ARRAY_SIZE(sof_dspless_bytes_ext_ops), 242662306a36Sopenharmony_ci}; 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ciint snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) 242962306a36Sopenharmony_ci{ 243062306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 243162306a36Sopenharmony_ci const struct firmware *fw; 243262306a36Sopenharmony_ci int ret; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci dev_dbg(scomp->dev, "loading topology:%s\n", file); 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci ret = request_firmware(&fw, file, scomp->dev); 243762306a36Sopenharmony_ci if (ret < 0) { 243862306a36Sopenharmony_ci dev_err(scomp->dev, "error: tplg request firmware %s failed err: %d\n", 243962306a36Sopenharmony_ci file, ret); 244062306a36Sopenharmony_ci dev_err(scomp->dev, 244162306a36Sopenharmony_ci "you may need to download the firmware from https://github.com/thesofproject/sof-bin/\n"); 244262306a36Sopenharmony_ci return ret; 244362306a36Sopenharmony_ci } 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci if (sdev->dspless_mode_selected) 244662306a36Sopenharmony_ci ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw); 244762306a36Sopenharmony_ci else 244862306a36Sopenharmony_ci ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw); 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci if (ret < 0) { 245162306a36Sopenharmony_ci dev_err(scomp->dev, "error: tplg component load failed %d\n", 245262306a36Sopenharmony_ci ret); 245362306a36Sopenharmony_ci ret = -EINVAL; 245462306a36Sopenharmony_ci } 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci release_firmware(fw); 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci if (ret >= 0 && sdev->led_present) 245962306a36Sopenharmony_ci ret = snd_ctl_led_request(); 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci return ret; 246262306a36Sopenharmony_ci} 246362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_load_topology); 2464