ALSA: usb-audio: scarlett2: Allow bit-level access to config

Add support for accessing configuration values when multiple values
are stored in one byte. Needed by the upcoming Solo and 2i2 Gen 3
support.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
Link: https://lore.kernel.org/r/4e54e9e106ec7029c1a668c51b4fc769a7eb4ed0.1624379707.git.g@b4.vu
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Geoffrey D. Bennett 2021-06-23 02:32:25 +09:30 committed by Takashi Iwai
parent 8aea2e32a9
commit 9e15fae6c5

View file

@ -814,7 +814,7 @@ enum {
};
/* Location, size, and activation command number for the configuration
* parameters
* parameters. Size is in bits and may be 1, 8, or 16.
*/
struct scarlett2_config {
u8 offset;
@ -825,25 +825,25 @@ struct scarlett2_config {
static const struct scarlett2_config
scarlett2_config_items[SCARLETT2_CONFIG_COUNT] = {
[SCARLETT2_CONFIG_DIM_MUTE] = {
.offset = 0x31, .size = 1, .activate = 2 },
.offset = 0x31, .size = 8, .activate = 2 },
[SCARLETT2_CONFIG_LINE_OUT_VOLUME] = {
.offset = 0x34, .size = 2, .activate = 1 },
.offset = 0x34, .size = 16, .activate = 1 },
[SCARLETT2_CONFIG_MUTE_SWITCH] = {
.offset = 0x5c, .size = 1, .activate = 1 },
.offset = 0x5c, .size = 8, .activate = 1 },
[SCARLETT2_CONFIG_SW_HW_SWITCH] = {
.offset = 0x66, .size = 1, .activate = 3 },
.offset = 0x66, .size = 8, .activate = 3 },
[SCARLETT2_CONFIG_LEVEL_SWITCH] = {
.offset = 0x7c, .size = 1, .activate = 7 },
.offset = 0x7c, .size = 8, .activate = 7 },
[SCARLETT2_CONFIG_PAD_SWITCH] = {
.offset = 0x84, .size = 1, .activate = 8 },
.offset = 0x84, .size = 8, .activate = 8 },
[SCARLETT2_CONFIG_MSD_SWITCH] = {
.offset = 0x9d, .size = 1, .activate = 6 },
.offset = 0x9d, .size = 8, .activate = 6 },
};
/* proprietary request/response format */
@ -1008,9 +1008,25 @@ static int scarlett2_usb_get_config(
{
const struct scarlett2_config *config_item =
&scarlett2_config_items[config_item_num];
int size = config_item->size * count;
int size, err, i;
u8 value;
return scarlett2_usb_get(mixer, config_item->offset, buf, size);
/* For byte-sized parameters, retrieve directly into buf */
if (config_item->size >= 8) {
size = config_item->size / 8 * count;
return scarlett2_usb_get(mixer, config_item->offset, buf, size);
}
/* For bit-sized parameters, retrieve into value */
err = scarlett2_usb_get(mixer, config_item->offset, &value, 1);
if (err < 0)
return err;
/* then unpack from value into buf[] */
for (i = 0; i < 8 && i < count; i++, value >>= 1)
*(u8 *)buf++ = value & 1;
return 0;
}
/* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */
@ -1047,18 +1063,44 @@ static int scarlett2_usb_set_config(
__le32 value;
} __packed req;
__le32 req2;
int offset, size;
int err;
struct scarlett2_data *private = mixer->private_data;
/* Cancel any pending NVRAM save */
cancel_delayed_work_sync(&private->work);
/* Convert config_item->size in bits to size in bytes and
* calculate offset
*/
if (config_item->size >= 8) {
size = config_item->size / 8;
offset = config_item->offset + index * size;
/* If updating a bit, retrieve the old value, set/clear the
* bit as needed, and update value
*/
} else {
u8 tmp;
size = 1;
offset = config_item->offset;
scarlett2_usb_get(mixer, offset, &tmp, 1);
if (value)
tmp |= (1 << index);
else
tmp &= ~(1 << index);
value = tmp;
}
/* Send the configuration parameter data */
req.offset = cpu_to_le32(config_item->offset + index * config_item->size);
req.bytes = cpu_to_le32(config_item->size);
req.offset = cpu_to_le32(offset);
req.bytes = cpu_to_le32(size);
req.value = cpu_to_le32(value);
err = scarlett2_usb(mixer, SCARLETT2_USB_SET_DATA,
&req, sizeof(u32) * 2 + config_item->size,
&req, sizeof(u32) * 2 + size,
NULL, 0);
if (err < 0)
return err;