1 // SPDX-License-Identifier: GPL-2.0-only 2 // Copyright (c) 2025 Šerif Rami <ramiserifpersia@gmail.com> 3 4 #include "us144mkii.h" 5 6 /** 7 * @brief Text descriptions for playback output source options. 8 * 9 * Used by ALSA kcontrol elements to provide user-friendly names for 10 * the playback routing options (e.g., "Playback 1-2", "Playback 3-4"). 11 */ 12 static const char *const playback_source_texts[] = { "Playback 1-2", 13 "Playback 3-4" }; 14 15 /** 16 * @brief Text descriptions for capture input source options. 17 * 18 * Used by ALSA kcontrol elements to provide user-friendly names for 19 * the capture routing options (e.g., "Analog In", "Digital In"). 20 */ 21 static const char *const capture_source_texts[] = { "Analog In", "Digital In" }; 22 23 /** 24 * tascam_playback_source_info() - ALSA control info callback for playback 25 * source. 26 * @kcontrol: The ALSA kcontrol instance. 27 * @uinfo: The ALSA control element info structure to fill. 28 * 29 * This function provides information about the enumerated playback source 30 * control, including its type, count, and available items (Playback 1-2, 31 * Playback 3-4). 32 * 33 * Return: 0 on success. 34 */ 35 static int tascam_playback_source_info(struct snd_kcontrol *kcontrol, 36 struct snd_ctl_elem_info *uinfo) 37 { 38 return snd_ctl_enum_info(uinfo, 1, 2, playback_source_texts); 39 } 40 41 /** 42 * tascam_line_out_get() - ALSA control get callback for Line Outputs Source. 43 * @kcontrol: The ALSA kcontrol instance. 44 * @ucontrol: The ALSA control element value structure to fill. 45 * 46 * This function retrieves the current selection for the Line Outputs source 47 * (Playback 1-2 or Playback 3-4) from the driver's private data and populates 48 * the ALSA control element value. 49 * 50 * Return: 0 on success. 51 */ 52 static int tascam_line_out_get(struct snd_kcontrol *kcontrol, 53 struct snd_ctl_elem_value *ucontrol) 54 { 55 struct tascam_card *tascam = snd_kcontrol_chip(kcontrol); 56 57 scoped_guard(spinlock_irqsave, &tascam->lock) { 58 ucontrol->value.enumerated.item[0] = tascam->line_out_source; 59 } 60 return 0; 61 } 62 63 /** 64 * tascam_line_out_put() - ALSA control put callback for Line Outputs Source. 65 * @kcontrol: The ALSA kcontrol instance. 66 * @ucontrol: The ALSA control element value structure containing the new value. 67 * 68 * This function sets the Line Outputs source (Playback 1-2 or Playback 3-4) 69 * based on the user's selection from the ALSA control element. It validates 70 * the input and updates the driver's private data. 71 * 72 * Return: 1 if the value was changed, 0 if unchanged, or a negative error code. 73 */ 74 static int tascam_line_out_put(struct snd_kcontrol *kcontrol, 75 struct snd_ctl_elem_value *ucontrol) 76 { 77 struct tascam_card *tascam = snd_kcontrol_chip(kcontrol); 78 int changed = 0; 79 80 if (ucontrol->value.enumerated.item[0] > 1) 81 return -EINVAL; 82 83 scoped_guard(spinlock_irqsave, &tascam->lock) { 84 if (tascam->line_out_source != ucontrol->value.enumerated.item[0]) { 85 tascam->line_out_source = ucontrol->value.enumerated.item[0]; 86 changed = 1; 87 } 88 } 89 return changed; 90 } 91 92 /** 93 * tascam_line_out_control - ALSA kcontrol definition for Line Outputs Source. 94 * 95 * This defines a new ALSA mixer control named "Line OUTPUTS Source" that allows 96 * the user to select between "Playback 1-2" and "Playback 3-4" for the analog 97 * line outputs of the device. It uses the `tascam_playback_source_info` for 98 * information and `tascam_line_out_get`/`tascam_line_out_put` for value 99 * handling. 100 */ 101 static const struct snd_kcontrol_new tascam_line_out_control = { 102 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 103 .name = "Line Playback Source", 104 .info = tascam_playback_source_info, 105 .get = tascam_line_out_get, 106 .put = tascam_line_out_put, 107 }; 108 109 /** 110 * tascam_digital_out_get() - ALSA control get callback for Digital Outputs 111 * Source. 112 * @kcontrol: The ALSA kcontrol instance. 113 * @ucontrol: The ALSA control element value structure to fill. 114 * 115 * This function retrieves the current selection for the Digital Outputs source 116 * (Playback 1-2 or Playback 3-4) from the driver's private data and populates 117 * the ALSA control element value. 118 * 119 * Return: 0 on success. 120 */ 121 static int tascam_digital_out_get(struct snd_kcontrol *kcontrol, 122 struct snd_ctl_elem_value *ucontrol) 123 { 124 struct tascam_card *tascam = snd_kcontrol_chip(kcontrol); 125 126 scoped_guard(spinlock_irqsave, &tascam->lock) { 127 ucontrol->value.enumerated.item[0] = tascam->digital_out_source; 128 } 129 return 0; 130 } 131 132 /** 133 * tascam_digital_out_put() - ALSA control put callback for Digital Outputs 134 * Source. 135 * @kcontrol: The ALSA kcontrol instance. 136 * @ucontrol: The ALSA control element value structure containing the new value. 137 * 138 * This function sets the Digital Outputs source (Playback 1-2 or Playback 3-4) 139 * based on the user's selection from the ALSA control element. It validates 140 * the input and updates the driver's private data. 141 * 142 * Return: 1 if the value was changed, 0 if unchanged, or a negative error code. 143 */ 144 static int tascam_digital_out_put(struct snd_kcontrol *kcontrol, 145 struct snd_ctl_elem_value *ucontrol) 146 { 147 struct tascam_card *tascam = snd_kcontrol_chip(kcontrol); 148 int changed = 0; 149 150 if (ucontrol->value.enumerated.item[0] > 1) 151 return -EINVAL; 152 153 scoped_guard(spinlock_irqsave, &tascam->lock) { 154 if (tascam->digital_out_source != ucontrol->value.enumerated.item[0]) { 155 tascam->digital_out_source = ucontrol->value.enumerated.item[0]; 156 changed = 1; 157 } 158 } 159 return changed; 160 } 161 162 /** 163 * tascam_digital_out_control - ALSA kcontrol definition for Digital Outputs 164 * Source. 165 * 166 * This defines a new ALSA mixer control named "Digital OUTPUTS Source" that 167 * allows the user to select between "Playback 1-2" and "Playback 3-4" for the 168 * digital outputs of the device. It uses the `tascam_playback_source_info` for 169 * information and `tascam_digital_out_get`/`tascam_digital_out_put` for value 170 * handling. 171 */ 172 static const struct snd_kcontrol_new tascam_digital_out_control = { 173 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 174 .name = "Digital Playback Source", 175 .info = tascam_playback_source_info, 176 .get = tascam_digital_out_get, 177 .put = tascam_digital_out_put, 178 }; 179 180 /** 181 * tascam_capture_source_info() - ALSA control info callback for capture source. 182 * @kcontrol: The ALSA kcontrol instance. 183 * @uinfo: The ALSA control element info structure to fill. 184 * 185 * This function provides information about the enumerated capture source 186 * control, including its type, count, and available items (Analog In, Digital 187 * In). 188 * 189 * Return: 0 on success. 190 */ 191 static int tascam_capture_source_info(struct snd_kcontrol *kcontrol, 192 struct snd_ctl_elem_info *uinfo) 193 { 194 return snd_ctl_enum_info(uinfo, 1, 2, capture_source_texts); 195 } 196 197 /** 198 * tascam_capture_12_get() - ALSA control get callback for Capture channels 1 199 * and 2 Source. 200 * @kcontrol: The ALSA kcontrol instance. 201 * @ucontrol: The ALSA control element value structure to fill. 202 * 203 * This function retrieves the current selection for the Capture channels 1 and 204 * 2 source (Analog In or Digital In) from the driver's private data and 205 * populates the ALSA control element value. 206 * 207 * Return: 0 on success. 208 */ 209 static int tascam_capture_12_get(struct snd_kcontrol *kcontrol, 210 struct snd_ctl_elem_value *ucontrol) 211 { 212 struct tascam_card *tascam = snd_kcontrol_chip(kcontrol); 213 214 scoped_guard(spinlock_irqsave, &tascam->lock) { 215 ucontrol->value.enumerated.item[0] = tascam->capture_12_source; 216 } 217 return 0; 218 } 219 220 /** 221 * tascam_capture_12_put() - ALSA control put callback for Capture channels 1 222 * and 2 Source. 223 * @kcontrol: The ALSA kcontrol instance. 224 * @ucontrol: The ALSA control element value structure containing the new value. 225 * 226 * This function sets the Capture channels 1 and 2 source (Analog In or Digital 227 * In) based on the user's selection from the ALSA control element. It validates 228 * the input and updates the driver's private data. 229 * 230 * Return: 1 if the value was changed, 0 if unchanged, or a negative error code. 231 */ 232 static int tascam_capture_12_put(struct snd_kcontrol *kcontrol, 233 struct snd_ctl_elem_value *ucontrol) 234 { 235 struct tascam_card *tascam = snd_kcontrol_chip(kcontrol); 236 int changed = 0; 237 238 if (ucontrol->value.enumerated.item[0] > 1) 239 return -EINVAL; 240 241 scoped_guard(spinlock_irqsave, &tascam->lock) { 242 if (tascam->capture_12_source != ucontrol->value.enumerated.item[0]) { 243 tascam->capture_12_source = ucontrol->value.enumerated.item[0]; 244 changed = 1; 245 } 246 } 247 return changed; 248 } 249 250 /** 251 * tascam_capture_12_control - ALSA kcontrol definition for Capture channels 1 252 * and 2 Source. 253 * 254 * This defines a new ALSA mixer control named "ch1 and ch2 Source" that allows 255 * the user to select between "Analog In" and "Digital In" for the first two 256 * capture channels of the device. It uses the `tascam_capture_source_info` for 257 * information and `tascam_capture_12_get`/`tascam_capture_12_put` for value 258 * handling. 259 */ 260 static const struct snd_kcontrol_new tascam_capture_12_control = { 261 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 262 .name = "Ch1/2 Capture Source", 263 .info = tascam_capture_source_info, 264 .get = tascam_capture_12_get, 265 .put = tascam_capture_12_put, 266 }; 267 268 /** 269 * tascam_capture_34_get() - ALSA control get callback for Capture channels 3 270 * and 4 Source. 271 * @kcontrol: The ALSA kcontrol instance. 272 * @ucontrol: The ALSA control element value structure to fill. 273 * 274 * This function retrieves the current selection for the Capture channels 3 and 275 * 4 source (Analog In or Digital In) from the driver's private data and 276 * populates the ALSA control element value. 277 * 278 * Return: 0 on success. 279 */ 280 static int tascam_capture_34_get(struct snd_kcontrol *kcontrol, 281 struct snd_ctl_elem_value *ucontrol) 282 { 283 struct tascam_card *tascam = snd_kcontrol_chip(kcontrol); 284 285 scoped_guard(spinlock_irqsave, &tascam->lock) { 286 ucontrol->value.enumerated.item[0] = tascam->capture_34_source; 287 } 288 return 0; 289 } 290 291 /** 292 * tascam_capture_34_put() - ALSA control put callback for Capture channels 3 293 * and 4 Source. 294 * @kcontrol: The ALSA kcontrol instance. 295 * @ucontrol: The ALSA control element value structure containing the new value. 296 * 297 * This function sets the Capture channels 3 and 4 source (Analog In or Digital 298 * In) based on the user's selection from the ALSA control element. It validates 299 * the input and updates the driver's private data. 300 * 301 * Return: 1 if the value was changed, 0 if unchanged, or a negative error code. 302 */ 303 static int tascam_capture_34_put(struct snd_kcontrol *kcontrol, 304 struct snd_ctl_elem_value *ucontrol) 305 { 306 struct tascam_card *tascam = snd_kcontrol_chip(kcontrol); 307 int changed = 0; 308 309 if (ucontrol->value.enumerated.item[0] > 1) 310 return -EINVAL; 311 312 scoped_guard(spinlock_irqsave, &tascam->lock) { 313 if (tascam->capture_34_source != ucontrol->value.enumerated.item[0]) { 314 tascam->capture_34_source = ucontrol->value.enumerated.item[0]; 315 changed = 1; 316 } 317 } 318 return changed; 319 } 320 321 /** 322 * tascam_capture_34_control - ALSA kcontrol definition for Capture channels 3 323 * and 4 Source. 324 * 325 * This defines a new ALSA mixer control named "ch3 and ch4 Source" that allows 326 * the user to select between "Analog In" and "Digital In" for the third and 327 * fourth capture channels of the device. It uses the 328 * `tascam_capture_source_info` for information and 329 * `tascam_capture_34_get`/`tascam_capture_34_put` for value handling. 330 */ 331 static const struct snd_kcontrol_new tascam_capture_34_control = { 332 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 333 .name = "Ch3/4 Capture Source", 334 .info = tascam_capture_source_info, 335 .get = tascam_capture_34_get, 336 .put = tascam_capture_34_put, 337 }; 338 339 /** 340 * tascam_samplerate_info() - ALSA control info callback for Sample Rate. 341 * @kcontrol: The ALSA kcontrol instance. 342 * @uinfo: The ALSA control element info structure to fill. 343 * 344 * This function provides information about the Sample Rate control, defining 345 * it as an integer type with a minimum value of 0 and a maximum of 96000. 346 * 347 * Return: 0 on success. 348 */ 349 static int tascam_samplerate_info(struct snd_kcontrol *kcontrol, 350 struct snd_ctl_elem_info *uinfo) 351 { 352 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 353 uinfo->count = 1; 354 uinfo->value.integer.min = 0; 355 uinfo->value.integer.max = 96000; 356 return 0; 357 } 358 359 /** 360 * tascam_samplerate_get() - ALSA control get callback for Sample Rate. 361 * @kcontrol: The ALSA kcontrol instance. 362 * @ucontrol: The ALSA control element value structure to fill. 363 * 364 * This function retrieves the current sample rate from the device via a USB 365 * control message and populates the ALSA control element value. If the rate 366 * is already known (i.e., `current_rate` is set), it returns that value 367 * directly. 368 * 369 * Return: 0 on success, or a negative error code on failure. 370 */ 371 static int tascam_samplerate_get(struct snd_kcontrol *kcontrol, 372 struct snd_ctl_elem_value *ucontrol) 373 { 374 struct tascam_card *tascam = 375 (struct tascam_card *)snd_kcontrol_chip(kcontrol); 376 u8 *buf __free(kfree) = NULL; 377 int err; 378 u32 rate = 0; 379 380 scoped_guard(spinlock_irqsave, &tascam->lock) { 381 if (tascam->current_rate > 0) { 382 ucontrol->value.integer.value[0] = tascam->current_rate; 383 return 0; 384 } 385 } 386 387 buf = kmalloc(3, GFP_KERNEL); 388 if (!buf) 389 return -ENOMEM; 390 391 err = usb_control_msg(tascam->dev, usb_rcvctrlpipe(tascam->dev, 0), 392 UAC_GET_CUR, RT_D2H_CLASS_EP, 393 UAC_SAMPLING_FREQ_CONTROL, EP_AUDIO_IN, buf, 3, 394 USB_CTRL_TIMEOUT_MS); 395 396 if (err >= 3) 397 rate = buf[0] | (buf[1] << 8) | (buf[2] << 16); 398 399 ucontrol->value.integer.value[0] = rate; 400 return 0; 401 } 402 403 /** 404 * tascam_samplerate_control - ALSA kcontrol definition for Sample Rate. 405 * 406 * This defines a new ALSA mixer control named "Sample Rate" that displays 407 * the current sample rate of the device. It is a read-only control. 408 */ 409 static const struct snd_kcontrol_new tascam_samplerate_control = { 410 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 411 .name = "Sample Rate", 412 .info = tascam_samplerate_info, 413 .get = tascam_samplerate_get, 414 .access = SNDRV_CTL_ELEM_ACCESS_READ, 415 }; 416 417 int tascam_create_controls(struct tascam_card *tascam) 418 { 419 int err; 420 421 err = snd_ctl_add(tascam->card, 422 snd_ctl_new1(&tascam_line_out_control, tascam)); 423 if (err < 0) 424 return err; 425 err = snd_ctl_add(tascam->card, 426 snd_ctl_new1(&tascam_digital_out_control, tascam)); 427 if (err < 0) 428 return err; 429 err = snd_ctl_add(tascam->card, 430 snd_ctl_new1(&tascam_capture_12_control, tascam)); 431 if (err < 0) 432 return err; 433 err = snd_ctl_add(tascam->card, 434 snd_ctl_new1(&tascam_capture_34_control, tascam)); 435 if (err < 0) 436 return err; 437 438 err = snd_ctl_add(tascam->card, 439 snd_ctl_new1(&tascam_samplerate_control, tascam)); 440 if (err < 0) 441 return err; 442 443 return 0; 444 } 445