diff options
author | Takashi Sakamoto <o-takashi@sakamocchi.jp> | 2014-11-28 10:59:29 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2014-11-29 14:22:37 -0500 |
commit | 31514bfb4ab8ba6f93b5ce5fcc543cb2ac4f96e5 (patch) | |
tree | 4bfee4594141dbead179350c7b666183843bf84d | |
parent | 3713d93a6a12f8629c2660bb4a30d48b98105fca (diff) |
ALSA: oxfw: Split control functionality to a new file
This is a help for works in followed patches.
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r-- | sound/firewire/oxfw/Makefile | 2 | ||||
-rw-r--r-- | sound/firewire/oxfw/oxfw-control.c | 283 | ||||
-rw-r--r-- | sound/firewire/oxfw/oxfw.c | 277 | ||||
-rw-r--r-- | sound/firewire/oxfw/oxfw.h | 2 |
4 files changed, 287 insertions, 277 deletions
diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile index 7fb4d09a5f07..0cf48fd87688 100644 --- a/sound/firewire/oxfw/Makefile +++ b/sound/firewire/oxfw/Makefile | |||
@@ -1,2 +1,2 @@ | |||
1 | snd-oxfw-objs := oxfw-stream.o oxfw-pcm.o oxfw.o | 1 | snd-oxfw-objs := oxfw-stream.o oxfw-control.o oxfw-pcm.o oxfw.o |
2 | obj-m += snd-oxfw.o | 2 | obj-m += snd-oxfw.o |
diff --git a/sound/firewire/oxfw/oxfw-control.c b/sound/firewire/oxfw/oxfw-control.c new file mode 100644 index 000000000000..02a1cb90f20d --- /dev/null +++ b/sound/firewire/oxfw/oxfw-control.c | |||
@@ -0,0 +1,283 @@ | |||
1 | /* | ||
2 | * oxfw_stream.c - a part of driver for OXFW970/971 based devices | ||
3 | * | ||
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | ||
5 | * Licensed under the terms of the GNU General Public License, version 2. | ||
6 | */ | ||
7 | |||
8 | #include "oxfw.h" | ||
9 | |||
10 | enum control_action { CTL_READ, CTL_WRITE }; | ||
11 | enum control_attribute { | ||
12 | CTL_MIN = 0x02, | ||
13 | CTL_MAX = 0x03, | ||
14 | CTL_CURRENT = 0x10, | ||
15 | }; | ||
16 | |||
17 | static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value, | ||
18 | enum control_action action) | ||
19 | { | ||
20 | u8 *buf; | ||
21 | u8 response_ok; | ||
22 | int err; | ||
23 | |||
24 | buf = kmalloc(11, GFP_KERNEL); | ||
25 | if (!buf) | ||
26 | return -ENOMEM; | ||
27 | |||
28 | if (action == CTL_READ) { | ||
29 | buf[0] = 0x01; /* AV/C, STATUS */ | ||
30 | response_ok = 0x0c; /* STABLE */ | ||
31 | } else { | ||
32 | buf[0] = 0x00; /* AV/C, CONTROL */ | ||
33 | response_ok = 0x09; /* ACCEPTED */ | ||
34 | } | ||
35 | buf[1] = 0x08; /* audio unit 0 */ | ||
36 | buf[2] = 0xb8; /* FUNCTION BLOCK */ | ||
37 | buf[3] = 0x81; /* function block type: feature */ | ||
38 | buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */ | ||
39 | buf[5] = 0x10; /* control attribute: current */ | ||
40 | buf[6] = 0x02; /* selector length */ | ||
41 | buf[7] = 0x00; /* audio channel number */ | ||
42 | buf[8] = 0x01; /* control selector: mute */ | ||
43 | buf[9] = 0x01; /* control data length */ | ||
44 | if (action == CTL_READ) | ||
45 | buf[10] = 0xff; | ||
46 | else | ||
47 | buf[10] = *value ? 0x70 : 0x60; | ||
48 | |||
49 | err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe); | ||
50 | if (err < 0) | ||
51 | goto error; | ||
52 | if (err < 11) { | ||
53 | dev_err(&oxfw->unit->device, "short FCP response\n"); | ||
54 | err = -EIO; | ||
55 | goto error; | ||
56 | } | ||
57 | if (buf[0] != response_ok) { | ||
58 | dev_err(&oxfw->unit->device, "mute command failed\n"); | ||
59 | err = -EIO; | ||
60 | goto error; | ||
61 | } | ||
62 | if (action == CTL_READ) | ||
63 | *value = buf[10] == 0x70; | ||
64 | |||
65 | err = 0; | ||
66 | |||
67 | error: | ||
68 | kfree(buf); | ||
69 | |||
70 | return err; | ||
71 | } | ||
72 | |||
73 | static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value, | ||
74 | unsigned int channel, | ||
75 | enum control_attribute attribute, | ||
76 | enum control_action action) | ||
77 | { | ||
78 | u8 *buf; | ||
79 | u8 response_ok; | ||
80 | int err; | ||
81 | |||
82 | buf = kmalloc(12, GFP_KERNEL); | ||
83 | if (!buf) | ||
84 | return -ENOMEM; | ||
85 | |||
86 | if (action == CTL_READ) { | ||
87 | buf[0] = 0x01; /* AV/C, STATUS */ | ||
88 | response_ok = 0x0c; /* STABLE */ | ||
89 | } else { | ||
90 | buf[0] = 0x00; /* AV/C, CONTROL */ | ||
91 | response_ok = 0x09; /* ACCEPTED */ | ||
92 | } | ||
93 | buf[1] = 0x08; /* audio unit 0 */ | ||
94 | buf[2] = 0xb8; /* FUNCTION BLOCK */ | ||
95 | buf[3] = 0x81; /* function block type: feature */ | ||
96 | buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */ | ||
97 | buf[5] = attribute; /* control attribute */ | ||
98 | buf[6] = 0x02; /* selector length */ | ||
99 | buf[7] = channel; /* audio channel number */ | ||
100 | buf[8] = 0x02; /* control selector: volume */ | ||
101 | buf[9] = 0x02; /* control data length */ | ||
102 | if (action == CTL_READ) { | ||
103 | buf[10] = 0xff; | ||
104 | buf[11] = 0xff; | ||
105 | } else { | ||
106 | buf[10] = *value >> 8; | ||
107 | buf[11] = *value; | ||
108 | } | ||
109 | |||
110 | err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe); | ||
111 | if (err < 0) | ||
112 | goto error; | ||
113 | if (err < 12) { | ||
114 | dev_err(&oxfw->unit->device, "short FCP response\n"); | ||
115 | err = -EIO; | ||
116 | goto error; | ||
117 | } | ||
118 | if (buf[0] != response_ok) { | ||
119 | dev_err(&oxfw->unit->device, "volume command failed\n"); | ||
120 | err = -EIO; | ||
121 | goto error; | ||
122 | } | ||
123 | if (action == CTL_READ) | ||
124 | *value = (buf[10] << 8) | buf[11]; | ||
125 | |||
126 | err = 0; | ||
127 | |||
128 | error: | ||
129 | kfree(buf); | ||
130 | |||
131 | return err; | ||
132 | } | ||
133 | |||
134 | static int oxfw_mute_get(struct snd_kcontrol *control, | ||
135 | struct snd_ctl_elem_value *value) | ||
136 | { | ||
137 | struct snd_oxfw *oxfw = control->private_data; | ||
138 | |||
139 | value->value.integer.value[0] = !oxfw->mute; | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static int oxfw_mute_put(struct snd_kcontrol *control, | ||
145 | struct snd_ctl_elem_value *value) | ||
146 | { | ||
147 | struct snd_oxfw *oxfw = control->private_data; | ||
148 | bool mute; | ||
149 | int err; | ||
150 | |||
151 | mute = !value->value.integer.value[0]; | ||
152 | |||
153 | if (mute == oxfw->mute) | ||
154 | return 0; | ||
155 | |||
156 | err = oxfw_mute_command(oxfw, &mute, CTL_WRITE); | ||
157 | if (err < 0) | ||
158 | return err; | ||
159 | oxfw->mute = mute; | ||
160 | |||
161 | return 1; | ||
162 | } | ||
163 | |||
164 | static int oxfw_volume_info(struct snd_kcontrol *control, | ||
165 | struct snd_ctl_elem_info *info) | ||
166 | { | ||
167 | struct snd_oxfw *oxfw = control->private_data; | ||
168 | |||
169 | info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
170 | info->count = oxfw->device_info->mixer_channels; | ||
171 | info->value.integer.min = oxfw->volume_min; | ||
172 | info->value.integer.max = oxfw->volume_max; | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; | ||
178 | |||
179 | static int oxfw_volume_get(struct snd_kcontrol *control, | ||
180 | struct snd_ctl_elem_value *value) | ||
181 | { | ||
182 | struct snd_oxfw *oxfw = control->private_data; | ||
183 | unsigned int i; | ||
184 | |||
185 | for (i = 0; i < oxfw->device_info->mixer_channels; ++i) | ||
186 | value->value.integer.value[channel_map[i]] = oxfw->volume[i]; | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static int oxfw_volume_put(struct snd_kcontrol *control, | ||
192 | struct snd_ctl_elem_value *value) | ||
193 | { | ||
194 | struct snd_oxfw *oxfw = control->private_data; | ||
195 | unsigned int i, changed_channels; | ||
196 | bool equal_values = true; | ||
197 | s16 volume; | ||
198 | int err; | ||
199 | |||
200 | for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { | ||
201 | if (value->value.integer.value[i] < oxfw->volume_min || | ||
202 | value->value.integer.value[i] > oxfw->volume_max) | ||
203 | return -EINVAL; | ||
204 | if (value->value.integer.value[i] != | ||
205 | value->value.integer.value[0]) | ||
206 | equal_values = false; | ||
207 | } | ||
208 | |||
209 | changed_channels = 0; | ||
210 | for (i = 0; i < oxfw->device_info->mixer_channels; ++i) | ||
211 | if (value->value.integer.value[channel_map[i]] != | ||
212 | oxfw->volume[i]) | ||
213 | changed_channels |= 1 << (i + 1); | ||
214 | |||
215 | if (equal_values && changed_channels != 0) | ||
216 | changed_channels = 1 << 0; | ||
217 | |||
218 | for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) { | ||
219 | volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; | ||
220 | if (changed_channels & (1 << i)) { | ||
221 | err = oxfw_volume_command(oxfw, &volume, i, | ||
222 | CTL_CURRENT, CTL_WRITE); | ||
223 | if (err < 0) | ||
224 | return err; | ||
225 | } | ||
226 | if (i > 0) | ||
227 | oxfw->volume[i - 1] = volume; | ||
228 | } | ||
229 | |||
230 | return changed_channels != 0; | ||
231 | } | ||
232 | |||
233 | int snd_oxfw_create_mixer(struct snd_oxfw *oxfw) | ||
234 | { | ||
235 | static const struct snd_kcontrol_new controls[] = { | ||
236 | { | ||
237 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
238 | .name = "PCM Playback Switch", | ||
239 | .info = snd_ctl_boolean_mono_info, | ||
240 | .get = oxfw_mute_get, | ||
241 | .put = oxfw_mute_put, | ||
242 | }, | ||
243 | { | ||
244 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
245 | .name = "PCM Playback Volume", | ||
246 | .info = oxfw_volume_info, | ||
247 | .get = oxfw_volume_get, | ||
248 | .put = oxfw_volume_put, | ||
249 | }, | ||
250 | }; | ||
251 | unsigned int i, first_ch; | ||
252 | int err; | ||
253 | |||
254 | err = oxfw_volume_command(oxfw, &oxfw->volume_min, | ||
255 | 0, CTL_MIN, CTL_READ); | ||
256 | if (err < 0) | ||
257 | return err; | ||
258 | err = oxfw_volume_command(oxfw, &oxfw->volume_max, | ||
259 | 0, CTL_MAX, CTL_READ); | ||
260 | if (err < 0) | ||
261 | return err; | ||
262 | |||
263 | err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ); | ||
264 | if (err < 0) | ||
265 | return err; | ||
266 | |||
267 | first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1; | ||
268 | for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { | ||
269 | err = oxfw_volume_command(oxfw, &oxfw->volume[i], | ||
270 | first_ch + i, CTL_CURRENT, CTL_READ); | ||
271 | if (err < 0) | ||
272 | return err; | ||
273 | } | ||
274 | |||
275 | for (i = 0; i < ARRAY_SIZE(controls); ++i) { | ||
276 | err = snd_ctl_add(oxfw->card, | ||
277 | snd_ctl_new1(&controls[i], oxfw)); | ||
278 | if (err < 0) | ||
279 | return err; | ||
280 | } | ||
281 | |||
282 | return 0; | ||
283 | } | ||
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 24bb6dff23cc..951d9a4e2102 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c | |||
@@ -25,281 +25,6 @@ MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); | |||
25 | MODULE_LICENSE("GPL v2"); | 25 | MODULE_LICENSE("GPL v2"); |
26 | MODULE_ALIAS("snd-firewire-speakers"); | 26 | MODULE_ALIAS("snd-firewire-speakers"); |
27 | 27 | ||
28 | enum control_action { CTL_READ, CTL_WRITE }; | ||
29 | enum control_attribute { | ||
30 | CTL_MIN = 0x02, | ||
31 | CTL_MAX = 0x03, | ||
32 | CTL_CURRENT = 0x10, | ||
33 | }; | ||
34 | |||
35 | static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value, | ||
36 | enum control_action action) | ||
37 | { | ||
38 | u8 *buf; | ||
39 | u8 response_ok; | ||
40 | int err; | ||
41 | |||
42 | buf = kmalloc(11, GFP_KERNEL); | ||
43 | if (!buf) | ||
44 | return -ENOMEM; | ||
45 | |||
46 | if (action == CTL_READ) { | ||
47 | buf[0] = 0x01; /* AV/C, STATUS */ | ||
48 | response_ok = 0x0c; /* STABLE */ | ||
49 | } else { | ||
50 | buf[0] = 0x00; /* AV/C, CONTROL */ | ||
51 | response_ok = 0x09; /* ACCEPTED */ | ||
52 | } | ||
53 | buf[1] = 0x08; /* audio unit 0 */ | ||
54 | buf[2] = 0xb8; /* FUNCTION BLOCK */ | ||
55 | buf[3] = 0x81; /* function block type: feature */ | ||
56 | buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */ | ||
57 | buf[5] = 0x10; /* control attribute: current */ | ||
58 | buf[6] = 0x02; /* selector length */ | ||
59 | buf[7] = 0x00; /* audio channel number */ | ||
60 | buf[8] = 0x01; /* control selector: mute */ | ||
61 | buf[9] = 0x01; /* control data length */ | ||
62 | if (action == CTL_READ) | ||
63 | buf[10] = 0xff; | ||
64 | else | ||
65 | buf[10] = *value ? 0x70 : 0x60; | ||
66 | |||
67 | err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe); | ||
68 | if (err < 0) | ||
69 | goto error; | ||
70 | if (err < 11) { | ||
71 | dev_err(&oxfw->unit->device, "short FCP response\n"); | ||
72 | err = -EIO; | ||
73 | goto error; | ||
74 | } | ||
75 | if (buf[0] != response_ok) { | ||
76 | dev_err(&oxfw->unit->device, "mute command failed\n"); | ||
77 | err = -EIO; | ||
78 | goto error; | ||
79 | } | ||
80 | if (action == CTL_READ) | ||
81 | *value = buf[10] == 0x70; | ||
82 | |||
83 | err = 0; | ||
84 | |||
85 | error: | ||
86 | kfree(buf); | ||
87 | |||
88 | return err; | ||
89 | } | ||
90 | |||
91 | static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value, | ||
92 | unsigned int channel, | ||
93 | enum control_attribute attribute, | ||
94 | enum control_action action) | ||
95 | { | ||
96 | u8 *buf; | ||
97 | u8 response_ok; | ||
98 | int err; | ||
99 | |||
100 | buf = kmalloc(12, GFP_KERNEL); | ||
101 | if (!buf) | ||
102 | return -ENOMEM; | ||
103 | |||
104 | if (action == CTL_READ) { | ||
105 | buf[0] = 0x01; /* AV/C, STATUS */ | ||
106 | response_ok = 0x0c; /* STABLE */ | ||
107 | } else { | ||
108 | buf[0] = 0x00; /* AV/C, CONTROL */ | ||
109 | response_ok = 0x09; /* ACCEPTED */ | ||
110 | } | ||
111 | buf[1] = 0x08; /* audio unit 0 */ | ||
112 | buf[2] = 0xb8; /* FUNCTION BLOCK */ | ||
113 | buf[3] = 0x81; /* function block type: feature */ | ||
114 | buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */ | ||
115 | buf[5] = attribute; /* control attribute */ | ||
116 | buf[6] = 0x02; /* selector length */ | ||
117 | buf[7] = channel; /* audio channel number */ | ||
118 | buf[8] = 0x02; /* control selector: volume */ | ||
119 | buf[9] = 0x02; /* control data length */ | ||
120 | if (action == CTL_READ) { | ||
121 | buf[10] = 0xff; | ||
122 | buf[11] = 0xff; | ||
123 | } else { | ||
124 | buf[10] = *value >> 8; | ||
125 | buf[11] = *value; | ||
126 | } | ||
127 | |||
128 | err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe); | ||
129 | if (err < 0) | ||
130 | goto error; | ||
131 | if (err < 12) { | ||
132 | dev_err(&oxfw->unit->device, "short FCP response\n"); | ||
133 | err = -EIO; | ||
134 | goto error; | ||
135 | } | ||
136 | if (buf[0] != response_ok) { | ||
137 | dev_err(&oxfw->unit->device, "volume command failed\n"); | ||
138 | err = -EIO; | ||
139 | goto error; | ||
140 | } | ||
141 | if (action == CTL_READ) | ||
142 | *value = (buf[10] << 8) | buf[11]; | ||
143 | |||
144 | err = 0; | ||
145 | |||
146 | error: | ||
147 | kfree(buf); | ||
148 | |||
149 | return err; | ||
150 | } | ||
151 | |||
152 | static int oxfw_mute_get(struct snd_kcontrol *control, | ||
153 | struct snd_ctl_elem_value *value) | ||
154 | { | ||
155 | struct snd_oxfw *oxfw = control->private_data; | ||
156 | |||
157 | value->value.integer.value[0] = !oxfw->mute; | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int oxfw_mute_put(struct snd_kcontrol *control, | ||
163 | struct snd_ctl_elem_value *value) | ||
164 | { | ||
165 | struct snd_oxfw *oxfw = control->private_data; | ||
166 | bool mute; | ||
167 | int err; | ||
168 | |||
169 | mute = !value->value.integer.value[0]; | ||
170 | |||
171 | if (mute == oxfw->mute) | ||
172 | return 0; | ||
173 | |||
174 | err = oxfw_mute_command(oxfw, &mute, CTL_WRITE); | ||
175 | if (err < 0) | ||
176 | return err; | ||
177 | oxfw->mute = mute; | ||
178 | |||
179 | return 1; | ||
180 | } | ||
181 | |||
182 | static int oxfw_volume_info(struct snd_kcontrol *control, | ||
183 | struct snd_ctl_elem_info *info) | ||
184 | { | ||
185 | struct snd_oxfw *oxfw = control->private_data; | ||
186 | |||
187 | info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
188 | info->count = oxfw->device_info->mixer_channels; | ||
189 | info->value.integer.min = oxfw->volume_min; | ||
190 | info->value.integer.max = oxfw->volume_max; | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; | ||
196 | |||
197 | static int oxfw_volume_get(struct snd_kcontrol *control, | ||
198 | struct snd_ctl_elem_value *value) | ||
199 | { | ||
200 | struct snd_oxfw *oxfw = control->private_data; | ||
201 | unsigned int i; | ||
202 | |||
203 | for (i = 0; i < oxfw->device_info->mixer_channels; ++i) | ||
204 | value->value.integer.value[channel_map[i]] = oxfw->volume[i]; | ||
205 | |||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static int oxfw_volume_put(struct snd_kcontrol *control, | ||
210 | struct snd_ctl_elem_value *value) | ||
211 | { | ||
212 | struct snd_oxfw *oxfw = control->private_data; | ||
213 | unsigned int i, changed_channels; | ||
214 | bool equal_values = true; | ||
215 | s16 volume; | ||
216 | int err; | ||
217 | |||
218 | for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { | ||
219 | if (value->value.integer.value[i] < oxfw->volume_min || | ||
220 | value->value.integer.value[i] > oxfw->volume_max) | ||
221 | return -EINVAL; | ||
222 | if (value->value.integer.value[i] != | ||
223 | value->value.integer.value[0]) | ||
224 | equal_values = false; | ||
225 | } | ||
226 | |||
227 | changed_channels = 0; | ||
228 | for (i = 0; i < oxfw->device_info->mixer_channels; ++i) | ||
229 | if (value->value.integer.value[channel_map[i]] != | ||
230 | oxfw->volume[i]) | ||
231 | changed_channels |= 1 << (i + 1); | ||
232 | |||
233 | if (equal_values && changed_channels != 0) | ||
234 | changed_channels = 1 << 0; | ||
235 | |||
236 | for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) { | ||
237 | volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; | ||
238 | if (changed_channels & (1 << i)) { | ||
239 | err = oxfw_volume_command(oxfw, &volume, i, | ||
240 | CTL_CURRENT, CTL_WRITE); | ||
241 | if (err < 0) | ||
242 | return err; | ||
243 | } | ||
244 | if (i > 0) | ||
245 | oxfw->volume[i - 1] = volume; | ||
246 | } | ||
247 | |||
248 | return changed_channels != 0; | ||
249 | } | ||
250 | |||
251 | static int oxfw_create_mixer(struct snd_oxfw *oxfw) | ||
252 | { | ||
253 | static const struct snd_kcontrol_new controls[] = { | ||
254 | { | ||
255 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
256 | .name = "PCM Playback Switch", | ||
257 | .info = snd_ctl_boolean_mono_info, | ||
258 | .get = oxfw_mute_get, | ||
259 | .put = oxfw_mute_put, | ||
260 | }, | ||
261 | { | ||
262 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
263 | .name = "PCM Playback Volume", | ||
264 | .info = oxfw_volume_info, | ||
265 | .get = oxfw_volume_get, | ||
266 | .put = oxfw_volume_put, | ||
267 | }, | ||
268 | }; | ||
269 | unsigned int i, first_ch; | ||
270 | int err; | ||
271 | |||
272 | err = oxfw_volume_command(oxfw, &oxfw->volume_min, | ||
273 | 0, CTL_MIN, CTL_READ); | ||
274 | if (err < 0) | ||
275 | return err; | ||
276 | err = oxfw_volume_command(oxfw, &oxfw->volume_max, | ||
277 | 0, CTL_MAX, CTL_READ); | ||
278 | if (err < 0) | ||
279 | return err; | ||
280 | |||
281 | err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ); | ||
282 | if (err < 0) | ||
283 | return err; | ||
284 | |||
285 | first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1; | ||
286 | for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { | ||
287 | err = oxfw_volume_command(oxfw, &oxfw->volume[i], | ||
288 | first_ch + i, CTL_CURRENT, CTL_READ); | ||
289 | if (err < 0) | ||
290 | return err; | ||
291 | } | ||
292 | |||
293 | for (i = 0; i < ARRAY_SIZE(controls); ++i) { | ||
294 | err = snd_ctl_add(oxfw->card, | ||
295 | snd_ctl_new1(&controls[i], oxfw)); | ||
296 | if (err < 0) | ||
297 | return err; | ||
298 | } | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static u32 oxfw_read_firmware_version(struct fw_unit *unit) | 28 | static u32 oxfw_read_firmware_version(struct fw_unit *unit) |
304 | { | 29 | { |
305 | __be32 data; | 30 | __be32 data; |
@@ -353,7 +78,7 @@ static int oxfw_probe(struct fw_unit *unit, | |||
353 | if (err < 0) | 78 | if (err < 0) |
354 | goto error; | 79 | goto error; |
355 | 80 | ||
356 | err = oxfw_create_mixer(oxfw); | 81 | err = snd_oxfw_create_mixer(oxfw); |
357 | if (err < 0) | 82 | if (err < 0) |
358 | goto error; | 83 | goto error; |
359 | 84 | ||
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index 1196db8a6300..6164bf3e1f5a 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h | |||
@@ -58,3 +58,5 @@ void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw); | |||
58 | int firewave_constraints(struct snd_pcm_runtime *runtime); | 58 | int firewave_constraints(struct snd_pcm_runtime *runtime); |
59 | int lacie_speakers_constraints(struct snd_pcm_runtime *runtime); | 59 | int lacie_speakers_constraints(struct snd_pcm_runtime *runtime); |
60 | int snd_oxfw_create_pcm(struct snd_oxfw *oxfw); | 60 | int snd_oxfw_create_pcm(struct snd_oxfw *oxfw); |
61 | |||
62 | int snd_oxfw_create_mixer(struct snd_oxfw *oxfw); | ||