Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/qcom/lpass-apq8016.c
26427 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
4
*
5
* lpass-apq8016.c -- ALSA SoC CPU DAI driver for APQ8016 LPASS
6
*/
7
8
9
#include <linux/clk.h>
10
#include <linux/device.h>
11
#include <linux/err.h>
12
#include <linux/kernel.h>
13
#include <linux/module.h>
14
#include <linux/of.h>
15
#include <linux/platform_device.h>
16
#include <sound/pcm.h>
17
#include <sound/pcm_params.h>
18
#include <sound/soc.h>
19
#include <sound/soc-dai.h>
20
21
#include <dt-bindings/sound/apq8016-lpass.h>
22
#include "lpass-lpaif-reg.h"
23
#include "lpass.h"
24
25
static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
26
[MI2S_PRIMARY] = {
27
.id = MI2S_PRIMARY,
28
.name = "Primary MI2S",
29
.playback = {
30
.stream_name = "Primary Playback",
31
.formats = SNDRV_PCM_FMTBIT_S16 |
32
SNDRV_PCM_FMTBIT_S24 |
33
SNDRV_PCM_FMTBIT_S32,
34
.rates = SNDRV_PCM_RATE_8000 |
35
SNDRV_PCM_RATE_16000 |
36
SNDRV_PCM_RATE_32000 |
37
SNDRV_PCM_RATE_48000 |
38
SNDRV_PCM_RATE_96000,
39
.rate_min = 8000,
40
.rate_max = 96000,
41
.channels_min = 1,
42
.channels_max = 8,
43
},
44
.ops = &asoc_qcom_lpass_cpu_dai_ops,
45
},
46
[MI2S_SECONDARY] = {
47
.id = MI2S_SECONDARY,
48
.name = "Secondary MI2S",
49
.playback = {
50
.stream_name = "Secondary Playback",
51
.formats = SNDRV_PCM_FMTBIT_S16 |
52
SNDRV_PCM_FMTBIT_S24 |
53
SNDRV_PCM_FMTBIT_S32,
54
.rates = SNDRV_PCM_RATE_8000 |
55
SNDRV_PCM_RATE_16000 |
56
SNDRV_PCM_RATE_32000 |
57
SNDRV_PCM_RATE_48000 |
58
SNDRV_PCM_RATE_96000,
59
.rate_min = 8000,
60
.rate_max = 96000,
61
.channels_min = 1,
62
.channels_max = 8,
63
},
64
.ops = &asoc_qcom_lpass_cpu_dai_ops,
65
},
66
[MI2S_TERTIARY] = {
67
.id = MI2S_TERTIARY,
68
.name = "Tertiary MI2S",
69
.capture = {
70
.stream_name = "Tertiary Capture",
71
.formats = SNDRV_PCM_FMTBIT_S16 |
72
SNDRV_PCM_FMTBIT_S24 |
73
SNDRV_PCM_FMTBIT_S32,
74
.rates = SNDRV_PCM_RATE_8000 |
75
SNDRV_PCM_RATE_16000 |
76
SNDRV_PCM_RATE_32000 |
77
SNDRV_PCM_RATE_48000 |
78
SNDRV_PCM_RATE_96000,
79
.rate_min = 8000,
80
.rate_max = 96000,
81
.channels_min = 1,
82
.channels_max = 8,
83
},
84
.ops = &asoc_qcom_lpass_cpu_dai_ops,
85
},
86
[MI2S_QUATERNARY] = {
87
.id = MI2S_QUATERNARY,
88
.name = "Quatenary MI2S",
89
.playback = {
90
.stream_name = "Quatenary Playback",
91
.formats = SNDRV_PCM_FMTBIT_S16 |
92
SNDRV_PCM_FMTBIT_S24 |
93
SNDRV_PCM_FMTBIT_S32,
94
.rates = SNDRV_PCM_RATE_8000 |
95
SNDRV_PCM_RATE_16000 |
96
SNDRV_PCM_RATE_32000 |
97
SNDRV_PCM_RATE_48000 |
98
SNDRV_PCM_RATE_96000,
99
.rate_min = 8000,
100
.rate_max = 96000,
101
.channels_min = 1,
102
.channels_max = 8,
103
},
104
.capture = {
105
.stream_name = "Quatenary Capture",
106
.formats = SNDRV_PCM_FMTBIT_S16 |
107
SNDRV_PCM_FMTBIT_S24 |
108
SNDRV_PCM_FMTBIT_S32,
109
.rates = SNDRV_PCM_RATE_8000 |
110
SNDRV_PCM_RATE_16000 |
111
SNDRV_PCM_RATE_32000 |
112
SNDRV_PCM_RATE_48000 |
113
SNDRV_PCM_RATE_96000,
114
.rate_min = 8000,
115
.rate_max = 96000,
116
.channels_min = 1,
117
.channels_max = 8,
118
},
119
.ops = &asoc_qcom_lpass_cpu_dai_ops,
120
},
121
};
122
123
static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata,
124
int direction, unsigned int dai_id)
125
{
126
const struct lpass_variant *v = drvdata->variant;
127
int chan = 0;
128
129
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
130
chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
131
v->rdma_channels);
132
133
if (chan >= v->rdma_channels)
134
return -EBUSY;
135
} else {
136
chan = find_next_zero_bit(&drvdata->dma_ch_bit_map,
137
v->wrdma_channel_start +
138
v->wrdma_channels,
139
v->wrdma_channel_start);
140
141
if (chan >= v->wrdma_channel_start + v->wrdma_channels)
142
return -EBUSY;
143
}
144
145
set_bit(chan, &drvdata->dma_ch_bit_map);
146
147
return chan;
148
}
149
150
static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id)
151
{
152
clear_bit(chan, &drvdata->dma_ch_bit_map);
153
154
return 0;
155
}
156
157
static int apq8016_lpass_init(struct platform_device *pdev)
158
{
159
struct lpass_data *drvdata = platform_get_drvdata(pdev);
160
const struct lpass_variant *variant = drvdata->variant;
161
struct device *dev = &pdev->dev;
162
int ret, i;
163
164
165
drvdata->clks = devm_kcalloc(dev, variant->num_clks,
166
sizeof(*drvdata->clks), GFP_KERNEL);
167
if (!drvdata->clks)
168
return -ENOMEM;
169
drvdata->num_clks = variant->num_clks;
170
171
for (i = 0; i < drvdata->num_clks; i++)
172
drvdata->clks[i].id = variant->clk_name[i];
173
174
ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks);
175
if (ret) {
176
dev_err(dev, "Failed to get clocks %d\n", ret);
177
return ret;
178
}
179
180
ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
181
if (ret) {
182
dev_err(dev, "apq8016 clk_enable failed\n");
183
return ret;
184
}
185
186
drvdata->ahbix_clk = devm_clk_get(dev, "ahbix-clk");
187
if (IS_ERR(drvdata->ahbix_clk)) {
188
dev_err(dev, "error getting ahbix-clk: %ld\n",
189
PTR_ERR(drvdata->ahbix_clk));
190
ret = PTR_ERR(drvdata->ahbix_clk);
191
goto err_ahbix_clk;
192
}
193
194
ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY);
195
if (ret) {
196
dev_err(dev, "error setting rate on ahbix_clk: %d\n", ret);
197
goto err_ahbix_clk;
198
}
199
dev_dbg(dev, "set ahbix_clk rate to %lu\n",
200
clk_get_rate(drvdata->ahbix_clk));
201
202
ret = clk_prepare_enable(drvdata->ahbix_clk);
203
if (ret) {
204
dev_err(dev, "error enabling ahbix_clk: %d\n", ret);
205
goto err_ahbix_clk;
206
}
207
208
return 0;
209
210
err_ahbix_clk:
211
clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
212
return ret;
213
}
214
215
static int apq8016_lpass_exit(struct platform_device *pdev)
216
{
217
struct lpass_data *drvdata = platform_get_drvdata(pdev);
218
219
clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
220
clk_disable_unprepare(drvdata->ahbix_clk);
221
222
return 0;
223
}
224
225
226
static const struct lpass_variant apq8016_data = {
227
.i2sctrl_reg_base = 0x1000,
228
.i2sctrl_reg_stride = 0x1000,
229
.i2s_ports = 4,
230
.irq_reg_base = 0x6000,
231
.irq_reg_stride = 0x1000,
232
.irq_ports = 3,
233
.rdma_reg_base = 0x8400,
234
.rdma_reg_stride = 0x1000,
235
.rdma_channels = 2,
236
.dmactl_audif_start = 1,
237
.wrdma_reg_base = 0xB000,
238
.wrdma_reg_stride = 0x1000,
239
.wrdma_channel_start = 5,
240
.wrdma_channels = 2,
241
.loopback = REG_FIELD_ID(0x1000, 15, 15, 4, 0x1000),
242
.spken = REG_FIELD_ID(0x1000, 14, 14, 4, 0x1000),
243
.spkmode = REG_FIELD_ID(0x1000, 10, 13, 4, 0x1000),
244
.spkmono = REG_FIELD_ID(0x1000, 9, 9, 4, 0x1000),
245
.micen = REG_FIELD_ID(0x1000, 8, 8, 4, 0x1000),
246
.micmode = REG_FIELD_ID(0x1000, 4, 7, 4, 0x1000),
247
.micmono = REG_FIELD_ID(0x1000, 3, 3, 4, 0x1000),
248
.wssrc = REG_FIELD_ID(0x1000, 2, 2, 4, 0x1000),
249
.bitwidth = REG_FIELD_ID(0x1000, 0, 1, 4, 0x1000),
250
251
.rdma_dyncclk = REG_FIELD_ID(0x8400, 12, 12, 2, 0x1000),
252
.rdma_bursten = REG_FIELD_ID(0x8400, 11, 11, 2, 0x1000),
253
.rdma_wpscnt = REG_FIELD_ID(0x8400, 8, 10, 2, 0x1000),
254
.rdma_intf = REG_FIELD_ID(0x8400, 4, 7, 2, 0x1000),
255
.rdma_fifowm = REG_FIELD_ID(0x8400, 1, 3, 2, 0x1000),
256
.rdma_enable = REG_FIELD_ID(0x8400, 0, 0, 2, 0x1000),
257
258
.wrdma_dyncclk = REG_FIELD_ID(0xB000, 12, 12, 2, 0x1000),
259
.wrdma_bursten = REG_FIELD_ID(0xB000, 11, 11, 2, 0x1000),
260
.wrdma_wpscnt = REG_FIELD_ID(0xB000, 8, 10, 2, 0x1000),
261
.wrdma_intf = REG_FIELD_ID(0xB000, 4, 7, 2, 0x1000),
262
.wrdma_fifowm = REG_FIELD_ID(0xB000, 1, 3, 2, 0x1000),
263
.wrdma_enable = REG_FIELD_ID(0xB000, 0, 0, 2, 0x1000),
264
265
.clk_name = (const char*[]) {
266
"pcnoc-mport-clk",
267
"pcnoc-sway-clk",
268
},
269
.num_clks = 2,
270
.dai_driver = apq8016_lpass_cpu_dai_driver,
271
.num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),
272
.dai_osr_clk_names = (const char *[]) {
273
"mi2s-osr-clk0",
274
"mi2s-osr-clk1",
275
"mi2s-osr-clk2",
276
"mi2s-osr-clk3",
277
},
278
.dai_bit_clk_names = (const char *[]) {
279
"mi2s-bit-clk0",
280
"mi2s-bit-clk1",
281
"mi2s-bit-clk2",
282
"mi2s-bit-clk3",
283
},
284
.init = apq8016_lpass_init,
285
.exit = apq8016_lpass_exit,
286
.alloc_dma_channel = apq8016_lpass_alloc_dma_channel,
287
.free_dma_channel = apq8016_lpass_free_dma_channel,
288
};
289
290
static const struct of_device_id apq8016_lpass_cpu_device_id[] __maybe_unused = {
291
{ .compatible = "qcom,lpass-cpu-apq8016", .data = &apq8016_data },
292
{ .compatible = "qcom,apq8016-lpass-cpu", .data = &apq8016_data },
293
{}
294
};
295
MODULE_DEVICE_TABLE(of, apq8016_lpass_cpu_device_id);
296
297
static struct platform_driver apq8016_lpass_cpu_platform_driver = {
298
.driver = {
299
.name = "apq8016-lpass-cpu",
300
.of_match_table = of_match_ptr(apq8016_lpass_cpu_device_id),
301
},
302
.probe = asoc_qcom_lpass_cpu_platform_probe,
303
.remove = asoc_qcom_lpass_cpu_platform_remove,
304
};
305
module_platform_driver(apq8016_lpass_cpu_platform_driver);
306
307
MODULE_DESCRIPTION("APQ8016 LPASS CPU Driver");
308
MODULE_LICENSE("GPL");
309
310
311