diff options
Diffstat (limited to 'sound/soc/intel/sst-baytrail-pcm.c')
-rw-r--r-- | sound/soc/intel/sst-baytrail-pcm.c | 162 |
1 files changed, 132 insertions, 30 deletions
diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c index 6d101f3813b4..3af38576e91e 100644 --- a/sound/soc/intel/sst-baytrail-pcm.c +++ b/sound/soc/intel/sst-baytrail-pcm.c | |||
@@ -45,6 +45,11 @@ struct sst_byt_pcm_data { | |||
45 | struct sst_byt_stream *stream; | 45 | struct sst_byt_stream *stream; |
46 | struct snd_pcm_substream *substream; | 46 | struct snd_pcm_substream *substream; |
47 | struct mutex mutex; | 47 | struct mutex mutex; |
48 | |||
49 | /* latest DSP DMA hw pointer */ | ||
50 | u32 hw_ptr; | ||
51 | |||
52 | struct work_struct work; | ||
48 | }; | 53 | }; |
49 | 54 | ||
50 | /* private data for the driver */ | 55 | /* private data for the driver */ |
@@ -63,7 +68,7 @@ static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream, | |||
63 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 68 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
64 | struct sst_byt_priv_data *pdata = | 69 | struct sst_byt_priv_data *pdata = |
65 | snd_soc_platform_get_drvdata(rtd->platform); | 70 | snd_soc_platform_get_drvdata(rtd->platform); |
66 | struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); | 71 | struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; |
67 | struct sst_byt *byt = pdata->byt; | 72 | struct sst_byt *byt = pdata->byt; |
68 | u32 rate, bits; | 73 | u32 rate, bits; |
69 | u8 channels; | 74 | u8 channels; |
@@ -130,21 +135,56 @@ static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream) | |||
130 | return 0; | 135 | return 0; |
131 | } | 136 | } |
132 | 137 | ||
138 | static int sst_byt_pcm_restore_stream_context(struct snd_pcm_substream *substream) | ||
139 | { | ||
140 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
141 | struct sst_byt_priv_data *pdata = | ||
142 | snd_soc_platform_get_drvdata(rtd->platform); | ||
143 | struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; | ||
144 | struct sst_byt *byt = pdata->byt; | ||
145 | int ret; | ||
146 | |||
147 | /* commit stream using existing stream params */ | ||
148 | ret = sst_byt_stream_commit(byt, pcm_data->stream); | ||
149 | if (ret < 0) { | ||
150 | dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret); | ||
151 | return ret; | ||
152 | } | ||
153 | |||
154 | sst_byt_stream_start(byt, pcm_data->stream, pcm_data->hw_ptr); | ||
155 | |||
156 | dev_dbg(rtd->dev, "stream context restored at offset %d\n", | ||
157 | pcm_data->hw_ptr); | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static void sst_byt_pcm_work(struct work_struct *work) | ||
163 | { | ||
164 | struct sst_byt_pcm_data *pcm_data = | ||
165 | container_of(work, struct sst_byt_pcm_data, work); | ||
166 | |||
167 | if (snd_pcm_running(pcm_data->substream)) | ||
168 | sst_byt_pcm_restore_stream_context(pcm_data->substream); | ||
169 | } | ||
170 | |||
133 | static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | 171 | static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) |
134 | { | 172 | { |
135 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 173 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
136 | struct sst_byt_priv_data *pdata = | 174 | struct sst_byt_priv_data *pdata = |
137 | snd_soc_platform_get_drvdata(rtd->platform); | 175 | snd_soc_platform_get_drvdata(rtd->platform); |
138 | struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); | 176 | struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; |
139 | struct sst_byt *byt = pdata->byt; | 177 | struct sst_byt *byt = pdata->byt; |
140 | 178 | ||
141 | dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd); | 179 | dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd); |
142 | 180 | ||
143 | switch (cmd) { | 181 | switch (cmd) { |
144 | case SNDRV_PCM_TRIGGER_START: | 182 | case SNDRV_PCM_TRIGGER_START: |
145 | sst_byt_stream_start(byt, pcm_data->stream); | 183 | sst_byt_stream_start(byt, pcm_data->stream, 0); |
146 | break; | 184 | break; |
147 | case SNDRV_PCM_TRIGGER_RESUME: | 185 | case SNDRV_PCM_TRIGGER_RESUME: |
186 | schedule_work(&pcm_data->work); | ||
187 | break; | ||
148 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 188 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
149 | sst_byt_stream_resume(byt, pcm_data->stream); | 189 | sst_byt_stream_resume(byt, pcm_data->stream); |
150 | break; | 190 | break; |
@@ -168,13 +208,19 @@ static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data) | |||
168 | struct snd_pcm_substream *substream = pcm_data->substream; | 208 | struct snd_pcm_substream *substream = pcm_data->substream; |
169 | struct snd_pcm_runtime *runtime = substream->runtime; | 209 | struct snd_pcm_runtime *runtime = substream->runtime; |
170 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 210 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
171 | u32 pos; | 211 | struct sst_byt_priv_data *pdata = |
212 | snd_soc_platform_get_drvdata(rtd->platform); | ||
213 | struct sst_byt *byt = pdata->byt; | ||
214 | u32 pos, hw_pos; | ||
172 | 215 | ||
216 | hw_pos = sst_byt_get_dsp_position(byt, pcm_data->stream, | ||
217 | snd_pcm_lib_buffer_bytes(substream)); | ||
218 | pcm_data->hw_ptr = hw_pos; | ||
173 | pos = frames_to_bytes(runtime, | 219 | pos = frames_to_bytes(runtime, |
174 | (runtime->control->appl_ptr % | 220 | (runtime->control->appl_ptr % |
175 | runtime->buffer_size)); | 221 | runtime->buffer_size)); |
176 | 222 | ||
177 | dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos); | 223 | dev_dbg(rtd->dev, "PCM: App/DMA pointer %u/%u bytes\n", pos, hw_pos); |
178 | 224 | ||
179 | snd_pcm_period_elapsed(substream); | 225 | snd_pcm_period_elapsed(substream); |
180 | return pos; | 226 | return pos; |
@@ -186,18 +232,11 @@ static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream | |||
186 | struct snd_pcm_runtime *runtime = substream->runtime; | 232 | struct snd_pcm_runtime *runtime = substream->runtime; |
187 | struct sst_byt_priv_data *pdata = | 233 | struct sst_byt_priv_data *pdata = |
188 | snd_soc_platform_get_drvdata(rtd->platform); | 234 | snd_soc_platform_get_drvdata(rtd->platform); |
189 | struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); | 235 | struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; |
190 | struct sst_byt *byt = pdata->byt; | ||
191 | snd_pcm_uframes_t offset; | ||
192 | int pos; | ||
193 | 236 | ||
194 | pos = sst_byt_get_dsp_position(byt, pcm_data->stream, | 237 | dev_dbg(rtd->dev, "PCM: DMA pointer %u bytes\n", pcm_data->hw_ptr); |
195 | snd_pcm_lib_buffer_bytes(substream)); | ||
196 | offset = bytes_to_frames(runtime, pos); | ||
197 | 238 | ||
198 | dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n", | 239 | return bytes_to_frames(runtime, pcm_data->hw_ptr); |
199 | frames_to_bytes(runtime, (u32)offset)); | ||
200 | return offset; | ||
201 | } | 240 | } |
202 | 241 | ||
203 | static int sst_byt_pcm_open(struct snd_pcm_substream *substream) | 242 | static int sst_byt_pcm_open(struct snd_pcm_substream *substream) |
@@ -205,20 +244,18 @@ static int sst_byt_pcm_open(struct snd_pcm_substream *substream) | |||
205 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 244 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
206 | struct sst_byt_priv_data *pdata = | 245 | struct sst_byt_priv_data *pdata = |
207 | snd_soc_platform_get_drvdata(rtd->platform); | 246 | snd_soc_platform_get_drvdata(rtd->platform); |
208 | struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); | 247 | struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; |
209 | struct sst_byt *byt = pdata->byt; | 248 | struct sst_byt *byt = pdata->byt; |
210 | 249 | ||
211 | dev_dbg(rtd->dev, "PCM: open\n"); | 250 | dev_dbg(rtd->dev, "PCM: open\n"); |
212 | 251 | ||
213 | pcm_data = &pdata->pcm[rtd->cpu_dai->id]; | ||
214 | mutex_lock(&pcm_data->mutex); | 252 | mutex_lock(&pcm_data->mutex); |
215 | 253 | ||
216 | snd_soc_pcm_set_drvdata(rtd, pcm_data); | ||
217 | pcm_data->substream = substream; | 254 | pcm_data->substream = substream; |
218 | 255 | ||
219 | snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware); | 256 | snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware); |
220 | 257 | ||
221 | pcm_data->stream = sst_byt_stream_new(byt, rtd->cpu_dai->id + 1, | 258 | pcm_data->stream = sst_byt_stream_new(byt, substream->stream + 1, |
222 | byt_notify_pointer, pcm_data); | 259 | byt_notify_pointer, pcm_data); |
223 | if (pcm_data->stream == NULL) { | 260 | if (pcm_data->stream == NULL) { |
224 | dev_err(rtd->dev, "failed to create stream\n"); | 261 | dev_err(rtd->dev, "failed to create stream\n"); |
@@ -235,12 +272,13 @@ static int sst_byt_pcm_close(struct snd_pcm_substream *substream) | |||
235 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 272 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
236 | struct sst_byt_priv_data *pdata = | 273 | struct sst_byt_priv_data *pdata = |
237 | snd_soc_platform_get_drvdata(rtd->platform); | 274 | snd_soc_platform_get_drvdata(rtd->platform); |
238 | struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); | 275 | struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; |
239 | struct sst_byt *byt = pdata->byt; | 276 | struct sst_byt *byt = pdata->byt; |
240 | int ret; | 277 | int ret; |
241 | 278 | ||
242 | dev_dbg(rtd->dev, "PCM: close\n"); | 279 | dev_dbg(rtd->dev, "PCM: close\n"); |
243 | 280 | ||
281 | cancel_work_sync(&pcm_data->work); | ||
244 | mutex_lock(&pcm_data->mutex); | 282 | mutex_lock(&pcm_data->mutex); |
245 | ret = sst_byt_stream_free(byt, pcm_data->stream); | 283 | ret = sst_byt_stream_free(byt, pcm_data->stream); |
246 | if (ret < 0) { | 284 | if (ret < 0) { |
@@ -283,18 +321,16 @@ static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd) | |||
283 | { | 321 | { |
284 | struct snd_pcm *pcm = rtd->pcm; | 322 | struct snd_pcm *pcm = rtd->pcm; |
285 | size_t size; | 323 | size_t size; |
324 | struct snd_soc_platform *platform = rtd->platform; | ||
325 | struct sst_pdata *pdata = dev_get_platdata(platform->dev); | ||
286 | int ret = 0; | 326 | int ret = 0; |
287 | 327 | ||
288 | ret = dma_coerce_mask_and_coherent(rtd->card->dev, DMA_BIT_MASK(32)); | ||
289 | if (ret) | ||
290 | return ret; | ||
291 | |||
292 | if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || | 328 | if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || |
293 | pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { | 329 | pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { |
294 | size = sst_byt_pcm_hardware.buffer_bytes_max; | 330 | size = sst_byt_pcm_hardware.buffer_bytes_max; |
295 | ret = snd_pcm_lib_preallocate_pages_for_all(pcm, | 331 | ret = snd_pcm_lib_preallocate_pages_for_all(pcm, |
296 | SNDRV_DMA_TYPE_DEV, | 332 | SNDRV_DMA_TYPE_DEV, |
297 | rtd->card->dev, | 333 | pdata->dma_dev, |
298 | size, size); | 334 | size, size); |
299 | if (ret) { | 335 | if (ret) { |
300 | dev_err(rtd->dev, "dma buffer allocation failed %d\n", | 336 | dev_err(rtd->dev, "dma buffer allocation failed %d\n", |
@@ -308,7 +344,7 @@ static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd) | |||
308 | 344 | ||
309 | static struct snd_soc_dai_driver byt_dais[] = { | 345 | static struct snd_soc_dai_driver byt_dais[] = { |
310 | { | 346 | { |
311 | .name = "Front-cpu-dai", | 347 | .name = "Baytrail PCM", |
312 | .playback = { | 348 | .playback = { |
313 | .stream_name = "System Playback", | 349 | .stream_name = "System Playback", |
314 | .channels_min = 2, | 350 | .channels_min = 2, |
@@ -317,9 +353,6 @@ static struct snd_soc_dai_driver byt_dais[] = { | |||
317 | .formats = SNDRV_PCM_FMTBIT_S24_3LE | | 353 | .formats = SNDRV_PCM_FMTBIT_S24_3LE | |
318 | SNDRV_PCM_FMTBIT_S16_LE, | 354 | SNDRV_PCM_FMTBIT_S16_LE, |
319 | }, | 355 | }, |
320 | }, | ||
321 | { | ||
322 | .name = "Mic1-cpu-dai", | ||
323 | .capture = { | 356 | .capture = { |
324 | .stream_name = "Analog Capture", | 357 | .stream_name = "Analog Capture", |
325 | .channels_min = 2, | 358 | .channels_min = 2, |
@@ -344,8 +377,10 @@ static int sst_byt_pcm_probe(struct snd_soc_platform *platform) | |||
344 | priv_data->byt = plat_data->dsp; | 377 | priv_data->byt = plat_data->dsp; |
345 | snd_soc_platform_set_drvdata(platform, priv_data); | 378 | snd_soc_platform_set_drvdata(platform, priv_data); |
346 | 379 | ||
347 | for (i = 0; i < ARRAY_SIZE(byt_dais); i++) | 380 | for (i = 0; i < BYT_PCM_COUNT; i++) { |
348 | mutex_init(&priv_data->pcm[i].mutex); | 381 | mutex_init(&priv_data->pcm[i].mutex); |
382 | INIT_WORK(&priv_data->pcm[i].work, sst_byt_pcm_work); | ||
383 | } | ||
349 | 384 | ||
350 | return 0; | 385 | return 0; |
351 | } | 386 | } |
@@ -367,6 +402,72 @@ static const struct snd_soc_component_driver byt_dai_component = { | |||
367 | .name = "byt-dai", | 402 | .name = "byt-dai", |
368 | }; | 403 | }; |
369 | 404 | ||
405 | #ifdef CONFIG_PM | ||
406 | static int sst_byt_pcm_dev_suspend_noirq(struct device *dev) | ||
407 | { | ||
408 | struct sst_pdata *sst_pdata = dev_get_platdata(dev); | ||
409 | int ret; | ||
410 | |||
411 | dev_dbg(dev, "suspending noirq\n"); | ||
412 | |||
413 | /* at this point all streams will be stopped and context saved */ | ||
414 | ret = sst_byt_dsp_suspend_noirq(dev, sst_pdata); | ||
415 | if (ret < 0) { | ||
416 | dev_err(dev, "failed to suspend %d\n", ret); | ||
417 | return ret; | ||
418 | } | ||
419 | |||
420 | return ret; | ||
421 | } | ||
422 | |||
423 | static int sst_byt_pcm_dev_suspend_late(struct device *dev) | ||
424 | { | ||
425 | struct sst_pdata *sst_pdata = dev_get_platdata(dev); | ||
426 | int ret; | ||
427 | |||
428 | dev_dbg(dev, "suspending late\n"); | ||
429 | |||
430 | ret = sst_byt_dsp_suspend_late(dev, sst_pdata); | ||
431 | if (ret < 0) { | ||
432 | dev_err(dev, "failed to suspend %d\n", ret); | ||
433 | return ret; | ||
434 | } | ||
435 | |||
436 | return ret; | ||
437 | } | ||
438 | |||
439 | static int sst_byt_pcm_dev_resume_early(struct device *dev) | ||
440 | { | ||
441 | struct sst_pdata *sst_pdata = dev_get_platdata(dev); | ||
442 | |||
443 | dev_dbg(dev, "resume early\n"); | ||
444 | |||
445 | /* load fw and boot DSP */ | ||
446 | return sst_byt_dsp_boot(dev, sst_pdata); | ||
447 | } | ||
448 | |||
449 | static int sst_byt_pcm_dev_resume(struct device *dev) | ||
450 | { | ||
451 | struct sst_pdata *sst_pdata = dev_get_platdata(dev); | ||
452 | |||
453 | dev_dbg(dev, "resume\n"); | ||
454 | |||
455 | /* wait for FW to finish booting */ | ||
456 | return sst_byt_dsp_wait_for_ready(dev, sst_pdata); | ||
457 | } | ||
458 | |||
459 | static const struct dev_pm_ops sst_byt_pm_ops = { | ||
460 | .suspend_noirq = sst_byt_pcm_dev_suspend_noirq, | ||
461 | .suspend_late = sst_byt_pcm_dev_suspend_late, | ||
462 | .resume_early = sst_byt_pcm_dev_resume_early, | ||
463 | .resume = sst_byt_pcm_dev_resume, | ||
464 | }; | ||
465 | |||
466 | #define SST_BYT_PM_OPS (&sst_byt_pm_ops) | ||
467 | #else | ||
468 | #define SST_BYT_PM_OPS NULL | ||
469 | #endif | ||
470 | |||
370 | static int sst_byt_pcm_dev_probe(struct platform_device *pdev) | 471 | static int sst_byt_pcm_dev_probe(struct platform_device *pdev) |
371 | { | 472 | { |
372 | struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); | 473 | struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); |
@@ -409,6 +510,7 @@ static struct platform_driver sst_byt_pcm_driver = { | |||
409 | .driver = { | 510 | .driver = { |
410 | .name = "baytrail-pcm-audio", | 511 | .name = "baytrail-pcm-audio", |
411 | .owner = THIS_MODULE, | 512 | .owner = THIS_MODULE, |
513 | .pm = SST_BYT_PM_OPS, | ||
412 | }, | 514 | }, |
413 | 515 | ||
414 | .probe = sst_byt_pcm_dev_probe, | 516 | .probe = sst_byt_pcm_dev_probe, |