// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2021 Intel Corporation
//
// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
#include <linux/acpi.h>
#include <acpi/nhlt.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "avs.h"
#include "control.h"
#include "path.h"
#include "topology.h"
/* Must be called with adev->comp_list_mutex held. */
static struct avs_tplg *
avs_path_find_tplg(struct avs_dev *adev, const char *name)
{
struct avs_soc_component *acomp;
list_for_each_entry(acomp, &adev->comp_list, node)
if (!strcmp(acomp->tplg->name, name))
return acomp->tplg;
return NULL;
}
static struct avs_path_module *
avs_path_find_module(struct avs_path_pipeline *ppl, u32 template_id)
{
struct avs_path_module *mod;
list_for_each_entry(mod, &ppl->mod_list, node)
if (mod->template->id == template_id)
return mod;
return NULL;
}
static struct avs_path_pipeline *
avs_path_find_pipeline(struct avs_path *path, u32 template_id)
{
struct avs_path_pipeline *ppl;
list_for_each_entry(ppl, &path->ppl_list, node)
if (ppl->template->id == template_id)
return ppl;
return NULL;
}
static struct avs_path *
avs_path_find_path(struct avs_dev *adev, const char *name, u32 template_id)
{
struct avs_tplg_path_template *pos, *template = NULL;
struct avs_tplg *tplg;
struct avs_path *path;
tplg = avs_path_find_tplg(adev, name);
if (!tplg)
return NULL;
list_for_each_entry(pos, &tplg->path_tmpl_list, node) {
if (pos->id == template_id) {
template = pos;
break;
}
}
if (!template)
return NULL;
spin_lock(&adev->path_list_lock);
/* Only one variant of given path template may be instantiated at a time. */
list_for_each_entry(path, &adev->path_list, node) {
if (path->template->owner == template) {
spin_unlock(&adev->path_list_lock);
return path;
}
}
spin_unlock(&adev->path_list_lock);
return NULL;
}
static bool avs_test_hw_params(struct snd_pcm_hw_params *params,
struct avs_audio_format *fmt)
{
return (params_rate(params) == fmt->sampling_freq &&
params_channels(params) == fmt->num_channels &&
params_physical_width(params) == fmt->bit_depth &&
snd_pcm_hw_params_bits(params) == fmt->valid_bit_depth);
}
static struct avs_tplg_path *
avs_path_find_variant(struct avs_dev *adev,
struct avs_tplg_path_template *template,
struct snd_pcm_hw_params *fe_params,
struct snd_pcm_hw_params *be_params)
{
struct avs_tplg_path *variant;
list_for_each_entry(variant, &template->path_list, node) {
dev_dbg(adev->dev, "check FE rate %d chn %d vbd %d bd %d\n",
variant->fe_fmt->sampling_freq, variant->fe_fmt->num_channels,
variant->fe_fmt->valid_bit_depth, variant->fe_fmt->bit_depth);
dev_dbg(adev->dev, "check BE rate %d chn %d vbd %d bd %d\n",
variant->be_fmt->sampling_freq, variant->be_fmt->num_channels,
variant->be_fmt->valid_bit_depth, variant->be_fmt->bit_depth);
if (variant->fe_fmt && avs_test_hw_params(fe_params, variant->fe_fmt) &&
variant->be_fmt && avs_test_hw_params(be_params, variant->be_fmt))
return variant;
}
return NULL;
}
__maybe_unused
static bool avs_dma_type_is_host(u32 dma_type)
{
return dma_type == AVS_DMA_HDA_HOST_OUTPUT ||
dma_type == AVS_DMA_HDA_HOST_INPUT;
}
__maybe_unused
static bool avs_dma_type_is_link(u32 dma_type)
{
return !avs_dma_type_is_host(dma_type);
}
__maybe_unused
static bool avs_dma_type_is_output(u32 dma_type)
{
return dma_type == AVS_DMA_HDA_HOST_OUTPUT ||
dma_type == AVS_DMA_HDA_LINK_OUTPUT ||
dma_type == AVS_DMA_I2S_LINK_OUTPUT;
}
__maybe_unused
static bool avs_dma_type_is_input(u32 dma_type)
{
return !avs_dma_type_is_output(dma_type);
}
static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod)
{
struct avs_tplg_module *t = mod->template;
struct avs_copier_cfg *cfg;
struct acpi_nhlt_format_config *ep_blob;
struct acpi_nhlt_endpoint *ep;
union avs_connector_node_id node_id = {0