Commit 2475b0d407614ea5a41b8325d45c614d94087088
Committed by
Takashi Iwai
1 parent
b84610b95f
Exists in
master
and in
7 other branches
ALSA: 6fire - Add support of digital-thru mixer
Digital Thru mixer element added (device can act as converter optical<->coax) Signed-off-by: Torsten Schenk <torsten.schenk@zoho.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Showing 3 changed files with 137 additions and 47 deletions Side-by-side Diff
sound/usb/6fire/control.c
... | ... | @@ -65,6 +65,15 @@ |
65 | 65 | { 0 } /* TERMINATING ENTRY */ |
66 | 66 | }; |
67 | 67 | |
68 | +static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 }; | |
69 | +/* values to write to soundcard register for all samplerates */ | |
70 | +static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01}; | |
71 | +static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00}; | |
72 | + | |
73 | +enum { | |
74 | + DIGITAL_THRU_ONLY_SAMPLERATE = 3 | |
75 | +}; | |
76 | + | |
68 | 77 | static void usb6fire_control_master_vol_update(struct control_runtime *rt) |
69 | 78 | { |
70 | 79 | struct comm_runtime *comm_rt = rt->chip->comm; |
... | ... | @@ -95,6 +104,67 @@ |
95 | 104 | } |
96 | 105 | } |
97 | 106 | |
107 | +static int usb6fire_control_set_rate(struct control_runtime *rt, int rate) | |
108 | +{ | |
109 | + int ret; | |
110 | + struct usb_device *device = rt->chip->dev; | |
111 | + struct comm_runtime *comm_rt = rt->chip->comm; | |
112 | + | |
113 | + if (rate < 0 || rate >= CONTROL_N_RATES) | |
114 | + return -EINVAL; | |
115 | + | |
116 | + ret = usb_set_interface(device, 1, rates_altsetting[rate]); | |
117 | + if (ret < 0) | |
118 | + return ret; | |
119 | + | |
120 | + /* set soundcard clock */ | |
121 | + ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate], | |
122 | + rates_6fire_vh[rate]); | |
123 | + if (ret < 0) | |
124 | + return ret; | |
125 | + | |
126 | + return 0; | |
127 | +} | |
128 | + | |
129 | +static int usb6fire_control_set_channels( | |
130 | + struct control_runtime *rt, int n_analog_out, | |
131 | + int n_analog_in, bool spdif_out, bool spdif_in) | |
132 | +{ | |
133 | + int ret; | |
134 | + struct comm_runtime *comm_rt = rt->chip->comm; | |
135 | + | |
136 | + /* enable analog inputs and outputs | |
137 | + * (one bit per stereo-channel) */ | |
138 | + ret = comm_rt->write16(comm_rt, 0x02, 0x02, | |
139 | + (1 << (n_analog_out / 2)) - 1, | |
140 | + (1 << (n_analog_in / 2)) - 1); | |
141 | + if (ret < 0) | |
142 | + return ret; | |
143 | + | |
144 | + /* disable digital inputs and outputs */ | |
145 | + /* TODO: use spdif_x to enable/disable digital channels */ | |
146 | + ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00); | |
147 | + if (ret < 0) | |
148 | + return ret; | |
149 | + | |
150 | + return 0; | |
151 | +} | |
152 | + | |
153 | +static int usb6fire_control_streaming_update(struct control_runtime *rt) | |
154 | +{ | |
155 | + struct comm_runtime *comm_rt = rt->chip->comm; | |
156 | + | |
157 | + if (comm_rt) { | |
158 | + if (!rt->usb_streaming && rt->digital_thru_switch) | |
159 | + usb6fire_control_set_rate(rt, | |
160 | + DIGITAL_THRU_ONLY_SAMPLERATE); | |
161 | + return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, | |
162 | + (rt->usb_streaming ? 0x01 : 0x00) | | |
163 | + (rt->digital_thru_switch ? 0x08 : 0x00)); | |
164 | + } | |
165 | + return -EINVAL; | |
166 | +} | |
167 | + | |
98 | 168 | static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol, |
99 | 169 | struct snd_ctl_elem_info *uinfo) |
100 | 170 | { |
... | ... | @@ -195,6 +265,28 @@ |
195 | 265 | return 0; |
196 | 266 | } |
197 | 267 | |
268 | +static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol, | |
269 | + struct snd_ctl_elem_value *ucontrol) | |
270 | +{ | |
271 | + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); | |
272 | + int changed = 0; | |
273 | + | |
274 | + if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) { | |
275 | + rt->digital_thru_switch = ucontrol->value.integer.value[0]; | |
276 | + usb6fire_control_streaming_update(rt); | |
277 | + changed = 1; | |
278 | + } | |
279 | + return changed; | |
280 | +} | |
281 | + | |
282 | +static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol, | |
283 | + struct snd_ctl_elem_value *ucontrol) | |
284 | +{ | |
285 | + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); | |
286 | + ucontrol->value.integer.value[0] = rt->digital_thru_switch; | |
287 | + return 0; | |
288 | +} | |
289 | + | |
198 | 290 | static struct __devinitdata snd_kcontrol_new elements[] = { |
199 | 291 | { |
200 | 292 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
... | ... | @@ -223,6 +315,15 @@ |
223 | 315 | .get = usb6fire_control_opt_coax_get, |
224 | 316 | .put = usb6fire_control_opt_coax_put |
225 | 317 | }, |
318 | + { | |
319 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
320 | + .name = "Digital Thru Playback Route", | |
321 | + .index = 0, | |
322 | + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | |
323 | + .info = snd_ctl_boolean_mono_info, | |
324 | + .get = usb6fire_control_digital_thru_get, | |
325 | + .put = usb6fire_control_digital_thru_put | |
326 | + }, | |
226 | 327 | {} |
227 | 328 | }; |
228 | 329 | |
... | ... | @@ -238,6 +339,9 @@ |
238 | 339 | return -ENOMEM; |
239 | 340 | |
240 | 341 | rt->chip = chip; |
342 | + rt->update_streaming = usb6fire_control_streaming_update; | |
343 | + rt->set_rate = usb6fire_control_set_rate; | |
344 | + rt->set_channels = usb6fire_control_set_channels; | |
241 | 345 | |
242 | 346 | i = 0; |
243 | 347 | while (init_data[i].type) { |
... | ... | @@ -249,6 +353,7 @@ |
249 | 353 | usb6fire_control_opt_coax_update(rt); |
250 | 354 | usb6fire_control_line_phono_update(rt); |
251 | 355 | usb6fire_control_master_vol_update(rt); |
356 | + usb6fire_control_streaming_update(rt); | |
252 | 357 | |
253 | 358 | i = 0; |
254 | 359 | while (elements[i].name) { |
sound/usb/6fire/control.h
... | ... | @@ -21,12 +21,29 @@ |
21 | 21 | CONTROL_MAX_ELEMENTS = 32 |
22 | 22 | }; |
23 | 23 | |
24 | +enum { | |
25 | + CONTROL_RATE_44KHZ, | |
26 | + CONTROL_RATE_48KHZ, | |
27 | + CONTROL_RATE_88KHZ, | |
28 | + CONTROL_RATE_96KHZ, | |
29 | + CONTROL_RATE_176KHZ, | |
30 | + CONTROL_RATE_192KHZ, | |
31 | + CONTROL_N_RATES | |
32 | +}; | |
33 | + | |
24 | 34 | struct control_runtime { |
35 | + int (*update_streaming)(struct control_runtime *rt); | |
36 | + int (*set_rate)(struct control_runtime *rt, int rate); | |
37 | + int (*set_channels)(struct control_runtime *rt, int n_analog_out, | |
38 | + int n_analog_in, bool spdif_out, bool spdif_in); | |
39 | + | |
25 | 40 | struct sfire_chip *chip; |
26 | 41 | |
27 | 42 | struct snd_kcontrol *element[CONTROL_MAX_ELEMENTS]; |
28 | 43 | bool opt_coax_switch; |
29 | 44 | bool line_phono_switch; |
45 | + bool digital_thru_switch; | |
46 | + bool usb_streaming; | |
30 | 47 | u8 master_vol; |
31 | 48 | }; |
32 | 49 |
sound/usb/6fire/pcm.c
... | ... | @@ -17,26 +17,23 @@ |
17 | 17 | #include "pcm.h" |
18 | 18 | #include "chip.h" |
19 | 19 | #include "comm.h" |
20 | +#include "control.h" | |
20 | 21 | |
21 | 22 | enum { |
22 | 23 | OUT_N_CHANNELS = 6, IN_N_CHANNELS = 4 |
23 | 24 | }; |
24 | 25 | |
25 | 26 | /* keep next two synced with |
26 | - * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE */ | |
27 | + * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE | |
28 | + * and CONTROL_RATE_XXX in control.h */ | |
27 | 29 | static const int rates_in_packet_size[] = { 228, 228, 420, 420, 404, 404 }; |
28 | 30 | static const int rates_out_packet_size[] = { 228, 228, 420, 420, 604, 604 }; |
29 | 31 | static const int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000 }; |
30 | -static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 }; | |
31 | 32 | static const int rates_alsaid[] = { |
32 | 33 | SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_48000, |
33 | 34 | SNDRV_PCM_RATE_88200, SNDRV_PCM_RATE_96000, |
34 | 35 | SNDRV_PCM_RATE_176400, SNDRV_PCM_RATE_192000 }; |
35 | 36 | |
36 | -/* values to write to soundcard register for all samplerates */ | |
37 | -static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01}; | |
38 | -static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00}; | |
39 | - | |
40 | 37 | enum { /* settings for pcm */ |
41 | 38 | OUT_EP = 6, IN_EP = 2, MAX_BUFSIZE = 128 * 1024 |
42 | 39 | }; |
... | ... | @@ -48,15 +45,6 @@ |
48 | 45 | STREAM_STOPPING |
49 | 46 | }; |
50 | 47 | |
51 | -enum { /* pcm sample rates (also index into RATES_XXX[]) */ | |
52 | - RATE_44KHZ, | |
53 | - RATE_48KHZ, | |
54 | - RATE_88KHZ, | |
55 | - RATE_96KHZ, | |
56 | - RATE_176KHZ, | |
57 | - RATE_192KHZ | |
58 | -}; | |
59 | - | |
60 | 48 | static const struct snd_pcm_hardware pcm_hw = { |
61 | 49 | .info = SNDRV_PCM_INFO_MMAP | |
62 | 50 | SNDRV_PCM_INFO_INTERLEAVED | |
63 | 51 | |
64 | 52 | |
65 | 53 | |
66 | 54 | |
67 | 55 | |
68 | 56 | |
69 | 57 | |
... | ... | @@ -87,57 +75,34 @@ |
87 | 75 | static int usb6fire_pcm_set_rate(struct pcm_runtime *rt) |
88 | 76 | { |
89 | 77 | int ret; |
90 | - struct usb_device *device = rt->chip->dev; | |
91 | - struct comm_runtime *comm_rt = rt->chip->comm; | |
78 | + struct control_runtime *ctrl_rt = rt->chip->control; | |
92 | 79 | |
93 | - if (rt->rate >= ARRAY_SIZE(rates)) | |
94 | - return -EINVAL; | |
95 | - /* disable streaming */ | |
96 | - ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x00); | |
80 | + ctrl_rt->usb_streaming = false; | |
81 | + ret = ctrl_rt->update_streaming(ctrl_rt); | |
97 | 82 | if (ret < 0) { |
98 | 83 | snd_printk(KERN_ERR PREFIX "error stopping streaming while " |
99 | 84 | "setting samplerate %d.\n", rates[rt->rate]); |
100 | 85 | return ret; |
101 | 86 | } |
102 | 87 | |
103 | - ret = usb_set_interface(device, 1, rates_altsetting[rt->rate]); | |
88 | + ret = ctrl_rt->set_rate(ctrl_rt, rt->rate); | |
104 | 89 | if (ret < 0) { |
105 | - snd_printk(KERN_ERR PREFIX "error setting interface " | |
106 | - "altsetting %d for samplerate %d.\n", | |
107 | - rates_altsetting[rt->rate], rates[rt->rate]); | |
108 | - return ret; | |
109 | - } | |
110 | - | |
111 | - /* set soundcard clock */ | |
112 | - ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rt->rate], | |
113 | - rates_6fire_vh[rt->rate]); | |
114 | - if (ret < 0) { | |
115 | 90 | snd_printk(KERN_ERR PREFIX "error setting samplerate %d.\n", |
116 | 91 | rates[rt->rate]); |
117 | 92 | return ret; |
118 | 93 | } |
119 | 94 | |
120 | - /* enable analog inputs and outputs | |
121 | - * (one bit per stereo-channel) */ | |
122 | - ret = comm_rt->write16(comm_rt, 0x02, 0x02, | |
123 | - (1 << (OUT_N_CHANNELS / 2)) - 1, | |
124 | - (1 << (IN_N_CHANNELS / 2)) - 1); | |
95 | + ret = ctrl_rt->set_channels(ctrl_rt, OUT_N_CHANNELS, IN_N_CHANNELS, | |
96 | + false, false); | |
125 | 97 | if (ret < 0) { |
126 | - snd_printk(KERN_ERR PREFIX "error initializing analog channels " | |
98 | + snd_printk(KERN_ERR PREFIX "error initializing channels " | |
127 | 99 | "while setting samplerate %d.\n", |
128 | 100 | rates[rt->rate]); |
129 | 101 | return ret; |
130 | 102 | } |
131 | - /* disable digital inputs and outputs */ | |
132 | - ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00); | |
133 | - if (ret < 0) { | |
134 | - snd_printk(KERN_ERR PREFIX "error initializing digital " | |
135 | - "channels while setting samplerate %d.\n", | |
136 | - rates[rt->rate]); | |
137 | - return ret; | |
138 | - } | |
139 | 103 | |
140 | - ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x01); | |
104 | + ctrl_rt->usb_streaming = true; | |
105 | + ret = ctrl_rt->update_streaming(ctrl_rt); | |
141 | 106 | if (ret < 0) { |
142 | 107 | snd_printk(KERN_ERR PREFIX "error starting streaming while " |
143 | 108 | "setting samplerate %d.\n", rates[rt->rate]); |
144 | 109 | |
... | ... | @@ -168,12 +133,15 @@ |
168 | 133 | static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt) |
169 | 134 | { |
170 | 135 | int i; |
136 | + struct control_runtime *ctrl_rt = rt->chip->control; | |
171 | 137 | |
172 | 138 | if (rt->stream_state != STREAM_DISABLED) { |
173 | 139 | for (i = 0; i < PCM_N_URBS; i++) { |
174 | 140 | usb_kill_urb(&rt->in_urbs[i].instance); |
175 | 141 | usb_kill_urb(&rt->out_urbs[i].instance); |
176 | 142 | } |
143 | + ctrl_rt->usb_streaming = false; | |
144 | + ctrl_rt->update_streaming(ctrl_rt); | |
177 | 145 | rt->stream_state = STREAM_DISABLED; |
178 | 146 | } |
179 | 147 | } |