// SPDX-License-Identifier: GPL-2.0 /* * mtk-soundcard-driver.c -- MediaTek soundcard driver common * * Copyright (c) 2022 MediaTek Inc. * Author: Trevor Wu */ #include #include #include #include "mtk-soundcard-driver.h" static int set_card_codec_info(struct snd_soc_card *card, struct device_node *sub_node, struct snd_soc_dai_link *dai_link) { struct device *dev = card->dev; struct device_node *codec_node; int ret; codec_node = of_get_child_by_name(sub_node, "codec"); if (!codec_node) { dev_dbg(dev, "%s no specified codec\n", dai_link->name); return 0; } /* set card codec info */ ret = snd_soc_of_get_dai_link_codecs(dev, codec_node, dai_link); of_node_put(codec_node); if (ret < 0) return dev_err_probe(dev, ret, "%s: codec dai not found\n", dai_link->name); return 0; } static int set_dailink_daifmt(struct snd_soc_card *card, struct device_node *sub_node, struct snd_soc_dai_link *dai_link) { unsigned int daifmt; const char *str; int ret; struct { char *name; unsigned int val; } of_clk_table[] = { { "cpu", SND_SOC_DAIFMT_CBC_CFC }, { "codec", SND_SOC_DAIFMT_CBP_CFP }, }; daifmt = snd_soc_daifmt_parse_format(sub_node, NULL); if (daifmt) { dai_link->dai_fmt &= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; dai_link->dai_fmt |= daifmt; } /* * check "mediatek,clk-provider = xxx" * SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK area */ ret = of_property_read_string(sub_node, "mediatek,clk-provider", &str); if (ret == 0) { int i; for (i = 0; i < ARRAY_SIZE(of_clk_table); i++) { if (strcmp(str, of_clk_table[i].name) == 0) { dai_link->dai_fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; dai_link->dai_fmt |= of_clk_table[i].val; break; } } } return 0; } int parse_dai_link_info(struct snd_soc_card *card) { struct device *dev = card->dev; struct device_node *sub_node; struct snd_soc_dai_link *dai_link; const char *dai_link_name; int ret, i; /* Loop over all the dai link sub nodes */ for_each_available_child_of_node(dev->of_node, sub_node) { if (of_property_read_string(sub_node, "link-name", &dai_link_name)) { of_node_put(sub_node); return -EINVAL; } for_each_card_prelinks(card, i, dai_link) { if (!strcmp(dai_link_name, dai_link->name)) break; } if (i >= card->num_links) { of_node_put(sub_node); return -EINVAL; } ret = set_card_codec_info(card, sub_node, dai_link); if (ret < 0) { of_node_put(sub_node); return ret; } ret = set_dailink_daifmt(card, sub_node, dai_link); if (ret < 0) { of_node_put(sub_node); return ret; } } return 0; } EXPORT_SYMBOL_GPL(parse_dai_link_info); void clean_card_reference(struct snd_soc_card *card) { struct snd_soc_dai_link *dai_link; int i; /* release codec reference gotten by set_card_codec_info */ for_each_card_prelinks(card, i, dai_link) snd_soc_of_put_dai_link_codecs(dai_link); } EXPORT_SYMBOL_GPL(clean_card_reference);