1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
4 */
5
6 #include <linux/usb.h>
7
8 #include <sound/core.h>
9 #include <sound/control.h>
10 #include <sound/soc-usb.h>
11
12 #include "../usbaudio.h"
13 #include "../card.h"
14 #include "../helper.h"
15 #include "../mixer.h"
16
17 #include "mixer_usb_offload.h"
18
19 #define PCM_IDX(n) ((n) & 0xffff)
20 #define CARD_IDX(n) ((n) >> 16)
21
22 static int
snd_usb_offload_card_route_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)23 snd_usb_offload_card_route_get(struct snd_kcontrol *kcontrol,
24 struct snd_ctl_elem_value *ucontrol)
25 {
26 struct device *sysdev = snd_kcontrol_chip(kcontrol);
27 int ret;
28
29 ret = snd_soc_usb_update_offload_route(sysdev,
30 CARD_IDX(kcontrol->private_value),
31 PCM_IDX(kcontrol->private_value),
32 SNDRV_PCM_STREAM_PLAYBACK,
33 SND_SOC_USB_KCTL_CARD_ROUTE,
34 ucontrol->value.integer.value);
35 if (ret < 0) {
36 ucontrol->value.integer.value[0] = -1;
37 ucontrol->value.integer.value[1] = -1;
38 }
39
40 return 0;
41 }
42
snd_usb_offload_card_route_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)43 static int snd_usb_offload_card_route_info(struct snd_kcontrol *kcontrol,
44 struct snd_ctl_elem_info *uinfo)
45 {
46 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
47 uinfo->count = 1;
48 uinfo->value.integer.min = -1;
49 uinfo->value.integer.max = SNDRV_CARDS;
50
51 return 0;
52 }
53
54 static struct snd_kcontrol_new snd_usb_offload_mapped_card_ctl = {
55 .iface = SNDRV_CTL_ELEM_IFACE_CARD,
56 .access = SNDRV_CTL_ELEM_ACCESS_READ,
57 .info = snd_usb_offload_card_route_info,
58 .get = snd_usb_offload_card_route_get,
59 };
60
61 static int
snd_usb_offload_pcm_route_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)62 snd_usb_offload_pcm_route_get(struct snd_kcontrol *kcontrol,
63 struct snd_ctl_elem_value *ucontrol)
64 {
65 struct device *sysdev = snd_kcontrol_chip(kcontrol);
66 int ret;
67
68 ret = snd_soc_usb_update_offload_route(sysdev,
69 CARD_IDX(kcontrol->private_value),
70 PCM_IDX(kcontrol->private_value),
71 SNDRV_PCM_STREAM_PLAYBACK,
72 SND_SOC_USB_KCTL_PCM_ROUTE,
73 ucontrol->value.integer.value);
74 if (ret < 0) {
75 ucontrol->value.integer.value[0] = -1;
76 ucontrol->value.integer.value[1] = -1;
77 }
78
79 return 0;
80 }
81
snd_usb_offload_pcm_route_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)82 static int snd_usb_offload_pcm_route_info(struct snd_kcontrol *kcontrol,
83 struct snd_ctl_elem_info *uinfo)
84 {
85 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
86 uinfo->count = 1;
87 uinfo->value.integer.min = -1;
88 /* Arbitrary max value, as there is no 'limit' on number of PCM devices */
89 uinfo->value.integer.max = 0xff;
90
91 return 0;
92 }
93
94 static struct snd_kcontrol_new snd_usb_offload_mapped_pcm_ctl = {
95 .iface = SNDRV_CTL_ELEM_IFACE_CARD,
96 .access = SNDRV_CTL_ELEM_ACCESS_READ,
97 .info = snd_usb_offload_pcm_route_info,
98 .get = snd_usb_offload_pcm_route_get,
99 };
100
101 /**
102 * snd_usb_offload_create_ctl() - Add USB offload bounded mixer
103 * @chip: USB SND chip device
104 * @bedev: Reference to USB backend DAI device
105 *
106 * Creates a sound control for a USB audio device, so that applications can
107 * query for if there is an available USB audio offload path, and which
108 * card is managing it.
109 */
snd_usb_offload_create_ctl(struct snd_usb_audio * chip,struct device * bedev)110 int snd_usb_offload_create_ctl(struct snd_usb_audio *chip, struct device *bedev)
111 {
112 struct snd_kcontrol_new *chip_kctl;
113 struct snd_usb_substream *subs;
114 struct snd_usb_stream *as;
115 char ctl_name[48];
116 int ret;
117
118 list_for_each_entry(as, &chip->pcm_list, list) {
119 subs = &as->substream[SNDRV_PCM_STREAM_PLAYBACK];
120 if (!subs->ep_num || as->pcm_index > 0xff)
121 continue;
122
123 chip_kctl = &snd_usb_offload_mapped_card_ctl;
124 chip_kctl->count = 1;
125 /*
126 * Store the associated USB SND card number and PCM index for
127 * the kctl.
128 */
129 chip_kctl->private_value = as->pcm_index |
130 chip->card->number << 16;
131 sprintf(ctl_name, "USB Offload Playback Card Route PCM#%d",
132 as->pcm_index);
133 chip_kctl->name = ctl_name;
134 ret = snd_ctl_add(chip->card, snd_ctl_new1(chip_kctl, bedev));
135 if (ret < 0)
136 break;
137
138 chip_kctl = &snd_usb_offload_mapped_pcm_ctl;
139 chip_kctl->count = 1;
140 /*
141 * Store the associated USB SND card number and PCM index for
142 * the kctl.
143 */
144 chip_kctl->private_value = as->pcm_index |
145 chip->card->number << 16;
146 sprintf(ctl_name, "USB Offload Playback PCM Route PCM#%d",
147 as->pcm_index);
148 chip_kctl->name = ctl_name;
149 ret = snd_ctl_add(chip->card, snd_ctl_new1(chip_kctl, bedev));
150 if (ret < 0)
151 break;
152 }
153
154 return ret;
155 }
156