summaryrefslogtreecommitdiffstats
path: root/sound/soc/intel/avs/pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/intel/avs/pcm.c')
-rw-r--r--sound/soc/intel/avs/pcm.c81
1 files changed, 75 insertions, 6 deletions
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index f25a293c09..2cafbc392c 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -356,7 +356,6 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn
stream_info->sig_bits);
format_val = snd_hdac_stream_format(runtime->channels, bits, runtime->rate);
- snd_hdac_ext_stream_decouple(bus, link_stream, true);
snd_hdac_ext_stream_reset(link_stream);
snd_hdac_ext_stream_setup(link_stream, format_val);
@@ -612,7 +611,6 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so
struct avs_dev *adev = to_avs_dev(dai->dev);
struct hdac_ext_stream *host_stream;
unsigned int format_val;
- struct hdac_bus *bus;
unsigned int bits;
int ret;
@@ -622,8 +620,6 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so
if (hdac_stream(host_stream)->prepared)
return 0;
- bus = hdac_stream(host_stream)->bus;
- snd_hdac_ext_stream_decouple(bus, data->host_stream, true);
snd_hdac_stream_reset(hdac_stream(host_stream));
stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream);
@@ -647,6 +643,79 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so
return 0;
}
+static void avs_hda_stream_start(struct hdac_bus *bus, struct hdac_ext_stream *host_stream)
+{
+ struct hdac_stream *first_running = NULL;
+ struct hdac_stream *pos;
+ struct avs_dev *adev = hdac_to_avs(bus);
+
+ list_for_each_entry(pos, &bus->stream_list, list) {
+ if (pos->running) {
+ if (first_running)
+ break; /* more than one running */
+ first_running = pos;
+ }
+ }
+
+ /*
+ * If host_stream is a CAPTURE stream and will be the only one running,
+ * disable L1SEN to avoid sound clipping.
+ */
+ if (!first_running) {
+ if (hdac_stream(host_stream)->direction == SNDRV_PCM_STREAM_CAPTURE)
+ avs_hda_l1sen_enable(adev, false);
+ snd_hdac_stream_start(hdac_stream(host_stream));
+ return;
+ }
+
+ snd_hdac_stream_start(hdac_stream(host_stream));
+ /*
+ * If host_stream is the first stream to break the rule above,
+ * re-enable L1SEN.
+ */
+ if (list_entry_is_head(pos, &bus->stream_list, list) &&
+ first_running->direction == SNDRV_PCM_STREAM_CAPTURE)
+ avs_hda_l1sen_enable(adev, true);
+}
+
+static void avs_hda_stream_stop(struct hdac_bus *bus, struct hdac_ext_stream *host_stream)
+{
+ struct hdac_stream *first_running = NULL;
+ struct hdac_stream *pos;
+ struct avs_dev *adev = hdac_to_avs(bus);
+
+ list_for_each_entry(pos, &bus->stream_list, list) {
+ if (pos == hdac_stream(host_stream))
+ continue; /* ignore stream that is about to be stopped */
+ if (pos->running) {
+ if (first_running)
+ break; /* more than one running */
+ first_running = pos;
+ }
+ }
+
+ /*
+ * If host_stream is a CAPTURE stream and is the only one running,
+ * re-enable L1SEN.
+ */
+ if (!first_running) {
+ snd_hdac_stream_stop(hdac_stream(host_stream));
+ if (hdac_stream(host_stream)->direction == SNDRV_PCM_STREAM_CAPTURE)
+ avs_hda_l1sen_enable(adev, true);
+ return;
+ }
+
+ /*
+ * If by stopping host_stream there is only a single, CAPTURE stream running
+ * left, disable L1SEN to avoid sound clipping.
+ */
+ if (list_entry_is_head(pos, &bus->stream_list, list) &&
+ first_running->direction == SNDRV_PCM_STREAM_CAPTURE)
+ avs_hda_l1sen_enable(adev, false);
+
+ snd_hdac_stream_stop(hdac_stream(host_stream));
+}
+
static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
@@ -668,7 +737,7 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
spin_lock_irqsave(&bus->reg_lock, flags);
- snd_hdac_stream_start(hdac_stream(host_stream));
+ avs_hda_stream_start(bus, host_stream);
spin_unlock_irqrestore(&bus->reg_lock, flags);
/* Timeout on DRSM poll shall not stop the resume so ignore the result. */
@@ -698,7 +767,7 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru
dev_err(dai->dev, "pause FE path failed: %d\n", ret);
spin_lock_irqsave(&bus->reg_lock, flags);
- snd_hdac_stream_stop(hdac_stream(host_stream));
+ avs_hda_stream_stop(bus, host_stream);
spin_unlock_irqrestore(&bus->reg_lock, flags);
ret = avs_path_reset(data->path);