12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2c6d43ba8STorsten Schenk /*
3c6d43ba8STorsten Schenk * Linux driver for TerraTec DMX 6Fire USB
4c6d43ba8STorsten Schenk *
5c6d43ba8STorsten Schenk * Mixer control
6c6d43ba8STorsten Schenk *
7c6d43ba8STorsten Schenk * Author: Torsten Schenk <torsten.schenk@zoho.com>
8c6d43ba8STorsten Schenk * Created: Jan 01, 2011
9c6d43ba8STorsten Schenk * Copyright: (C) Torsten Schenk
10c6d43ba8STorsten Schenk *
11f90ffbf3STorsten Schenk * Thanks to:
12f90ffbf3STorsten Schenk * - Holger Ruckdeschel: he found out how to control individual channel
13f90ffbf3STorsten Schenk * volumes and introduced mute switch
14c6d43ba8STorsten Schenk */
15c6d43ba8STorsten Schenk
16c6d43ba8STorsten Schenk #include <linux/interrupt.h>
17c6d43ba8STorsten Schenk #include <sound/control.h>
188e247a9cSTorsten Schenk #include <sound/tlv.h>
19c6d43ba8STorsten Schenk
20c6d43ba8STorsten Schenk #include "control.h"
21c6d43ba8STorsten Schenk #include "comm.h"
22c6d43ba8STorsten Schenk #include "chip.h"
23c6d43ba8STorsten Schenk
24c8dd33fcSTakashi Iwai static const char * const opt_coax_texts[2] = { "Optical", "Coax" };
25c8dd33fcSTakashi Iwai static const char * const line_phono_texts[2] = { "Line", "Phono" };
26c6d43ba8STorsten Schenk
27c6d43ba8STorsten Schenk /*
28c6d43ba8STorsten Schenk * data that needs to be sent to device. sets up card internal stuff.
29c6d43ba8STorsten Schenk * values dumped from windows driver and filtered by trial'n'error.
30c6d43ba8STorsten Schenk */
31c6d43ba8STorsten Schenk static const struct {
32c6d43ba8STorsten Schenk u8 type;
33c6d43ba8STorsten Schenk u8 reg;
34c6d43ba8STorsten Schenk u8 value;
35c6d43ba8STorsten Schenk }
36c6d43ba8STorsten Schenk init_data[] = {
37c6d43ba8STorsten Schenk { 0x22, 0x00, 0x00 }, { 0x20, 0x00, 0x08 }, { 0x22, 0x01, 0x01 },
38c6d43ba8STorsten Schenk { 0x20, 0x01, 0x08 }, { 0x22, 0x02, 0x00 }, { 0x20, 0x02, 0x08 },
39c6d43ba8STorsten Schenk { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 },
40c6d43ba8STorsten Schenk { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 },
41c6d43ba8STorsten Schenk { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 },
42f90ffbf3STorsten Schenk { 0x12, 0x0d, 0x38 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 },
43c6d43ba8STorsten Schenk { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 },
44c6d43ba8STorsten Schenk { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 },
45c6d43ba8STorsten Schenk { 0 } /* TERMINATING ENTRY */
46c6d43ba8STorsten Schenk };
47c6d43ba8STorsten Schenk
482475b0d4STorsten Schenk static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
492475b0d4STorsten Schenk /* values to write to soundcard register for all samplerates */
502475b0d4STorsten Schenk static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
512475b0d4STorsten Schenk static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
522475b0d4STorsten Schenk
538e247a9cSTorsten Schenk static DECLARE_TLV_DB_MINMAX(tlv_output, -9000, 0);
5406bb4e74STorsten Schenk static DECLARE_TLV_DB_MINMAX(tlv_input, -1500, 1500);
558e247a9cSTorsten Schenk
562475b0d4STorsten Schenk enum {
572475b0d4STorsten Schenk DIGITAL_THRU_ONLY_SAMPLERATE = 3
582475b0d4STorsten Schenk };
592475b0d4STorsten Schenk
usb6fire_control_output_vol_update(struct control_runtime * rt)60f90ffbf3STorsten Schenk static void usb6fire_control_output_vol_update(struct control_runtime *rt)
61c6d43ba8STorsten Schenk {
62c6d43ba8STorsten Schenk struct comm_runtime *comm_rt = rt->chip->comm;
63f90ffbf3STorsten Schenk int i;
64f90ffbf3STorsten Schenk
65f90ffbf3STorsten Schenk if (comm_rt)
66f90ffbf3STorsten Schenk for (i = 0; i < 6; i++)
67f90ffbf3STorsten Schenk if (!(rt->ovol_updated & (1 << i))) {
68f90ffbf3STorsten Schenk comm_rt->write8(comm_rt, 0x12, 0x0f + i,
69f90ffbf3STorsten Schenk 180 - rt->output_vol[i]);
70f90ffbf3STorsten Schenk rt->ovol_updated |= 1 << i;
71c6d43ba8STorsten Schenk }
72c6d43ba8STorsten Schenk }
73c6d43ba8STorsten Schenk
usb6fire_control_output_mute_update(struct control_runtime * rt)74d97c735aSTorsten Schenk static void usb6fire_control_output_mute_update(struct control_runtime *rt)
75d97c735aSTorsten Schenk {
76d97c735aSTorsten Schenk struct comm_runtime *comm_rt = rt->chip->comm;
77d97c735aSTorsten Schenk
78d97c735aSTorsten Schenk if (comm_rt)
79d97c735aSTorsten Schenk comm_rt->write8(comm_rt, 0x12, 0x0e, ~rt->output_mute);
80d97c735aSTorsten Schenk }
81d97c735aSTorsten Schenk
usb6fire_control_input_vol_update(struct control_runtime * rt)8206bb4e74STorsten Schenk static void usb6fire_control_input_vol_update(struct control_runtime *rt)
8306bb4e74STorsten Schenk {
8406bb4e74STorsten Schenk struct comm_runtime *comm_rt = rt->chip->comm;
8506bb4e74STorsten Schenk int i;
8606bb4e74STorsten Schenk
8706bb4e74STorsten Schenk if (comm_rt)
8806bb4e74STorsten Schenk for (i = 0; i < 2; i++)
8906bb4e74STorsten Schenk if (!(rt->ivol_updated & (1 << i))) {
9006bb4e74STorsten Schenk comm_rt->write8(comm_rt, 0x12, 0x1c + i,
9106bb4e74STorsten Schenk rt->input_vol[i] & 0x3f);
9206bb4e74STorsten Schenk rt->ivol_updated |= 1 << i;
9306bb4e74STorsten Schenk }
9406bb4e74STorsten Schenk }
9506bb4e74STorsten Schenk
usb6fire_control_line_phono_update(struct control_runtime * rt)96c6d43ba8STorsten Schenk static void usb6fire_control_line_phono_update(struct control_runtime *rt)
97c6d43ba8STorsten Schenk {
98c6d43ba8STorsten Schenk struct comm_runtime *comm_rt = rt->chip->comm;
99c6d43ba8STorsten Schenk if (comm_rt) {
100c6d43ba8STorsten Schenk comm_rt->write8(comm_rt, 0x22, 0x02, rt->line_phono_switch);
101c6d43ba8STorsten Schenk comm_rt->write8(comm_rt, 0x21, 0x02, rt->line_phono_switch);
102c6d43ba8STorsten Schenk }
103c6d43ba8STorsten Schenk }
104c6d43ba8STorsten Schenk
usb6fire_control_opt_coax_update(struct control_runtime * rt)105c6d43ba8STorsten Schenk static void usb6fire_control_opt_coax_update(struct control_runtime *rt)
106c6d43ba8STorsten Schenk {
107c6d43ba8STorsten Schenk struct comm_runtime *comm_rt = rt->chip->comm;
108c6d43ba8STorsten Schenk if (comm_rt) {
109c6d43ba8STorsten Schenk comm_rt->write8(comm_rt, 0x22, 0x00, rt->opt_coax_switch);
110c6d43ba8STorsten Schenk comm_rt->write8(comm_rt, 0x21, 0x00, rt->opt_coax_switch);
111c6d43ba8STorsten Schenk }
112c6d43ba8STorsten Schenk }
113c6d43ba8STorsten Schenk
usb6fire_control_set_rate(struct control_runtime * rt,int rate)1142475b0d4STorsten Schenk static int usb6fire_control_set_rate(struct control_runtime *rt, int rate)
1152475b0d4STorsten Schenk {
1162475b0d4STorsten Schenk int ret;
1172475b0d4STorsten Schenk struct usb_device *device = rt->chip->dev;
1182475b0d4STorsten Schenk struct comm_runtime *comm_rt = rt->chip->comm;
1192475b0d4STorsten Schenk
1202475b0d4STorsten Schenk if (rate < 0 || rate >= CONTROL_N_RATES)
1212475b0d4STorsten Schenk return -EINVAL;
1222475b0d4STorsten Schenk
1232475b0d4STorsten Schenk ret = usb_set_interface(device, 1, rates_altsetting[rate]);
1242475b0d4STorsten Schenk if (ret < 0)
1252475b0d4STorsten Schenk return ret;
1262475b0d4STorsten Schenk
1272475b0d4STorsten Schenk /* set soundcard clock */
1282475b0d4STorsten Schenk ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate],
1292475b0d4STorsten Schenk rates_6fire_vh[rate]);
1302475b0d4STorsten Schenk if (ret < 0)
1312475b0d4STorsten Schenk return ret;
1322475b0d4STorsten Schenk
1332475b0d4STorsten Schenk return 0;
1342475b0d4STorsten Schenk }
1352475b0d4STorsten Schenk
usb6fire_control_set_channels(struct control_runtime * rt,int n_analog_out,int n_analog_in,bool spdif_out,bool spdif_in)1362475b0d4STorsten Schenk static int usb6fire_control_set_channels(
1372475b0d4STorsten Schenk struct control_runtime *rt, int n_analog_out,
1382475b0d4STorsten Schenk int n_analog_in, bool spdif_out, bool spdif_in)
1392475b0d4STorsten Schenk {
1402475b0d4STorsten Schenk int ret;
1412475b0d4STorsten Schenk struct comm_runtime *comm_rt = rt->chip->comm;
1422475b0d4STorsten Schenk
1432475b0d4STorsten Schenk /* enable analog inputs and outputs
1442475b0d4STorsten Schenk * (one bit per stereo-channel) */
1452475b0d4STorsten Schenk ret = comm_rt->write16(comm_rt, 0x02, 0x02,
1462475b0d4STorsten Schenk (1 << (n_analog_out / 2)) - 1,
1472475b0d4STorsten Schenk (1 << (n_analog_in / 2)) - 1);
1482475b0d4STorsten Schenk if (ret < 0)
1492475b0d4STorsten Schenk return ret;
1502475b0d4STorsten Schenk
1512475b0d4STorsten Schenk /* disable digital inputs and outputs */
1522475b0d4STorsten Schenk /* TODO: use spdif_x to enable/disable digital channels */
1532475b0d4STorsten Schenk ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
1542475b0d4STorsten Schenk if (ret < 0)
1552475b0d4STorsten Schenk return ret;
1562475b0d4STorsten Schenk
1572475b0d4STorsten Schenk return 0;
1582475b0d4STorsten Schenk }
1592475b0d4STorsten Schenk
usb6fire_control_streaming_update(struct control_runtime * rt)1602475b0d4STorsten Schenk static int usb6fire_control_streaming_update(struct control_runtime *rt)
1612475b0d4STorsten Schenk {
1622475b0d4STorsten Schenk struct comm_runtime *comm_rt = rt->chip->comm;
1632475b0d4STorsten Schenk
1642475b0d4STorsten Schenk if (comm_rt) {
1652475b0d4STorsten Schenk if (!rt->usb_streaming && rt->digital_thru_switch)
1662475b0d4STorsten Schenk usb6fire_control_set_rate(rt,
1672475b0d4STorsten Schenk DIGITAL_THRU_ONLY_SAMPLERATE);
1682475b0d4STorsten Schenk return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00,
1692475b0d4STorsten Schenk (rt->usb_streaming ? 0x01 : 0x00) |
1702475b0d4STorsten Schenk (rt->digital_thru_switch ? 0x08 : 0x00));
1712475b0d4STorsten Schenk }
1722475b0d4STorsten Schenk return -EINVAL;
1732475b0d4STorsten Schenk }
1742475b0d4STorsten Schenk
usb6fire_control_output_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)175f90ffbf3STorsten Schenk static int usb6fire_control_output_vol_info(struct snd_kcontrol *kcontrol,
176c6d43ba8STorsten Schenk struct snd_ctl_elem_info *uinfo)
177c6d43ba8STorsten Schenk {
178c6d43ba8STorsten Schenk uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
179f90ffbf3STorsten Schenk uinfo->count = 2;
180c6d43ba8STorsten Schenk uinfo->value.integer.min = 0;
1818e247a9cSTorsten Schenk uinfo->value.integer.max = 180;
182c6d43ba8STorsten Schenk return 0;
183c6d43ba8STorsten Schenk }
184c6d43ba8STorsten Schenk
usb6fire_control_output_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)185f90ffbf3STorsten Schenk static int usb6fire_control_output_vol_put(struct snd_kcontrol *kcontrol,
186c6d43ba8STorsten Schenk struct snd_ctl_elem_value *ucontrol)
187c6d43ba8STorsten Schenk {
188c6d43ba8STorsten Schenk struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
189f90ffbf3STorsten Schenk unsigned int ch = kcontrol->private_value;
190c6d43ba8STorsten Schenk int changed = 0;
191f90ffbf3STorsten Schenk
192f90ffbf3STorsten Schenk if (ch > 4) {
193e3b3757bSTakashi Iwai dev_err(&rt->chip->dev->dev,
194e3b3757bSTakashi Iwai "Invalid channel in volume control.");
195f90ffbf3STorsten Schenk return -EINVAL;
196f90ffbf3STorsten Schenk }
197f90ffbf3STorsten Schenk
198f90ffbf3STorsten Schenk if (rt->output_vol[ch] != ucontrol->value.integer.value[0]) {
199f90ffbf3STorsten Schenk rt->output_vol[ch] = ucontrol->value.integer.value[0];
200f90ffbf3STorsten Schenk rt->ovol_updated &= ~(1 << ch);
201c6d43ba8STorsten Schenk changed = 1;
202c6d43ba8STorsten Schenk }
203f90ffbf3STorsten Schenk if (rt->output_vol[ch + 1] != ucontrol->value.integer.value[1]) {
204f90ffbf3STorsten Schenk rt->output_vol[ch + 1] = ucontrol->value.integer.value[1];
205f90ffbf3STorsten Schenk rt->ovol_updated &= ~(2 << ch);
206f90ffbf3STorsten Schenk changed = 1;
207f90ffbf3STorsten Schenk }
208f90ffbf3STorsten Schenk
209f90ffbf3STorsten Schenk if (changed)
210f90ffbf3STorsten Schenk usb6fire_control_output_vol_update(rt);
211f90ffbf3STorsten Schenk
212c6d43ba8STorsten Schenk return changed;
213c6d43ba8STorsten Schenk }
214c6d43ba8STorsten Schenk
usb6fire_control_output_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)215f90ffbf3STorsten Schenk static int usb6fire_control_output_vol_get(struct snd_kcontrol *kcontrol,
216c6d43ba8STorsten Schenk struct snd_ctl_elem_value *ucontrol)
217c6d43ba8STorsten Schenk {
218c6d43ba8STorsten Schenk struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
219f90ffbf3STorsten Schenk unsigned int ch = kcontrol->private_value;
220f90ffbf3STorsten Schenk
221f90ffbf3STorsten Schenk if (ch > 4) {
222e3b3757bSTakashi Iwai dev_err(&rt->chip->dev->dev,
223e3b3757bSTakashi Iwai "Invalid channel in volume control.");
224f90ffbf3STorsten Schenk return -EINVAL;
225f90ffbf3STorsten Schenk }
226f90ffbf3STorsten Schenk
227f90ffbf3STorsten Schenk ucontrol->value.integer.value[0] = rt->output_vol[ch];
228f90ffbf3STorsten Schenk ucontrol->value.integer.value[1] = rt->output_vol[ch + 1];
229c6d43ba8STorsten Schenk return 0;
230c6d43ba8STorsten Schenk }
231c6d43ba8STorsten Schenk
usb6fire_control_output_mute_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)232d97c735aSTorsten Schenk static int usb6fire_control_output_mute_put(struct snd_kcontrol *kcontrol,
233d97c735aSTorsten Schenk struct snd_ctl_elem_value *ucontrol)
234d97c735aSTorsten Schenk {
235d97c735aSTorsten Schenk struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
236d97c735aSTorsten Schenk unsigned int ch = kcontrol->private_value;
237d97c735aSTorsten Schenk u8 old = rt->output_mute;
238d97c735aSTorsten Schenk u8 value = 0;
239d97c735aSTorsten Schenk
240d97c735aSTorsten Schenk if (ch > 4) {
241e3b3757bSTakashi Iwai dev_err(&rt->chip->dev->dev,
242e3b3757bSTakashi Iwai "Invalid channel in volume control.");
243d97c735aSTorsten Schenk return -EINVAL;
244d97c735aSTorsten Schenk }
245d97c735aSTorsten Schenk
246d97c735aSTorsten Schenk rt->output_mute &= ~(3 << ch);
247d97c735aSTorsten Schenk if (ucontrol->value.integer.value[0])
248d97c735aSTorsten Schenk value |= 1;
249d97c735aSTorsten Schenk if (ucontrol->value.integer.value[1])
250d97c735aSTorsten Schenk value |= 2;
251d97c735aSTorsten Schenk rt->output_mute |= value << ch;
252d97c735aSTorsten Schenk
253d97c735aSTorsten Schenk if (rt->output_mute != old)
254d97c735aSTorsten Schenk usb6fire_control_output_mute_update(rt);
255d97c735aSTorsten Schenk
256d97c735aSTorsten Schenk return rt->output_mute != old;
257d97c735aSTorsten Schenk }
258d97c735aSTorsten Schenk
usb6fire_control_output_mute_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)259d97c735aSTorsten Schenk static int usb6fire_control_output_mute_get(struct snd_kcontrol *kcontrol,
260d97c735aSTorsten Schenk struct snd_ctl_elem_value *ucontrol)
261d97c735aSTorsten Schenk {
262d97c735aSTorsten Schenk struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
263d97c735aSTorsten Schenk unsigned int ch = kcontrol->private_value;
264d97c735aSTorsten Schenk u8 value = rt->output_mute >> ch;
265d97c735aSTorsten Schenk
266d97c735aSTorsten Schenk if (ch > 4) {
267e3b3757bSTakashi Iwai dev_err(&rt->chip->dev->dev,
268e3b3757bSTakashi Iwai "Invalid channel in volume control.");
269d97c735aSTorsten Schenk return -EINVAL;
270d97c735aSTorsten Schenk }
271d97c735aSTorsten Schenk
272d97c735aSTorsten Schenk ucontrol->value.integer.value[0] = 1 & value;
273d97c735aSTorsten Schenk value >>= 1;
274d97c735aSTorsten Schenk ucontrol->value.integer.value[1] = 1 & value;
275d97c735aSTorsten Schenk
276d97c735aSTorsten Schenk return 0;
277d97c735aSTorsten Schenk }
278d97c735aSTorsten Schenk
usb6fire_control_input_vol_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)27906bb4e74STorsten Schenk static int usb6fire_control_input_vol_info(struct snd_kcontrol *kcontrol,
28006bb4e74STorsten Schenk struct snd_ctl_elem_info *uinfo)
28106bb4e74STorsten Schenk {
28206bb4e74STorsten Schenk uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
28306bb4e74STorsten Schenk uinfo->count = 2;
28406bb4e74STorsten Schenk uinfo->value.integer.min = 0;
28506bb4e74STorsten Schenk uinfo->value.integer.max = 30;
28606bb4e74STorsten Schenk return 0;
28706bb4e74STorsten Schenk }
28806bb4e74STorsten Schenk
usb6fire_control_input_vol_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)28906bb4e74STorsten Schenk static int usb6fire_control_input_vol_put(struct snd_kcontrol *kcontrol,
29006bb4e74STorsten Schenk struct snd_ctl_elem_value *ucontrol)
29106bb4e74STorsten Schenk {
29206bb4e74STorsten Schenk struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
29306bb4e74STorsten Schenk int changed = 0;
29406bb4e74STorsten Schenk
29506bb4e74STorsten Schenk if (rt->input_vol[0] != ucontrol->value.integer.value[0]) {
29606bb4e74STorsten Schenk rt->input_vol[0] = ucontrol->value.integer.value[0] - 15;
29706bb4e74STorsten Schenk rt->ivol_updated &= ~(1 << 0);
29806bb4e74STorsten Schenk changed = 1;
29906bb4e74STorsten Schenk }
30006bb4e74STorsten Schenk if (rt->input_vol[1] != ucontrol->value.integer.value[1]) {
30106bb4e74STorsten Schenk rt->input_vol[1] = ucontrol->value.integer.value[1] - 15;
30206bb4e74STorsten Schenk rt->ivol_updated &= ~(1 << 1);
30306bb4e74STorsten Schenk changed = 1;
30406bb4e74STorsten Schenk }
30506bb4e74STorsten Schenk
30606bb4e74STorsten Schenk if (changed)
30706bb4e74STorsten Schenk usb6fire_control_input_vol_update(rt);
30806bb4e74STorsten Schenk
30906bb4e74STorsten Schenk return changed;
31006bb4e74STorsten Schenk }
31106bb4e74STorsten Schenk
usb6fire_control_input_vol_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)31206bb4e74STorsten Schenk static int usb6fire_control_input_vol_get(struct snd_kcontrol *kcontrol,
31306bb4e74STorsten Schenk struct snd_ctl_elem_value *ucontrol)
31406bb4e74STorsten Schenk {
31506bb4e74STorsten Schenk struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
31606bb4e74STorsten Schenk
31706bb4e74STorsten Schenk ucontrol->value.integer.value[0] = rt->input_vol[0] + 15;
31806bb4e74STorsten Schenk ucontrol->value.integer.value[1] = rt->input_vol[1] + 15;
31906bb4e74STorsten Schenk
32006bb4e74STorsten Schenk return 0;
32106bb4e74STorsten Schenk }
32206bb4e74STorsten Schenk
usb6fire_control_line_phono_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)323c6d43ba8STorsten Schenk static int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol,
324c6d43ba8STorsten Schenk struct snd_ctl_elem_info *uinfo)
325c6d43ba8STorsten Schenk {
326c8dd33fcSTakashi Iwai return snd_ctl_enum_info(uinfo, 1, 2, line_phono_texts);
327c6d43ba8STorsten Schenk }
328c6d43ba8STorsten Schenk
usb6fire_control_line_phono_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)329c6d43ba8STorsten Schenk static int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol,
330c6d43ba8STorsten Schenk struct snd_ctl_elem_value *ucontrol)
331c6d43ba8STorsten Schenk {
332c6d43ba8STorsten Schenk struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
333c6d43ba8STorsten Schenk int changed = 0;
334c6d43ba8STorsten Schenk if (rt->line_phono_switch != ucontrol->value.integer.value[0]) {
335c6d43ba8STorsten Schenk rt->line_phono_switch = ucontrol->value.integer.value[0];
336c6d43ba8STorsten Schenk usb6fire_control_line_phono_update(rt);
337c6d43ba8STorsten Schenk changed = 1;
338c6d43ba8STorsten Schenk }
339c6d43ba8STorsten Schenk return changed;
340c6d43ba8STorsten Schenk }
341c6d43ba8STorsten Schenk
usb6fire_control_line_phono_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)342c6d43ba8STorsten Schenk static int usb6fire_control_line_phono_get(struct snd_kcontrol *kcontrol,
343c6d43ba8STorsten Schenk struct snd_ctl_elem_value *ucontrol)
344c6d43ba8STorsten Schenk {
345c6d43ba8STorsten Schenk struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
346c6d43ba8STorsten Schenk ucontrol->value.integer.value[0] = rt->line_phono_switch;
347c6d43ba8STorsten Schenk return 0;
348c6d43ba8STorsten Schenk }
349c6d43ba8STorsten Schenk
usb6fire_control_opt_coax_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)350c6d43ba8STorsten Schenk static int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol,
351c6d43ba8STorsten Schenk struct snd_ctl_elem_info *uinfo)
352c6d43ba8STorsten Schenk {
353c8dd33fcSTakashi Iwai return snd_ctl_enum_info(uinfo, 1, 2, opt_coax_texts);
354c6d43ba8STorsten Schenk }
355c6d43ba8STorsten Schenk
usb6fire_control_opt_coax_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)356c6d43ba8STorsten Schenk static int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol,
357c6d43ba8STorsten Schenk struct snd_ctl_elem_value *ucontrol)
358c6d43ba8STorsten Schenk {
359c6d43ba8STorsten Schenk struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
360c6d43ba8STorsten Schenk int changed = 0;
361c6d43ba8STorsten Schenk
362c6d43ba8STorsten Schenk if (rt->opt_coax_switch != ucontrol->value.enumerated.item[0]) {
363c6d43ba8STorsten Schenk rt->opt_coax_switch = ucontrol->value.enumerated.item[0];
364c6d43ba8STorsten Schenk usb6fire_control_opt_coax_update(rt);
365c6d43ba8STorsten Schenk changed = 1;
366c6d43ba8STorsten Schenk }
367c6d43ba8STorsten Schenk return changed;
368c6d43ba8STorsten Schenk }
369c6d43ba8STorsten Schenk
usb6fire_control_opt_coax_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)370c6d43ba8STorsten Schenk static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol,
371c6d43ba8STorsten Schenk struct snd_ctl_elem_value *ucontrol)
372c6d43ba8STorsten Schenk {
373c6d43ba8STorsten Schenk struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
374c6d43ba8STorsten Schenk ucontrol->value.enumerated.item[0] = rt->opt_coax_switch;
375c6d43ba8STorsten Schenk return 0;
376c6d43ba8STorsten Schenk }
377c6d43ba8STorsten Schenk
usb6fire_control_digital_thru_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)3782475b0d4STorsten Schenk static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol,
3792475b0d4STorsten Schenk struct snd_ctl_elem_value *ucontrol)
3802475b0d4STorsten Schenk {
3812475b0d4STorsten Schenk struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
3822475b0d4STorsten Schenk int changed = 0;
3832475b0d4STorsten Schenk
3842475b0d4STorsten Schenk if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) {
3852475b0d4STorsten Schenk rt->digital_thru_switch = ucontrol->value.integer.value[0];
3862475b0d4STorsten Schenk usb6fire_control_streaming_update(rt);
3872475b0d4STorsten Schenk changed = 1;
3882475b0d4STorsten Schenk }
3892475b0d4STorsten Schenk return changed;
3902475b0d4STorsten Schenk }
3912475b0d4STorsten Schenk
usb6fire_control_digital_thru_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)3922475b0d4STorsten Schenk static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol,
3932475b0d4STorsten Schenk struct snd_ctl_elem_value *ucontrol)
3942475b0d4STorsten Schenk {
3952475b0d4STorsten Schenk struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
3962475b0d4STorsten Schenk ucontrol->value.integer.value[0] = rt->digital_thru_switch;
3972475b0d4STorsten Schenk return 0;
3982475b0d4STorsten Schenk }
3992475b0d4STorsten Schenk
400195727e8STakashi Iwai static const struct snd_kcontrol_new vol_elements[] = {
401c6d43ba8STorsten Schenk {
402c6d43ba8STorsten Schenk .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
403f90ffbf3STorsten Schenk .name = "Analog Playback Volume",
404c6d43ba8STorsten Schenk .index = 0,
405f90ffbf3STorsten Schenk .private_value = 0,
4068e247a9cSTorsten Schenk .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
4078e247a9cSTorsten Schenk SNDRV_CTL_ELEM_ACCESS_TLV_READ,
408f90ffbf3STorsten Schenk .info = usb6fire_control_output_vol_info,
409f90ffbf3STorsten Schenk .get = usb6fire_control_output_vol_get,
410f90ffbf3STorsten Schenk .put = usb6fire_control_output_vol_put,
4118e247a9cSTorsten Schenk .tlv = { .p = tlv_output }
412c6d43ba8STorsten Schenk },
413c6d43ba8STorsten Schenk {
414c6d43ba8STorsten Schenk .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
415f90ffbf3STorsten Schenk .name = "Analog Playback Volume",
416f90ffbf3STorsten Schenk .index = 1,
417f90ffbf3STorsten Schenk .private_value = 2,
418f90ffbf3STorsten Schenk .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
419f90ffbf3STorsten Schenk SNDRV_CTL_ELEM_ACCESS_TLV_READ,
420f90ffbf3STorsten Schenk .info = usb6fire_control_output_vol_info,
421f90ffbf3STorsten Schenk .get = usb6fire_control_output_vol_get,
422f90ffbf3STorsten Schenk .put = usb6fire_control_output_vol_put,
423f90ffbf3STorsten Schenk .tlv = { .p = tlv_output }
424f90ffbf3STorsten Schenk },
425f90ffbf3STorsten Schenk {
426f90ffbf3STorsten Schenk .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
427f90ffbf3STorsten Schenk .name = "Analog Playback Volume",
428f90ffbf3STorsten Schenk .index = 2,
429f90ffbf3STorsten Schenk .private_value = 4,
430f90ffbf3STorsten Schenk .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
431f90ffbf3STorsten Schenk SNDRV_CTL_ELEM_ACCESS_TLV_READ,
432f90ffbf3STorsten Schenk .info = usb6fire_control_output_vol_info,
433f90ffbf3STorsten Schenk .get = usb6fire_control_output_vol_get,
434f90ffbf3STorsten Schenk .put = usb6fire_control_output_vol_put,
435f90ffbf3STorsten Schenk .tlv = { .p = tlv_output }
436f90ffbf3STorsten Schenk },
437f90ffbf3STorsten Schenk {}
438f90ffbf3STorsten Schenk };
439f90ffbf3STorsten Schenk
440195727e8STakashi Iwai static const struct snd_kcontrol_new mute_elements[] = {
441d97c735aSTorsten Schenk {
442d97c735aSTorsten Schenk .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
443d97c735aSTorsten Schenk .name = "Analog Playback Switch",
444d97c735aSTorsten Schenk .index = 0,
445d97c735aSTorsten Schenk .private_value = 0,
446d97c735aSTorsten Schenk .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
447d97c735aSTorsten Schenk .info = snd_ctl_boolean_stereo_info,
448d97c735aSTorsten Schenk .get = usb6fire_control_output_mute_get,
449d97c735aSTorsten Schenk .put = usb6fire_control_output_mute_put,
450d97c735aSTorsten Schenk },
451d97c735aSTorsten Schenk {
452d97c735aSTorsten Schenk .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
453d97c735aSTorsten Schenk .name = "Analog Playback Switch",
454d97c735aSTorsten Schenk .index = 1,
455d97c735aSTorsten Schenk .private_value = 2,
456d97c735aSTorsten Schenk .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
457d97c735aSTorsten Schenk .info = snd_ctl_boolean_stereo_info,
458d97c735aSTorsten Schenk .get = usb6fire_control_output_mute_get,
459d97c735aSTorsten Schenk .put = usb6fire_control_output_mute_put,
460d97c735aSTorsten Schenk },
461d97c735aSTorsten Schenk {
462d97c735aSTorsten Schenk .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
463d97c735aSTorsten Schenk .name = "Analog Playback Switch",
464d97c735aSTorsten Schenk .index = 2,
465d97c735aSTorsten Schenk .private_value = 4,
466d97c735aSTorsten Schenk .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
467d97c735aSTorsten Schenk .info = snd_ctl_boolean_stereo_info,
468d97c735aSTorsten Schenk .get = usb6fire_control_output_mute_get,
469d97c735aSTorsten Schenk .put = usb6fire_control_output_mute_put,
470d97c735aSTorsten Schenk },
471d97c735aSTorsten Schenk {}
472d97c735aSTorsten Schenk };
473d97c735aSTorsten Schenk
474195727e8STakashi Iwai static const struct snd_kcontrol_new elements[] = {
475f90ffbf3STorsten Schenk {
476f90ffbf3STorsten Schenk .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
477c6d43ba8STorsten Schenk .name = "Line/Phono Capture Route",
478c6d43ba8STorsten Schenk .index = 0,
479c6d43ba8STorsten Schenk .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
480c6d43ba8STorsten Schenk .info = usb6fire_control_line_phono_info,
481c6d43ba8STorsten Schenk .get = usb6fire_control_line_phono_get,
482c6d43ba8STorsten Schenk .put = usb6fire_control_line_phono_put
483c6d43ba8STorsten Schenk },
484c6d43ba8STorsten Schenk {
485c6d43ba8STorsten Schenk .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
486c6d43ba8STorsten Schenk .name = "Opt/Coax Capture Route",
487c6d43ba8STorsten Schenk .index = 0,
488c6d43ba8STorsten Schenk .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
489c6d43ba8STorsten Schenk .info = usb6fire_control_opt_coax_info,
490c6d43ba8STorsten Schenk .get = usb6fire_control_opt_coax_get,
491c6d43ba8STorsten Schenk .put = usb6fire_control_opt_coax_put
492c6d43ba8STorsten Schenk },
4932475b0d4STorsten Schenk {
4942475b0d4STorsten Schenk .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4952475b0d4STorsten Schenk .name = "Digital Thru Playback Route",
4962475b0d4STorsten Schenk .index = 0,
4972475b0d4STorsten Schenk .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
4982475b0d4STorsten Schenk .info = snd_ctl_boolean_mono_info,
4992475b0d4STorsten Schenk .get = usb6fire_control_digital_thru_get,
5002475b0d4STorsten Schenk .put = usb6fire_control_digital_thru_put
5012475b0d4STorsten Schenk },
50206bb4e74STorsten Schenk {
50306bb4e74STorsten Schenk .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
50406bb4e74STorsten Schenk .name = "Analog Capture Volume",
50506bb4e74STorsten Schenk .index = 0,
50606bb4e74STorsten Schenk .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
50706bb4e74STorsten Schenk SNDRV_CTL_ELEM_ACCESS_TLV_READ,
50806bb4e74STorsten Schenk .info = usb6fire_control_input_vol_info,
50906bb4e74STorsten Schenk .get = usb6fire_control_input_vol_get,
51006bb4e74STorsten Schenk .put = usb6fire_control_input_vol_put,
51106bb4e74STorsten Schenk .tlv = { .p = tlv_input }
51206bb4e74STorsten Schenk },
513c6d43ba8STorsten Schenk {}
514c6d43ba8STorsten Schenk };
515c6d43ba8STorsten Schenk
usb6fire_control_add_virtual(struct control_runtime * rt,struct snd_card * card,char * name,const struct snd_kcontrol_new * elems)516f90ffbf3STorsten Schenk static int usb6fire_control_add_virtual(
517f90ffbf3STorsten Schenk struct control_runtime *rt,
518f90ffbf3STorsten Schenk struct snd_card *card,
519f90ffbf3STorsten Schenk char *name,
520195727e8STakashi Iwai const struct snd_kcontrol_new *elems)
521f90ffbf3STorsten Schenk {
522f90ffbf3STorsten Schenk int ret;
523f90ffbf3STorsten Schenk int i;
524f90ffbf3STorsten Schenk struct snd_kcontrol *vmaster =
525f90ffbf3STorsten Schenk snd_ctl_make_virtual_master(name, tlv_output);
526f90ffbf3STorsten Schenk struct snd_kcontrol *control;
527f90ffbf3STorsten Schenk
528f90ffbf3STorsten Schenk if (!vmaster)
529f90ffbf3STorsten Schenk return -ENOMEM;
530f90ffbf3STorsten Schenk ret = snd_ctl_add(card, vmaster);
531f90ffbf3STorsten Schenk if (ret < 0)
532f90ffbf3STorsten Schenk return ret;
533f90ffbf3STorsten Schenk
534f90ffbf3STorsten Schenk i = 0;
535f90ffbf3STorsten Schenk while (elems[i].name) {
536f90ffbf3STorsten Schenk control = snd_ctl_new1(&elems[i], rt);
537f90ffbf3STorsten Schenk if (!control)
538f90ffbf3STorsten Schenk return -ENOMEM;
539f90ffbf3STorsten Schenk ret = snd_ctl_add(card, control);
540f90ffbf3STorsten Schenk if (ret < 0)
541f90ffbf3STorsten Schenk return ret;
542*9ab0cb30STakashi Iwai ret = snd_ctl_add_follower(vmaster, control);
543f90ffbf3STorsten Schenk if (ret < 0)
544f90ffbf3STorsten Schenk return ret;
545f90ffbf3STorsten Schenk i++;
546f90ffbf3STorsten Schenk }
547f90ffbf3STorsten Schenk return 0;
548f90ffbf3STorsten Schenk }
549f90ffbf3STorsten Schenk
usb6fire_control_init(struct sfire_chip * chip)55087f9796aSBill Pemberton int usb6fire_control_init(struct sfire_chip *chip)
551c6d43ba8STorsten Schenk {
552c6d43ba8STorsten Schenk int i;
553c6d43ba8STorsten Schenk int ret;
554c6d43ba8STorsten Schenk struct control_runtime *rt = kzalloc(sizeof(struct control_runtime),
555c6d43ba8STorsten Schenk GFP_KERNEL);
556c6d43ba8STorsten Schenk struct comm_runtime *comm_rt = chip->comm;
557c6d43ba8STorsten Schenk
558c6d43ba8STorsten Schenk if (!rt)
559c6d43ba8STorsten Schenk return -ENOMEM;
560c6d43ba8STorsten Schenk
561c6d43ba8STorsten Schenk rt->chip = chip;
5622475b0d4STorsten Schenk rt->update_streaming = usb6fire_control_streaming_update;
5632475b0d4STorsten Schenk rt->set_rate = usb6fire_control_set_rate;
5642475b0d4STorsten Schenk rt->set_channels = usb6fire_control_set_channels;
565c6d43ba8STorsten Schenk
566c6d43ba8STorsten Schenk i = 0;
567c6d43ba8STorsten Schenk while (init_data[i].type) {
568c6d43ba8STorsten Schenk comm_rt->write8(comm_rt, init_data[i].type, init_data[i].reg,
569c6d43ba8STorsten Schenk init_data[i].value);
570c6d43ba8STorsten Schenk i++;
571c6d43ba8STorsten Schenk }
572c6d43ba8STorsten Schenk
573c6d43ba8STorsten Schenk usb6fire_control_opt_coax_update(rt);
574c6d43ba8STorsten Schenk usb6fire_control_line_phono_update(rt);
575f90ffbf3STorsten Schenk usb6fire_control_output_vol_update(rt);
576d97c735aSTorsten Schenk usb6fire_control_output_mute_update(rt);
57706bb4e74STorsten Schenk usb6fire_control_input_vol_update(rt);
5782475b0d4STorsten Schenk usb6fire_control_streaming_update(rt);
579c6d43ba8STorsten Schenk
580f90ffbf3STorsten Schenk ret = usb6fire_control_add_virtual(rt, chip->card,
581f90ffbf3STorsten Schenk "Master Playback Volume", vol_elements);
582f90ffbf3STorsten Schenk if (ret) {
583e3b3757bSTakashi Iwai dev_err(&chip->dev->dev, "cannot add control.\n");
584d97c735aSTorsten Schenk kfree(rt);
585d97c735aSTorsten Schenk return ret;
586d97c735aSTorsten Schenk }
587d97c735aSTorsten Schenk ret = usb6fire_control_add_virtual(rt, chip->card,
588d97c735aSTorsten Schenk "Master Playback Switch", mute_elements);
589d97c735aSTorsten Schenk if (ret) {
590e3b3757bSTakashi Iwai dev_err(&chip->dev->dev, "cannot add control.\n");
591d97c735aSTorsten Schenk kfree(rt);
592f90ffbf3STorsten Schenk return ret;
593f90ffbf3STorsten Schenk }
594f90ffbf3STorsten Schenk
595c6d43ba8STorsten Schenk i = 0;
596c6d43ba8STorsten Schenk while (elements[i].name) {
597c6d43ba8STorsten Schenk ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt));
598c6d43ba8STorsten Schenk if (ret < 0) {
599c6d43ba8STorsten Schenk kfree(rt);
600e3b3757bSTakashi Iwai dev_err(&chip->dev->dev, "cannot add control.\n");
601c6d43ba8STorsten Schenk return ret;
602c6d43ba8STorsten Schenk }
603c6d43ba8STorsten Schenk i++;
604c6d43ba8STorsten Schenk }
605c6d43ba8STorsten Schenk
606c6d43ba8STorsten Schenk chip->control = rt;
607c6d43ba8STorsten Schenk return 0;
608c6d43ba8STorsten Schenk }
609c6d43ba8STorsten Schenk
usb6fire_control_abort(struct sfire_chip * chip)610c6d43ba8STorsten Schenk void usb6fire_control_abort(struct sfire_chip *chip)
611c6d43ba8STorsten Schenk {}
612c6d43ba8STorsten Schenk
usb6fire_control_destroy(struct sfire_chip * chip)613c6d43ba8STorsten Schenk void usb6fire_control_destroy(struct sfire_chip *chip)
614c6d43ba8STorsten Schenk {
615c6d43ba8STorsten Schenk kfree(chip->control);
616c6d43ba8STorsten Schenk chip->control = NULL;
617c6d43ba8STorsten Schenk }
618