diff options
Diffstat (limited to 'sound/soc/intel/skylake/skl-topology.c')
-rw-r--r-- | sound/soc/intel/skylake/skl-topology.c | 184 |
1 files changed, 176 insertions, 8 deletions
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 69cd7a81bf2a..b9aab47d1202 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -14,6 +14,7 @@ #include <linux/uuid.h> #include <sound/intel-nhlt.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include <sound/soc-topology.h> #include <uapi/sound/snd_sst_tokens.h> #include <uapi/sound/skl-tplg-interface.h> @@ -578,6 +579,38 @@ static int skl_tplg_unload_pipe_modules(struct skl_dev *skl, return ret; } +static bool skl_tplg_is_multi_fmt(struct skl_dev *skl, struct skl_pipe *pipe) +{ + struct skl_pipe_fmt *cur_fmt; + struct skl_pipe_fmt *next_fmt; + int i; + + if (pipe->nr_cfgs <= 1) + return false; + + if (pipe->conn_type != SKL_PIPE_CONN_TYPE_FE) + return true; + + for (i = 0; i < pipe->nr_cfgs - 1; i++) { + if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) { + cur_fmt = &pipe->configs[i].out_fmt; + next_fmt = &pipe->configs[i + 1].out_fmt; + } else { + cur_fmt = &pipe->configs[i].in_fmt; + next_fmt = &pipe->configs[i + 1].in_fmt; + } + + if (!CHECK_HW_PARAMS(cur_fmt->channels, cur_fmt->freq, + cur_fmt->bps, + next_fmt->channels, + next_fmt->freq, + next_fmt->bps)) + return true; + } + + return false; +} + /* * Here, we select pipe format based on the pipe type and pipe * direction to determine the current config index for the pipeline. @@ -600,6 +633,14 @@ skl_tplg_get_pipe_config(struct skl_dev *skl, struct skl_module_cfg *mconfig) return 0; } + if (skl_tplg_is_multi_fmt(skl, pipe)) { + pipe->cur_config_idx = pipe->pipe_config_idx; + pipe->memory_pages = pconfig->mem_pages; + dev_dbg(skl->dev, "found pipe config idx:%d\n", + pipe->cur_config_idx); + return 0; + } + if (pipe->conn_type == SKL_PIPE_CONN_TYPE_NONE) { dev_dbg(skl->dev, "No conn_type detected, take 0th config\n"); pipe->cur_config_idx = 0; @@ -1314,6 +1355,68 @@ static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w, return 0; } +static int skl_tplg_multi_config_set_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + bool is_set) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct hdac_bus *bus = snd_soc_component_get_drvdata(component); + struct skl_dev *skl = bus_to_skl(bus); + struct skl_pipeline *ppl; + struct skl_pipe *pipe = NULL; + struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value; + u32 *pipe_id; + + if (!ec) + return -EINVAL; + + if (is_set && ucontrol->value.enumerated.item[0] > ec->items) + return -EINVAL; + + pipe_id = ec->dobj.private; + + list_for_each_entry(ppl, &skl->ppl_list, node) { + if (ppl->pipe->ppl_id == *pipe_id) { + pipe = ppl->pipe; + break; + } + } + if (!pipe) + return -EIO; + + if (is_set) + pipe->pipe_config_idx = ucontrol->value.enumerated.item[0]; + else + ucontrol->value.enumerated.item[0] = pipe->pipe_config_idx; + + return 0; +} + +static int skl_tplg_multi_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return skl_tplg_multi_config_set_get(kcontrol, ucontrol, false); +} + +static int skl_tplg_multi_config_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return skl_tplg_multi_config_set_get(kcontrol, ucontrol, true); +} + +static int skl_tplg_multi_config_get_dmic(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return skl_tplg_multi_config_set_get(kcontrol, ucontrol, false); +} + +static int skl_tplg_multi_config_set_dmic(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return skl_tplg_multi_config_set_get(kcontrol, ucontrol, true); +} + static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol, unsigned int __user *data, unsigned int size) { @@ -1853,6 +1956,16 @@ static const struct snd_soc_tplg_kcontrol_ops skl_tplg_kcontrol_ops[] = { .get = skl_tplg_mic_control_get, .put = skl_tplg_mic_control_set, }, + { + .id = SKL_CONTROL_TYPE_MULTI_IO_SELECT, + .get = skl_tplg_multi_config_get, + .put = skl_tplg_multi_config_set, + }, + { + .id = SKL_CONTROL_TYPE_MULTI_IO_SELECT_DMIC, + .get = skl_tplg_multi_config_get_dmic, + .put = skl_tplg_multi_config_set_dmic, + } }; static int skl_tplg_fill_pipe_cfg(struct device *dev, @@ -1989,7 +2102,7 @@ static int skl_tplg_get_uuid(struct device *dev, guid_t *guid, struct snd_soc_tplg_vendor_uuid_elem *uuid_tkn) { if (uuid_tkn->token == SKL_TKN_UUID) { - guid_copy(guid, (guid_t *)&uuid_tkn->uuid); + import_guid(guid, uuid_tkn->uuid); return 0; } @@ -3013,12 +3126,21 @@ static int skl_tplg_control_load(struct snd_soc_component *cmpnt, case SND_SOC_TPLG_CTL_ENUM: tplg_ec = container_of(hdr, struct snd_soc_tplg_enum_control, hdr); - if (kctl->access & SNDRV_CTL_ELEM_ACCESS_READWRITE) { + if (kctl->access & SNDRV_CTL_ELEM_ACCESS_READ) { se = (struct soc_enum *)kctl->private_value; if (tplg_ec->priv.size) - return skl_init_enum_data(bus->dev, se, - tplg_ec); + skl_init_enum_data(bus->dev, se, tplg_ec); } + + /* + * now that the control initializations are done, remove + * write permission for the DMIC configuration enums to + * avoid conflicts between NHLT settings and user interaction + */ + + if (hdr->ops.get == SKL_CONTROL_TYPE_MULTI_IO_SELECT_DMIC) + kctl->access = SNDRV_CTL_ELEM_ACCESS_READ; + break; default: @@ -3376,8 +3498,8 @@ static int skl_tplg_get_manifest_tkn(struct device *dev, dev_err(dev, "Too many UUID tokens\n"); return -EINVAL; } - guid_copy(&skl->modules[uuid_index++]->uuid, - (guid_t *)&array->uuid->uuid); + import_guid(&skl->modules[uuid_index++]->uuid, + array->uuid->uuid); tuple_size += sizeof(*array->uuid); continue; @@ -3488,6 +3610,37 @@ static int skl_manifest_load(struct snd_soc_component *cmpnt, int index, return 0; } +static void skl_tplg_complete(struct snd_soc_component *component) +{ + struct snd_soc_dobj *dobj; + struct snd_soc_acpi_mach *mach = + dev_get_platdata(component->card->dev); + int i; + + list_for_each_entry(dobj, &component->dobj_list, list) { + struct snd_kcontrol *kcontrol = dobj->control.kcontrol; + struct soc_enum *se = + (struct soc_enum *)kcontrol->private_value; + char **texts = dobj->control.dtexts; + char chan_text[4]; + + if (dobj->type != SND_SOC_DOBJ_ENUM || + dobj->control.kcontrol->put != + skl_tplg_multi_config_set_dmic) + continue; + sprintf(chan_text, "c%d", mach->mach_params.dmic_num); + + for (i = 0; i < se->items; i++) { + struct snd_ctl_elem_value val; + + if (strstr(texts[i], chan_text)) { + val.value.enumerated.item[0] = i; + kcontrol->put(kcontrol, &val); + } + } + } +} + static struct snd_soc_tplg_ops skl_tplg_ops = { .widget_load = skl_tplg_widget_load, .control_load = skl_tplg_control_load, @@ -3497,6 +3650,7 @@ static struct snd_soc_tplg_ops skl_tplg_ops = { .io_ops_count = ARRAY_SIZE(skl_tplg_kcontrol_ops), .manifest = skl_manifest_load, .dai_load = skl_dai_load, + .complete = skl_tplg_complete, }; /* @@ -3565,8 +3719,20 @@ int skl_tplg_init(struct snd_soc_component *component, struct hdac_bus *bus) ret = request_firmware(&fw, skl->tplg_name, bus->dev); if (ret < 0) { - dev_info(bus->dev, "tplg fw %s load failed with %d, falling back to dfw_sst.bin", - skl->tplg_name, ret); + char alt_tplg_name[64]; + + snprintf(alt_tplg_name, sizeof(alt_tplg_name), "%s-tplg.bin", + skl->mach->drv_name); + dev_info(bus->dev, "tplg fw %s load failed with %d, trying alternative tplg name %s", + skl->tplg_name, ret, alt_tplg_name); + + ret = request_firmware(&fw, alt_tplg_name, bus->dev); + if (!ret) + goto component_load; + + dev_info(bus->dev, "tplg %s failed with %d, falling back to dfw_sst.bin", + alt_tplg_name, ret); + ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); if (ret < 0) { dev_err(bus->dev, "Fallback tplg fw %s load failed with %d\n", @@ -3575,6 +3741,8 @@ int skl_tplg_init(struct snd_soc_component *component, struct hdac_bus *bus) } } +component_load: + /* * The complete tplg for SKL is loaded as index 0, we don't use * any other index |