diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/intel/sst-haswell-ipc.c | 28 | ||||
-rw-r--r-- | sound/soc/intel/sst-haswell-ipc.h | 9 | ||||
-rw-r--r-- | sound/soc/intel/sst-haswell-pcm.c | 65 |
3 files changed, 102 insertions, 0 deletions
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c index 0ab1309ef274..394af5684c05 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/sst-haswell-ipc.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/dma-mapping.h> | 31 | #include <linux/dma-mapping.h> |
32 | #include <linux/debugfs.h> | 32 | #include <linux/debugfs.h> |
33 | #include <linux/pm_runtime.h> | 33 | #include <linux/pm_runtime.h> |
34 | #include <sound/asound.h> | ||
34 | 35 | ||
35 | #include "sst-haswell-ipc.h" | 36 | #include "sst-haswell-ipc.h" |
36 | #include "sst-dsp.h" | 37 | #include "sst-dsp.h" |
@@ -242,6 +243,9 @@ struct sst_hsw_stream { | |||
242 | u32 (*notify_position)(struct sst_hsw_stream *stream, void *data); | 243 | u32 (*notify_position)(struct sst_hsw_stream *stream, void *data); |
243 | void *pdata; | 244 | void *pdata; |
244 | 245 | ||
246 | /* record the fw read position when playback */ | ||
247 | snd_pcm_uframes_t old_position; | ||
248 | bool play_silence; | ||
245 | struct list_head node; | 249 | struct list_head node; |
246 | }; | 250 | }; |
247 | 251 | ||
@@ -1347,6 +1351,30 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) | |||
1347 | return 0; | 1351 | return 0; |
1348 | } | 1352 | } |
1349 | 1353 | ||
1354 | snd_pcm_uframes_t sst_hsw_stream_get_old_position(struct sst_hsw *hsw, | ||
1355 | struct sst_hsw_stream *stream) | ||
1356 | { | ||
1357 | return stream->old_position; | ||
1358 | } | ||
1359 | |||
1360 | void sst_hsw_stream_set_old_position(struct sst_hsw *hsw, | ||
1361 | struct sst_hsw_stream *stream, snd_pcm_uframes_t val) | ||
1362 | { | ||
1363 | stream->old_position = val; | ||
1364 | } | ||
1365 | |||
1366 | bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw, | ||
1367 | struct sst_hsw_stream *stream) | ||
1368 | { | ||
1369 | return stream->play_silence; | ||
1370 | } | ||
1371 | |||
1372 | void sst_hsw_stream_set_silence_start(struct sst_hsw *hsw, | ||
1373 | struct sst_hsw_stream *stream, bool val) | ||
1374 | { | ||
1375 | stream->play_silence = val; | ||
1376 | } | ||
1377 | |||
1350 | /* Stream Information - these calls could be inline but we want the IPC | 1378 | /* Stream Information - these calls could be inline but we want the IPC |
1351 | ABI to be opaque to client PCM drivers to cope with any future ABI changes */ | 1379 | ABI to be opaque to client PCM drivers to cope with any future ABI changes */ |
1352 | int sst_hsw_mixer_get_info(struct sst_hsw *hsw) | 1380 | int sst_hsw_mixer_get_info(struct sst_hsw *hsw) |
diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h index c1ad901342f2..858096041cb1 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/sst-haswell-ipc.h | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/types.h> | 20 | #include <linux/types.h> |
21 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #include <sound/asound.h> | ||
23 | 24 | ||
24 | #define SST_HSW_NO_CHANNELS 4 | 25 | #define SST_HSW_NO_CHANNELS 4 |
25 | #define SST_HSW_MAX_DX_REGIONS 14 | 26 | #define SST_HSW_MAX_DX_REGIONS 14 |
@@ -425,6 +426,14 @@ int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw, | |||
425 | struct sst_hsw_stream *stream, u32 offset, u32 size); | 426 | struct sst_hsw_stream *stream, u32 offset, u32 size); |
426 | int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw, | 427 | int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw, |
427 | struct sst_hsw_stream *stream, u32 offset, u32 size); | 428 | struct sst_hsw_stream *stream, u32 offset, u32 size); |
429 | snd_pcm_uframes_t sst_hsw_stream_get_old_position(struct sst_hsw *hsw, | ||
430 | struct sst_hsw_stream *stream); | ||
431 | void sst_hsw_stream_set_old_position(struct sst_hsw *hsw, | ||
432 | struct sst_hsw_stream *stream, snd_pcm_uframes_t val); | ||
433 | bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw, | ||
434 | struct sst_hsw_stream *stream); | ||
435 | void sst_hsw_stream_set_silence_start(struct sst_hsw *hsw, | ||
436 | struct sst_hsw_stream *stream, bool val); | ||
428 | int sst_hsw_mixer_get_info(struct sst_hsw *hsw); | 437 | int sst_hsw_mixer_get_info(struct sst_hsw *hsw); |
429 | 438 | ||
430 | /* Stream ALSA trigger operations */ | 439 | /* Stream ALSA trigger operations */ |
diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c index 78fa01be57f2..d6fa9d5514e1 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/sst-haswell-pcm.c | |||
@@ -36,6 +36,11 @@ | |||
36 | #define HSW_PCM_COUNT 6 | 36 | #define HSW_PCM_COUNT 6 |
37 | #define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */ | 37 | #define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */ |
38 | 38 | ||
39 | #define SST_OLD_POSITION(d, r, o) ((d) + \ | ||
40 | frames_to_bytes(r, o)) | ||
41 | #define SST_SAMPLES(r, x) (bytes_to_samples(r, \ | ||
42 | frames_to_bytes(r, (x)))) | ||
43 | |||
39 | /* simple volume table */ | 44 | /* simple volume table */ |
40 | static const u32 volume_map[] = { | 45 | static const u32 volume_map[] = { |
41 | HSW_VOLUME_MAX >> 30, | 46 | HSW_VOLUME_MAX >> 30, |
@@ -577,23 +582,34 @@ static int hsw_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
577 | struct hsw_priv_data *pdata = | 582 | struct hsw_priv_data *pdata = |
578 | snd_soc_platform_get_drvdata(rtd->platform); | 583 | snd_soc_platform_get_drvdata(rtd->platform); |
579 | struct hsw_pcm_data *pcm_data; | 584 | struct hsw_pcm_data *pcm_data; |
585 | struct sst_hsw_stream *sst_stream; | ||
580 | struct sst_hsw *hsw = pdata->hsw; | 586 | struct sst_hsw *hsw = pdata->hsw; |
587 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
588 | snd_pcm_uframes_t pos; | ||
581 | int dai; | 589 | int dai; |
582 | 590 | ||
583 | dai = mod_map[rtd->cpu_dai->id].dai_id; | 591 | dai = mod_map[rtd->cpu_dai->id].dai_id; |
584 | pcm_data = &pdata->pcm[dai][substream->stream]; | 592 | pcm_data = &pdata->pcm[dai][substream->stream]; |
593 | sst_stream = pcm_data->stream; | ||
585 | 594 | ||
586 | switch (cmd) { | 595 | switch (cmd) { |
587 | case SNDRV_PCM_TRIGGER_START: | 596 | case SNDRV_PCM_TRIGGER_START: |
588 | case SNDRV_PCM_TRIGGER_RESUME: | 597 | case SNDRV_PCM_TRIGGER_RESUME: |
589 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 598 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
599 | sst_hsw_stream_set_silence_start(hsw, sst_stream, false); | ||
590 | sst_hsw_stream_resume(hsw, pcm_data->stream, 0); | 600 | sst_hsw_stream_resume(hsw, pcm_data->stream, 0); |
591 | break; | 601 | break; |
592 | case SNDRV_PCM_TRIGGER_STOP: | 602 | case SNDRV_PCM_TRIGGER_STOP: |
593 | case SNDRV_PCM_TRIGGER_SUSPEND: | 603 | case SNDRV_PCM_TRIGGER_SUSPEND: |
594 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 604 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
605 | sst_hsw_stream_set_silence_start(hsw, sst_stream, false); | ||
595 | sst_hsw_stream_pause(hsw, pcm_data->stream, 0); | 606 | sst_hsw_stream_pause(hsw, pcm_data->stream, 0); |
596 | break; | 607 | break; |
608 | case SNDRV_PCM_TRIGGER_DRAIN: | ||
609 | pos = runtime->control->appl_ptr % runtime->buffer_size; | ||
610 | sst_hsw_stream_set_old_position(hsw, pcm_data->stream, pos); | ||
611 | sst_hsw_stream_set_silence_start(hsw, sst_stream, true); | ||
612 | break; | ||
597 | default: | 613 | default: |
598 | break; | 614 | break; |
599 | } | 615 | } |
@@ -607,13 +623,62 @@ static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data) | |||
607 | struct snd_pcm_substream *substream = pcm_data->substream; | 623 | struct snd_pcm_substream *substream = pcm_data->substream; |
608 | struct snd_pcm_runtime *runtime = substream->runtime; | 624 | struct snd_pcm_runtime *runtime = substream->runtime; |
609 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 625 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
626 | struct hsw_priv_data *pdata = | ||
627 | snd_soc_platform_get_drvdata(rtd->platform); | ||
628 | struct sst_hsw *hsw = pdata->hsw; | ||
610 | u32 pos; | 629 | u32 pos; |
630 | snd_pcm_uframes_t position = bytes_to_frames(runtime, | ||
631 | sst_hsw_get_dsp_position(hsw, pcm_data->stream)); | ||
632 | unsigned char *dma_area = runtime->dma_area; | ||
633 | snd_pcm_uframes_t dma_frames = | ||
634 | bytes_to_frames(runtime, runtime->dma_bytes); | ||
635 | snd_pcm_uframes_t old_position; | ||
636 | ssize_t samples; | ||
611 | 637 | ||
612 | pos = frames_to_bytes(runtime, | 638 | pos = frames_to_bytes(runtime, |
613 | (runtime->control->appl_ptr % runtime->buffer_size)); | 639 | (runtime->control->appl_ptr % runtime->buffer_size)); |
614 | 640 | ||
615 | dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos); | 641 | dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos); |
616 | 642 | ||
643 | /* SST fw don't know where to stop dma | ||
644 | * So, SST driver need to clean the data which has been consumed | ||
645 | */ | ||
646 | if (dma_area == NULL || dma_frames <= 0 | ||
647 | || (substream->stream == SNDRV_PCM_STREAM_CAPTURE) | ||
648 | || !sst_hsw_stream_get_silence_start(hsw, stream)) { | ||
649 | snd_pcm_period_elapsed(substream); | ||
650 | return pos; | ||
651 | } | ||
652 | |||
653 | old_position = sst_hsw_stream_get_old_position(hsw, stream); | ||
654 | if (position > old_position) { | ||
655 | if (position < dma_frames) { | ||
656 | samples = SST_SAMPLES(runtime, position - old_position); | ||
657 | snd_pcm_format_set_silence(runtime->format, | ||
658 | SST_OLD_POSITION(dma_area, | ||
659 | runtime, old_position), | ||
660 | samples); | ||
661 | } else | ||
662 | dev_err(rtd->dev, "PCM: position is wrong\n"); | ||
663 | } else { | ||
664 | if (old_position < dma_frames) { | ||
665 | samples = SST_SAMPLES(runtime, | ||
666 | dma_frames - old_position); | ||
667 | snd_pcm_format_set_silence(runtime->format, | ||
668 | SST_OLD_POSITION(dma_area, | ||
669 | runtime, old_position), | ||
670 | samples); | ||
671 | } else | ||
672 | dev_err(rtd->dev, "PCM: dma_bytes is wrong\n"); | ||
673 | if (position < dma_frames) { | ||
674 | samples = SST_SAMPLES(runtime, position); | ||
675 | snd_pcm_format_set_silence(runtime->format, | ||
676 | dma_area, samples); | ||
677 | } else | ||
678 | dev_err(rtd->dev, "PCM: position is wrong\n"); | ||
679 | } | ||
680 | sst_hsw_stream_set_old_position(hsw, stream, position); | ||
681 | |||
617 | /* let alsa know we have play a period */ | 682 | /* let alsa know we have play a period */ |
618 | snd_pcm_period_elapsed(substream); | 683 | snd_pcm_period_elapsed(substream); |
619 | return pos; | 684 | return pos; |