Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/au1x/i2sc.c
26427 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Au1000/Au1500/Au1100 I2S controller driver for ASoC
4
*
5
* (c) 2011 Manuel Lauss <[email protected]>
6
*
7
* Note: clock supplied to the I2S controller must be 256x samplerate.
8
*/
9
10
#include <linux/init.h>
11
#include <linux/module.h>
12
#include <linux/slab.h>
13
#include <linux/suspend.h>
14
#include <sound/core.h>
15
#include <sound/pcm.h>
16
#include <sound/initval.h>
17
#include <sound/soc.h>
18
#include <asm/mach-au1x00/au1000.h>
19
20
#include "psc.h"
21
22
#define I2S_RXTX 0x00
23
#define I2S_CFG 0x04
24
#define I2S_ENABLE 0x08
25
26
#define CFG_XU (1 << 25) /* tx underflow */
27
#define CFG_XO (1 << 24)
28
#define CFG_RU (1 << 23)
29
#define CFG_RO (1 << 22)
30
#define CFG_TR (1 << 21)
31
#define CFG_TE (1 << 20)
32
#define CFG_TF (1 << 19)
33
#define CFG_RR (1 << 18)
34
#define CFG_RF (1 << 17)
35
#define CFG_ICK (1 << 12) /* clock invert */
36
#define CFG_PD (1 << 11) /* set to make I2SDIO INPUT */
37
#define CFG_LB (1 << 10) /* loopback */
38
#define CFG_IC (1 << 9) /* word select invert */
39
#define CFG_FM_I2S (0 << 7) /* I2S format */
40
#define CFG_FM_LJ (1 << 7) /* left-justified */
41
#define CFG_FM_RJ (2 << 7) /* right-justified */
42
#define CFG_FM_MASK (3 << 7)
43
#define CFG_TN (1 << 6) /* tx fifo en */
44
#define CFG_RN (1 << 5) /* rx fifo en */
45
#define CFG_SZ_8 (0x08)
46
#define CFG_SZ_16 (0x10)
47
#define CFG_SZ_18 (0x12)
48
#define CFG_SZ_20 (0x14)
49
#define CFG_SZ_24 (0x18)
50
#define CFG_SZ_MASK (0x1f)
51
#define EN_D (1 << 1) /* DISable */
52
#define EN_CE (1 << 0) /* clock enable */
53
54
/* only limited by clock generator and board design */
55
#define AU1XI2SC_RATES \
56
SNDRV_PCM_RATE_CONTINUOUS
57
58
#define AU1XI2SC_FMTS \
59
(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \
60
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
61
SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE | \
62
SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE | \
63
SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_U18_3BE | \
64
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
65
SNDRV_PCM_FMTBIT_S20_3BE | SNDRV_PCM_FMTBIT_U20_3BE | \
66
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
67
SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE | \
68
0)
69
70
static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg)
71
{
72
return __raw_readl(ctx->mmio + reg);
73
}
74
75
static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v)
76
{
77
__raw_writel(v, ctx->mmio + reg);
78
wmb();
79
}
80
81
static int au1xi2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
82
{
83
struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(cpu_dai);
84
unsigned long c;
85
int ret;
86
87
ret = -EINVAL;
88
c = ctx->cfg;
89
90
c &= ~CFG_FM_MASK;
91
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
92
case SND_SOC_DAIFMT_I2S:
93
c |= CFG_FM_I2S;
94
break;
95
case SND_SOC_DAIFMT_MSB:
96
c |= CFG_FM_RJ;
97
break;
98
case SND_SOC_DAIFMT_LSB:
99
c |= CFG_FM_LJ;
100
break;
101
default:
102
goto out;
103
}
104
105
c &= ~(CFG_IC | CFG_ICK); /* IB-IF */
106
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
107
case SND_SOC_DAIFMT_NB_NF:
108
c |= CFG_IC | CFG_ICK;
109
break;
110
case SND_SOC_DAIFMT_NB_IF:
111
c |= CFG_IC;
112
break;
113
case SND_SOC_DAIFMT_IB_NF:
114
c |= CFG_ICK;
115
break;
116
case SND_SOC_DAIFMT_IB_IF:
117
break;
118
default:
119
goto out;
120
}
121
122
/* I2S controller only supports provider */
123
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
124
case SND_SOC_DAIFMT_BP_FP: /* CODEC consumer */
125
break;
126
default:
127
goto out;
128
}
129
130
ret = 0;
131
ctx->cfg = c;
132
out:
133
return ret;
134
}
135
136
static int au1xi2s_trigger(struct snd_pcm_substream *substream,
137
int cmd, struct snd_soc_dai *dai)
138
{
139
struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
140
int stype = SUBSTREAM_TYPE(substream);
141
142
switch (cmd) {
143
case SNDRV_PCM_TRIGGER_START:
144
case SNDRV_PCM_TRIGGER_RESUME:
145
/* power up */
146
WR(ctx, I2S_ENABLE, EN_D | EN_CE);
147
WR(ctx, I2S_ENABLE, EN_CE);
148
ctx->cfg |= (stype == PCM_TX) ? CFG_TN : CFG_RN;
149
WR(ctx, I2S_CFG, ctx->cfg);
150
break;
151
case SNDRV_PCM_TRIGGER_STOP:
152
case SNDRV_PCM_TRIGGER_SUSPEND:
153
ctx->cfg &= ~((stype == PCM_TX) ? CFG_TN : CFG_RN);
154
WR(ctx, I2S_CFG, ctx->cfg);
155
WR(ctx, I2S_ENABLE, EN_D); /* power off */
156
break;
157
default:
158
return -EINVAL;
159
}
160
161
return 0;
162
}
163
164
static unsigned long msbits_to_reg(int msbits)
165
{
166
switch (msbits) {
167
case 8:
168
return CFG_SZ_8;
169
case 16:
170
return CFG_SZ_16;
171
case 18:
172
return CFG_SZ_18;
173
case 20:
174
return CFG_SZ_20;
175
case 24:
176
return CFG_SZ_24;
177
}
178
return 0;
179
}
180
181
static int au1xi2s_hw_params(struct snd_pcm_substream *substream,
182
struct snd_pcm_hw_params *params,
183
struct snd_soc_dai *dai)
184
{
185
struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
186
unsigned long v;
187
188
v = msbits_to_reg(params->msbits);
189
if (!v)
190
return -EINVAL;
191
192
ctx->cfg &= ~CFG_SZ_MASK;
193
ctx->cfg |= v;
194
return 0;
195
}
196
197
static int au1xi2s_startup(struct snd_pcm_substream *substream,
198
struct snd_soc_dai *dai)
199
{
200
struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
201
snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]);
202
return 0;
203
}
204
205
static const struct snd_soc_dai_ops au1xi2s_dai_ops = {
206
.startup = au1xi2s_startup,
207
.trigger = au1xi2s_trigger,
208
.hw_params = au1xi2s_hw_params,
209
.set_fmt = au1xi2s_set_fmt,
210
};
211
212
static struct snd_soc_dai_driver au1xi2s_dai_driver = {
213
.symmetric_rate = 1,
214
.playback = {
215
.rates = AU1XI2SC_RATES,
216
.formats = AU1XI2SC_FMTS,
217
.channels_min = 2,
218
.channels_max = 2,
219
},
220
.capture = {
221
.rates = AU1XI2SC_RATES,
222
.formats = AU1XI2SC_FMTS,
223
.channels_min = 2,
224
.channels_max = 2,
225
},
226
.ops = &au1xi2s_dai_ops,
227
};
228
229
static const struct snd_soc_component_driver au1xi2s_component = {
230
.name = "au1xi2s",
231
.legacy_dai_naming = 1,
232
};
233
234
static int au1xi2s_drvprobe(struct platform_device *pdev)
235
{
236
struct resource *iores, *dmares;
237
struct au1xpsc_audio_data *ctx;
238
239
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
240
if (!ctx)
241
return -ENOMEM;
242
243
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
244
if (!iores)
245
return -ENODEV;
246
247
if (!devm_request_mem_region(&pdev->dev, iores->start,
248
resource_size(iores),
249
pdev->name))
250
return -EBUSY;
251
252
ctx->mmio = devm_ioremap(&pdev->dev, iores->start,
253
resource_size(iores));
254
if (!ctx->mmio)
255
return -EBUSY;
256
257
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
258
if (!dmares)
259
return -EBUSY;
260
ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = dmares->start;
261
262
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 1);
263
if (!dmares)
264
return -EBUSY;
265
ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = dmares->start;
266
267
platform_set_drvdata(pdev, ctx);
268
269
return snd_soc_register_component(&pdev->dev, &au1xi2s_component,
270
&au1xi2s_dai_driver, 1);
271
}
272
273
static void au1xi2s_drvremove(struct platform_device *pdev)
274
{
275
struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
276
277
snd_soc_unregister_component(&pdev->dev);
278
279
WR(ctx, I2S_ENABLE, EN_D); /* clock off, disable */
280
}
281
282
static int au1xi2s_drvsuspend(struct device *dev)
283
{
284
struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
285
286
WR(ctx, I2S_ENABLE, EN_D); /* clock off, disable */
287
288
return 0;
289
}
290
291
static int au1xi2s_drvresume(struct device *dev)
292
{
293
return 0;
294
}
295
296
static DEFINE_SIMPLE_DEV_PM_OPS(au1xi2sc_pmops, au1xi2s_drvsuspend,
297
au1xi2s_drvresume);
298
299
static struct platform_driver au1xi2s_driver = {
300
.driver = {
301
.name = "alchemy-i2sc",
302
.pm = pm_ptr(&au1xi2sc_pmops),
303
},
304
.probe = au1xi2s_drvprobe,
305
.remove = au1xi2s_drvremove,
306
};
307
308
module_platform_driver(au1xi2s_driver);
309
310
MODULE_LICENSE("GPL");
311
MODULE_DESCRIPTION("Au1000/1500/1100 I2S ASoC driver");
312
MODULE_AUTHOR("Manuel Lauss");
313
314