2 * oxfw-spkr.c - a part of driver for OXFW970/971 based devices
4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
5 * Licensed under the terms of the GNU General Public License, version 2.
17 enum control_action
{ CTL_READ
, CTL_WRITE
};
18 enum control_attribute
{
24 static int avc_audio_feature_mute(struct fw_unit
*unit
, u8 fb_id
, bool *value
,
25 enum control_action action
)
31 buf
= kmalloc(11, GFP_KERNEL
);
35 if (action
== CTL_READ
) {
36 buf
[0] = 0x01; /* AV/C, STATUS */
37 response_ok
= 0x0c; /* STABLE */
39 buf
[0] = 0x00; /* AV/C, CONTROL */
40 response_ok
= 0x09; /* ACCEPTED */
42 buf
[1] = 0x08; /* audio unit 0 */
43 buf
[2] = 0xb8; /* FUNCTION BLOCK */
44 buf
[3] = 0x81; /* function block type: feature */
45 buf
[4] = fb_id
; /* function block ID */
46 buf
[5] = 0x10; /* control attribute: current */
47 buf
[6] = 0x02; /* selector length */
48 buf
[7] = 0x00; /* audio channel number */
49 buf
[8] = 0x01; /* control selector: mute */
50 buf
[9] = 0x01; /* control data length */
51 if (action
== CTL_READ
)
54 buf
[10] = *value
? 0x70 : 0x60;
56 err
= fcp_avc_transaction(unit
, buf
, 11, buf
, 11, 0x3fe);
60 dev_err(&unit
->device
, "short FCP response\n");
64 if (buf
[0] != response_ok
) {
65 dev_err(&unit
->device
, "mute command failed\n");
69 if (action
== CTL_READ
)
70 *value
= buf
[10] == 0x70;
80 static int avc_audio_feature_volume(struct fw_unit
*unit
, u8 fb_id
, s16
*value
,
82 enum control_attribute attribute
,
83 enum control_action action
)
89 buf
= kmalloc(12, GFP_KERNEL
);
93 if (action
== CTL_READ
) {
94 buf
[0] = 0x01; /* AV/C, STATUS */
95 response_ok
= 0x0c; /* STABLE */
97 buf
[0] = 0x00; /* AV/C, CONTROL */
98 response_ok
= 0x09; /* ACCEPTED */
100 buf
[1] = 0x08; /* audio unit 0 */
101 buf
[2] = 0xb8; /* FUNCTION BLOCK */
102 buf
[3] = 0x81; /* function block type: feature */
103 buf
[4] = fb_id
; /* function block ID */
104 buf
[5] = attribute
; /* control attribute */
105 buf
[6] = 0x02; /* selector length */
106 buf
[7] = channel
; /* audio channel number */
107 buf
[8] = 0x02; /* control selector: volume */
108 buf
[9] = 0x02; /* control data length */
109 if (action
== CTL_READ
) {
113 buf
[10] = *value
>> 8;
117 err
= fcp_avc_transaction(unit
, buf
, 12, buf
, 12, 0x3fe);
121 dev_err(&unit
->device
, "short FCP response\n");
125 if (buf
[0] != response_ok
) {
126 dev_err(&unit
->device
, "volume command failed\n");
130 if (action
== CTL_READ
)
131 *value
= (buf
[10] << 8) | buf
[11];
141 static int spkr_mute_get(struct snd_kcontrol
*control
,
142 struct snd_ctl_elem_value
*value
)
144 struct snd_oxfw
*oxfw
= control
->private_data
;
145 struct fw_spkr
*spkr
= oxfw
->spec
;
147 value
->value
.integer
.value
[0] = !spkr
->mute
;
152 static int spkr_mute_put(struct snd_kcontrol
*control
,
153 struct snd_ctl_elem_value
*value
)
155 struct snd_oxfw
*oxfw
= control
->private_data
;
156 struct fw_spkr
*spkr
= oxfw
->spec
;
160 mute
= !value
->value
.integer
.value
[0];
162 if (mute
== spkr
->mute
)
165 err
= avc_audio_feature_mute(oxfw
->unit
, oxfw
->device_info
->mute_fb_id
,
174 static int spkr_volume_info(struct snd_kcontrol
*control
,
175 struct snd_ctl_elem_info
*info
)
177 struct snd_oxfw
*oxfw
= control
->private_data
;
178 struct fw_spkr
*spkr
= oxfw
->spec
;
180 info
->type
= SNDRV_CTL_ELEM_TYPE_INTEGER
;
181 info
->count
= oxfw
->device_info
->mixer_channels
;
182 info
->value
.integer
.min
= spkr
->volume_min
;
183 info
->value
.integer
.max
= spkr
->volume_max
;
188 static const u8 channel_map
[6] = { 0, 1, 4, 5, 2, 3 };
190 static int spkr_volume_get(struct snd_kcontrol
*control
,
191 struct snd_ctl_elem_value
*value
)
193 struct snd_oxfw
*oxfw
= control
->private_data
;
194 struct fw_spkr
*spkr
= oxfw
->spec
;
197 for (i
= 0; i
< oxfw
->device_info
->mixer_channels
; ++i
)
198 value
->value
.integer
.value
[channel_map
[i
]] = spkr
->volume
[i
];
203 static int spkr_volume_put(struct snd_kcontrol
*control
,
204 struct snd_ctl_elem_value
*value
)
206 struct snd_oxfw
*oxfw
= control
->private_data
;
207 struct fw_spkr
*spkr
= oxfw
->spec
;
208 unsigned int i
, changed_channels
;
209 bool equal_values
= true;
213 for (i
= 0; i
< oxfw
->device_info
->mixer_channels
; ++i
) {
214 if (value
->value
.integer
.value
[i
] < spkr
->volume_min
||
215 value
->value
.integer
.value
[i
] > spkr
->volume_max
)
217 if (value
->value
.integer
.value
[i
] !=
218 value
->value
.integer
.value
[0])
219 equal_values
= false;
222 changed_channels
= 0;
223 for (i
= 0; i
< oxfw
->device_info
->mixer_channels
; ++i
)
224 if (value
->value
.integer
.value
[channel_map
[i
]] !=
226 changed_channels
|= 1 << (i
+ 1);
228 if (equal_values
&& changed_channels
!= 0)
229 changed_channels
= 1 << 0;
231 for (i
= 0; i
<= oxfw
->device_info
->mixer_channels
; ++i
) {
232 volume
= value
->value
.integer
.value
[channel_map
[i
? i
- 1 : 0]];
233 if (changed_channels
& (1 << i
)) {
234 err
= avc_audio_feature_volume(oxfw
->unit
,
235 oxfw
->device_info
->mute_fb_id
,
237 i
, CTL_CURRENT
, CTL_WRITE
);
242 spkr
->volume
[i
- 1] = volume
;
245 return changed_channels
!= 0;
248 int snd_oxfw_add_spkr(struct snd_oxfw
*oxfw
)
250 static const struct snd_kcontrol_new controls
[] = {
252 .iface
= SNDRV_CTL_ELEM_IFACE_MIXER
,
253 .name
= "PCM Playback Switch",
254 .info
= snd_ctl_boolean_mono_info
,
255 .get
= spkr_mute_get
,
256 .put
= spkr_mute_put
,
259 .iface
= SNDRV_CTL_ELEM_IFACE_MIXER
,
260 .name
= "PCM Playback Volume",
261 .info
= spkr_volume_info
,
262 .get
= spkr_volume_get
,
263 .put
= spkr_volume_put
,
266 struct fw_spkr
*spkr
;
267 unsigned int i
, first_ch
;
270 spkr
= kzalloc(sizeof(struct fw_spkr
), GFP_KERNEL
);
275 err
= avc_audio_feature_volume(oxfw
->unit
,
276 oxfw
->device_info
->volume_fb_id
,
278 0, CTL_MIN
, CTL_READ
);
281 err
= avc_audio_feature_volume(oxfw
->unit
,
282 oxfw
->device_info
->volume_fb_id
,
284 0, CTL_MAX
, CTL_READ
);
288 err
= avc_audio_feature_mute(oxfw
->unit
, oxfw
->device_info
->mute_fb_id
,
289 &spkr
->mute
, CTL_READ
);
293 first_ch
= oxfw
->device_info
->mixer_channels
== 1 ? 0 : 1;
294 for (i
= 0; i
< oxfw
->device_info
->mixer_channels
; ++i
) {
295 err
= avc_audio_feature_volume(oxfw
->unit
,
296 oxfw
->device_info
->volume_fb_id
,
298 first_ch
+ i
, CTL_CURRENT
, CTL_READ
);
303 for (i
= 0; i
< ARRAY_SIZE(controls
); ++i
) {
304 err
= snd_ctl_add(oxfw
->card
,
305 snd_ctl_new1(&controls
[i
], oxfw
));