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