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
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 
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
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 
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  */
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