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