ALSA: usb-audio: scarlett2: Allow bit-level access to config
authorGeoffrey D. Bennett <g@b4.vu>
Tue, 22 Jun 2021 17:02:25 +0000 (02:32 +0930)
committerTakashi Iwai <tiwai@suse.de>
Tue, 22 Jun 2021 19:42:24 +0000 (21:42 +0200)
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>
sound/usb/mixer_scarlett_gen2.c

index 7a5346c..08e7b68 100644 (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;