spa: alsa: Support volume control commands for external volume control

This commit is contained in:
Arun Raghavan 2026-03-03 15:44:44 -08:00
parent 681169b415
commit e863e71f6b
3 changed files with 99 additions and 0 deletions

View file

@ -2486,6 +2486,24 @@ int acp_device_set_volume(struct acp_device *dev, const float *volume, uint32_t
return 0;
}
int acp_device_set_volume_updown(struct acp_device *dev, bool up)
{
pa_alsa_device *d = (pa_alsa_device*)dev;
pa_card *impl = d->card;
if (!impl->card.ext_volume.client)
return -EINVAL;
if (!d->active_port)
return -EINVAL;
pa_log_info("Volume %s", up ? "up" : "down");
/* TODO: use step size from capabilities */
return spa_acp_ext_volume_write_volume_relative(&impl->card.ext_volume, impl->name,
d->active_port->name, up ? 1.0 : -1.0);
}
static int get_volume(pa_cvolume *v, float *volume, uint32_t n_volume)
{
uint32_t i;
@ -2539,6 +2557,23 @@ int acp_device_get_mute(struct acp_device *dev, bool *mute)
return 0;
}
int acp_device_toggle_mute(struct acp_device *dev)
{
pa_alsa_device *d = (pa_alsa_device*)dev;
pa_card *impl = d->card;
if (!impl->card.ext_volume.client)
return -EINVAL;
if (!d->active_port)
return -EINVAL;
pa_log_info("Toggle mute");
return spa_acp_ext_volume_write_mute_toggle(&impl->card.ext_volume, impl->name,
d->active_port->name);
}
void acp_set_log_func(acp_log_func func, void *data)
{
_acp_log_func = func;

View file

@ -308,9 +308,11 @@ uint32_t acp_device_find_best_port_index(struct acp_device *dev, const char *nam
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_set_volume_updown(struct acp_device *dev, bool up);
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_toggle_mute(struct acp_device *dev);
int acp_device_get_mute(struct acp_device *dev, bool *mute);
typedef void (*acp_log_func) (void *data,

View file

@ -893,12 +893,74 @@ static int impl_set_param(void *object,
return 0;
}
static int impl_send_command(void *object, const struct spa_command *command)
{
struct impl *this = object;
struct acp_card *card = this->card;
spa_return_val_if_fail(this != NULL, -EINVAL);
spa_return_val_if_fail(command != NULL, -EINVAL);
switch (SPA_DEVICE_COMMAND_ID(command)) {
case SPA_DEVICE_COMMAND_VolumeControl:
{
const struct spa_pod_prop *prop;
/* We support some volume control commands for external volumes */
if (card->ext_volume.client == NULL)
return -ENOTSUP;
SPA_POD_OBJECT_FOREACH(&command->body, prop) {
struct acp_port *port;
struct acp_device *dev = NULL;
uint32_t id, i;
if (spa_pod_get_id(&prop->value, &id) < 0 || id > card->n_ports)
return -EINVAL;
port = card->ports[id];
for (i = 0; i < port->n_devices; i++) {
if (port->devices[i]->flags & ACP_DEVICE_ACTIVE) {
dev = port->devices[i];
break;
}
}
if (dev == NULL)
return -EINVAL;
switch (prop->key) {
case SPA_COMMAND_DEVICE_volumeUp:
acp_device_set_volume_updown(dev, true);
break;
case SPA_COMMAND_DEVICE_volumeDown:
acp_device_set_volume_updown(dev, false);
break;
case SPA_COMMAND_DEVICE_muteToggle:
acp_device_toggle_mute(dev);
break;
default:
return -ENOTSUP;
}
}
break;
}
default:
return -ENOTSUP;
}
return 0;
}
static const struct spa_device_methods impl_device = {
SPA_VERSION_DEVICE_METHODS,
.add_listener = impl_add_listener,
.sync = impl_sync,
.enum_params = impl_enum_params,
.set_param = impl_set_param,
.send_command = impl_send_command,
};
static void card_props_changed(void *data)