Commit f785f5ee authored by Takashi Iwai's avatar Takashi Iwai

ALSA: hda/hdmi: Preserve the previous PCM device upon re-enablement

When a DRM driver turns on or off the screen with the audio
capability, it notifies the ELD to HD-audio HDMI codec driver via
component ops.  HDMI codec driver, in turn, attaches or detaches the
PCM stream for the given port on the fly.

The problem is that, since the recent code change, the HDMI driver
always treats the PCM stream assignment dynamically; this ended up the
confusion of the PCM device appearance.  e.g. when a screen goes once
off and on again, it may appear on a different PCM device before the
screen-off.  Although the application should treat such a change, it
doesn't seem working gracefully with the current pipewire (maybe
PulseAudio, too).

As a workaround, this patch changes the HDMI codec driver behavior
slightly to be more consistent.  Now it remembers the previous PCM
slot for the given port and try to assign to it.  That is, if a port
is re-enabled, the driver tries to use the same PCM slot that was
assigned to that port previously.  If it conflicts, a new slot is
searched and used like before, instead.

Note that multiple monitor connections are the only typical case where
the PCM slot preservation is effective.  As long as only a single
monitor is connected, the behavior isn't changed, and the first PCM
slot is still assigned always.

Fixes: ef6f5494 ("ALSA: hda/hdmi: Use only dynamic PCM device allocation")
Reviewed-by: default avatarJaroslav Kysela <perex@perex.cz>
Link: https://bugzilla.kernel.org/show_bug.cgi?id=217259
Link: https://lore.kernel.org/r/20230331142217.19791-1-tiwai@suse.deSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 36d4d213
...@@ -81,6 +81,7 @@ struct hdmi_spec_per_pin { ...@@ -81,6 +81,7 @@ struct hdmi_spec_per_pin {
struct delayed_work work; struct delayed_work work;
struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/ struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/
int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */ int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */
int prev_pcm_idx; /* previously assigned pcm index */
int repoll_count; int repoll_count;
bool setup; /* the stream has been set up by prepare callback */ bool setup; /* the stream has been set up by prepare callback */
bool silent_stream; bool silent_stream;
...@@ -1380,9 +1381,17 @@ static void hdmi_attach_hda_pcm(struct hdmi_spec *spec, ...@@ -1380,9 +1381,17 @@ static void hdmi_attach_hda_pcm(struct hdmi_spec *spec,
/* pcm already be attached to the pin */ /* pcm already be attached to the pin */
if (per_pin->pcm) if (per_pin->pcm)
return; return;
/* try the previously used slot at first */
idx = per_pin->prev_pcm_idx;
if (idx >= 0) {
if (!test_bit(idx, &spec->pcm_bitmap))
goto found;
per_pin->prev_pcm_idx = -1; /* no longer valid, clear it */
}
idx = hdmi_find_pcm_slot(spec, per_pin); idx = hdmi_find_pcm_slot(spec, per_pin);
if (idx == -EBUSY) if (idx == -EBUSY)
return; return;
found:
per_pin->pcm_idx = idx; per_pin->pcm_idx = idx;
per_pin->pcm = get_hdmi_pcm(spec, idx); per_pin->pcm = get_hdmi_pcm(spec, idx);
set_bit(idx, &spec->pcm_bitmap); set_bit(idx, &spec->pcm_bitmap);
...@@ -1398,6 +1407,7 @@ static void hdmi_detach_hda_pcm(struct hdmi_spec *spec, ...@@ -1398,6 +1407,7 @@ static void hdmi_detach_hda_pcm(struct hdmi_spec *spec,
return; return;
idx = per_pin->pcm_idx; idx = per_pin->pcm_idx;
per_pin->pcm_idx = -1; per_pin->pcm_idx = -1;
per_pin->prev_pcm_idx = idx; /* remember the previous index */
per_pin->pcm = NULL; per_pin->pcm = NULL;
if (idx >= 0 && idx < spec->pcm_used) if (idx >= 0 && idx < spec->pcm_used)
clear_bit(idx, &spec->pcm_bitmap); clear_bit(idx, &spec->pcm_bitmap);
...@@ -1924,6 +1934,7 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) ...@@ -1924,6 +1934,7 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
per_pin->pcm = NULL; per_pin->pcm = NULL;
per_pin->pcm_idx = -1; per_pin->pcm_idx = -1;
per_pin->prev_pcm_idx = -1;
per_pin->pin_nid = pin_nid; per_pin->pin_nid = pin_nid;
per_pin->pin_nid_idx = spec->num_nids; per_pin->pin_nid_idx = spec->num_nids;
per_pin->dev_id = i; per_pin->dev_id = i;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment