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