blob: b00b8bb88c63f100469d1f377ad3caee71356d99 [file] [log] [blame]
Torsten Schenkc6d43ba2011-01-24 18:45:30 +01001/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Mixer control
5 *
6 * Author: Torsten Schenk <torsten.schenk@zoho.com>
7 * Created: Jan 01, 2011
Torsten Schenkc6d43ba2011-01-24 18:45:30 +01008 * Copyright: (C) Torsten Schenk
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 */
15
16#include <linux/interrupt.h>
17#include <sound/control.h>
Torsten Schenk8e247a92012-02-22 15:20:54 +010018#include <sound/tlv.h>
Torsten Schenkc6d43ba2011-01-24 18:45:30 +010019
20#include "control.h"
21#include "comm.h"
22#include "chip.h"
23
24static char *opt_coax_texts[2] = { "Optical", "Coax" };
25static char *line_phono_texts[2] = { "Line", "Phono" };
26
27/*
Torsten Schenkc6d43ba2011-01-24 18:45:30 +010028 * 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 */
31static const struct {
32 u8 type;
33 u8 reg;
34 u8 value;
35}
36init_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, 0x78 }, { 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
Torsten Schenk2475b0d2011-04-04 11:50:53 +020048static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
49/* values to write to soundcard register for all samplerates */
50static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
51static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
52
Torsten Schenk8e247a92012-02-22 15:20:54 +010053static DECLARE_TLV_DB_MINMAX(tlv_output, -9000, 0);
54
Torsten Schenk2475b0d2011-04-04 11:50:53 +020055enum {
56 DIGITAL_THRU_ONLY_SAMPLERATE = 3
57};
58
Torsten Schenkc6d43ba2011-01-24 18:45:30 +010059static void usb6fire_control_master_vol_update(struct control_runtime *rt)
60{
61 struct comm_runtime *comm_rt = rt->chip->comm;
62 if (comm_rt) {
63 /* set volume */
Torsten Schenk8e247a92012-02-22 15:20:54 +010064 comm_rt->write8(comm_rt, 0x12, 0x0f, 180 - rt->master_vol);
Torsten Schenkc6d43ba2011-01-24 18:45:30 +010065 /* unmute */
66 comm_rt->write8(comm_rt, 0x12, 0x0e, 0x00);
67 }
68}
69
70static void usb6fire_control_line_phono_update(struct control_runtime *rt)
71{
72 struct comm_runtime *comm_rt = rt->chip->comm;
73 if (comm_rt) {
74 comm_rt->write8(comm_rt, 0x22, 0x02, rt->line_phono_switch);
75 comm_rt->write8(comm_rt, 0x21, 0x02, rt->line_phono_switch);
76 }
77}
78
79static void usb6fire_control_opt_coax_update(struct control_runtime *rt)
80{
81 struct comm_runtime *comm_rt = rt->chip->comm;
82 if (comm_rt) {
83 comm_rt->write8(comm_rt, 0x22, 0x00, rt->opt_coax_switch);
84 comm_rt->write8(comm_rt, 0x21, 0x00, rt->opt_coax_switch);
85 }
86}
87
Torsten Schenk2475b0d2011-04-04 11:50:53 +020088static int usb6fire_control_set_rate(struct control_runtime *rt, int rate)
89{
90 int ret;
91 struct usb_device *device = rt->chip->dev;
92 struct comm_runtime *comm_rt = rt->chip->comm;
93
94 if (rate < 0 || rate >= CONTROL_N_RATES)
95 return -EINVAL;
96
97 ret = usb_set_interface(device, 1, rates_altsetting[rate]);
98 if (ret < 0)
99 return ret;
100
101 /* set soundcard clock */
102 ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate],
103 rates_6fire_vh[rate]);
104 if (ret < 0)
105 return ret;
106
107 return 0;
108}
109
110static int usb6fire_control_set_channels(
111 struct control_runtime *rt, int n_analog_out,
112 int n_analog_in, bool spdif_out, bool spdif_in)
113{
114 int ret;
115 struct comm_runtime *comm_rt = rt->chip->comm;
116
117 /* enable analog inputs and outputs
118 * (one bit per stereo-channel) */
119 ret = comm_rt->write16(comm_rt, 0x02, 0x02,
120 (1 << (n_analog_out / 2)) - 1,
121 (1 << (n_analog_in / 2)) - 1);
122 if (ret < 0)
123 return ret;
124
125 /* disable digital inputs and outputs */
126 /* TODO: use spdif_x to enable/disable digital channels */
127 ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
128 if (ret < 0)
129 return ret;
130
131 return 0;
132}
133
134static int usb6fire_control_streaming_update(struct control_runtime *rt)
135{
136 struct comm_runtime *comm_rt = rt->chip->comm;
137
138 if (comm_rt) {
139 if (!rt->usb_streaming && rt->digital_thru_switch)
140 usb6fire_control_set_rate(rt,
141 DIGITAL_THRU_ONLY_SAMPLERATE);
142 return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00,
143 (rt->usb_streaming ? 0x01 : 0x00) |
144 (rt->digital_thru_switch ? 0x08 : 0x00));
145 }
146 return -EINVAL;
147}
148
Torsten Schenkc6d43ba2011-01-24 18:45:30 +0100149static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol,
150 struct snd_ctl_elem_info *uinfo)
151{
152 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
153 uinfo->count = 1;
154 uinfo->value.integer.min = 0;
Torsten Schenk8e247a92012-02-22 15:20:54 +0100155 uinfo->value.integer.max = 180;
Torsten Schenkc6d43ba2011-01-24 18:45:30 +0100156 return 0;
157}
158
159static int usb6fire_control_master_vol_put(struct snd_kcontrol *kcontrol,
160 struct snd_ctl_elem_value *ucontrol)
161{
162 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
163 int changed = 0;
164 if (rt->master_vol != ucontrol->value.integer.value[0]) {
165 rt->master_vol = ucontrol->value.integer.value[0];
166 usb6fire_control_master_vol_update(rt);
167 changed = 1;
168 }
169 return changed;
170}
171
172static int usb6fire_control_master_vol_get(struct snd_kcontrol *kcontrol,
173 struct snd_ctl_elem_value *ucontrol)
174{
175 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
176 ucontrol->value.integer.value[0] = rt->master_vol;
177 return 0;
178}
179
180static int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol,
181 struct snd_ctl_elem_info *uinfo)
182{
183 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
184 uinfo->count = 1;
185 uinfo->value.enumerated.items = 2;
186 if (uinfo->value.enumerated.item > 1)
187 uinfo->value.enumerated.item = 1;
188 strcpy(uinfo->value.enumerated.name,
189 line_phono_texts[uinfo->value.enumerated.item]);
190 return 0;
191}
192
193static int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol,
194 struct snd_ctl_elem_value *ucontrol)
195{
196 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
197 int changed = 0;
198 if (rt->line_phono_switch != ucontrol->value.integer.value[0]) {
199 rt->line_phono_switch = ucontrol->value.integer.value[0];
200 usb6fire_control_line_phono_update(rt);
201 changed = 1;
202 }
203 return changed;
204}
205
206static int usb6fire_control_line_phono_get(struct snd_kcontrol *kcontrol,
207 struct snd_ctl_elem_value *ucontrol)
208{
209 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
210 ucontrol->value.integer.value[0] = rt->line_phono_switch;
211 return 0;
212}
213
214static int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol,
215 struct snd_ctl_elem_info *uinfo)
216{
217 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
218 uinfo->count = 1;
219 uinfo->value.enumerated.items = 2;
220 if (uinfo->value.enumerated.item > 1)
221 uinfo->value.enumerated.item = 1;
222 strcpy(uinfo->value.enumerated.name,
223 opt_coax_texts[uinfo->value.enumerated.item]);
224 return 0;
225}
226
227static int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol,
228 struct snd_ctl_elem_value *ucontrol)
229{
230 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
231 int changed = 0;
232
233 if (rt->opt_coax_switch != ucontrol->value.enumerated.item[0]) {
234 rt->opt_coax_switch = ucontrol->value.enumerated.item[0];
235 usb6fire_control_opt_coax_update(rt);
236 changed = 1;
237 }
238 return changed;
239}
240
241static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol,
242 struct snd_ctl_elem_value *ucontrol)
243{
244 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
245 ucontrol->value.enumerated.item[0] = rt->opt_coax_switch;
246 return 0;
247}
248
Torsten Schenk2475b0d2011-04-04 11:50:53 +0200249static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol,
250 struct snd_ctl_elem_value *ucontrol)
251{
252 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
253 int changed = 0;
254
255 if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) {
256 rt->digital_thru_switch = ucontrol->value.integer.value[0];
257 usb6fire_control_streaming_update(rt);
258 changed = 1;
259 }
260 return changed;
261}
262
263static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol,
264 struct snd_ctl_elem_value *ucontrol)
265{
266 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
267 ucontrol->value.integer.value[0] = rt->digital_thru_switch;
268 return 0;
269}
270
Torsten Schenkc6d43ba2011-01-24 18:45:30 +0100271static struct __devinitdata snd_kcontrol_new elements[] = {
272 {
273 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
274 .name = "Master Playback Volume",
275 .index = 0,
Torsten Schenk8e247a92012-02-22 15:20:54 +0100276 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
277 SNDRV_CTL_ELEM_ACCESS_TLV_READ,
Torsten Schenkc6d43ba2011-01-24 18:45:30 +0100278 .info = usb6fire_control_master_vol_info,
279 .get = usb6fire_control_master_vol_get,
Torsten Schenk8e247a92012-02-22 15:20:54 +0100280 .put = usb6fire_control_master_vol_put,
281 .tlv = { .p = tlv_output }
Torsten Schenkc6d43ba2011-01-24 18:45:30 +0100282 },
283 {
284 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
285 .name = "Line/Phono Capture Route",
286 .index = 0,
287 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
288 .info = usb6fire_control_line_phono_info,
289 .get = usb6fire_control_line_phono_get,
290 .put = usb6fire_control_line_phono_put
291 },
292 {
293 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
294 .name = "Opt/Coax Capture Route",
295 .index = 0,
296 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
297 .info = usb6fire_control_opt_coax_info,
298 .get = usb6fire_control_opt_coax_get,
299 .put = usb6fire_control_opt_coax_put
300 },
Torsten Schenk2475b0d2011-04-04 11:50:53 +0200301 {
302 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
303 .name = "Digital Thru Playback Route",
304 .index = 0,
305 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
306 .info = snd_ctl_boolean_mono_info,
307 .get = usb6fire_control_digital_thru_get,
308 .put = usb6fire_control_digital_thru_put
309 },
Torsten Schenkc6d43ba2011-01-24 18:45:30 +0100310 {}
311};
312
313int __devinit usb6fire_control_init(struct sfire_chip *chip)
314{
315 int i;
316 int ret;
317 struct control_runtime *rt = kzalloc(sizeof(struct control_runtime),
318 GFP_KERNEL);
319 struct comm_runtime *comm_rt = chip->comm;
320
321 if (!rt)
322 return -ENOMEM;
323
324 rt->chip = chip;
Torsten Schenk2475b0d2011-04-04 11:50:53 +0200325 rt->update_streaming = usb6fire_control_streaming_update;
326 rt->set_rate = usb6fire_control_set_rate;
327 rt->set_channels = usb6fire_control_set_channels;
Torsten Schenkc6d43ba2011-01-24 18:45:30 +0100328
329 i = 0;
330 while (init_data[i].type) {
331 comm_rt->write8(comm_rt, init_data[i].type, init_data[i].reg,
332 init_data[i].value);
333 i++;
334 }
335
336 usb6fire_control_opt_coax_update(rt);
337 usb6fire_control_line_phono_update(rt);
338 usb6fire_control_master_vol_update(rt);
Torsten Schenk2475b0d2011-04-04 11:50:53 +0200339 usb6fire_control_streaming_update(rt);
Torsten Schenkc6d43ba2011-01-24 18:45:30 +0100340
341 i = 0;
342 while (elements[i].name) {
343 ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt));
344 if (ret < 0) {
345 kfree(rt);
346 snd_printk(KERN_ERR PREFIX "cannot add control.\n");
347 return ret;
348 }
349 i++;
350 }
351
352 chip->control = rt;
353 return 0;
354}
355
356void usb6fire_control_abort(struct sfire_chip *chip)
357{}
358
359void usb6fire_control_destroy(struct sfire_chip *chip)
360{
361 kfree(chip->control);
362 chip->control = NULL;
363}