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