File Index
Files and directories
Sign in
FILE

smdk_spdif.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/samsung/smdk_spdif.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2//
  3// smdk_spdif.c - S/PDIF audio for SMDK
  4//
  5// Copyright (C) 2010 Samsung Electronics Co., Ltd.
  6
  7#include <linux/clk.h>
  8#include <linux/module.h>
  9
 10#include <sound/soc.h>
 11
 12#include "spdif.h"
 13
 14/* Audio clock settings are belonged to board specific part. Every
 15 * board can set audio source clock setting which is matched with H/W
 16 * like this function-'set_audio_clock_heirachy'.
 17 */
 18static int set_audio_clock_heirachy(struct platform_device *pdev)
 19{
 20	struct clk *fout_epll, *mout_epll, *sclk_audio0, *sclk_spdif;
 21	int ret = 0;
 22
 23	fout_epll = clk_get(NULL, "fout_epll");
 24	if (IS_ERR(fout_epll)) {
 25		printk(KERN_WARNING "%s: Cannot find fout_epll.\n",
 26				__func__);
 27		return -EINVAL;
 28	}
 29
 30	mout_epll = clk_get(NULL, "mout_epll");
 31	if (IS_ERR(mout_epll)) {
 32		printk(KERN_WARNING "%s: Cannot find mout_epll.\n",
 33				__func__);
 34		ret = -EINVAL;
 35		goto out1;
 36	}
 37
 38	sclk_audio0 = clk_get(&pdev->dev, "sclk_audio");
 39	if (IS_ERR(sclk_audio0)) {
 40		printk(KERN_WARNING "%s: Cannot find sclk_audio.\n",
 41				__func__);
 42		ret = -EINVAL;
 43		goto out2;
 44	}
 45
 46	sclk_spdif = clk_get(NULL, "sclk_spdif");
 47	if (IS_ERR(sclk_spdif)) {
 48		printk(KERN_WARNING "%s: Cannot find sclk_spdif.\n",
 49				__func__);
 50		ret = -EINVAL;
 51		goto out3;
 52	}
 53
 54	/* Set audio clock hierarchy for S/PDIF */
 55	clk_set_parent(mout_epll, fout_epll);
 56	clk_set_parent(sclk_audio0, mout_epll);
 57	clk_set_parent(sclk_spdif, sclk_audio0);
 58
 59	clk_put(sclk_spdif);
 60out3:
 61	clk_put(sclk_audio0);
 62out2:
 63	clk_put(mout_epll);
 64out1:
 65	clk_put(fout_epll);
 66
 67	return ret;
 68}
 69
 70/* We should haved to set clock directly on this part because of clock
 71 * scheme of Samsudng SoCs did not support to set rates from abstrct
 72 * clock of it's hierarchy.
 73 */
 74static int set_audio_clock_rate(unsigned long epll_rate,
 75				unsigned long audio_rate)
 76{
 77	struct clk *fout_epll, *sclk_spdif;
 78
 79	fout_epll = clk_get(NULL, "fout_epll");
 80	if (IS_ERR(fout_epll)) {
 81		printk(KERN_ERR "%s: failed to get fout_epll\n", __func__);
 82		return -ENOENT;
 83	}
 84
 85	clk_set_rate(fout_epll, epll_rate);
 86	clk_put(fout_epll);
 87
 88	sclk_spdif = clk_get(NULL, "sclk_spdif");
 89	if (IS_ERR(sclk_spdif)) {
 90		printk(KERN_ERR "%s: failed to get sclk_spdif\n", __func__);
 91		return -ENOENT;
 92	}
 93
 94	clk_set_rate(sclk_spdif, audio_rate);
 95	clk_put(sclk_spdif);
 96
 97	return 0;
 98}
 99
100static int smdk_hw_params(struct snd_pcm_substream *substream,
101		struct snd_pcm_hw_params *params)
102{
103	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
104	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
105	unsigned long pll_out, rclk_rate;
106	int ret, ratio;
107
108	switch (params_rate(params)) {
109	case 44100:
110		pll_out = 45158400;
111		break;
112	case 32000:
113	case 48000:
114	case 96000:
115		pll_out = 49152000;
116		break;
117	default:
118		return -EINVAL;
119	}
120
121	/* Setting ratio to 512fs helps to use S/PDIF with HDMI without
122	 * modify S/PDIF ASoC machine driver.
123	 */
124	ratio = 512;
125	rclk_rate = params_rate(params) * ratio;
126
127	/* Set audio source clock rates */
128	ret = set_audio_clock_rate(pll_out, rclk_rate);
129	if (ret < 0)
130		return ret;
131
132	/* Set S/PDIF uses internal source clock */
133	ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK,
134					rclk_rate, SND_SOC_CLOCK_IN);
135	if (ret < 0)
136		return ret;
137
138	return ret;
139}
140
141static const struct snd_soc_ops smdk_spdif_ops = {
142	.hw_params = smdk_hw_params,
143};
144
145SND_SOC_DAILINK_DEFS(spdif,
146	DAILINK_COMP_ARRAY(COMP_CPU("samsung-spdif")),
147	DAILINK_COMP_ARRAY(COMP_CODEC("spdif-dit", "dit-hifi")),
148	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-spdif")));
149
150static struct snd_soc_dai_link smdk_dai = {
151	.name = "S/PDIF",
152	.stream_name = "S/PDIF PCM Playback",
153	.ops = &smdk_spdif_ops,
154	SND_SOC_DAILINK_REG(spdif),
155};
156
157static struct snd_soc_card smdk = {
158	.name = "SMDK-S/PDIF",
159	.owner = THIS_MODULE,
160	.dai_link = &smdk_dai,
161	.num_links = 1,
162};
163
164static struct platform_device *smdk_snd_spdif_dit_device;
165static struct platform_device *smdk_snd_spdif_device;
166
167static int __init smdk_init(void)
168{
169	int ret;
170
171	smdk_snd_spdif_dit_device = platform_device_alloc("spdif-dit", -1);
172	if (!smdk_snd_spdif_dit_device)
173		return -ENOMEM;
174
175	ret = platform_device_add(smdk_snd_spdif_dit_device);
176	if (ret)
177		goto err1;
178
179	smdk_snd_spdif_device = platform_device_alloc("soc-audio", -1);
180	if (!smdk_snd_spdif_device) {
181		ret = -ENOMEM;
182		goto err2;
183	}
184
185	platform_set_drvdata(smdk_snd_spdif_device, &smdk);
186
187	ret = platform_device_add(smdk_snd_spdif_device);
188	if (ret)
189		goto err3;
190
191	/* Set audio clock hierarchy manually */
192	ret = set_audio_clock_heirachy(smdk_snd_spdif_device);
193	if (ret)
194		goto err4;
195
196	return 0;
197err4:
198	platform_device_del(smdk_snd_spdif_device);
199err3:
200	platform_device_put(smdk_snd_spdif_device);
201err2:
202	platform_device_del(smdk_snd_spdif_dit_device);
203err1:
204	platform_device_put(smdk_snd_spdif_dit_device);
205	return ret;
206}
207
208static void __exit smdk_exit(void)
209{
210	platform_device_unregister(smdk_snd_spdif_device);
211	platform_device_unregister(smdk_snd_spdif_dit_device);
212}
213
214module_init(smdk_init);
215module_exit(smdk_exit);
216
217MODULE_AUTHOR("Seungwhan Youn, <sw.youn@samsung.com>");
218MODULE_DESCRIPTION("ALSA SoC SMDK+S/PDIF");
219MODULE_LICENSE("GPL");
~/ Read-only file preview.