Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 140 additions & 12 deletions sound/soc/sdw_utils/soc_sdw_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <sound/sdca_function.h>
#include <sound/soc_sdw_utils.h>

static const struct snd_soc_dapm_widget generic_dmic_widgets[] = {
Expand Down Expand Up @@ -934,10 +935,10 @@ static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_li
return true;
}

const char *asoc_sdw_get_codec_name(struct device *dev,
const struct asoc_sdw_codec_info *codec_info,
const struct snd_soc_acpi_link_adr *adr_link,
int adr_index)
static const char *_asoc_sdw_get_codec_name(struct device *dev,
const struct asoc_sdw_codec_info *codec_info,
const struct snd_soc_acpi_link_adr *adr_link,
int adr_index)
{
u64 adr = adr_link->adr_d[adr_index].adr;
unsigned int sdw_version = SDW_VERSION(adr);
Expand All @@ -947,17 +948,24 @@ const char *asoc_sdw_get_codec_name(struct device *dev,
unsigned int part_id = SDW_PART_ID(adr);
unsigned int class_id = SDW_CLASS_ID(adr);

if (codec_info->codec_name)
return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL);
else if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id,
class_id, adr_index))
if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id,
class_id, adr_index))
return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x",
link_id, mfg_id, part_id, class_id);
else
return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x",
link_id, mfg_id, part_id, class_id, unique_id);

return NULL;
return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x",
link_id, mfg_id, part_id, class_id, unique_id);
}

const char *asoc_sdw_get_codec_name(struct device *dev,
const struct asoc_sdw_codec_info *codec_info,
const struct snd_soc_acpi_link_adr *adr_link,
int adr_index)
{
if (codec_info->codec_name)
return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL);

return _asoc_sdw_get_codec_name(dev, codec_info, adr_link, adr_index);
}
EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, "SND_SOC_SDW_UTILS");

Expand Down Expand Up @@ -1124,6 +1132,106 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks
}
EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, "SND_SOC_SDW_UTILS");

static int asoc_sdw_get_dai_type(u32 type)
{
switch (type) {
case SDCA_FUNCTION_TYPE_SMART_AMP:
case SDCA_FUNCTION_TYPE_SIMPLE_AMP:
return SOC_SDW_DAI_TYPE_AMP;
case SDCA_FUNCTION_TYPE_SMART_MIC:
case SDCA_FUNCTION_TYPE_SIMPLE_MIC:
case SDCA_FUNCTION_TYPE_SPEAKER_MIC:
return SOC_SDW_DAI_TYPE_MIC;
case SDCA_FUNCTION_TYPE_UAJ:
case SDCA_FUNCTION_TYPE_RJ:
case SDCA_FUNCTION_TYPE_SIMPLE_JACK:
return SOC_SDW_DAI_TYPE_JACK;
default:
return -EINVAL;
}
}

/*
* Check if the SDCA endpoint is present by the SDW peripheral
*
* @dev: Device pointer
* @codec_info: Codec info pointer
* @adr_link: ACPI link address
* @adr_index: Index of the ACPI link address
* @end_index: Index of the endpoint
*
* Return: 1 if the endpoint is present,
* 0 if the endpoint is not present,
* negative error code.
*/

static int is_sdca_endpoint_present(struct device *dev,
struct asoc_sdw_codec_info *codec_info,
const struct snd_soc_acpi_link_adr *adr_link,
int adr_index, int end_index)
{
const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index];
const struct snd_soc_acpi_endpoint *adr_end;
const struct asoc_sdw_dai_info *dai_info;
struct snd_soc_dai_link_component *dlc;
struct snd_soc_dai *codec_dai;
struct sdw_slave *slave;
struct device *sdw_dev;
const char *sdw_codec_name;
int i;

dlc = kzalloc(sizeof(*dlc), GFP_KERNEL);

adr_end = &adr_dev->endpoints[end_index];
dai_info = &codec_info->dais[adr_end->num];

dlc->dai_name = dai_info->dai_name;
codec_dai = snd_soc_find_dai_with_mutex(dlc);
if (!codec_dai) {
dev_warn(dev, "codec dai %s not registered yet\n", dlc->dai_name);
kfree(dlc);
return -EPROBE_DEFER;
}
kfree(dlc);

sdw_codec_name = _asoc_sdw_get_codec_name(dev, codec_info,
adr_link, adr_index);
if (!sdw_codec_name)
return -ENOMEM;

sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name);
if (!sdw_dev) {
dev_err(dev, "codec %s not found\n", sdw_codec_name);
return -EINVAL;
}

slave = dev_to_sdw_dev(sdw_dev);
if (!slave)
return -EINVAL;

/* Make sure BIOS provides SDCA properties */
if (!slave->sdca_data.interface_revision) {
dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n");
return 1;
}

for (i = 0; i < slave->sdca_data.num_functions; i++) {
int dai_type = asoc_sdw_get_dai_type(slave->sdca_data.function[i].type);

if (dai_type == dai_info->dai_type) {
dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n",
dai_type, slave->sdca_data.function[i].name);
return 1;
}
}

dev_dbg(&slave->dev,
"SDCA device function for DAI type %d not supported, skip endpoint\n",
dai_info->dai_type);

return 0;
}

int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
struct asoc_sdw_dailink *soc_dais,
struct asoc_sdw_endpoint *soc_ends,
Expand Down Expand Up @@ -1152,6 +1260,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i];
struct asoc_sdw_codec_info *codec_info;
const char *codec_name;
bool check_sdca = false;

if (!adr_dev->name_prefix) {
dev_err(dev, "codec 0x%llx does not have a name prefix\n",
Expand Down Expand Up @@ -1182,6 +1291,9 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
soc_end->include_sidecar = true;
}

if (SDW_CLASS_ID(adr_dev->adr) && adr_dev->num_endpoints > 1)
check_sdca = true;

for (j = 0; j < adr_dev->num_endpoints; j++) {
const struct snd_soc_acpi_endpoint *adr_end;
const struct asoc_sdw_dai_info *dai_info;
Expand All @@ -1196,6 +1308,22 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
!(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk)))
continue;

/*
* Skip the sdca endpoint presence check if machine quirk is set.
* In other words, quirk should have higher priority than the sdca
* properties in the BIOS.
*/
if (check_sdca && !(dai_info->quirk & ctx->mc_quirk)) {
ret = is_sdca_endpoint_present(dev, codec_info,
adr_link, i, j);
if (ret < 0)
return ret;

/* The endpoint is not present, skip */
if (!ret)
continue;
}

dev_dbg(dev,
"Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n",
ffs(adr_link->mask) - 1, adr_dev->adr,
Expand Down
Loading