From 43308015f5228c6de19ab94fe1648f935293333d Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Mon, 9 Mar 2020 12:52:21 +0800 Subject: [PATCH] alsa-ucm: allow binding same mic to multiple profiles When UCM configure assigns the same mic to different profiles, such as Speaker.conf: ... SectionDevice."Mic" { xxxx } ... HiFi.conf: (use the same Mic} ... SectionDevice."Mic" { xxxx } ... In this case, Speaker and HiFi profiles should both support "Mic" device. However, in ucm_add_port_combination(), pulseaudio will check if the port has been already created before (In speaker.conf in in case), HiFi will never have chance to put the port to itself. This will cause HiFi don't support "MiC". This patch's logic is: Create the port if necessary. Add the port as per the UCM requirement. Signed-off-by: Libin Yang --- src/modules/alsa/alsa-ucm.c | 109 ++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 23 deletions(-) diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c index 9d6456162..816286404 100644 --- a/src/modules/alsa/alsa-ucm.c +++ b/src/modules/alsa/alsa-ucm.c @@ -84,6 +84,8 @@ static pa_alsa_ucm_device *verb_find_device(pa_alsa_ucm_verb *verb, const char * static void ucm_port_data_init(pa_alsa_ucm_port_data *port, pa_alsa_ucm_config *ucm, pa_device_port *core_port, pa_alsa_ucm_device **devices, unsigned n_devices); +static void ucm_port_add_devices(pa_device_port *port, pa_alsa_ucm_device **pdevices, int num); +static void device_add_ucm_port(pa_alsa_ucm_device *device, pa_alsa_ucm_port_data *port); static void ucm_port_data_free(pa_device_port *port); static void ucm_port_update_available(pa_alsa_ucm_port_data *port); @@ -934,6 +936,61 @@ fail: } } +static int devs_have_same_property(pa_alsa_ucm_device *dev1, + pa_alsa_ucm_device *dev2, + const char *prop) +{ + const char *val1, *val2; + + /* check whether the 2 devices have the exactly same property */ + val1 = pa_proplist_gets(dev1->proplist, prop); + val2 = pa_proplist_gets(dev2->proplist, prop); + if (val1 && val2) + return pa_streq(val1, val2); + + return 1; +} + +static void ucm_port_add_devices(pa_device_port *port, + pa_alsa_ucm_device **pdevices, + int num) +{ + pa_alsa_ucm_device *device; + pa_alsa_ucm_port_data *portdata; + unsigned idx; + int found, i; + + pa_assert(port); + + portdata = PA_DEVICE_PORT_DATA(port); + for (i = 0; i < num; i++) { + found = 0; + PA_DYNARRAY_FOREACH(device, portdata->devices, idx) { + if (pdevices[i] == device) { + found = 1; + break; + } else { + if (!devs_have_same_property(pdevices[i], device, PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS)) + pa_log_warn("port %s has different playback channel devices\n", port->name); + if (!devs_have_same_property(pdevices[i], device, PA_ALSA_PROP_UCM_PLAYBACK_RATE)) + pa_log_warn("port %s has different playback rate devices\n", port->name); + if (!devs_have_same_property(pdevices[i], device, PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY)) + pa_log_warn("port %s has different playback priority devices\n", port->name); + if (!devs_have_same_property(pdevices[i], device, PA_ALSA_PROP_UCM_CAPTURE_CHANNELS)) + pa_log_warn("port %s has different capture channel devices\n", port->name); + if (!devs_have_same_property(pdevices[i], device, PA_ALSA_PROP_UCM_CAPTURE_RATE)) + pa_log_warn("port %s has different capture rate devices\n", port->name); + if (!devs_have_same_property(pdevices[i], device, PA_ALSA_PROP_UCM_CAPTURE_PRIORITY)) + pa_log_warn("port %s has different capture priority devices\n", port->name); + } + } + if (found == 0) { + pa_dynarray_append(portdata->devices, pdevices[i]); + device_add_ucm_port(pdevices[i], portdata); + } + } +} + static void ucm_add_port_combination( pa_hashmap *hash, pa_alsa_ucm_mapping_context *context, @@ -1019,34 +1076,40 @@ static void ucm_add_port_combination( pa_hashmap_put(ports, port->name, port); pa_log_debug("Add port %s: %s", port->name, port->description); + } - if (num == 1) { - /* To keep things simple and not worry about stacking controls, we only support hardware volumes on non-combination - * ports. */ - data = PA_DEVICE_PORT_DATA(port); + /* If the pdevices are not added to the portdata, let's add them */ + ucm_port_add_devices(port, pdevices, num); - PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) { - pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem, - is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT); + /* It needs to setup the paths when setup the profiles */ + if (num == 1 && cp) { + /* + * To keep things simple and not worry about stacking controls, + * we only support hardware volumes on non-combination ports. + */ + data = PA_DEVICE_PORT_DATA(port); - if (!path) - pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem); - else { - if (vol->master_elem) { - pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false); - e->switch_use = PA_ALSA_SWITCH_MUTE; - e->volume_use = PA_ALSA_VOLUME_MERGE; - } + PA_HASHMAP_FOREACH_KV(profile, vol, is_sink ? dev->playback_volumes : dev->capture_volumes, state) { + pa_alsa_path *path = pa_alsa_path_synthesize(vol->mixer_elem, + is_sink ? PA_ALSA_DIRECTION_OUTPUT : PA_ALSA_DIRECTION_INPUT); - pa_hashmap_put(data->paths, pa_xstrdup(profile), path); - - /* Add path also to already created empty path set */ - dev = sorted[0]; - if (is_sink) - pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path); - else - pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path); + if (!path) + pa_log_warn("Failed to set up volume control: %s", vol->mixer_elem); + else { + if (vol->master_elem) { + pa_alsa_element *e = pa_alsa_element_get(path, vol->master_elem, false); + e->switch_use = PA_ALSA_SWITCH_MUTE; + e->volume_use = PA_ALSA_VOLUME_MERGE; } + + pa_hashmap_put(data->paths, pa_xstrdup(profile), path); + + /* Add path also to already created empty path set */ + dev = sorted[0]; + if (is_sink) + pa_hashmap_put(dev->playback_mapping->output_path_set->paths, pa_xstrdup(vol->mixer_elem), path); + else + pa_hashmap_put(dev->capture_mapping->input_path_set->paths, pa_xstrdup(vol->mixer_elem), path); } } }