aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/intel/sst-haswell-ipc.c28
-rw-r--r--sound/soc/intel/sst-haswell-ipc.h9
-rw-r--r--sound/soc/intel/sst-haswell-pcm.c65
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
1354snd_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
1360void 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
1366bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw,
1367 struct sst_hsw_stream *stream)
1368{
1369 return stream->play_silence;
1370}
1371
1372void 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 */
1352int sst_hsw_mixer_get_info(struct sst_hsw *hsw) 1380int 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);
426int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw, 427int 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);
429snd_pcm_uframes_t sst_hsw_stream_get_old_position(struct sst_hsw *hsw,
430 struct sst_hsw_stream *stream);
431void sst_hsw_stream_set_old_position(struct sst_hsw *hsw,
432 struct sst_hsw_stream *stream, snd_pcm_uframes_t val);
433bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw,
434 struct sst_hsw_stream *stream);
435void sst_hsw_stream_set_silence_start(struct sst_hsw *hsw,
436 struct sst_hsw_stream *stream, bool val);
428int sst_hsw_mixer_get_info(struct sst_hsw *hsw); 437int 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 */
40static const u32 volume_map[] = { 45static 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;