The Intel HDMI LPE driver works in a peculiar way when the HDMI cable is
not plugged in: any written audio is immediately discarded and underrun
is reported. That resulted in an infinite loop, because PulseAudio tried
to keep the buffer filled, which was futile since the written audio was
immediately consumed/discarded.
This patch adds special handling for the LPE driver: if the active port
of the sink is unavailable, the sink suspends itself. A new suspend
cause is added: PA_SUSPEND_UNAVAILABLE.
Change-Id: I440b50d4a3804d1a6f6e7cd9d036a1a4230c02eb
BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=100488
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
struct pa_alsa_port_data {
pa_alsa_path *path;
pa_alsa_setting *setting;
+ bool suspend_when_unavailable;
};
void pa_alsa_add_ports(void *sink_or_source_new_data, pa_alsa_path_set *ps, pa_card *card);
s->set_volume(s);
}
+ if (data->suspend_when_unavailable && p->available == PA_AVAILABLE_NO)
+ pa_sink_suspend(s, true, PA_SUSPEND_UNAVAILABLE);
+ else
+ pa_sink_suspend(s, false, PA_SUSPEND_UNAVAILABLE);
+
return 0;
}
if (profile_set)
pa_alsa_profile_set_free(profile_set);
+ /* Suspend if necessary. FIXME: It would be better to start suspended, but
+ * that would require some core changes. It's possible to set
+ * pa_sink_new_data.suspend_cause, but that has to be done before the
+ * pa_sink_new() call, and we know if we need to suspend only after the
+ * pa_sink_new() call when the initial port has been chosen. Calling
+ * pa_sink_suspend() between pa_sink_new() and pa_sink_put() would
+ * otherwise work, but currently pa_sink_suspend() will crash if
+ * pa_sink_put() hasn't been called. */
+ if (u->sink->active_port) {
+ pa_alsa_port_data *port_data;
+
+ port_data = PA_DEVICE_PORT_DATA(u->sink->active_port);
+
+ if (port_data->suspend_when_unavailable && u->sink->active_port->available == PA_AVAILABLE_NO)
+ pa_sink_suspend(u->sink, true, PA_SUSPEND_UNAVAILABLE);
+ }
+
return u->sink;
fail:
if (tp->avail == PA_AVAILABLE_NO)
pa_device_port_set_available(tp->port, tp->avail);
+ for (tp = tports; tp->port; tp++) {
+ pa_alsa_port_data *data;
+ pa_sink *sink;
+ uint32_t idx;
+
+ data = PA_DEVICE_PORT_DATA(tp->port);
+
+ if (!data->suspend_when_unavailable)
+ continue;
+
+ PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
+ if (sink->active_port == tp->port)
+ pa_sink_suspend(sink, tp->avail == PA_AVAILABLE_NO, PA_SUSPEND_UNAVAILABLE);
+ }
+ }
+
/* Update profile availabilities. The logic could be improved; for now we
* only set obviously unavailable profiles (those that contain only
* unavailable ports) to PA_AVAILABLE_NO and all others to
goto fail;
}
+ /* The Intel HDMI LPE driver needs some special handling. When the HDMI
+ * cable is not plugged in, trying to play audio doesn't work. Any written
+ * audio is immediately discarded and an underrun is reported, and that
+ * results in an infinite loop of "fill buffer, handle underrun". To work
+ * around this issue, the suspend_when_unavailable flag is used to stop
+ * playback when the HDMI cable is unplugged. */
+ if (pa_safe_streq(pa_proplist_gets(data.proplist, "alsa.driver_name"), "snd_hdmi_lpe_audio")) {
+ pa_device_port *port;
+ void *state;
+
+ PA_HASHMAP_FOREACH(port, data.ports, state) {
+ pa_alsa_port_data *port_data;
+
+ port_data = PA_DEVICE_PORT_DATA(port);
+ port_data->suspend_when_unavailable = true;
+ }
+ }
+
u->card = pa_card_new(m->core, &data);
pa_card_new_data_done(&data);
PA_SUSPEND_SESSION = 8, /* Used by module-hal for mark inactive sessions */
PA_SUSPEND_PASSTHROUGH = 16, /* Used to suspend monitor sources when the sink is in passthrough mode */
PA_SUSPEND_INTERNAL = 32, /* This is used for short period server-internal suspends, such as for sample rate updates */
+ PA_SUSPEND_UNAVAILABLE = 64, /* Used by device implementations that have to suspend when the device is unavailable */
PA_SUSPEND_ALL = 0xFFFF /* Magic cause that can be used to resume forcibly */
} pa_suspend_cause_t;