]>
Commit | Line | Data |
---|---|---|
2bd8d1d5 RY |
1 | /* |
2 | * SiRF USP in I2S/DSP mode | |
3 | * | |
4 | * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. | |
5 | * | |
6 | * Licensed under GPLv2 or later. | |
7 | */ | |
8 | #include <linux/module.h> | |
9 | #include <linux/io.h> | |
10 | #include <linux/of.h> | |
11 | #include <linux/clk.h> | |
12 | #include <linux/pm_runtime.h> | |
13 | #include <sound/soc.h> | |
14 | #include <sound/pcm_params.h> | |
15 | #include <sound/dmaengine_pcm.h> | |
16 | ||
17 | #include "sirf-usp.h" | |
18 | ||
19 | struct sirf_usp { | |
20 | struct regmap *regmap; | |
21 | struct clk *clk; | |
22 | u32 mode1_reg; | |
23 | u32 mode2_reg; | |
24 | int daifmt_format; | |
25 | struct snd_dmaengine_dai_dma_data playback_dma_data; | |
26 | struct snd_dmaengine_dai_dma_data capture_dma_data; | |
27 | }; | |
28 | ||
29 | static void sirf_usp_tx_enable(struct sirf_usp *usp) | |
30 | { | |
31 | regmap_update_bits(usp->regmap, USP_TX_FIFO_OP, | |
32 | USP_TX_FIFO_RESET, USP_TX_FIFO_RESET); | |
33 | regmap_write(usp->regmap, USP_TX_FIFO_OP, 0); | |
34 | ||
35 | regmap_update_bits(usp->regmap, USP_TX_FIFO_OP, | |
36 | USP_TX_FIFO_START, USP_TX_FIFO_START); | |
37 | ||
38 | regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE, | |
39 | USP_TX_ENA, USP_TX_ENA); | |
40 | } | |
41 | ||
42 | static void sirf_usp_tx_disable(struct sirf_usp *usp) | |
43 | { | |
44 | regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE, | |
45 | USP_TX_ENA, ~USP_TX_ENA); | |
46 | /* FIFO stop */ | |
47 | regmap_write(usp->regmap, USP_TX_FIFO_OP, 0); | |
48 | } | |
49 | ||
50 | static void sirf_usp_rx_enable(struct sirf_usp *usp) | |
51 | { | |
52 | regmap_update_bits(usp->regmap, USP_RX_FIFO_OP, | |
53 | USP_RX_FIFO_RESET, USP_RX_FIFO_RESET); | |
54 | regmap_write(usp->regmap, USP_RX_FIFO_OP, 0); | |
55 | ||
56 | regmap_update_bits(usp->regmap, USP_RX_FIFO_OP, | |
57 | USP_RX_FIFO_START, USP_RX_FIFO_START); | |
58 | ||
59 | regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE, | |
60 | USP_RX_ENA, USP_RX_ENA); | |
61 | } | |
62 | ||
63 | static void sirf_usp_rx_disable(struct sirf_usp *usp) | |
64 | { | |
65 | regmap_update_bits(usp->regmap, USP_TX_RX_ENABLE, | |
66 | USP_RX_ENA, ~USP_RX_ENA); | |
67 | /* FIFO stop */ | |
68 | regmap_write(usp->regmap, USP_RX_FIFO_OP, 0); | |
69 | } | |
70 | ||
71 | static int sirf_usp_pcm_dai_probe(struct snd_soc_dai *dai) | |
72 | { | |
73 | struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai); | |
adbdba3f | 74 | |
2bd8d1d5 RY |
75 | snd_soc_dai_init_dma_data(dai, &usp->playback_dma_data, |
76 | &usp->capture_dma_data); | |
77 | return 0; | |
78 | } | |
79 | ||
80 | static int sirf_usp_pcm_set_dai_fmt(struct snd_soc_dai *dai, | |
81 | unsigned int fmt) | |
82 | { | |
83 | struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai); | |
84 | ||
85 | /* set master/slave audio interface */ | |
86 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | |
87 | case SND_SOC_DAIFMT_CBM_CFM: | |
88 | break; | |
89 | default: | |
90 | dev_err(dai->dev, "Only CBM and CFM supported\n"); | |
91 | return -EINVAL; | |
92 | } | |
93 | ||
94 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
95 | case SND_SOC_DAIFMT_I2S: | |
96 | case SND_SOC_DAIFMT_DSP_A: | |
97 | usp->daifmt_format = (fmt & SND_SOC_DAIFMT_FORMAT_MASK); | |
98 | break; | |
99 | default: | |
100 | dev_err(dai->dev, "Only I2S and DSP_A format supported\n"); | |
101 | return -EINVAL; | |
102 | } | |
103 | ||
0d985b1c RY |
104 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
105 | case SND_SOC_DAIFMT_NB_NF: | |
106 | break; | |
107 | case SND_SOC_DAIFMT_IB_NF: | |
108 | usp->daifmt_format |= (fmt & SND_SOC_DAIFMT_INV_MASK); | |
109 | break; | |
110 | default: | |
111 | return -EINVAL; | |
112 | } | |
113 | ||
2bd8d1d5 RY |
114 | return 0; |
115 | } | |
116 | ||
9779caf9 | 117 | static void sirf_usp_i2s_init(struct sirf_usp *usp) |
2bd8d1d5 | 118 | { |
2bd8d1d5 RY |
119 | /* Configure RISC mode */ |
120 | regmap_update_bits(usp->regmap, USP_RISC_DSP_MODE, | |
121 | USP_RISC_DSP_SEL, ~USP_RISC_DSP_SEL); | |
122 | ||
123 | /* | |
124 | * Configure DMA IO Length register | |
125 | * Set no limit, USP can receive data continuously until it is diabled | |
126 | */ | |
127 | regmap_write(usp->regmap, USP_TX_DMA_IO_LEN, 0); | |
128 | regmap_write(usp->regmap, USP_RX_DMA_IO_LEN, 0); | |
129 | ||
2bd8d1d5 RY |
130 | /* Configure Mode2 register */ |
131 | regmap_write(usp->regmap, USP_MODE2, (1 << USP_RXD_DELAY_LEN_OFFSET) | | |
9779caf9 RY |
132 | (0 << USP_TXD_DELAY_LEN_OFFSET) | |
133 | USP_TFS_CLK_SLAVE_MODE | USP_RFS_CLK_SLAVE_MODE); | |
2bd8d1d5 RY |
134 | |
135 | /* Configure Mode1 register */ | |
136 | regmap_write(usp->regmap, USP_MODE1, | |
137 | USP_SYNC_MODE | USP_EN | USP_TXD_ACT_EDGE_FALLING | | |
138 | USP_RFS_ACT_LEVEL_LOGIC1 | USP_TFS_ACT_LEVEL_LOGIC1 | | |
9779caf9 | 139 | USP_TX_UFLOW_REPEAT_ZERO | USP_CLOCK_MODE_SLAVE); |
2bd8d1d5 RY |
140 | |
141 | /* Configure RX DMA IO Control register */ | |
142 | regmap_write(usp->regmap, USP_RX_DMA_IO_CTRL, 0); | |
143 | ||
144 | /* Congiure RX FIFO Control register */ | |
145 | regmap_write(usp->regmap, USP_RX_FIFO_CTRL, | |
146 | (USP_RX_FIFO_THRESHOLD << USP_RX_FIFO_THD_OFFSET) | | |
147 | (USP_TX_RX_FIFO_WIDTH_DWORD << USP_RX_FIFO_WIDTH_OFFSET)); | |
148 | ||
149 | /* Congiure RX FIFO Level Check register */ | |
150 | regmap_write(usp->regmap, USP_RX_FIFO_LEVEL_CHK, | |
151 | RX_FIFO_SC(0x04) | RX_FIFO_LC(0x0E) | RX_FIFO_HC(0x1B)); | |
152 | ||
153 | /* Configure TX DMA IO Control register*/ | |
154 | regmap_write(usp->regmap, USP_TX_DMA_IO_CTRL, 0); | |
155 | ||
156 | /* Configure TX FIFO Control register */ | |
157 | regmap_write(usp->regmap, USP_TX_FIFO_CTRL, | |
158 | (USP_TX_FIFO_THRESHOLD << USP_TX_FIFO_THD_OFFSET) | | |
159 | (USP_TX_RX_FIFO_WIDTH_DWORD << USP_TX_FIFO_WIDTH_OFFSET)); | |
160 | /* Congiure TX FIFO Level Check register */ | |
161 | regmap_write(usp->regmap, USP_TX_FIFO_LEVEL_CHK, | |
162 | TX_FIFO_SC(0x1B) | TX_FIFO_LC(0x0E) | TX_FIFO_HC(0x04)); | |
2bd8d1d5 RY |
163 | } |
164 | ||
165 | static int sirf_usp_pcm_hw_params(struct snd_pcm_substream *substream, | |
166 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) | |
167 | { | |
168 | struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai); | |
169 | u32 data_len, frame_len, shifter_len; | |
170 | ||
171 | switch (params_format(params)) { | |
172 | case SNDRV_PCM_FORMAT_S16_LE: | |
173 | data_len = 16; | |
174 | frame_len = 16; | |
175 | break; | |
176 | case SNDRV_PCM_FORMAT_S24_LE: | |
177 | data_len = 24; | |
178 | frame_len = 32; | |
179 | break; | |
180 | case SNDRV_PCM_FORMAT_S24_3LE: | |
181 | data_len = 24; | |
182 | frame_len = 24; | |
183 | break; | |
184 | default: | |
185 | dev_err(dai->dev, "Format unsupported\n"); | |
186 | return -EINVAL; | |
187 | } | |
188 | ||
189 | shifter_len = data_len; | |
190 | ||
0d985b1c | 191 | switch (usp->daifmt_format & SND_SOC_DAIFMT_FORMAT_MASK) { |
2bd8d1d5 RY |
192 | case SND_SOC_DAIFMT_I2S: |
193 | regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL, | |
194 | USP_I2S_SYNC_CHG, USP_I2S_SYNC_CHG); | |
195 | break; | |
196 | case SND_SOC_DAIFMT_DSP_A: | |
197 | regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL, | |
198 | USP_I2S_SYNC_CHG, 0); | |
199 | frame_len = data_len * params_channels(params); | |
200 | data_len = frame_len; | |
201 | break; | |
202 | default: | |
203 | dev_err(dai->dev, "Only support I2S and DSP_A mode\n"); | |
204 | return -EINVAL; | |
205 | } | |
206 | ||
0d985b1c RY |
207 | switch (usp->daifmt_format & SND_SOC_DAIFMT_INV_MASK) { |
208 | case SND_SOC_DAIFMT_NB_NF: | |
209 | break; | |
210 | case SND_SOC_DAIFMT_IB_NF: | |
211 | regmap_update_bits(usp->regmap, USP_MODE1, | |
212 | USP_RXD_ACT_EDGE_FALLING | USP_TXD_ACT_EDGE_FALLING, | |
213 | USP_RXD_ACT_EDGE_FALLING); | |
214 | break; | |
215 | default: | |
216 | return -EINVAL; | |
217 | } | |
218 | ||
2bd8d1d5 RY |
219 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
220 | regmap_update_bits(usp->regmap, USP_TX_FRAME_CTRL, | |
221 | USP_TXC_DATA_LEN_MASK | USP_TXC_FRAME_LEN_MASK | |
9779caf9 | 222 | | USP_TXC_SHIFTER_LEN_MASK | USP_TXC_SLAVE_CLK_SAMPLE, |
2bd8d1d5 RY |
223 | ((data_len - 1) << USP_TXC_DATA_LEN_OFFSET) |
224 | | ((frame_len - 1) << USP_TXC_FRAME_LEN_OFFSET) | |
9779caf9 RY |
225 | | ((shifter_len - 1) << USP_TXC_SHIFTER_LEN_OFFSET) |
226 | | USP_TXC_SLAVE_CLK_SAMPLE); | |
2bd8d1d5 RY |
227 | else |
228 | regmap_update_bits(usp->regmap, USP_RX_FRAME_CTRL, | |
229 | USP_RXC_DATA_LEN_MASK | USP_RXC_FRAME_LEN_MASK | |
9779caf9 | 230 | | USP_RXC_SHIFTER_LEN_MASK | USP_SINGLE_SYNC_MODE, |
2bd8d1d5 RY |
231 | ((data_len - 1) << USP_RXC_DATA_LEN_OFFSET) |
232 | | ((frame_len - 1) << USP_RXC_FRAME_LEN_OFFSET) | |
9779caf9 RY |
233 | | ((shifter_len - 1) << USP_RXC_SHIFTER_LEN_OFFSET) |
234 | | USP_SINGLE_SYNC_MODE); | |
2bd8d1d5 RY |
235 | |
236 | return 0; | |
237 | } | |
238 | ||
239 | static int sirf_usp_pcm_trigger(struct snd_pcm_substream *substream, int cmd, | |
240 | struct snd_soc_dai *dai) | |
241 | { | |
242 | struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai); | |
243 | ||
244 | switch (cmd) { | |
245 | case SNDRV_PCM_TRIGGER_START: | |
246 | case SNDRV_PCM_TRIGGER_RESUME: | |
247 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
248 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
249 | sirf_usp_tx_enable(usp); | |
250 | else | |
251 | sirf_usp_rx_enable(usp); | |
252 | break; | |
253 | case SNDRV_PCM_TRIGGER_STOP: | |
254 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
255 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
256 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
257 | sirf_usp_tx_disable(usp); | |
258 | else | |
259 | sirf_usp_rx_disable(usp); | |
260 | break; | |
261 | } | |
262 | ||
263 | return 0; | |
264 | } | |
265 | ||
266 | static const struct snd_soc_dai_ops sirf_usp_pcm_dai_ops = { | |
2bd8d1d5 RY |
267 | .trigger = sirf_usp_pcm_trigger, |
268 | .set_fmt = sirf_usp_pcm_set_dai_fmt, | |
269 | .hw_params = sirf_usp_pcm_hw_params, | |
270 | }; | |
271 | ||
272 | static struct snd_soc_dai_driver sirf_usp_pcm_dai = { | |
273 | .probe = sirf_usp_pcm_dai_probe, | |
274 | .name = "sirf-usp-pcm", | |
275 | .id = 0, | |
276 | .playback = { | |
277 | .stream_name = "SiRF USP PCM Playback", | |
278 | .channels_min = 1, | |
279 | .channels_max = 2, | |
280 | .rates = SNDRV_PCM_RATE_8000_192000, | |
281 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | | |
282 | SNDRV_PCM_FMTBIT_S24_3LE, | |
283 | }, | |
284 | .capture = { | |
285 | .stream_name = "SiRF USP PCM Capture", | |
286 | .channels_min = 1, | |
287 | .channels_max = 2, | |
288 | .rates = SNDRV_PCM_RATE_8000_192000, | |
289 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | | |
290 | SNDRV_PCM_FMTBIT_S24_3LE, | |
291 | }, | |
292 | .ops = &sirf_usp_pcm_dai_ops, | |
293 | }; | |
294 | ||
2bd8d1d5 RY |
295 | static int sirf_usp_pcm_runtime_suspend(struct device *dev) |
296 | { | |
297 | struct sirf_usp *usp = dev_get_drvdata(dev); | |
adbdba3f | 298 | |
2bd8d1d5 RY |
299 | clk_disable_unprepare(usp->clk); |
300 | return 0; | |
301 | } | |
302 | ||
303 | static int sirf_usp_pcm_runtime_resume(struct device *dev) | |
304 | { | |
305 | struct sirf_usp *usp = dev_get_drvdata(dev); | |
9779caf9 | 306 | int ret; |
adbdba3f | 307 | |
9779caf9 RY |
308 | ret = clk_prepare_enable(usp->clk); |
309 | if (ret) { | |
310 | dev_err(dev, "clk_enable failed: %d\n", ret); | |
311 | return ret; | |
312 | } | |
313 | sirf_usp_i2s_init(usp); | |
314 | return 0; | |
2bd8d1d5 | 315 | } |
2bd8d1d5 RY |
316 | |
317 | #ifdef CONFIG_PM_SLEEP | |
318 | static int sirf_usp_pcm_suspend(struct device *dev) | |
319 | { | |
320 | struct sirf_usp *usp = dev_get_drvdata(dev); | |
321 | ||
322 | if (!pm_runtime_status_suspended(dev)) { | |
323 | regmap_read(usp->regmap, USP_MODE1, &usp->mode1_reg); | |
324 | regmap_read(usp->regmap, USP_MODE2, &usp->mode2_reg); | |
325 | sirf_usp_pcm_runtime_suspend(dev); | |
326 | } | |
327 | return 0; | |
328 | } | |
329 | ||
330 | static int sirf_usp_pcm_resume(struct device *dev) | |
331 | { | |
332 | struct sirf_usp *usp = dev_get_drvdata(dev); | |
333 | int ret; | |
334 | ||
335 | if (!pm_runtime_status_suspended(dev)) { | |
336 | ret = sirf_usp_pcm_runtime_resume(dev); | |
337 | if (ret) | |
338 | return ret; | |
339 | regmap_write(usp->regmap, USP_MODE1, usp->mode1_reg); | |
340 | regmap_write(usp->regmap, USP_MODE2, usp->mode2_reg); | |
341 | } | |
342 | return 0; | |
343 | } | |
344 | #endif | |
345 | ||
346 | static const struct snd_soc_component_driver sirf_usp_component = { | |
347 | .name = "sirf-usp", | |
348 | }; | |
349 | ||
350 | static const struct regmap_config sirf_usp_regmap_config = { | |
351 | .reg_bits = 32, | |
352 | .reg_stride = 4, | |
353 | .val_bits = 32, | |
354 | .max_register = USP_RX_FIFO_DATA, | |
355 | .cache_type = REGCACHE_NONE, | |
356 | }; | |
357 | ||
358 | static int sirf_usp_pcm_probe(struct platform_device *pdev) | |
359 | { | |
360 | int ret; | |
361 | struct sirf_usp *usp; | |
362 | void __iomem *base; | |
363 | struct resource *mem_res; | |
364 | ||
365 | usp = devm_kzalloc(&pdev->dev, sizeof(struct sirf_usp), | |
366 | GFP_KERNEL); | |
367 | if (!usp) | |
368 | return -ENOMEM; | |
369 | ||
370 | platform_set_drvdata(pdev, usp); | |
371 | ||
372 | mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
373 | base = devm_ioremap(&pdev->dev, mem_res->start, | |
374 | resource_size(mem_res)); | |
375 | if (base == NULL) | |
376 | return -ENOMEM; | |
377 | usp->regmap = devm_regmap_init_mmio(&pdev->dev, base, | |
378 | &sirf_usp_regmap_config); | |
379 | if (IS_ERR(usp->regmap)) | |
380 | return PTR_ERR(usp->regmap); | |
381 | ||
382 | usp->clk = devm_clk_get(&pdev->dev, NULL); | |
383 | if (IS_ERR(usp->clk)) { | |
384 | dev_err(&pdev->dev, "Get clock failed.\n"); | |
385 | return PTR_ERR(usp->clk); | |
386 | } | |
387 | ||
388 | pm_runtime_enable(&pdev->dev); | |
9779caf9 RY |
389 | if (!pm_runtime_enabled(&pdev->dev)) { |
390 | ret = sirf_usp_pcm_runtime_resume(&pdev->dev); | |
391 | if (ret) | |
392 | return ret; | |
393 | } | |
2bd8d1d5 RY |
394 | |
395 | ret = devm_snd_soc_register_component(&pdev->dev, &sirf_usp_component, | |
396 | &sirf_usp_pcm_dai, 1); | |
397 | if (ret) { | |
398 | dev_err(&pdev->dev, "Register Audio SoC dai failed.\n"); | |
399 | return ret; | |
400 | } | |
401 | return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); | |
402 | } | |
403 | ||
404 | static int sirf_usp_pcm_remove(struct platform_device *pdev) | |
405 | { | |
9779caf9 RY |
406 | if (!pm_runtime_enabled(&pdev->dev)) |
407 | sirf_usp_pcm_runtime_suspend(&pdev->dev); | |
408 | else | |
409 | pm_runtime_disable(&pdev->dev); | |
2bd8d1d5 RY |
410 | return 0; |
411 | } | |
412 | ||
413 | static const struct of_device_id sirf_usp_pcm_of_match[] = { | |
414 | { .compatible = "sirf,prima2-usp-pcm", }, | |
415 | {} | |
416 | }; | |
417 | MODULE_DEVICE_TABLE(of, sirf_usp_pcm_of_match); | |
418 | ||
419 | static const struct dev_pm_ops sirf_usp_pcm_pm_ops = { | |
420 | SET_RUNTIME_PM_OPS(sirf_usp_pcm_runtime_suspend, | |
421 | sirf_usp_pcm_runtime_resume, NULL) | |
422 | SET_SYSTEM_SLEEP_PM_OPS(sirf_usp_pcm_suspend, sirf_usp_pcm_resume) | |
423 | }; | |
424 | ||
425 | static struct platform_driver sirf_usp_pcm_driver = { | |
426 | .driver = { | |
427 | .name = "sirf-usp-pcm", | |
2bd8d1d5 RY |
428 | .of_match_table = sirf_usp_pcm_of_match, |
429 | .pm = &sirf_usp_pcm_pm_ops, | |
430 | }, | |
431 | .probe = sirf_usp_pcm_probe, | |
432 | .remove = sirf_usp_pcm_remove, | |
433 | }; | |
434 | ||
435 | module_platform_driver(sirf_usp_pcm_driver); | |
436 | ||
437 | MODULE_DESCRIPTION("SiRF SoC USP PCM bus driver"); | |
438 | MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>"); | |
439 | MODULE_LICENSE("GPL v2"); |