diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index ca22f195f..898156435 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -195,6 +195,7 @@ enum { static void userdata_free(struct userdata *u); static int unsuspend(struct userdata *u, bool recovering); +static void sync_mixer(struct userdata *u, pa_device_port *port); /* FIXME: Is there a better way to do this than device names? */ static bool is_iec958(struct userdata *u) { @@ -1087,6 +1088,12 @@ static void suspend(struct userdata *u) { pa_sink_set_max_rewind_within_thread(u->sink, 0); pa_sink_set_max_request_within_thread(u->sink, 0); + /* Disabling the UCM devices may save some power. */ + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(u->sink->active_port); + pa_alsa_ucm_port_device_disable(data); + } + pa_log_info("Device suspended..."); } @@ -1202,6 +1209,13 @@ static int unsuspend(struct userdata *u, bool recovering) { pa_log_info("Trying resume..."); + /* We disable all UCM devices when suspending, so let's enable them again. */ + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(u->sink->active_port); + pa_alsa_ucm_port_device_enable(data); + sync_mixer(u, u->sink->active_port); + } + if ((is_iec958(u) || is_hdmi(u)) && pa_sink_is_passthrough(u->sink)) { /* Need to open device in NONAUDIO mode */ int len = strlen(u->device_name) + 8; @@ -1508,6 +1522,12 @@ static void sink_get_volume_cb(pa_sink *s) { pa_assert(u->mixer_path); pa_assert(u->mixer_handle); + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(s->active_port); + if (pa_alsa_ucm_port_device_status(data) <= 0) + return; + } + if (pa_alsa_path_get_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0) return; @@ -1538,6 +1558,12 @@ static void sink_set_volume_cb(pa_sink *s) { pa_assert(u->mixer_path); pa_assert(u->mixer_handle); + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(s->active_port); + if (pa_alsa_ucm_port_device_status(data) <= 0) + return; + } + /* Shift up by the base volume */ pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume); @@ -1601,6 +1627,12 @@ static void sink_write_volume_cb(pa_sink *s) { pa_assert(u->mixer_handle); pa_assert(s->flags & PA_SINK_DEFERRED_VOLUME); + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(s->active_port); + if (pa_alsa_ucm_port_device_status(data) <= 0) + return; + } + /* Shift up by the base volume */ pa_sw_cvolume_divide_scalar(&hw_vol, &hw_vol, s->base_volume); @@ -1639,6 +1671,14 @@ static int sink_get_mute_cb(pa_sink *s, bool *mute) { pa_assert(u->mixer_path); pa_assert(u->mixer_handle); + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(s->active_port); + if (pa_alsa_ucm_port_device_status(data) <= 0) { + *mute = s->muted; + return 0; + } + } + if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, mute) < 0) return -1; @@ -1652,6 +1692,12 @@ static void sink_set_mute_cb(pa_sink *s) { pa_assert(u->mixer_path); pa_assert(u->mixer_handle); + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(s->active_port); + if (pa_alsa_ucm_port_device_status(data) <= 0) + return; + } + pa_alsa_path_set_mute(u->mixer_path, u->mixer_handle, s->muted); } diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index d88c47f1f..b2b1d0185 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -176,6 +176,7 @@ enum { static void userdata_free(struct userdata *u); static int unsuspend(struct userdata *u, bool recovering); +static void sync_mixer(struct userdata *u, pa_device_port *port); static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct userdata *u) { pa_assert(r); @@ -995,6 +996,12 @@ static void suspend(struct userdata *u) { /* Close PCM device */ close_pcm(u); + /* Disabling the UCM devices may save some power. */ + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(u->source->active_port); + pa_alsa_ucm_port_device_disable(data); + } + pa_log_info("Device suspended..."); } @@ -1085,6 +1092,13 @@ static int unsuspend(struct userdata *u, bool recovering) { pa_log_info("Trying resume..."); + /* We disable all UCM devices when suspending, so let's enable them again. */ + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(u->source->active_port); + pa_alsa_ucm_port_device_enable(data); + sync_mixer(u, u->source->active_port); + } + /* * On some machines, during the system suspend and resume, the thread_func could receive * POLLERR events before the dev nodes in /dev/snd/ are accessible, and thread_func calls @@ -1382,6 +1396,12 @@ static void source_get_volume_cb(pa_source *s) { pa_assert(u->mixer_path); pa_assert(u->mixer_handle); + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(s->active_port); + if (pa_alsa_ucm_port_device_status(data) <= 0) + return; + } + if (pa_alsa_path_get_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0) return; @@ -1412,6 +1432,12 @@ static void source_set_volume_cb(pa_source *s) { pa_assert(u->mixer_path); pa_assert(u->mixer_handle); + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(s->active_port); + if (pa_alsa_ucm_port_device_status(data) <= 0) + return; + } + /* Shift up by the base volume */ pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume); @@ -1475,6 +1501,12 @@ static void source_write_volume_cb(pa_source *s) { pa_assert(u->mixer_handle); pa_assert(s->flags & PA_SOURCE_DEFERRED_VOLUME); + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(s->active_port); + if (pa_alsa_ucm_port_device_status(data) <= 0) + return; + } + /* Shift up by the base volume */ pa_sw_cvolume_divide_scalar(&hw_vol, &hw_vol, s->base_volume); @@ -1513,6 +1545,14 @@ static int source_get_mute_cb(pa_source *s, bool *mute) { pa_assert(u->mixer_path); pa_assert(u->mixer_handle); + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(s->active_port); + if (pa_alsa_ucm_port_device_status(data) <= 0) { + *mute = s->muted; + return 0; + } + } + if (pa_alsa_path_get_mute(u->mixer_path, u->mixer_handle, mute) < 0) return -1; @@ -1526,6 +1566,12 @@ static void source_set_mute_cb(pa_source *s) { pa_assert(u->mixer_path); pa_assert(u->mixer_handle); + if (u->ucm_context) { + pa_alsa_ucm_port_data *data = PA_DEVICE_PORT_DATA(s->active_port); + if (pa_alsa_ucm_port_device_status(data) <= 0) + return; + } + pa_alsa_path_set_mute(u->mixer_path, u->mixer_handle, s->muted); } diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c index 018c01739..c3c7cf0e3 100644 --- a/src/modules/alsa/alsa-ucm.c +++ b/src/modules/alsa/alsa-ucm.c @@ -2535,6 +2535,18 @@ static void ucm_port_data_free(pa_device_port *port) { pa_xfree(ucm_port->eld_mixer_device_name); } +int pa_alsa_ucm_port_device_enable(pa_alsa_ucm_port_data *data) { + return ucm_device_enable(data->ucm, data->device); +} + +int pa_alsa_ucm_port_device_disable(pa_alsa_ucm_port_data *data) { + return ucm_device_disable(data->ucm, data->device); +} + +long pa_alsa_ucm_port_device_status(pa_alsa_ucm_port_data *data) { + return ucm_device_status(data->ucm, data->device); +} + #else /* HAVE_ALSA_UCM */ /* Dummy functions for systems without UCM support */ @@ -2591,4 +2603,16 @@ void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, p void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) { } +int pa_alsa_ucm_port_device_enable(pa_alsa_ucm_port_data *data) { + return -1; +} + +int pa_alsa_ucm_port_device_disable(pa_alsa_ucm_port_data *data) { + return -1; +} + +long pa_alsa_ucm_port_device_status(pa_alsa_ucm_port_data *data) { + return -1; +} + #endif diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h index 543eefe0f..dced5f8ea 100644 --- a/src/modules/alsa/alsa-ucm.h +++ b/src/modules/alsa/alsa-ucm.h @@ -286,6 +286,10 @@ struct pa_alsa_ucm_port_data { int eld_device; /* PCM device number */ }; +int pa_alsa_ucm_port_device_enable(pa_alsa_ucm_port_data *data); +int pa_alsa_ucm_port_device_disable(pa_alsa_ucm_port_data *data); +long pa_alsa_ucm_port_device_status(pa_alsa_ucm_port_data *data); + struct pa_alsa_ucm_volume { char *mixer_elem; /* mixer element identifier */ char *master_elem; /* master mixer element identifier */