Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/oxfw/oxfw-spkr.c
26451 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* oxfw-spkr.c - a part of driver for OXFW970/971 based devices
4
*
5
* Copyright (c) Clemens Ladisch <[email protected]>
6
*/
7
8
#include "oxfw.h"
9
10
struct fw_spkr {
11
bool mute;
12
s16 volume[6];
13
s16 volume_min;
14
s16 volume_max;
15
16
unsigned int mixer_channels;
17
u8 mute_fb_id;
18
u8 volume_fb_id;
19
};
20
21
enum control_action { CTL_READ, CTL_WRITE };
22
enum control_attribute {
23
CTL_MIN = 0x02,
24
CTL_MAX = 0x03,
25
CTL_CURRENT = 0x10,
26
};
27
28
static int avc_audio_feature_mute(struct fw_unit *unit, u8 fb_id, bool *value,
29
enum control_action action)
30
{
31
u8 *buf;
32
u8 response_ok;
33
int err;
34
35
buf = kmalloc(11, GFP_KERNEL);
36
if (!buf)
37
return -ENOMEM;
38
39
if (action == CTL_READ) {
40
buf[0] = 0x01; /* AV/C, STATUS */
41
response_ok = 0x0c; /* STABLE */
42
} else {
43
buf[0] = 0x00; /* AV/C, CONTROL */
44
response_ok = 0x09; /* ACCEPTED */
45
}
46
buf[1] = 0x08; /* audio unit 0 */
47
buf[2] = 0xb8; /* FUNCTION BLOCK */
48
buf[3] = 0x81; /* function block type: feature */
49
buf[4] = fb_id; /* function block ID */
50
buf[5] = 0x10; /* control attribute: current */
51
buf[6] = 0x02; /* selector length */
52
buf[7] = 0x00; /* audio channel number */
53
buf[8] = 0x01; /* control selector: mute */
54
buf[9] = 0x01; /* control data length */
55
if (action == CTL_READ)
56
buf[10] = 0xff;
57
else
58
buf[10] = *value ? 0x70 : 0x60;
59
60
err = fcp_avc_transaction(unit, buf, 11, buf, 11, 0x3fe);
61
if (err < 0)
62
goto error;
63
if (err < 11) {
64
dev_err(&unit->device, "short FCP response\n");
65
err = -EIO;
66
goto error;
67
}
68
if (buf[0] != response_ok) {
69
dev_err(&unit->device, "mute command failed\n");
70
err = -EIO;
71
goto error;
72
}
73
if (action == CTL_READ)
74
*value = buf[10] == 0x70;
75
76
err = 0;
77
78
error:
79
kfree(buf);
80
81
return err;
82
}
83
84
static int avc_audio_feature_volume(struct fw_unit *unit, u8 fb_id, s16 *value,
85
unsigned int channel,
86
enum control_attribute attribute,
87
enum control_action action)
88
{
89
u8 *buf;
90
u8 response_ok;
91
int err;
92
93
buf = kmalloc(12, GFP_KERNEL);
94
if (!buf)
95
return -ENOMEM;
96
97
if (action == CTL_READ) {
98
buf[0] = 0x01; /* AV/C, STATUS */
99
response_ok = 0x0c; /* STABLE */
100
} else {
101
buf[0] = 0x00; /* AV/C, CONTROL */
102
response_ok = 0x09; /* ACCEPTED */
103
}
104
buf[1] = 0x08; /* audio unit 0 */
105
buf[2] = 0xb8; /* FUNCTION BLOCK */
106
buf[3] = 0x81; /* function block type: feature */
107
buf[4] = fb_id; /* function block ID */
108
buf[5] = attribute; /* control attribute */
109
buf[6] = 0x02; /* selector length */
110
buf[7] = channel; /* audio channel number */
111
buf[8] = 0x02; /* control selector: volume */
112
buf[9] = 0x02; /* control data length */
113
if (action == CTL_READ) {
114
buf[10] = 0xff;
115
buf[11] = 0xff;
116
} else {
117
buf[10] = *value >> 8;
118
buf[11] = *value;
119
}
120
121
err = fcp_avc_transaction(unit, buf, 12, buf, 12, 0x3fe);
122
if (err < 0)
123
goto error;
124
if (err < 12) {
125
dev_err(&unit->device, "short FCP response\n");
126
err = -EIO;
127
goto error;
128
}
129
if (buf[0] != response_ok) {
130
dev_err(&unit->device, "volume command failed\n");
131
err = -EIO;
132
goto error;
133
}
134
if (action == CTL_READ)
135
*value = (buf[10] << 8) | buf[11];
136
137
err = 0;
138
139
error:
140
kfree(buf);
141
142
return err;
143
}
144
145
static int spkr_mute_get(struct snd_kcontrol *control,
146
struct snd_ctl_elem_value *value)
147
{
148
struct snd_oxfw *oxfw = control->private_data;
149
struct fw_spkr *spkr = oxfw->spec;
150
151
value->value.integer.value[0] = !spkr->mute;
152
153
return 0;
154
}
155
156
static int spkr_mute_put(struct snd_kcontrol *control,
157
struct snd_ctl_elem_value *value)
158
{
159
struct snd_oxfw *oxfw = control->private_data;
160
struct fw_spkr *spkr = oxfw->spec;
161
bool mute;
162
int err;
163
164
mute = !value->value.integer.value[0];
165
166
if (mute == spkr->mute)
167
return 0;
168
169
err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &mute,
170
CTL_WRITE);
171
if (err < 0)
172
return err;
173
spkr->mute = mute;
174
175
return 1;
176
}
177
178
static int spkr_volume_info(struct snd_kcontrol *control,
179
struct snd_ctl_elem_info *info)
180
{
181
struct snd_oxfw *oxfw = control->private_data;
182
struct fw_spkr *spkr = oxfw->spec;
183
184
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
185
info->count = spkr->mixer_channels;
186
info->value.integer.min = spkr->volume_min;
187
info->value.integer.max = spkr->volume_max;
188
189
return 0;
190
}
191
192
static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
193
194
static int spkr_volume_get(struct snd_kcontrol *control,
195
struct snd_ctl_elem_value *value)
196
{
197
struct snd_oxfw *oxfw = control->private_data;
198
struct fw_spkr *spkr = oxfw->spec;
199
unsigned int i;
200
201
for (i = 0; i < spkr->mixer_channels; ++i)
202
value->value.integer.value[channel_map[i]] = spkr->volume[i];
203
204
return 0;
205
}
206
207
static int spkr_volume_put(struct snd_kcontrol *control,
208
struct snd_ctl_elem_value *value)
209
{
210
struct snd_oxfw *oxfw = control->private_data;
211
struct fw_spkr *spkr = oxfw->spec;
212
unsigned int i, changed_channels;
213
bool equal_values = true;
214
s16 volume;
215
int err;
216
217
for (i = 0; i < spkr->mixer_channels; ++i) {
218
if (value->value.integer.value[i] < spkr->volume_min ||
219
value->value.integer.value[i] > spkr->volume_max)
220
return -EINVAL;
221
if (value->value.integer.value[i] !=
222
value->value.integer.value[0])
223
equal_values = false;
224
}
225
226
changed_channels = 0;
227
for (i = 0; i < spkr->mixer_channels; ++i)
228
if (value->value.integer.value[channel_map[i]] !=
229
spkr->volume[i])
230
changed_channels |= 1 << (i + 1);
231
232
if (equal_values && changed_channels != 0)
233
changed_channels = 1 << 0;
234
235
for (i = 0; i <= spkr->mixer_channels; ++i) {
236
volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
237
if (changed_channels & (1 << i)) {
238
err = avc_audio_feature_volume(oxfw->unit,
239
spkr->volume_fb_id, &volume,
240
i, CTL_CURRENT, CTL_WRITE);
241
if (err < 0)
242
return err;
243
}
244
if (i > 0)
245
spkr->volume[i - 1] = volume;
246
}
247
248
return changed_channels != 0;
249
}
250
251
int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie)
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 = spkr_mute_get,
259
.put = spkr_mute_put,
260
},
261
{
262
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
263
.name = "PCM Playback Volume",
264
.info = spkr_volume_info,
265
.get = spkr_volume_get,
266
.put = spkr_volume_put,
267
},
268
};
269
struct fw_spkr *spkr;
270
unsigned int i, first_ch;
271
int err;
272
273
spkr = devm_kzalloc(&oxfw->card->card_dev, sizeof(struct fw_spkr),
274
GFP_KERNEL);
275
if (!spkr)
276
return -ENOMEM;
277
oxfw->spec = spkr;
278
279
if (is_lacie) {
280
spkr->mixer_channels = 1;
281
spkr->mute_fb_id = 0x01;
282
spkr->volume_fb_id = 0x01;
283
} else {
284
spkr->mixer_channels = 6;
285
spkr->mute_fb_id = 0x01;
286
spkr->volume_fb_id = 0x02;
287
}
288
289
err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
290
&spkr->volume_min, 0, CTL_MIN, CTL_READ);
291
if (err < 0)
292
return err;
293
err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
294
&spkr->volume_max, 0, CTL_MAX, CTL_READ);
295
if (err < 0)
296
return err;
297
298
err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &spkr->mute,
299
CTL_READ);
300
if (err < 0)
301
return err;
302
303
first_ch = spkr->mixer_channels == 1 ? 0 : 1;
304
for (i = 0; i < spkr->mixer_channels; ++i) {
305
err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
306
&spkr->volume[i], first_ch + i,
307
CTL_CURRENT, CTL_READ);
308
if (err < 0)
309
return err;
310
}
311
312
for (i = 0; i < ARRAY_SIZE(controls); ++i) {
313
err = snd_ctl_add(oxfw->card,
314
snd_ctl_new1(&controls[i], oxfw));
315
if (err < 0)
316
return err;
317
}
318
319
return 0;
320
}
321
322