xref: /OK3568_Linux_fs/kernel/sound/soc/rockchip/rockchip_hdmi.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Rockchip machine ASoC driver for Rockchip HDMI audio
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2021 Rockchip Electronics Co., Ltd
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Authors: XiaoTan Luo <lxt@rock-chips.com>
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <linux/module.h>
12*4882a593Smuzhiyun #include <linux/platform_device.h>
13*4882a593Smuzhiyun #include <linux/slab.h>
14*4882a593Smuzhiyun #include <sound/core.h>
15*4882a593Smuzhiyun #include <sound/jack.h>
16*4882a593Smuzhiyun #include <sound/pcm.h>
17*4882a593Smuzhiyun #include <sound/pcm_params.h>
18*4882a593Smuzhiyun #include <sound/soc.h>
19*4882a593Smuzhiyun #include <sound/soc-dapm.h>
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #define DRV_NAME "rk-hdmi-sound"
22*4882a593Smuzhiyun #define MAX_CODECS	2
23*4882a593Smuzhiyun #define DEFAULT_MCLK_FS	256
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun struct rk_hdmi_data {
26*4882a593Smuzhiyun 	struct snd_soc_card	card;
27*4882a593Smuzhiyun 	struct snd_soc_dai_link	dai;
28*4882a593Smuzhiyun 	struct snd_soc_jack	hdmi_jack;
29*4882a593Smuzhiyun 	struct snd_soc_jack_pin	hdmi_jack_pin;
30*4882a593Smuzhiyun 	unsigned int		mclk_fs;
31*4882a593Smuzhiyun 	bool			jack_det;
32*4882a593Smuzhiyun };
33*4882a593Smuzhiyun 
rk_hdmi_fill_widget_info(struct device * dev,struct snd_soc_dapm_widget * w,enum snd_soc_dapm_type id,void * priv,const char * wname,const char * stream,struct snd_kcontrol_new * wc,int numkc,int (* event)(struct snd_soc_dapm_widget *,struct snd_kcontrol *,int),unsigned short event_flags)34*4882a593Smuzhiyun static int rk_hdmi_fill_widget_info(struct device *dev,
35*4882a593Smuzhiyun 		struct snd_soc_dapm_widget *w, enum snd_soc_dapm_type id,
36*4882a593Smuzhiyun 		void *priv, const char *wname, const char *stream,
37*4882a593Smuzhiyun 		struct snd_kcontrol_new *wc, int numkc,
38*4882a593Smuzhiyun 		int (*event)(struct snd_soc_dapm_widget *,
39*4882a593Smuzhiyun 		struct snd_kcontrol *, int), unsigned short event_flags)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun 	w->id = id;
42*4882a593Smuzhiyun 	w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
43*4882a593Smuzhiyun 	if (!w->name)
44*4882a593Smuzhiyun 		return -ENOMEM;
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 	w->sname = stream;
47*4882a593Smuzhiyun 	w->reg = SND_SOC_NOPM;
48*4882a593Smuzhiyun 	w->shift = 0;
49*4882a593Smuzhiyun 	w->kcontrol_news = wc;
50*4882a593Smuzhiyun 	w->num_kcontrols = numkc;
51*4882a593Smuzhiyun 	w->priv = priv;
52*4882a593Smuzhiyun 	w->event = event;
53*4882a593Smuzhiyun 	w->event_flags = event_flags;
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	return 0;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun 
rk_dailink_init(struct snd_soc_pcm_runtime * rtd)58*4882a593Smuzhiyun static int rk_dailink_init(struct snd_soc_pcm_runtime *rtd)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
61*4882a593Smuzhiyun 	struct snd_soc_card *card = rtd->card;
62*4882a593Smuzhiyun 	struct rk_hdmi_data *rk_data = snd_soc_card_get_drvdata(rtd->card);
63*4882a593Smuzhiyun 	struct device *dev = rtd->card->dev;
64*4882a593Smuzhiyun 	int ret = 0;
65*4882a593Smuzhiyun 	struct snd_soc_dapm_widget *widgets;
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	if (!rk_data->jack_det)
68*4882a593Smuzhiyun 		return 0;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	widgets = devm_kcalloc(card->dapm.dev, 1,
71*4882a593Smuzhiyun 			       sizeof(*widgets), GFP_KERNEL);
72*4882a593Smuzhiyun 	if (!widgets)
73*4882a593Smuzhiyun 		return -ENOMEM;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	ret = rk_hdmi_fill_widget_info(card->dapm.dev, widgets,
76*4882a593Smuzhiyun 				       snd_soc_dapm_line, NULL,
77*4882a593Smuzhiyun 				       rk_data->hdmi_jack_pin.pin,
78*4882a593Smuzhiyun 				       NULL, NULL, 0, NULL, 0);
79*4882a593Smuzhiyun 	if (ret < 0)
80*4882a593Smuzhiyun 		return ret;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	ret = snd_soc_dapm_new_controls(&card->dapm, widgets, 1);
83*4882a593Smuzhiyun 	if (ret < 0)
84*4882a593Smuzhiyun 		return ret;
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	ret = snd_soc_dapm_new_widgets(rtd->card);
87*4882a593Smuzhiyun 	if (ret < 0)
88*4882a593Smuzhiyun 		return ret;
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	ret = snd_soc_card_jack_new(rtd->card,
91*4882a593Smuzhiyun 				    rk_data->hdmi_jack_pin.pin,
92*4882a593Smuzhiyun 				    rk_data->hdmi_jack_pin.mask,
93*4882a593Smuzhiyun 				    &rk_data->hdmi_jack,
94*4882a593Smuzhiyun 				    &rk_data->hdmi_jack_pin, 1);
95*4882a593Smuzhiyun 	if (ret) {
96*4882a593Smuzhiyun 		dev_err(dev, "Can't new HDMI Jack %d\n", ret);
97*4882a593Smuzhiyun 		return ret;
98*4882a593Smuzhiyun 	}
99*4882a593Smuzhiyun 	return snd_soc_component_set_jack(codec_dai->component,
100*4882a593Smuzhiyun 					  &rk_data->hdmi_jack, NULL);
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
rk_hdmi_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)104*4882a593Smuzhiyun static int rk_hdmi_hw_params(struct snd_pcm_substream *substream,
105*4882a593Smuzhiyun 				struct snd_pcm_hw_params *params)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
108*4882a593Smuzhiyun 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
109*4882a593Smuzhiyun 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
110*4882a593Smuzhiyun 	struct rk_hdmi_data *rk_data = snd_soc_card_get_drvdata(rtd->card);
111*4882a593Smuzhiyun 	unsigned int mclk;
112*4882a593Smuzhiyun 	int ret;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	mclk = params_rate(params) * rk_data->mclk_fs;
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	ret = snd_soc_dai_set_sysclk(codec_dai, substream->stream, mclk,
117*4882a593Smuzhiyun 				     SND_SOC_CLOCK_IN);
118*4882a593Smuzhiyun 	if (ret && ret != -ENOTSUPP) {
119*4882a593Smuzhiyun 		dev_err(codec_dai->dev,
120*4882a593Smuzhiyun 			"Set codec_dai sysclk failed: %d\n", ret);
121*4882a593Smuzhiyun 		return ret;
122*4882a593Smuzhiyun 	}
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	ret = snd_soc_dai_set_sysclk(cpu_dai, substream->stream, mclk,
125*4882a593Smuzhiyun 				     SND_SOC_CLOCK_OUT);
126*4882a593Smuzhiyun 	if (ret && ret != -ENOTSUPP) {
127*4882a593Smuzhiyun 		dev_err(cpu_dai->dev,
128*4882a593Smuzhiyun 			"Set cpu_dai sysclk failed: %d\n", ret);
129*4882a593Smuzhiyun 		return ret;
130*4882a593Smuzhiyun 	}
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	return 0;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun static const struct snd_soc_ops rk_ops = {
137*4882a593Smuzhiyun 	.hw_params = rk_hdmi_hw_params,
138*4882a593Smuzhiyun };
139*4882a593Smuzhiyun 
rk_hdmi_parse_daifmt(struct device_node * node,struct device_node * cpu,char * prefix)140*4882a593Smuzhiyun static unsigned int rk_hdmi_parse_daifmt(struct device_node *node,
141*4882a593Smuzhiyun 				struct device_node *cpu,
142*4882a593Smuzhiyun 				char *prefix)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun 	struct device_node *bitclkmaster = NULL;
145*4882a593Smuzhiyun 	struct device_node *framemaster = NULL;
146*4882a593Smuzhiyun 	unsigned int daifmt;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	daifmt = snd_soc_of_parse_daifmt(node, prefix,
149*4882a593Smuzhiyun 					 &bitclkmaster, &framemaster);
150*4882a593Smuzhiyun 	daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	if (!bitclkmaster || cpu == bitclkmaster)
153*4882a593Smuzhiyun 		daifmt |= (!framemaster || cpu == framemaster) ?
154*4882a593Smuzhiyun 			SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM;
155*4882a593Smuzhiyun 	else
156*4882a593Smuzhiyun 		daifmt |= (!framemaster || cpu == framemaster) ?
157*4882a593Smuzhiyun 			SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM;
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	/*
160*4882a593Smuzhiyun 	 * If there is NULL format means that the format isn't specified, we
161*4882a593Smuzhiyun 	 * need to set i2s format by default.
162*4882a593Smuzhiyun 	 */
163*4882a593Smuzhiyun 	if (!(daifmt & SND_SOC_DAIFMT_FORMAT_MASK))
164*4882a593Smuzhiyun 		daifmt |= SND_SOC_DAIFMT_I2S;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	of_node_put(bitclkmaster);
167*4882a593Smuzhiyun 	of_node_put(framemaster);
168*4882a593Smuzhiyun 	return daifmt;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun 
rk_hdmi_probe(struct platform_device * pdev)171*4882a593Smuzhiyun static int rk_hdmi_probe(struct platform_device *pdev)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun 	struct device_node *np = pdev->dev.of_node;
174*4882a593Smuzhiyun 	struct snd_soc_dai_link_component *codecs;
175*4882a593Smuzhiyun 	struct snd_soc_dai_link_component *platforms;
176*4882a593Smuzhiyun 	struct snd_soc_dai_link_component *cpus;
177*4882a593Smuzhiyun 	struct of_phandle_args args;
178*4882a593Smuzhiyun 	struct device_node *cpu_np;
179*4882a593Smuzhiyun 	struct rk_hdmi_data *rk_data;
180*4882a593Smuzhiyun 	int count;
181*4882a593Smuzhiyun 	u32 val;
182*4882a593Smuzhiyun 	int ret = 0, i = 0, idx = 0;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	rk_data = devm_kzalloc(&pdev->dev, sizeof(*rk_data), GFP_KERNEL);
185*4882a593Smuzhiyun 	if (!rk_data)
186*4882a593Smuzhiyun 		return -ENOMEM;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	cpus = devm_kzalloc(&pdev->dev, sizeof(*cpus), GFP_KERNEL);
189*4882a593Smuzhiyun 	if (!cpus)
190*4882a593Smuzhiyun 		return -ENOMEM;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	platforms = devm_kzalloc(&pdev->dev, sizeof(*platforms), GFP_KERNEL);
193*4882a593Smuzhiyun 	if (!platforms)
194*4882a593Smuzhiyun 		return -ENOMEM;
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	rk_data->card.dev = &pdev->dev;
197*4882a593Smuzhiyun 	rk_data->dai.init = &rk_dailink_init;
198*4882a593Smuzhiyun 	rk_data->dai.ops = &rk_ops;
199*4882a593Smuzhiyun 	rk_data->dai.cpus = cpus;
200*4882a593Smuzhiyun 	rk_data->dai.platforms = platforms;
201*4882a593Smuzhiyun 	rk_data->dai.num_cpus = 1;
202*4882a593Smuzhiyun 	rk_data->dai.num_platforms = 1;
203*4882a593Smuzhiyun 	/* Parse the card name from DT */
204*4882a593Smuzhiyun 	ret = snd_soc_of_parse_card_name(&rk_data->card, "rockchip,card-name");
205*4882a593Smuzhiyun 	if (ret < 0)
206*4882a593Smuzhiyun 		return ret;
207*4882a593Smuzhiyun 	rk_data->dai.name = rk_data->card.name;
208*4882a593Smuzhiyun 	rk_data->dai.stream_name = rk_data->card.name;
209*4882a593Smuzhiyun 	count = of_count_phandle_with_args(np, "rockchip,codec", "#sound-dai-cells");
210*4882a593Smuzhiyun 	if (count < 0 || count > MAX_CODECS)
211*4882a593Smuzhiyun 		return -EINVAL;
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	/* refine codecs, remove unavailable node */
214*4882a593Smuzhiyun 	for (i = 0; i < count; i++) {
215*4882a593Smuzhiyun 		ret = of_parse_phandle_with_args(np, "rockchip,codec", "#sound-dai-cells", i, &args);
216*4882a593Smuzhiyun 		if (ret) {
217*4882a593Smuzhiyun 			dev_err(&pdev->dev, "error getting codec phandle index %d\n", i);
218*4882a593Smuzhiyun 			return -ENODEV;
219*4882a593Smuzhiyun 		}
220*4882a593Smuzhiyun 		if (of_device_is_available(args.np))
221*4882a593Smuzhiyun 			idx++;
222*4882a593Smuzhiyun 		of_node_put(args.np);
223*4882a593Smuzhiyun 	}
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	if (!idx)
226*4882a593Smuzhiyun 		return -ENODEV;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	codecs = devm_kcalloc(&pdev->dev, idx,
229*4882a593Smuzhiyun 			      sizeof(*codecs), GFP_KERNEL);
230*4882a593Smuzhiyun 	if (!codecs)
231*4882a593Smuzhiyun 		return -ENOMEM;
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	rk_data->dai.codecs = codecs;
234*4882a593Smuzhiyun 	rk_data->dai.num_codecs = idx;
235*4882a593Smuzhiyun 	idx = 0;
236*4882a593Smuzhiyun 	for (i = 0; i < count; i++) {
237*4882a593Smuzhiyun 		ret = of_parse_phandle_with_args(np, "rockchip,codec", "#sound-dai-cells", i, &args);
238*4882a593Smuzhiyun 		if (ret) {
239*4882a593Smuzhiyun 			dev_err(&pdev->dev, "error getting codec phandle index %d\n", i);
240*4882a593Smuzhiyun 			return -ENODEV;
241*4882a593Smuzhiyun 		}
242*4882a593Smuzhiyun 		if (!of_device_is_available(args.np)) {
243*4882a593Smuzhiyun 			of_node_put(args.np);
244*4882a593Smuzhiyun 			continue;
245*4882a593Smuzhiyun 		}
246*4882a593Smuzhiyun 		codecs[idx].of_node = args.np;
247*4882a593Smuzhiyun 		ret = snd_soc_get_dai_name(&args, &codecs[idx].dai_name);
248*4882a593Smuzhiyun 		if (ret)
249*4882a593Smuzhiyun 			return ret;
250*4882a593Smuzhiyun 		idx++;
251*4882a593Smuzhiyun 	}
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	cpu_np = of_parse_phandle(np, "rockchip,cpu", 0);
254*4882a593Smuzhiyun 	if (!cpu_np)
255*4882a593Smuzhiyun 		return -ENODEV;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	rk_data->dai.dai_fmt = rk_hdmi_parse_daifmt(np, cpu_np, "rockchip,");
258*4882a593Smuzhiyun 	rk_data->mclk_fs = DEFAULT_MCLK_FS;
259*4882a593Smuzhiyun 	if (!of_property_read_u32(np, "rockchip,mclk-fs", &val))
260*4882a593Smuzhiyun 		rk_data->mclk_fs = val;
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	rk_data->jack_det =
263*4882a593Smuzhiyun 		of_property_read_bool(np, "rockchip,jack-det");
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	rk_data->dai.cpus->of_node = cpu_np;
266*4882a593Smuzhiyun 	rk_data->dai.platforms->of_node = cpu_np;
267*4882a593Smuzhiyun 	of_node_put(cpu_np);
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	rk_data->hdmi_jack_pin.pin = rk_data->card.name;
270*4882a593Smuzhiyun 	rk_data->hdmi_jack_pin.mask = SND_JACK_LINEOUT;
271*4882a593Smuzhiyun 	rk_data->card.num_links = 1;
272*4882a593Smuzhiyun 	rk_data->card.owner = THIS_MODULE;
273*4882a593Smuzhiyun 	rk_data->card.dai_link = &rk_data->dai;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	snd_soc_card_set_drvdata(&rk_data->card, rk_data);
276*4882a593Smuzhiyun 	ret = devm_snd_soc_register_card(&pdev->dev, &rk_data->card);
277*4882a593Smuzhiyun 	if (ret == -EPROBE_DEFER)
278*4882a593Smuzhiyun 		return -EPROBE_DEFER;
279*4882a593Smuzhiyun 	if (ret) {
280*4882a593Smuzhiyun 		dev_err(&pdev->dev, "card register failed %d\n", ret);
281*4882a593Smuzhiyun 		return ret;
282*4882a593Smuzhiyun 	}
283*4882a593Smuzhiyun 	platform_set_drvdata(pdev, &rk_data->card);
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun 	return ret;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun static const struct of_device_id rockchip_sound_of_match[] = {
289*4882a593Smuzhiyun 	{ .compatible = "rockchip,hdmi", },
290*4882a593Smuzhiyun 	{},
291*4882a593Smuzhiyun };
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, rockchip_sound_of_match);
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun static struct platform_driver rockchip_sound_driver = {
296*4882a593Smuzhiyun 	.probe = rk_hdmi_probe,
297*4882a593Smuzhiyun 	.driver = {
298*4882a593Smuzhiyun 		.name = DRV_NAME,
299*4882a593Smuzhiyun 		.pm = &snd_soc_pm_ops,
300*4882a593Smuzhiyun 		.of_match_table = rockchip_sound_of_match,
301*4882a593Smuzhiyun 	},
302*4882a593Smuzhiyun };
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun module_platform_driver(rockchip_sound_driver);
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun MODULE_AUTHOR("XiaoTan Luo <lxt@rock-chips.com>");
307*4882a593Smuzhiyun MODULE_DESCRIPTION("Rockchip HDMI ASoC machine driver");
308*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
309*4882a593Smuzhiyun MODULE_ALIAS("platform:" DRV_NAME);
310