Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/samsung/smdk_spdif.c
26428 views
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
*/
18
static 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);
60
out3:
61
clk_put(sclk_audio0);
62
out2:
63
clk_put(mout_epll);
64
out1:
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
*/
74
static 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
100
static 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
141
static const struct snd_soc_ops smdk_spdif_ops = {
142
.hw_params = smdk_hw_params,
143
};
144
145
SND_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
150
static 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
157
static 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
164
static struct platform_device *smdk_snd_spdif_dit_device;
165
static struct platform_device *smdk_snd_spdif_device;
166
167
static 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;
197
err4:
198
platform_device_del(smdk_snd_spdif_device);
199
err3:
200
platform_device_put(smdk_snd_spdif_device);
201
err2:
202
platform_device_del(smdk_snd_spdif_dit_device);
203
err1:
204
platform_device_put(smdk_snd_spdif_dit_device);
205
return ret;
206
}
207
208
static 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
214
module_init(smdk_init);
215
module_exit(smdk_exit);
216
217
MODULE_AUTHOR("Seungwhan Youn, <[email protected]>");
218
MODULE_DESCRIPTION("ALSA SoC SMDK+S/PDIF");
219
MODULE_LICENSE("GPL");
220
221