Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/soc/pxa/raumfeld.c
10817 views
1
/*
2
* raumfeld_audio.c -- SoC audio for Raumfeld audio devices
3
*
4
* Copyright (c) 2009 Daniel Mack <[email protected]>
5
*
6
* based on code from:
7
*
8
* Wolfson Microelectronics PLC.
9
* Openedhand Ltd.
10
* Liam Girdwood <[email protected]>
11
* Richard Purdie <[email protected]>
12
*
13
* This program is free software; you can redistribute it and/or modify it
14
* under the terms of the GNU General Public License as published by the
15
* Free Software Foundation; either version 2 of the License, or (at your
16
* option) any later version.
17
*/
18
19
#include <linux/module.h>
20
#include <linux/i2c.h>
21
#include <linux/delay.h>
22
#include <linux/gpio.h>
23
#include <sound/pcm.h>
24
#include <sound/soc.h>
25
26
#include <asm/mach-types.h>
27
28
#include "pxa-ssp.h"
29
30
#define GPIO_SPDIF_RESET (38)
31
#define GPIO_MCLK_RESET (111)
32
#define GPIO_CODEC_RESET (120)
33
34
static struct i2c_client *max9486_client;
35
static struct i2c_board_info max9486_hwmon_info = {
36
I2C_BOARD_INFO("max9485", 0x63),
37
};
38
39
#define MAX9485_MCLK_FREQ_112896 0x22
40
#define MAX9485_MCLK_FREQ_122880 0x23
41
#define MAX9485_MCLK_FREQ_225792 0x32
42
#define MAX9485_MCLK_FREQ_245760 0x33
43
44
static void set_max9485_clk(char clk)
45
{
46
i2c_master_send(max9486_client, &clk, 1);
47
}
48
49
static void raumfeld_enable_audio(bool en)
50
{
51
if (en) {
52
gpio_set_value(GPIO_MCLK_RESET, 1);
53
54
/* wait some time to let the clocks become stable */
55
msleep(100);
56
57
gpio_set_value(GPIO_SPDIF_RESET, 1);
58
gpio_set_value(GPIO_CODEC_RESET, 1);
59
} else {
60
gpio_set_value(GPIO_MCLK_RESET, 0);
61
gpio_set_value(GPIO_SPDIF_RESET, 0);
62
gpio_set_value(GPIO_CODEC_RESET, 0);
63
}
64
}
65
66
/* CS4270 */
67
static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream)
68
{
69
struct snd_soc_pcm_runtime *rtd = substream->private_data;
70
struct snd_soc_dai *codec_dai = rtd->codec_dai;
71
72
/* set freq to 0 to enable all possible codec sample rates */
73
return snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0);
74
}
75
76
static void raumfeld_cs4270_shutdown(struct snd_pcm_substream *substream)
77
{
78
struct snd_soc_pcm_runtime *rtd = substream->private_data;
79
struct snd_soc_dai *codec_dai = rtd->codec_dai;
80
81
/* set freq to 0 to enable all possible codec sample rates */
82
snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0);
83
}
84
85
static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream,
86
struct snd_pcm_hw_params *params)
87
{
88
struct snd_soc_pcm_runtime *rtd = substream->private_data;
89
struct snd_soc_dai *codec_dai = rtd->codec_dai;
90
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
91
unsigned int fmt, clk = 0;
92
int ret = 0;
93
94
switch (params_rate(params)) {
95
case 44100:
96
set_max9485_clk(MAX9485_MCLK_FREQ_112896);
97
clk = 11289600;
98
break;
99
case 48000:
100
set_max9485_clk(MAX9485_MCLK_FREQ_122880);
101
clk = 12288000;
102
break;
103
case 88200:
104
set_max9485_clk(MAX9485_MCLK_FREQ_225792);
105
clk = 22579200;
106
break;
107
case 96000:
108
set_max9485_clk(MAX9485_MCLK_FREQ_245760);
109
clk = 24576000;
110
break;
111
default:
112
return -EINVAL;
113
}
114
115
fmt = SND_SOC_DAIFMT_I2S |
116
SND_SOC_DAIFMT_NB_NF |
117
SND_SOC_DAIFMT_CBS_CFS;
118
119
/* setup the CODEC DAI */
120
ret = snd_soc_dai_set_fmt(codec_dai, fmt);
121
if (ret < 0)
122
return ret;
123
124
ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0);
125
if (ret < 0)
126
return ret;
127
128
/* setup the CPU DAI */
129
ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk);
130
if (ret < 0)
131
return ret;
132
133
ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
134
if (ret < 0)
135
return ret;
136
137
ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4);
138
if (ret < 0)
139
return ret;
140
141
ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1);
142
if (ret < 0)
143
return ret;
144
145
return 0;
146
}
147
148
static struct snd_soc_ops raumfeld_cs4270_ops = {
149
.startup = raumfeld_cs4270_startup,
150
.shutdown = raumfeld_cs4270_shutdown,
151
.hw_params = raumfeld_cs4270_hw_params,
152
};
153
154
static int raumfeld_analog_suspend(struct snd_soc_card *card)
155
{
156
raumfeld_enable_audio(false);
157
return 0;
158
}
159
160
static int raumfeld_analog_resume(struct snd_soc_card *card)
161
{
162
raumfeld_enable_audio(true);
163
return 0;
164
}
165
166
/* AK4104 */
167
168
static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream,
169
struct snd_pcm_hw_params *params)
170
{
171
struct snd_soc_pcm_runtime *rtd = substream->private_data;
172
struct snd_soc_dai *codec_dai = rtd->codec_dai;
173
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
174
int fmt, ret = 0, clk = 0;
175
176
switch (params_rate(params)) {
177
case 44100:
178
set_max9485_clk(MAX9485_MCLK_FREQ_112896);
179
clk = 11289600;
180
break;
181
case 48000:
182
set_max9485_clk(MAX9485_MCLK_FREQ_122880);
183
clk = 12288000;
184
break;
185
case 88200:
186
set_max9485_clk(MAX9485_MCLK_FREQ_225792);
187
clk = 22579200;
188
break;
189
case 96000:
190
set_max9485_clk(MAX9485_MCLK_FREQ_245760);
191
clk = 24576000;
192
break;
193
default:
194
return -EINVAL;
195
}
196
197
fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF;
198
199
/* setup the CODEC DAI */
200
ret = snd_soc_dai_set_fmt(codec_dai, fmt | SND_SOC_DAIFMT_CBS_CFS);
201
if (ret < 0)
202
return ret;
203
204
/* setup the CPU DAI */
205
ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk);
206
if (ret < 0)
207
return ret;
208
209
ret = snd_soc_dai_set_fmt(cpu_dai, fmt | SND_SOC_DAIFMT_CBS_CFS);
210
if (ret < 0)
211
return ret;
212
213
ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4);
214
if (ret < 0)
215
return ret;
216
217
ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1);
218
if (ret < 0)
219
return ret;
220
221
return 0;
222
}
223
224
static struct snd_soc_ops raumfeld_ak4104_ops = {
225
.hw_params = raumfeld_ak4104_hw_params,
226
};
227
228
#define DAI_LINK_CS4270 \
229
{ \
230
.name = "CS4270", \
231
.stream_name = "CS4270", \
232
.cpu_dai_name = "pxa-ssp-dai.0", \
233
.platform_name = "pxa-pcm-audio", \
234
.codec_dai_name = "cs4270-hifi", \
235
.codec_name = "cs4270-codec.0-0048", \
236
.ops = &raumfeld_cs4270_ops, \
237
}
238
239
#define DAI_LINK_AK4104 \
240
{ \
241
.name = "ak4104", \
242
.stream_name = "Playback", \
243
.cpu_dai_name = "pxa-ssp-dai.1", \
244
.codec_dai_name = "ak4104-hifi", \
245
.platform_name = "pxa-pcm-audio", \
246
.ops = &raumfeld_ak4104_ops, \
247
.codec_name = "spi0.0", \
248
}
249
250
static struct snd_soc_dai_link snd_soc_raumfeld_connector_dai[] =
251
{
252
DAI_LINK_CS4270,
253
DAI_LINK_AK4104,
254
};
255
256
static struct snd_soc_dai_link snd_soc_raumfeld_speaker_dai[] =
257
{
258
DAI_LINK_CS4270,
259
};
260
261
static struct snd_soc_card snd_soc_raumfeld_connector = {
262
.name = "Raumfeld Connector",
263
.dai_link = snd_soc_raumfeld_connector_dai,
264
.num_links = ARRAY_SIZE(snd_soc_raumfeld_connector_dai),
265
.suspend_post = raumfeld_analog_suspend,
266
.resume_pre = raumfeld_analog_resume,
267
};
268
269
static struct snd_soc_card snd_soc_raumfeld_speaker = {
270
.name = "Raumfeld Speaker",
271
.dai_link = snd_soc_raumfeld_speaker_dai,
272
.num_links = ARRAY_SIZE(snd_soc_raumfeld_speaker_dai),
273
.suspend_post = raumfeld_analog_suspend,
274
.resume_pre = raumfeld_analog_resume,
275
};
276
277
static struct platform_device *raumfeld_audio_device;
278
279
static int __init raumfeld_audio_init(void)
280
{
281
int ret;
282
283
if (!machine_is_raumfeld_speaker() &&
284
!machine_is_raumfeld_connector())
285
return 0;
286
287
max9486_client = i2c_new_device(i2c_get_adapter(0),
288
&max9486_hwmon_info);
289
290
if (!max9486_client)
291
return -ENOMEM;
292
293
set_max9485_clk(MAX9485_MCLK_FREQ_122880);
294
295
/* Register analog device */
296
raumfeld_audio_device = platform_device_alloc("soc-audio", 0);
297
if (!raumfeld_audio_device)
298
return -ENOMEM;
299
300
if (machine_is_raumfeld_speaker())
301
platform_set_drvdata(raumfeld_audio_device,
302
&snd_soc_raumfeld_speaker);
303
304
if (machine_is_raumfeld_connector())
305
platform_set_drvdata(raumfeld_audio_device,
306
&snd_soc_raumfeld_connector);
307
308
ret = platform_device_add(raumfeld_audio_device);
309
if (ret < 0)
310
return ret;
311
312
raumfeld_enable_audio(true);
313
return 0;
314
}
315
316
static void __exit raumfeld_audio_exit(void)
317
{
318
raumfeld_enable_audio(false);
319
320
platform_device_unregister(raumfeld_audio_device);
321
322
i2c_unregister_device(max9486_client);
323
324
gpio_free(GPIO_MCLK_RESET);
325
gpio_free(GPIO_CODEC_RESET);
326
gpio_free(GPIO_SPDIF_RESET);
327
}
328
329
module_init(raumfeld_audio_init);
330
module_exit(raumfeld_audio_exit);
331
332
/* Module information */
333
MODULE_AUTHOR("Daniel Mack <[email protected]>");
334
MODULE_DESCRIPTION("Raumfeld audio SoC");
335
MODULE_LICENSE("GPL");
336
337