aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/sound/soc/mediatek/common/mtk-soundcard-driver.c
blob: a58e1e3674deca03f71c28c3b38500375d2e5545 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// SPDX-License-Identifier: GPL-2.0
/*
 * mtk-soundcard-driver.c  --  MediaTek soundcard driver common
 *
 * Copyright (c) 2022 MediaTek Inc.
 * Author: Trevor Wu <trevor.wu@mediatek.com>
 */

#include <linux/module.h>
#include <linux/of.h>
#include <sound/soc.h>

#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);