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 * 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 * 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 */
tascam_playback_source_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 */
tascam_line_out_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 */
tascam_line_out_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 */
tascam_digital_out_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 */
tascam_digital_out_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 */
tascam_capture_source_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 */
tascam_capture_12_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 */
tascam_capture_12_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 */
tascam_capture_34_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 */
tascam_capture_34_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 */
tascam_samplerate_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 */
tascam_samplerate_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 int err;
377 u32 rate = 0;
378
379 scoped_guard(spinlock_irqsave, &tascam->lock) {
380 if (tascam->current_rate > 0) {
381 ucontrol->value.integer.value[0] = tascam->current_rate;
382 return 0;
383 }
384 }
385
386 u8 *buf __free(kfree) =
387 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
tascam_create_controls(struct tascam_card * tascam)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