Commit 36817559 authored by Wim Taymans's avatar Wim Taymans
Browse files

sync with pipewire

parent 6a660bbd
......@@ -266,6 +266,131 @@ static void profile_free(void *data)
}
}
static int add_pro_profile(pa_card *impl, uint32_t index)
{
snd_ctl_t *ctl_hndl;
int err, dev, count = 0;
pa_alsa_profile *ap;
pa_alsa_profile_set *ps = impl->profile_set;
pa_alsa_mapping *m;
char *device;
snd_pcm_info_t *pcminfo;
pa_sample_spec ss;
snd_pcm_uframes_t try_period_size, try_buffer_size;
ss.format = PA_SAMPLE_S32LE;
ss.rate = 48000;
ss.channels = 64;
ap = pa_xnew0(pa_alsa_profile, 1);
ap->profile_set = ps;
ap->profile.name = ap->name = pa_xstrdup("pro-audio");
ap->profile.description = ap->description = pa_xstrdup(_("Pro Audio"));
ap->profile.available = ACP_AVAILABLE_YES;
ap->output_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
ap->input_mappings = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
pa_hashmap_put(ps->profiles, ap->name, ap);
ap->output_name = pa_xstrdup("pro-output");
ap->input_name = pa_xstrdup("pro-input");
ap->priority = 1;
pa_assert_se(asprintf(&device, "hw:%d", index) >= 0);
if ((err = snd_ctl_open(&ctl_hndl, device, 0)) < 0) {
pa_log_error("can't open control for card %s: %s",
device, snd_strerror(err));
return err;
}
snd_pcm_info_alloca(&pcminfo);
dev = -1;
while (1) {
char desc[128], devstr[128], *name;
if ((err = snd_ctl_pcm_next_device(ctl_hndl, &dev)) < 0) {
pa_log_error("error iterating devices: %s", snd_strerror(err));
break;
}
if (dev < 0)
break;
snd_pcm_info_set_device(pcminfo, dev);
snd_pcm_info_set_subdevice(pcminfo, 0);
snprintf(devstr, sizeof(devstr), "hw:%d,%d", index, dev);
if (count++ == 0)
snprintf(desc, sizeof(desc), "Pro");
else
snprintf(desc, sizeof(desc), "Pro %d", dev);
snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK);
if ((err = snd_ctl_pcm_info(ctl_hndl, pcminfo)) < 0) {
if (err != -ENOENT)
pa_log_error("error pcm info: %s", snd_strerror(err));
}
if (err >= 0) {
pa_assert_se(asprintf(&name, "Mapping pro-output-%d", dev) >= 0);
m = pa_alsa_mapping_get(ps, name);
m->description = pa_xstrdup(desc);
m->device_strings = pa_split_spaces_strv(devstr);
try_period_size = 1024;
try_buffer_size = 1024 * 64;
m->sample_spec = ss;
if ((m->output_pcm = pa_alsa_open_by_template(m->device_strings,
devstr, NULL, &m->sample_spec,
&m->channel_map, SND_PCM_STREAM_PLAYBACK,
&try_period_size, &try_buffer_size,
0, NULL, NULL, false))) {
pa_alsa_init_proplist_pcm(NULL, m->output_proplist, m->output_pcm);
snd_pcm_close(m->output_pcm);
m->output_pcm = NULL;
m->supported = true;
pa_channel_map_init_pro(&m->channel_map, m->sample_spec.channels);
}
pa_idxset_put(ap->output_mappings, m, NULL);
free(name);
}
snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_CAPTURE);
if ((err = snd_ctl_pcm_info(ctl_hndl, pcminfo)) < 0) {
if (err != -ENOENT)
pa_log_error("error pcm info: %s", snd_strerror(err));
}
if (err >= 0) {
pa_assert_se(asprintf(&name, "Mapping pro-input-%d", dev) >= 0);
m = pa_alsa_mapping_get(ps, name);
m->description = pa_xstrdup(desc);
m->device_strings = pa_split_spaces_strv(devstr);
try_period_size = 1024;
try_buffer_size = 1024 * 64;
m->sample_spec = ss;
if ((m->input_pcm = pa_alsa_open_by_template(m->device_strings,
devstr, NULL, &m->sample_spec,
&m->channel_map, SND_PCM_STREAM_CAPTURE,
&try_period_size, &try_buffer_size,
0, NULL, NULL, false))) {
pa_alsa_init_proplist_pcm(NULL, m->input_proplist, m->input_pcm);
snd_pcm_close(m->input_pcm);
m->input_pcm = NULL;
m->supported = true;
pa_channel_map_init_pro(&m->channel_map, m->sample_spec.channels);
}
pa_idxset_put(ap->input_mappings, m, NULL);
free(name);
}
}
snd_ctl_close(ctl_hndl);
return 0;
}
static void add_profiles(pa_card *impl)
{
pa_alsa_profile *ap;
......@@ -286,6 +411,9 @@ static void add_profiles(pa_card *impl)
ap->profile.flags = ACP_PROFILE_OFF;
pa_hashmap_put(impl->profiles, ap->name, ap);
if (!impl->use_ucm)
add_pro_profile(impl, impl->card.index);
PA_HASHMAP_FOREACH(ap, impl->profile_set->profiles, state) {
pa_alsa_mapping *m;
......@@ -446,7 +574,7 @@ static void profile_set_available(pa_card *impl, uint32_t index,
p->available = status;
if (emit && impl && impl->events && impl->events->profile_available)
if (emit && impl->events && impl->events->profile_available)
impl->events->profile_available(impl->user_data, index,
old, status);
}
......@@ -807,11 +935,9 @@ uint32_t acp_card_find_best_profile_index(struct acp_card *card, const char *nam
struct acp_card_profile *p = profiles[i];
if (name) {
if (strcmp(name, p->name))
if (strcmp(name, p->name) == 0)
best = i;
continue;
}
if (p->flags & ACP_PROFILE_OFF) {
} else if (p->flags & ACP_PROFILE_OFF) {
off = i;
} else if (p->available == ACP_AVAILABLE_YES) {
if (best == ACP_INVALID_INDEX || p->priority > profiles[best]->priority)
......@@ -917,7 +1043,6 @@ static int read_volume(pa_alsa_device *dev)
static void set_volume(pa_alsa_device *dev, const pa_cvolume *v)
{
pa_card *impl = dev->card;
pa_cvolume r;
dev->real_volume = *v;
......@@ -957,18 +1082,7 @@ static void set_volume(pa_alsa_device *dev, const pa_cvolume *v)
if (accurate_enough)
pa_cvolume_reset(&new_soft_volume, new_soft_volume.channels);
if (!pa_cvolume_equal(&dev->soft_volume, &new_soft_volume)) {
dev->soft_volume = new_soft_volume;
if (impl->events && impl->events->set_soft_volume) {
uint32_t i, n_volumes = new_soft_volume.channels;
float volumes[n_volumes];
for (i = 0; i < n_volumes; i++)
volumes[i] = pa_sw_volume_to_linear(new_soft_volume.values[i]);
impl->events->set_soft_volume(impl->user_data, &dev->device, volumes, n_volumes);
}
}
dev->soft_volume = new_soft_volume;
} else {
pa_log_debug("Wrote hardware volume: %d", pa_cvolume_max(&r));
/* We can't match exactly what the user requested, hence let's
......@@ -1179,11 +1293,16 @@ static int device_enable(pa_card *impl, pa_alsa_mapping *mapping, pa_alsa_device
p->port.priority = p->priority;
}
port_index = acp_device_find_best_port_index(&dev->device, NULL);
if (impl->auto_port)
port_index = acp_device_find_best_port_index(&dev->device, NULL);
else
port_index = ACP_INVALID_INDEX;
if (port_index == ACP_INVALID_INDEX)
dev->active_port = NULL;
else
dev->active_port = (pa_device_port*)impl->card.ports[port_index];
if (dev->active_port)
dev->active_port->port.flags |= ACP_PORT_ACTIVE;
......@@ -1197,7 +1316,7 @@ static int device_enable(pa_card *impl, pa_alsa_mapping *mapping, pa_alsa_device
return 0;
}
int acp_card_set_profile(struct acp_card *card, uint32_t new_index)
int acp_card_set_profile(struct acp_card *card, uint32_t new_index, uint32_t flags)
{
pa_card *impl = (pa_card *)card;
pa_alsa_mapping *am;
......@@ -1260,14 +1379,14 @@ int acp_card_set_profile(struct acp_card *card, uint32_t new_index)
PA_IDXSET_FOREACH(am, np->input_mappings, idx) {
if (impl->use_ucm)
/* Update ports priorities */
pa_alsa_ucm_add_ports_combination(am->output.ports, &am->ucm_context,
pa_alsa_ucm_add_ports_combination(am->input.ports, &am->ucm_context,
false, impl->ports, np, NULL);
device_enable(impl, am, &am->input);
}
}
if (op)
op->profile.flags &= ~ACP_PROFILE_ACTIVE;
np->profile.flags |= ACP_PROFILE_ACTIVE;
op->profile.flags &= ~(ACP_PROFILE_ACTIVE | ACP_PROFILE_SAVE);
np->profile.flags |= ACP_PROFILE_ACTIVE | flags;
impl->card.active_profile_index = new_index;
if (impl->events && impl->events->profile_changed)
......@@ -1346,6 +1465,8 @@ struct acp_card *acp_card_new(uint32_t index, const struct acp_dict *props)
card->active_profile_index = ACP_INVALID_INDEX;
impl->use_ucm = true;
impl->auto_profile = true;
impl->auto_port = true;
if (props) {
if ((s = acp_dict_lookup(props, "api.alsa.use-ucm")) != NULL)
......@@ -1358,6 +1479,10 @@ struct acp_card *acp_card_new(uint32_t index, const struct acp_dict *props)
profile_set = s;
if ((s = acp_dict_lookup(props, "device.profile")) != NULL)
profile = s;
if ((s = acp_dict_lookup(props, "api.acp.auto-profile")) != NULL)
impl->auto_profile = (strcmp(s, "true") == 0 || atoi(s) == 1);
if ((s = acp_dict_lookup(props, "api.acp.auto-port")) != NULL)
impl->auto_port = (strcmp(s, "true") == 0 || atoi(s) == 1);
}
impl->ucm.default_sample_spec.format = PA_SAMPLE_S16NE;
......@@ -1425,8 +1550,11 @@ struct acp_card *acp_card_new(uint32_t index, const struct acp_dict *props)
init_jacks(impl);
if (!impl->auto_profile && profile == NULL)
profile = "off";
profile_index = acp_card_find_best_profile_index(&impl->card, profile);
acp_card_set_profile(&impl->card, profile_index);
acp_card_set_profile(&impl->card, profile_index, 0);
init_eld_ctls(impl);
......@@ -1579,11 +1707,9 @@ uint32_t acp_device_find_best_port_index(struct acp_device *dev, const char *nam
struct acp_port *p = ports[i];
if (name) {
if (strcmp(name, p->name))
if (strcmp(name, p->name) == 0)
best = i;
continue;
}
if (p->available == ACP_AVAILABLE_YES) {
} else if (p->available == ACP_AVAILABLE_YES) {
if (best == ACP_INVALID_INDEX || p->priority > ports[best]->priority)
best = i;
} else if (p->available != ACP_AVAILABLE_NO) {
......@@ -1606,7 +1732,7 @@ uint32_t acp_device_find_best_port_index(struct acp_device *dev, const char *nam
return ACP_INVALID_INDEX;
}
int acp_device_set_port(struct acp_device *dev, uint32_t port_index)
int acp_device_set_port(struct acp_device *dev, uint32_t port_index, uint32_t flags)
{
pa_alsa_device *d = (pa_alsa_device*)dev;
pa_card *impl = d->card;
......@@ -1617,16 +1743,15 @@ int acp_device_set_port(struct acp_device *dev, uint32_t port_index)
return -EINVAL;
p = (pa_device_port*)impl->card.ports[port_index];
if (p == old)
return 0;
if (!pa_hashmap_get(d->ports, p->name))
return -EINVAL;
p->port.flags = ACP_PORT_ACTIVE | flags;
if (p == old)
return 0;
if (old)
old->port.flags &= ~ACP_PORT_ACTIVE;
old->port.flags &= ~(ACP_PORT_ACTIVE | ACP_PORT_SAVE);
d->active_port = p;
p->port.flags |= ACP_PORT_ACTIVE;
if (impl->use_ucm) {
pa_alsa_ucm_port_data *data;
......@@ -1636,7 +1761,8 @@ int acp_device_set_port(struct acp_device *dev, uint32_t port_index)
mixer_volume_init(impl, d);
sync_mixer(d, p);
res = pa_alsa_ucm_set_port(d->ucm_context, p, true);
res = pa_alsa_ucm_set_port(d->ucm_context, p,
dev->direction == ACP_DIRECTION_PLAYBACK);
} else {
pa_alsa_port_data *data;
......@@ -1684,8 +1810,6 @@ int acp_device_set_volume(struct acp_device *dev, const float *volume, uint32_t
} else {
d->real_volume = v;
d->soft_volume = v;
if (impl->events && impl->events->set_soft_volume)
impl->events->set_soft_volume(impl->user_data, dev, volume, n_volume);
}
if (!pa_cvolume_equal(&d->real_volume, &old_volume))
if (impl->events && impl->events->volume_changed)
......@@ -1693,19 +1817,28 @@ int acp_device_set_volume(struct acp_device *dev, const float *volume, uint32_t
return 0;
}
int acp_device_get_volume(struct acp_device *dev, float *volume, uint32_t n_volume)
static int get_volume(pa_cvolume *v, float *volume, uint32_t n_volume)
{
pa_alsa_device *d = (pa_alsa_device*)dev;
pa_cvolume v;
uint32_t i;
v = d->real_volume;
if (v.channels == 0)
if (v->channels == 0)
return -EIO;
for (i = 0; i < n_volume; i++)
volume[i] = pa_sw_volume_to_linear(v.values[i % v.channels]);
volume[i] = pa_sw_volume_to_linear(v->values[i % v->channels]);
return 0;
}
int acp_device_get_soft_volume(struct acp_device *dev, float *volume, uint32_t n_volume)
{
pa_alsa_device *d = (pa_alsa_device*)dev;
return get_volume(&d->soft_volume, volume, n_volume);
}
int acp_device_get_volume(struct acp_device *dev, float *volume, uint32_t n_volume)
{
pa_alsa_device *d = (pa_alsa_device*)dev;
return get_volume(&d->real_volume, volume, n_volume);
}
int acp_device_set_mute(struct acp_device *dev, bool mute)
{
pa_alsa_device *d = (pa_alsa_device*)dev;
......@@ -1721,8 +1854,6 @@ int acp_device_set_mute(struct acp_device *dev, bool mute)
d->set_mute(d, mute);
} else {
d->muted = mute;
if (impl->events && impl->events->set_soft_mute)
impl->events->set_soft_mute(impl->user_data, dev, mute);
}
if (old_muted != mute)
if (impl->events && impl->events->mute_changed)
......
......@@ -136,7 +136,7 @@ const char *acp_available_str(enum acp_available status);
#define ACP_KEY_PORT_TYPE "port.type" /**< a Port type, like "aux", "speaker", ... */
#define ACP_KEY_PORT_AVAILABILITY_GROUP "port.availability-group"
/**< An indentifier for the group of ports that share their availability status with
/**< An identifier for the group of ports that share their availability status with
* each other. This is meant especially for handling cases where one 3.5 mm connector
* is used for headphones, headsets and microphones, and the hardware can only tell
* that something was plugged in but not what exactly. In this situation the ports for
......@@ -177,15 +177,12 @@ struct acp_card_events {
void (*volume_changed) (void *data, struct acp_device *dev);
void (*mute_changed) (void *data, struct acp_device *dev);
void (*set_soft_volume) (void *data, struct acp_device *dev,
const float *volume, uint32_t n_volume);
void (*set_soft_mute) (void *data, struct acp_device *dev, bool mute);
};
struct acp_port {
uint32_t index; /**< unique index for this port */
#define ACP_PORT_ACTIVE (1<<0)
#define ACP_PORT_SAVE (1<<1) /* if the port needs saving */
uint32_t flags; /**< extra port flags */
const char *name; /**< Name of this port */
......@@ -229,6 +226,7 @@ struct acp_card_profile {
uint32_t index;
#define ACP_PROFILE_ACTIVE (1<<0)
#define ACP_PROFILE_OFF (1<<1) /* the Off profile */
#define ACP_PROFILE_SAVE (1<<2) /* if the profile needs saving */
uint32_t flags;
const char *name;
......@@ -274,12 +272,13 @@ int acp_card_poll_descriptors_revents(struct acp_card *card, struct pollfd *pfds
int acp_card_handle_events(struct acp_card *card);
uint32_t acp_card_find_best_profile_index(struct acp_card *card, const char *name);
int acp_card_set_profile(struct acp_card *card, uint32_t profile_index);
int acp_card_set_profile(struct acp_card *card, uint32_t profile_index, uint32_t flags);
uint32_t acp_device_find_best_port_index(struct acp_device *dev, const char *name);
int acp_device_set_port(struct acp_device *dev, uint32_t port_index);
int acp_device_set_port(struct acp_device *dev, uint32_t port_index, uint32_t flags);
int acp_device_set_volume(struct acp_device *dev, const float *volume, uint32_t n_volume);
int acp_device_get_soft_volume(struct acp_device *dev, float *volume, uint32_t n_volume);
int acp_device_get_volume(struct acp_device *dev, float *volume, uint32_t n_volume);
int acp_device_set_mute(struct acp_device *dev, bool mute);
int acp_device_get_mute(struct acp_device *dev, bool *mute);
......
......@@ -4948,10 +4948,16 @@ pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel
fn = pa_maybe_prefix_path(fname ? fname : "default.conf",
get_default_profile_dir());
if (access(fn, R_OK) != 0 && fname != NULL) {
pa_log_error("profile-set '%s' can't be accessed, trying default.conf", fn);
fn = pa_maybe_prefix_path("default.conf",
if ((r = access(fn, R_OK)) != 0) {
if (fname != NULL) {
pa_log_warn("profile-set '%s' can't be accessed: %m", fn);
fn = pa_maybe_prefix_path("default.conf",
get_default_profile_dir());
r = access(fn, R_OK);
}
if (r != 0) {
pa_log_warn("profile-set '%s' can't be accessed: %m", fn);
}
}
r = pa_config_parse(fn, NULL, items, NULL, false, ps);
pa_xfree(fn);
......@@ -5152,6 +5158,8 @@ void pa_alsa_profile_set_probe(
uint32_t idx;
p = *pp;
pa_log_debug("Check Profile %s.", p->name);
/* Skip if fallback and already found something */
if (found_input && p->fallback_input)
continue;
......
......@@ -180,7 +180,6 @@ struct pa_alsa_jack {
struct pa_alsa_mixer_id alsa_id;
char *name; /* E g "Headphone" */
char *alsa_name; /* E g "Headphone Jack" */
bool has_control; /* is the jack itself present? */
bool plugged_in; /* is this jack currently plugged in? */
snd_mixer_elem_t *melem; /* Jack detection handle */
......
......@@ -717,7 +717,7 @@ snd_pcm_t *pa_alsa_open_by_device_string(
if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) {
char *t;
t = pa_sprintf_malloc("plug:%s", d);
t = pa_sprintf_malloc("plug:SLAVE='%s'", d);
pa_xfree(d);
d = t;
......
......@@ -44,6 +44,8 @@ struct pa_card {
bool use_ucm;
bool soft_mixer;
bool auto_profile;
bool auto_port;
pa_alsa_ucm_config ucm;
pa_alsa_profile_set *profile_set;
......
......@@ -201,6 +201,17 @@ static inline pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m,
return NULL;
}
static inline pa_channel_map* pa_channel_map_init_pro(pa_channel_map *m,
unsigned channels)
{
unsigned i;
pa_channel_map_init(m);
for (i = 0; i < channels; i++)
m->map[i] = PA_CHANNEL_POSITION_INVALID;
m->channels = (uint8_t) channels;
return m;
}
typedef uint64_t pa_channel_position_mask_t;
#define PA_CHANNEL_POSITION_MASK(f) ((pa_channel_position_mask_t) (1ULL << (f)))
......
......@@ -72,7 +72,7 @@ struct pa_device_port {
unsigned priority;
pa_available_t available; /* PA_AVAILABLE_UNKNOWN, PA_AVAILABLE_NO or PA_AVAILABLE_YES */
char *availability_group; /* a string indentifier which determine the group of devices handling the available state simulteneously */
char *availability_group; /* a string identifier which determine the group of devices handling the available state simulteneously */
pa_direction_t direction;
int64_t latency_offset;
......
......@@ -461,6 +461,102 @@ channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
priority = 6
direction = output
[Mapping hdmi-stereo-extra8]
description = Digital Stereo (HDMI 9)
device-strings = hdmi:%f,8
paths-output = hdmi-output-8
channel-map = left,right
priority = 7
direction = output
[Mapping hdmi-surround-extra8]
description = Digital Surround 5.1 (HDMI 9)
device-strings = hdmi:%f,8
paths-output = hdmi-output-8
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
priority = 6
direction = output
[Mapping hdmi-surround71-extra8]
description = Digital Surround 7.1 (HDMI 9)
device-strings = hdmi:%f,8
paths-output = hdmi-output-8
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
priority = 6
direction = output
[Mapping hdmi-dts-surround-extra8]
description = Digital Surround 5.1 (HDMI 9/DTS)
device-strings = dcahdmi:%f,8
paths-output = hdmi-output-8
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
priority = 6
direction = output
[Mapping hdmi-stereo-extra9]
description = Digital Stereo (HDMI 10)
device-strings = hdmi:%f,9
paths-output = hdmi-output-9
channel-map = left,right
priority = 7
direction = output
[Mapping hdmi-surround-extra9]
description = Digital Surround 5.1 (HDMI 10)
device-strings = hdmi:%f,9
paths-output = hdmi-output-9
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
priority = 6
direction = output
[Mapping hdmi-surround71-extra9]
description = Digital Surround 7.1 (HDMI 10)
device-strings = hdmi:%f,9
paths-output = hdmi-output-9
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
priority = 6
direction = output
[Mapping hdmi-dts-surround-extra9]
description = Digital Surround 5.1 (HDMI 10/DTS)
device-strings = dcahdmi:%f,9
paths-output = hdmi-output-9
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
priority = 6
direction = output
[Mapping hdmi-stereo-extra10]
description = Digital Stereo (HDMI 11)
device-strings = hdmi:%f,10
paths-output = hdmi-output-10
channel-map = left,right
priority = 7
direction = output
[Mapping hdmi-surround-extra10]
description = Digital Surround 5.1 (HDMI 11)
device-strings = hdmi:%f,10
paths-output = hdmi-output-10
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
priority = 6
direction = output
[Mapping hdmi-surround71-extra10]
description = Digital Surround 7.1 (HDMI 11)
device-strings = hdmi:%f,10
paths-output = hdmi-output-10
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right
priority = 6
direction = output
[Mapping hdmi-dts-surround-extra10]
description = Digital Surround 5.1 (HDMI 11/DTS)
device-strings = dcahdmi:%f,10
paths-output = hdmi-output-10
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
priority = 6
direction = output
[Mapping multichannel-output]
device-strings = hw:%f
channel-map = left,right,rear-left,rear-right
......