Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/loongson/loongson1_ac97.c
26436 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* AC97 Controller Driver for Loongson-1 SoC
4
*
5
* Copyright (C) 2025 Keguang Zhang <[email protected]>
6
*/
7
8
#include <linux/bitfield.h>
9
#include <linux/dma-mapping.h>
10
#include <linux/init.h>
11
#include <linux/module.h>
12
#include <linux/platform_device.h>
13
#include <linux/regmap.h>
14
15
#include <sound/dmaengine_pcm.h>
16
#include <sound/pcm.h>
17
#include <sound/pcm_params.h>
18
#include <sound/soc.h>
19
20
/* Loongson-1 AC97 Controller Registers */
21
#define AC97_CSR 0x0
22
#define AC97_OCC0 0x4
23
#define AC97_ICC 0x10
24
#define AC97_CRAC 0x18
25
#define AC97_INTRAW 0x54
26
#define AC97_INTM 0x58
27
#define AC97_INT_CW_CLR 0x68
28
#define AC97_INT_CR_CLR 0x6c
29
30
/* Control Status Register Bits (CSR) */
31
#define CSR_RESUME BIT(1)
32
#define CSR_RST_FORCE BIT(0)
33
34
/* MIC Channel Configuration Bits */
35
#define M_DMA_EN BIT(22)
36
#define M_FIFO_THRES GENMASK(21, 20)
37
#define M_FIFO_THRES_FULL FIELD_PREP(M_FIFO_THRES, 3)
38
#define M_FIFO_THRES_HALF FIELD_PREP(M_FIFO_THRES, 1)
39
#define M_FIFO_THRES_QUARTER FIELD_PREP(M_FIFO_THRES, 0)
40
#define M_SW GENMASK(19, 18)
41
#define M_SW_16_BITS FIELD_PREP(M_SW, 2)
42
#define M_SW_8_BITS FIELD_PREP(M_SW, 0)
43
#define M_VSR BIT(17)
44
#define M_CH_EN BIT(16)
45
/* Right Channel Configuration Bits */
46
#define R_DMA_EN BIT(14)
47
#define R_FIFO_THRES GENMASK(13, 12)
48
#define R_FIFO_THRES_EMPTY FIELD_PREP(R_FIFO_THRES, 3)
49
#define R_FIFO_THRES_HALF FIELD_PREP(R_FIFO_THRES, 1)
50
#define R_FIFO_THRES_QUARTER FIELD_PREP(R_FIFO_THRES, 0)
51
#define R_SW GENMASK(11, 10)
52
#define R_SW_16_BITS FIELD_PREP(R_SW, 2)
53
#define R_SW_8_BITS FIELD_PREP(R_SW, 0)
54
#define R_VSR BIT(9)
55
#define R_CH_EN BIT(8)
56
/* Left Channel Configuration Bits */
57
#define L_DMA_EN BIT(6)
58
#define L_FIFO_THRES GENMASK(5, 4)
59
#define L_FIFO_THRES_EMPTY FIELD_PREP(L_FIFO_THRES, 3)
60
#define L_FIFO_THRES_HALF FIELD_PREP(L_FIFO_THRES, 1)
61
#define L_FIFO_THRES_QUARTER FIELD_PREP(L_FIFO_THRES, 0)
62
#define L_SW GENMASK(3, 2)
63
#define L_SW_16_BITS FIELD_PREP(L_SW, 2)
64
#define L_SW_8_BITS FIELD_PREP(L_SW, 0)
65
#define L_VSR BIT(1)
66
#define L_CH_EN BIT(0)
67
68
/* Codec Register Access Command Bits (CRAC) */
69
#define CODEC_WR BIT(31)
70
#define CODEC_ADR GENMASK(22, 16)
71
#define CODEC_DAT GENMASK(15, 0)
72
73
/* Interrupt Register (INTRAW) */
74
#define CW_DONE BIT(1)
75
#define CR_DONE BIT(0)
76
77
#define LS1X_AC97_DMA_TX_EN BIT(31)
78
#define LS1X_AC97_DMA_STEREO BIT(30)
79
#define LS1X_AC97_DMA_TX_BYTES GENMASK(29, 28)
80
#define LS1X_AC97_DMA_TX_4_BYTES FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 2)
81
#define LS1X_AC97_DMA_TX_2_BYTES FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 1)
82
#define LS1X_AC97_DMA_TX_1_BYTE FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 0)
83
#define LS1X_AC97_DMA_DADDR_MASK GENMASK(27, 0)
84
85
#define LS1X_AC97_DMA_FIFO_SIZE 128
86
87
#define LS1X_AC97_TIMEOUT 3000
88
89
struct ls1x_ac97 {
90
void __iomem *reg_base;
91
struct regmap *regmap;
92
dma_addr_t tx_dma_base;
93
dma_addr_t rx_dma_base;
94
struct snd_dmaengine_dai_dma_data capture_dma_data;
95
struct snd_dmaengine_dai_dma_data playback_dma_data;
96
};
97
98
static struct ls1x_ac97 *ls1x_ac97;
99
100
static const struct regmap_config ls1x_ac97_regmap_config = {
101
.reg_bits = 32,
102
.val_bits = 32,
103
.reg_stride = 4,
104
};
105
106
static void ls1x_ac97_reset(struct snd_ac97 *ac97)
107
{
108
int val;
109
110
regmap_write(ls1x_ac97->regmap, AC97_CSR, CSR_RST_FORCE);
111
regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
112
!(val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
113
}
114
115
static void ls1x_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
116
{
117
int tmp, ret;
118
119
tmp = FIELD_PREP(CODEC_ADR, reg) | FIELD_PREP(CODEC_DAT, val);
120
regmap_write(ls1x_ac97->regmap, AC97_CRAC, tmp);
121
ret = regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_INTRAW, tmp,
122
(tmp & CW_DONE), 0, LS1X_AC97_TIMEOUT);
123
if (ret)
124
pr_err("timeout on AC97 write! %d\n", ret);
125
126
regmap_read(ls1x_ac97->regmap, AC97_INT_CW_CLR, &ret);
127
}
128
129
static unsigned short ls1x_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
130
{
131
int val, ret;
132
133
val = CODEC_WR | FIELD_PREP(CODEC_ADR, reg);
134
regmap_write(ls1x_ac97->regmap, AC97_CRAC, val);
135
ret = regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_INTRAW, val,
136
(val & CR_DONE), 0, LS1X_AC97_TIMEOUT);
137
if (ret) {
138
pr_err("timeout on AC97 read! %d\n", ret);
139
return ret;
140
}
141
142
regmap_read(ls1x_ac97->regmap, AC97_INT_CR_CLR, &ret);
143
regmap_read(ls1x_ac97->regmap, AC97_CRAC, &ret);
144
145
return (ret & CODEC_DAT);
146
}
147
148
static void ls1x_ac97_init(struct snd_ac97 *ac97)
149
{
150
writel(0, ls1x_ac97->reg_base + AC97_INTRAW);
151
writel(0, ls1x_ac97->reg_base + AC97_INTM);
152
153
/* Config output channels */
154
regmap_update_bits(ls1x_ac97->regmap, AC97_OCC0,
155
R_DMA_EN | R_FIFO_THRES | R_CH_EN |
156
L_DMA_EN | L_FIFO_THRES | L_CH_EN,
157
R_DMA_EN | R_FIFO_THRES_EMPTY | R_CH_EN |
158
L_DMA_EN | L_FIFO_THRES_EMPTY | L_CH_EN);
159
160
/* Config inputs channel */
161
regmap_update_bits(ls1x_ac97->regmap, AC97_ICC,
162
M_DMA_EN | M_FIFO_THRES | M_CH_EN |
163
R_DMA_EN | R_FIFO_THRES | R_CH_EN |
164
L_DMA_EN | L_FIFO_THRES | L_CH_EN,
165
M_DMA_EN | M_FIFO_THRES_FULL | M_CH_EN |
166
R_DMA_EN | R_FIFO_THRES_EMPTY | R_CH_EN |
167
L_DMA_EN | L_FIFO_THRES_EMPTY | L_CH_EN);
168
169
if (ac97->ext_id & AC97_EI_VRA) {
170
regmap_update_bits(ls1x_ac97->regmap, AC97_OCC0, R_VSR | L_VSR, R_VSR | L_VSR);
171
regmap_update_bits(ls1x_ac97->regmap, AC97_ICC, M_VSR, M_VSR);
172
}
173
}
174
175
static struct snd_ac97_bus_ops ls1x_ac97_ops = {
176
.reset = ls1x_ac97_reset,
177
.write = ls1x_ac97_write,
178
.read = ls1x_ac97_read,
179
.init = ls1x_ac97_init,
180
};
181
182
static int ls1x_ac97_hw_params(struct snd_pcm_substream *substream,
183
struct snd_pcm_hw_params *params,
184
struct snd_soc_dai *cpu_dai)
185
{
186
struct ls1x_ac97 *ac97 = dev_get_drvdata(cpu_dai->dev);
187
struct snd_dmaengine_dai_dma_data *dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
188
189
switch (params_channels(params)) {
190
case 1:
191
dma_data->addr &= ~LS1X_AC97_DMA_STEREO;
192
break;
193
case 2:
194
dma_data->addr |= LS1X_AC97_DMA_STEREO;
195
break;
196
default:
197
dev_err(cpu_dai->dev, "unsupported channels! %d\n", params_channels(params));
198
return -EINVAL;
199
}
200
201
switch (params_format(params)) {
202
case SNDRV_PCM_FORMAT_S8:
203
case SNDRV_PCM_FORMAT_U8:
204
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
205
regmap_update_bits(ac97->regmap, AC97_OCC0,
206
R_SW | L_SW,
207
R_SW_8_BITS | L_SW_8_BITS);
208
else
209
regmap_update_bits(ac97->regmap, AC97_ICC,
210
M_SW | R_SW | L_SW,
211
M_SW_8_BITS | R_SW_8_BITS | L_SW_8_BITS);
212
break;
213
case SNDRV_PCM_FORMAT_S16_LE:
214
case SNDRV_PCM_FORMAT_U16_LE:
215
case SNDRV_PCM_FORMAT_S16_BE:
216
case SNDRV_PCM_FORMAT_U16_BE:
217
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
218
regmap_update_bits(ac97->regmap, AC97_OCC0,
219
R_SW | L_SW,
220
R_SW_16_BITS | L_SW_16_BITS);
221
else
222
regmap_update_bits(ac97->regmap, AC97_ICC,
223
M_SW | R_SW | L_SW,
224
M_SW_16_BITS | R_SW_16_BITS | L_SW_16_BITS);
225
break;
226
default:
227
dev_err(cpu_dai->dev, "unsupported format! %d\n", params_format(params));
228
return -EINVAL;
229
}
230
231
return 0;
232
}
233
234
static int ls1x_ac97_dai_probe(struct snd_soc_dai *cpu_dai)
235
{
236
struct ls1x_ac97 *ac97 = dev_get_drvdata(cpu_dai->dev);
237
238
ac97->capture_dma_data.addr = ac97->rx_dma_base & LS1X_AC97_DMA_DADDR_MASK;
239
ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
240
ac97->capture_dma_data.fifo_size = LS1X_AC97_DMA_FIFO_SIZE;
241
242
ac97->playback_dma_data.addr = ac97->tx_dma_base & LS1X_AC97_DMA_DADDR_MASK;
243
ac97->playback_dma_data.addr |= LS1X_AC97_DMA_TX_4_BYTES;
244
ac97->playback_dma_data.addr |= LS1X_AC97_DMA_TX_EN;
245
ac97->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
246
ac97->playback_dma_data.fifo_size = LS1X_AC97_DMA_FIFO_SIZE;
247
248
snd_soc_dai_init_dma_data(cpu_dai, &ac97->playback_dma_data, &ac97->capture_dma_data);
249
snd_soc_dai_set_drvdata(cpu_dai, ac97);
250
251
return 0;
252
}
253
254
static const struct snd_soc_dai_ops ls1x_ac97_dai_ops = {
255
.probe = ls1x_ac97_dai_probe,
256
.hw_params = ls1x_ac97_hw_params,
257
};
258
259
#define LS1X_AC97_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |\
260
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
261
SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE)
262
263
static struct snd_soc_dai_driver ls1x_ac97_dai[] = {
264
{
265
.name = "ls1x-ac97",
266
.playback = {
267
.stream_name = "AC97 Playback",
268
.channels_min = 1,
269
.channels_max = 2,
270
.rates = SNDRV_PCM_RATE_8000_48000,
271
.formats = LS1X_AC97_FMTS,
272
},
273
.capture = {
274
.stream_name = "AC97 Capture",
275
.channels_min = 1,
276
.channels_max = 2,
277
.rates = SNDRV_PCM_RATE_8000_48000,
278
.formats = LS1X_AC97_FMTS,
279
},
280
.ops = &ls1x_ac97_dai_ops,
281
},
282
};
283
284
static const struct snd_soc_component_driver ls1x_ac97_component = {
285
.name = KBUILD_MODNAME,
286
.legacy_dai_naming = 1,
287
};
288
289
static int ls1x_ac97_probe(struct platform_device *pdev)
290
{
291
struct device *dev = &pdev->dev;
292
struct ls1x_ac97 *ac97;
293
struct resource *res;
294
int ret;
295
296
ac97 = devm_kzalloc(dev, sizeof(struct ls1x_ac97), GFP_KERNEL);
297
if (!ac97)
298
return -ENOMEM;
299
ls1x_ac97 = ac97;
300
platform_set_drvdata(pdev, ac97);
301
302
ac97->reg_base = devm_platform_ioremap_resource(pdev, 0);
303
if (IS_ERR(ac97->reg_base))
304
return PTR_ERR(ac97->reg_base);
305
306
ac97->regmap = devm_regmap_init_mmio(dev, ac97->reg_base, &ls1x_ac97_regmap_config);
307
if (IS_ERR(ac97->regmap))
308
return dev_err_probe(dev, PTR_ERR(ac97->regmap), "devm_regmap_init_mmio failed\n");
309
310
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-tx");
311
if (!res)
312
return dev_err_probe(dev, -EINVAL, "Missing 'audio-tx' in reg-names property\n");
313
314
ac97->tx_dma_base = dma_map_resource(dev, res->start, resource_size(res),
315
DMA_TO_DEVICE, 0);
316
if (dma_mapping_error(dev, ac97->tx_dma_base))
317
return -ENXIO;
318
319
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-rx");
320
if (!res)
321
return dev_err_probe(dev, -EINVAL, "Missing 'audio-rx' in reg-names property\n");
322
323
ac97->rx_dma_base = dma_map_resource(dev, res->start, resource_size(res),
324
DMA_FROM_DEVICE, 0);
325
if (dma_mapping_error(dev, ac97->rx_dma_base))
326
return -ENXIO;
327
328
ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
329
if (ret)
330
dev_err_probe(dev, ret, "failed to register PCM\n");
331
332
ret = devm_snd_soc_register_component(dev, &ls1x_ac97_component,
333
ls1x_ac97_dai, ARRAY_SIZE(ls1x_ac97_dai));
334
if (ret)
335
dev_err_probe(dev, ret, "failed to register DAI\n");
336
337
return snd_soc_set_ac97_ops(&ls1x_ac97_ops);
338
}
339
340
static void ls1x_ac97_remove(struct platform_device *pdev)
341
{
342
ls1x_ac97 = NULL;
343
snd_soc_set_ac97_ops(NULL);
344
}
345
346
#ifdef CONFIG_PM_SLEEP
347
static int ls1x_ac97_suspend(struct device *dev)
348
{
349
int val;
350
351
regmap_clear_bits(ls1x_ac97->regmap, AC97_OCC0, R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
352
regmap_clear_bits(ls1x_ac97->regmap, AC97_ICC,
353
M_DMA_EN | M_CH_EN | R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
354
regmap_set_bits(ls1x_ac97->regmap, AC97_CSR, CSR_RESUME);
355
356
return regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
357
(val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
358
}
359
360
static int ls1x_ac97_resume(struct device *dev)
361
{
362
int val;
363
364
regmap_set_bits(ls1x_ac97->regmap, AC97_OCC0, R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
365
regmap_set_bits(ls1x_ac97->regmap, AC97_ICC,
366
M_DMA_EN | M_CH_EN | R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
367
regmap_set_bits(ls1x_ac97->regmap, AC97_CSR, CSR_RESUME);
368
369
return regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
370
!(val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
371
}
372
#endif
373
374
static const struct dev_pm_ops ls1x_ac97_pm_ops = {
375
SET_SYSTEM_SLEEP_PM_OPS(ls1x_ac97_suspend, ls1x_ac97_resume)
376
};
377
378
static const struct of_device_id ls1x_ac97_match[] = {
379
{ .compatible = "loongson,ls1b-ac97" },
380
{ /* sentinel */ }
381
};
382
MODULE_DEVICE_TABLE(of, ls1x_ac97_match);
383
384
static struct platform_driver ls1x_ac97_driver = {
385
.probe = ls1x_ac97_probe,
386
.remove = ls1x_ac97_remove,
387
.driver = {
388
.name = KBUILD_MODNAME,
389
.of_match_table = ls1x_ac97_match,
390
.pm = &ls1x_ac97_pm_ops,
391
},
392
};
393
394
module_platform_driver(ls1x_ac97_driver);
395
396
MODULE_AUTHOR("Keguang Zhang <[email protected]>");
397
MODULE_DESCRIPTION("Loongson-1 AC97 Controller Driver");
398
MODULE_LICENSE("GPL");
399
400