1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Linux driver for TerraTec DMX 6Fire USB |
4 | * |
5 | * Mixer control |
6 | * |
7 | * Author: Torsten Schenk <torsten.schenk@zoho.com> |
8 | * Created: Jan 01, 2011 |
9 | * Copyright: (C) Torsten Schenk |
10 | * |
11 | * Thanks to: |
12 | * - Holger Ruckdeschel: he found out how to control individual channel |
13 | * volumes and introduced mute switch |
14 | */ |
15 | |
16 | #include <linux/interrupt.h> |
17 | #include <sound/control.h> |
18 | #include <sound/tlv.h> |
19 | |
20 | #include "control.h" |
21 | #include "comm.h" |
22 | #include "chip.h" |
23 | |
24 | static const char * const opt_coax_texts[2] = { "Optical" , "Coax" }; |
25 | static const char * const line_phono_texts[2] = { "Line" , "Phono" }; |
26 | |
27 | /* |
28 | * data that needs to be sent to device. sets up card internal stuff. |
29 | * values dumped from windows driver and filtered by trial'n'error. |
30 | */ |
31 | static const struct { |
32 | u8 type; |
33 | u8 reg; |
34 | u8 value; |
35 | } |
36 | init_data[] = { |
37 | { 0x22, 0x00, 0x00 }, { 0x20, 0x00, 0x08 }, { 0x22, 0x01, 0x01 }, |
38 | { 0x20, 0x01, 0x08 }, { 0x22, 0x02, 0x00 }, { 0x20, 0x02, 0x08 }, |
39 | { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 }, |
40 | { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 }, |
41 | { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 }, |
42 | { 0x12, 0x0d, 0x38 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 }, |
43 | { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 }, |
44 | { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 }, |
45 | { 0 } /* TERMINATING ENTRY */ |
46 | }; |
47 | |
48 | static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 }; |
49 | /* values to write to soundcard register for all samplerates */ |
50 | static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01}; |
51 | static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00}; |
52 | |
53 | static DECLARE_TLV_DB_MINMAX(tlv_output, -9000, 0); |
54 | static DECLARE_TLV_DB_MINMAX(tlv_input, -1500, 1500); |
55 | |
56 | enum { |
57 | DIGITAL_THRU_ONLY_SAMPLERATE = 3 |
58 | }; |
59 | |
60 | static void usb6fire_control_output_vol_update(struct control_runtime *rt) |
61 | { |
62 | struct comm_runtime *comm_rt = rt->chip->comm; |
63 | int i; |
64 | |
65 | if (comm_rt) |
66 | for (i = 0; i < 6; i++) |
67 | if (!(rt->ovol_updated & (1 << i))) { |
68 | comm_rt->write8(comm_rt, 0x12, 0x0f + i, |
69 | 180 - rt->output_vol[i]); |
70 | rt->ovol_updated |= 1 << i; |
71 | } |
72 | } |
73 | |
74 | static void usb6fire_control_output_mute_update(struct control_runtime *rt) |
75 | { |
76 | struct comm_runtime *comm_rt = rt->chip->comm; |
77 | |
78 | if (comm_rt) |
79 | comm_rt->write8(comm_rt, 0x12, 0x0e, ~rt->output_mute); |
80 | } |
81 | |
82 | static void usb6fire_control_input_vol_update(struct control_runtime *rt) |
83 | { |
84 | struct comm_runtime *comm_rt = rt->chip->comm; |
85 | int i; |
86 | |
87 | if (comm_rt) |
88 | for (i = 0; i < 2; i++) |
89 | if (!(rt->ivol_updated & (1 << i))) { |
90 | comm_rt->write8(comm_rt, 0x12, 0x1c + i, |
91 | rt->input_vol[i] & 0x3f); |
92 | rt->ivol_updated |= 1 << i; |
93 | } |
94 | } |
95 | |
96 | static void usb6fire_control_line_phono_update(struct control_runtime *rt) |
97 | { |
98 | struct comm_runtime *comm_rt = rt->chip->comm; |
99 | if (comm_rt) { |
100 | comm_rt->write8(comm_rt, 0x22, 0x02, rt->line_phono_switch); |
101 | comm_rt->write8(comm_rt, 0x21, 0x02, rt->line_phono_switch); |
102 | } |
103 | } |
104 | |
105 | static void usb6fire_control_opt_coax_update(struct control_runtime *rt) |
106 | { |
107 | struct comm_runtime *comm_rt = rt->chip->comm; |
108 | if (comm_rt) { |
109 | comm_rt->write8(comm_rt, 0x22, 0x00, rt->opt_coax_switch); |
110 | comm_rt->write8(comm_rt, 0x21, 0x00, rt->opt_coax_switch); |
111 | } |
112 | } |
113 | |
114 | static int usb6fire_control_set_rate(struct control_runtime *rt, int rate) |
115 | { |
116 | int ret; |
117 | struct usb_device *device = rt->chip->dev; |
118 | struct comm_runtime *comm_rt = rt->chip->comm; |
119 | |
120 | if (rate < 0 || rate >= CONTROL_N_RATES) |
121 | return -EINVAL; |
122 | |
123 | ret = usb_set_interface(dev: device, ifnum: 1, alternate: rates_altsetting[rate]); |
124 | if (ret < 0) |
125 | return ret; |
126 | |
127 | /* set soundcard clock */ |
128 | ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate], |
129 | rates_6fire_vh[rate]); |
130 | if (ret < 0) |
131 | return ret; |
132 | |
133 | return 0; |
134 | } |
135 | |
136 | static int usb6fire_control_set_channels( |
137 | struct control_runtime *rt, int n_analog_out, |
138 | int n_analog_in, bool spdif_out, bool spdif_in) |
139 | { |
140 | int ret; |
141 | struct comm_runtime *comm_rt = rt->chip->comm; |
142 | |
143 | /* enable analog inputs and outputs |
144 | * (one bit per stereo-channel) */ |
145 | ret = comm_rt->write16(comm_rt, 0x02, 0x02, |
146 | (1 << (n_analog_out / 2)) - 1, |
147 | (1 << (n_analog_in / 2)) - 1); |
148 | if (ret < 0) |
149 | return ret; |
150 | |
151 | /* disable digital inputs and outputs */ |
152 | /* TODO: use spdif_x to enable/disable digital channels */ |
153 | ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00); |
154 | if (ret < 0) |
155 | return ret; |
156 | |
157 | return 0; |
158 | } |
159 | |
160 | static int usb6fire_control_streaming_update(struct control_runtime *rt) |
161 | { |
162 | struct comm_runtime *comm_rt = rt->chip->comm; |
163 | |
164 | if (comm_rt) { |
165 | if (!rt->usb_streaming && rt->digital_thru_switch) |
166 | usb6fire_control_set_rate(rt, |
167 | rate: DIGITAL_THRU_ONLY_SAMPLERATE); |
168 | return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, |
169 | (rt->usb_streaming ? 0x01 : 0x00) | |
170 | (rt->digital_thru_switch ? 0x08 : 0x00)); |
171 | } |
172 | return -EINVAL; |
173 | } |
174 | |
175 | static int usb6fire_control_output_vol_info(struct snd_kcontrol *kcontrol, |
176 | struct snd_ctl_elem_info *uinfo) |
177 | { |
178 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
179 | uinfo->count = 2; |
180 | uinfo->value.integer.min = 0; |
181 | uinfo->value.integer.max = 180; |
182 | return 0; |
183 | } |
184 | |
185 | static int usb6fire_control_output_vol_put(struct snd_kcontrol *kcontrol, |
186 | struct snd_ctl_elem_value *ucontrol) |
187 | { |
188 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
189 | unsigned int ch = kcontrol->private_value; |
190 | int changed = 0; |
191 | |
192 | if (ch > 4) { |
193 | dev_err(&rt->chip->dev->dev, |
194 | "Invalid channel in volume control." ); |
195 | return -EINVAL; |
196 | } |
197 | |
198 | if (rt->output_vol[ch] != ucontrol->value.integer.value[0]) { |
199 | rt->output_vol[ch] = ucontrol->value.integer.value[0]; |
200 | rt->ovol_updated &= ~(1 << ch); |
201 | changed = 1; |
202 | } |
203 | if (rt->output_vol[ch + 1] != ucontrol->value.integer.value[1]) { |
204 | rt->output_vol[ch + 1] = ucontrol->value.integer.value[1]; |
205 | rt->ovol_updated &= ~(2 << ch); |
206 | changed = 1; |
207 | } |
208 | |
209 | if (changed) |
210 | usb6fire_control_output_vol_update(rt); |
211 | |
212 | return changed; |
213 | } |
214 | |
215 | static int usb6fire_control_output_vol_get(struct snd_kcontrol *kcontrol, |
216 | struct snd_ctl_elem_value *ucontrol) |
217 | { |
218 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
219 | unsigned int ch = kcontrol->private_value; |
220 | |
221 | if (ch > 4) { |
222 | dev_err(&rt->chip->dev->dev, |
223 | "Invalid channel in volume control." ); |
224 | return -EINVAL; |
225 | } |
226 | |
227 | ucontrol->value.integer.value[0] = rt->output_vol[ch]; |
228 | ucontrol->value.integer.value[1] = rt->output_vol[ch + 1]; |
229 | return 0; |
230 | } |
231 | |
232 | static int usb6fire_control_output_mute_put(struct snd_kcontrol *kcontrol, |
233 | struct snd_ctl_elem_value *ucontrol) |
234 | { |
235 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
236 | unsigned int ch = kcontrol->private_value; |
237 | u8 old = rt->output_mute; |
238 | u8 value = 0; |
239 | |
240 | if (ch > 4) { |
241 | dev_err(&rt->chip->dev->dev, |
242 | "Invalid channel in volume control." ); |
243 | return -EINVAL; |
244 | } |
245 | |
246 | rt->output_mute &= ~(3 << ch); |
247 | if (ucontrol->value.integer.value[0]) |
248 | value |= 1; |
249 | if (ucontrol->value.integer.value[1]) |
250 | value |= 2; |
251 | rt->output_mute |= value << ch; |
252 | |
253 | if (rt->output_mute != old) |
254 | usb6fire_control_output_mute_update(rt); |
255 | |
256 | return rt->output_mute != old; |
257 | } |
258 | |
259 | static int usb6fire_control_output_mute_get(struct snd_kcontrol *kcontrol, |
260 | struct snd_ctl_elem_value *ucontrol) |
261 | { |
262 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
263 | unsigned int ch = kcontrol->private_value; |
264 | u8 value = rt->output_mute >> ch; |
265 | |
266 | if (ch > 4) { |
267 | dev_err(&rt->chip->dev->dev, |
268 | "Invalid channel in volume control." ); |
269 | return -EINVAL; |
270 | } |
271 | |
272 | ucontrol->value.integer.value[0] = 1 & value; |
273 | value >>= 1; |
274 | ucontrol->value.integer.value[1] = 1 & value; |
275 | |
276 | return 0; |
277 | } |
278 | |
279 | static int usb6fire_control_input_vol_info(struct snd_kcontrol *kcontrol, |
280 | struct snd_ctl_elem_info *uinfo) |
281 | { |
282 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
283 | uinfo->count = 2; |
284 | uinfo->value.integer.min = 0; |
285 | uinfo->value.integer.max = 30; |
286 | return 0; |
287 | } |
288 | |
289 | static int usb6fire_control_input_vol_put(struct snd_kcontrol *kcontrol, |
290 | struct snd_ctl_elem_value *ucontrol) |
291 | { |
292 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
293 | int changed = 0; |
294 | |
295 | if (rt->input_vol[0] != ucontrol->value.integer.value[0]) { |
296 | rt->input_vol[0] = ucontrol->value.integer.value[0] - 15; |
297 | rt->ivol_updated &= ~(1 << 0); |
298 | changed = 1; |
299 | } |
300 | if (rt->input_vol[1] != ucontrol->value.integer.value[1]) { |
301 | rt->input_vol[1] = ucontrol->value.integer.value[1] - 15; |
302 | rt->ivol_updated &= ~(1 << 1); |
303 | changed = 1; |
304 | } |
305 | |
306 | if (changed) |
307 | usb6fire_control_input_vol_update(rt); |
308 | |
309 | return changed; |
310 | } |
311 | |
312 | static int usb6fire_control_input_vol_get(struct snd_kcontrol *kcontrol, |
313 | struct snd_ctl_elem_value *ucontrol) |
314 | { |
315 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
316 | |
317 | ucontrol->value.integer.value[0] = rt->input_vol[0] + 15; |
318 | ucontrol->value.integer.value[1] = rt->input_vol[1] + 15; |
319 | |
320 | return 0; |
321 | } |
322 | |
323 | static int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol, |
324 | struct snd_ctl_elem_info *uinfo) |
325 | { |
326 | return snd_ctl_enum_info(info: uinfo, channels: 1, items: 2, names: line_phono_texts); |
327 | } |
328 | |
329 | static int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol, |
330 | struct snd_ctl_elem_value *ucontrol) |
331 | { |
332 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
333 | int changed = 0; |
334 | if (rt->line_phono_switch != ucontrol->value.integer.value[0]) { |
335 | rt->line_phono_switch = ucontrol->value.integer.value[0]; |
336 | usb6fire_control_line_phono_update(rt); |
337 | changed = 1; |
338 | } |
339 | return changed; |
340 | } |
341 | |
342 | static int usb6fire_control_line_phono_get(struct snd_kcontrol *kcontrol, |
343 | struct snd_ctl_elem_value *ucontrol) |
344 | { |
345 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
346 | ucontrol->value.integer.value[0] = rt->line_phono_switch; |
347 | return 0; |
348 | } |
349 | |
350 | static int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol, |
351 | struct snd_ctl_elem_info *uinfo) |
352 | { |
353 | return snd_ctl_enum_info(info: uinfo, channels: 1, items: 2, names: opt_coax_texts); |
354 | } |
355 | |
356 | static int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol, |
357 | struct snd_ctl_elem_value *ucontrol) |
358 | { |
359 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
360 | int changed = 0; |
361 | |
362 | if (rt->opt_coax_switch != ucontrol->value.enumerated.item[0]) { |
363 | rt->opt_coax_switch = ucontrol->value.enumerated.item[0]; |
364 | usb6fire_control_opt_coax_update(rt); |
365 | changed = 1; |
366 | } |
367 | return changed; |
368 | } |
369 | |
370 | static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol, |
371 | struct snd_ctl_elem_value *ucontrol) |
372 | { |
373 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
374 | ucontrol->value.enumerated.item[0] = rt->opt_coax_switch; |
375 | return 0; |
376 | } |
377 | |
378 | static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol, |
379 | struct snd_ctl_elem_value *ucontrol) |
380 | { |
381 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
382 | int changed = 0; |
383 | |
384 | if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) { |
385 | rt->digital_thru_switch = ucontrol->value.integer.value[0]; |
386 | usb6fire_control_streaming_update(rt); |
387 | changed = 1; |
388 | } |
389 | return changed; |
390 | } |
391 | |
392 | static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol, |
393 | struct snd_ctl_elem_value *ucontrol) |
394 | { |
395 | struct control_runtime *rt = snd_kcontrol_chip(kcontrol); |
396 | ucontrol->value.integer.value[0] = rt->digital_thru_switch; |
397 | return 0; |
398 | } |
399 | |
400 | static const struct snd_kcontrol_new vol_elements[] = { |
401 | { |
402 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
403 | .name = "Analog Playback Volume" , |
404 | .index = 0, |
405 | .private_value = 0, |
406 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | |
407 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, |
408 | .info = usb6fire_control_output_vol_info, |
409 | .get = usb6fire_control_output_vol_get, |
410 | .put = usb6fire_control_output_vol_put, |
411 | .tlv = { .p = tlv_output } |
412 | }, |
413 | { |
414 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
415 | .name = "Analog Playback Volume" , |
416 | .index = 1, |
417 | .private_value = 2, |
418 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | |
419 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, |
420 | .info = usb6fire_control_output_vol_info, |
421 | .get = usb6fire_control_output_vol_get, |
422 | .put = usb6fire_control_output_vol_put, |
423 | .tlv = { .p = tlv_output } |
424 | }, |
425 | { |
426 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
427 | .name = "Analog Playback Volume" , |
428 | .index = 2, |
429 | .private_value = 4, |
430 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | |
431 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, |
432 | .info = usb6fire_control_output_vol_info, |
433 | .get = usb6fire_control_output_vol_get, |
434 | .put = usb6fire_control_output_vol_put, |
435 | .tlv = { .p = tlv_output } |
436 | }, |
437 | {} |
438 | }; |
439 | |
440 | static const struct snd_kcontrol_new mute_elements[] = { |
441 | { |
442 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
443 | .name = "Analog Playback Switch" , |
444 | .index = 0, |
445 | .private_value = 0, |
446 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, |
447 | .info = snd_ctl_boolean_stereo_info, |
448 | .get = usb6fire_control_output_mute_get, |
449 | .put = usb6fire_control_output_mute_put, |
450 | }, |
451 | { |
452 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
453 | .name = "Analog Playback Switch" , |
454 | .index = 1, |
455 | .private_value = 2, |
456 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, |
457 | .info = snd_ctl_boolean_stereo_info, |
458 | .get = usb6fire_control_output_mute_get, |
459 | .put = usb6fire_control_output_mute_put, |
460 | }, |
461 | { |
462 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
463 | .name = "Analog Playback Switch" , |
464 | .index = 2, |
465 | .private_value = 4, |
466 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, |
467 | .info = snd_ctl_boolean_stereo_info, |
468 | .get = usb6fire_control_output_mute_get, |
469 | .put = usb6fire_control_output_mute_put, |
470 | }, |
471 | {} |
472 | }; |
473 | |
474 | static const struct snd_kcontrol_new elements[] = { |
475 | { |
476 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
477 | .name = "Line/Phono Capture Route" , |
478 | .index = 0, |
479 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, |
480 | .info = usb6fire_control_line_phono_info, |
481 | .get = usb6fire_control_line_phono_get, |
482 | .put = usb6fire_control_line_phono_put |
483 | }, |
484 | { |
485 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
486 | .name = "Opt/Coax Capture Route" , |
487 | .index = 0, |
488 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, |
489 | .info = usb6fire_control_opt_coax_info, |
490 | .get = usb6fire_control_opt_coax_get, |
491 | .put = usb6fire_control_opt_coax_put |
492 | }, |
493 | { |
494 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
495 | .name = "Digital Thru Playback Route" , |
496 | .index = 0, |
497 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, |
498 | .info = snd_ctl_boolean_mono_info, |
499 | .get = usb6fire_control_digital_thru_get, |
500 | .put = usb6fire_control_digital_thru_put |
501 | }, |
502 | { |
503 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
504 | .name = "Analog Capture Volume" , |
505 | .index = 0, |
506 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | |
507 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, |
508 | .info = usb6fire_control_input_vol_info, |
509 | .get = usb6fire_control_input_vol_get, |
510 | .put = usb6fire_control_input_vol_put, |
511 | .tlv = { .p = tlv_input } |
512 | }, |
513 | {} |
514 | }; |
515 | |
516 | static int usb6fire_control_add_virtual( |
517 | struct control_runtime *rt, |
518 | struct snd_card *card, |
519 | char *name, |
520 | const struct snd_kcontrol_new *elems) |
521 | { |
522 | int ret; |
523 | int i; |
524 | struct snd_kcontrol *vmaster = |
525 | snd_ctl_make_virtual_master(name, tlv: tlv_output); |
526 | struct snd_kcontrol *control; |
527 | |
528 | if (!vmaster) |
529 | return -ENOMEM; |
530 | ret = snd_ctl_add(card, kcontrol: vmaster); |
531 | if (ret < 0) |
532 | return ret; |
533 | |
534 | i = 0; |
535 | while (elems[i].name) { |
536 | control = snd_ctl_new1(kcontrolnew: &elems[i], private_data: rt); |
537 | if (!control) |
538 | return -ENOMEM; |
539 | ret = snd_ctl_add(card, kcontrol: control); |
540 | if (ret < 0) |
541 | return ret; |
542 | ret = snd_ctl_add_follower(master: vmaster, follower: control); |
543 | if (ret < 0) |
544 | return ret; |
545 | i++; |
546 | } |
547 | return 0; |
548 | } |
549 | |
550 | int usb6fire_control_init(struct sfire_chip *chip) |
551 | { |
552 | int i; |
553 | int ret; |
554 | struct control_runtime *rt = kzalloc(size: sizeof(struct control_runtime), |
555 | GFP_KERNEL); |
556 | struct comm_runtime *comm_rt = chip->comm; |
557 | |
558 | if (!rt) |
559 | return -ENOMEM; |
560 | |
561 | rt->chip = chip; |
562 | rt->update_streaming = usb6fire_control_streaming_update; |
563 | rt->set_rate = usb6fire_control_set_rate; |
564 | rt->set_channels = usb6fire_control_set_channels; |
565 | |
566 | i = 0; |
567 | while (init_data[i].type) { |
568 | comm_rt->write8(comm_rt, init_data[i].type, init_data[i].reg, |
569 | init_data[i].value); |
570 | i++; |
571 | } |
572 | |
573 | usb6fire_control_opt_coax_update(rt); |
574 | usb6fire_control_line_phono_update(rt); |
575 | usb6fire_control_output_vol_update(rt); |
576 | usb6fire_control_output_mute_update(rt); |
577 | usb6fire_control_input_vol_update(rt); |
578 | usb6fire_control_streaming_update(rt); |
579 | |
580 | ret = usb6fire_control_add_virtual(rt, card: chip->card, |
581 | name: "Master Playback Volume" , elems: vol_elements); |
582 | if (ret) { |
583 | dev_err(&chip->dev->dev, "cannot add control.\n" ); |
584 | kfree(objp: rt); |
585 | return ret; |
586 | } |
587 | ret = usb6fire_control_add_virtual(rt, card: chip->card, |
588 | name: "Master Playback Switch" , elems: mute_elements); |
589 | if (ret) { |
590 | dev_err(&chip->dev->dev, "cannot add control.\n" ); |
591 | kfree(objp: rt); |
592 | return ret; |
593 | } |
594 | |
595 | i = 0; |
596 | while (elements[i].name) { |
597 | ret = snd_ctl_add(card: chip->card, kcontrol: snd_ctl_new1(kcontrolnew: &elements[i], private_data: rt)); |
598 | if (ret < 0) { |
599 | kfree(objp: rt); |
600 | dev_err(&chip->dev->dev, "cannot add control.\n" ); |
601 | return ret; |
602 | } |
603 | i++; |
604 | } |
605 | |
606 | chip->control = rt; |
607 | return 0; |
608 | } |
609 | |
610 | void usb6fire_control_abort(struct sfire_chip *chip) |
611 | {} |
612 | |
613 | void usb6fire_control_destroy(struct sfire_chip *chip) |
614 | { |
615 | kfree(objp: chip->control); |
616 | chip->control = NULL; |
617 | } |
618 | |