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