xref: /openbmc/linux/sound/firewire/oxfw/oxfw-spkr.c (revision da607e19)
1da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f3a0e32aSTakashi Sakamoto /*
3f3a0e32aSTakashi Sakamoto  * oxfw-spkr.c - a part of driver for OXFW970/971 based devices
4f3a0e32aSTakashi Sakamoto  *
5f3a0e32aSTakashi Sakamoto  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
6f3a0e32aSTakashi Sakamoto  */
7f3a0e32aSTakashi Sakamoto 
8f3a0e32aSTakashi Sakamoto #include "oxfw.h"
9f3a0e32aSTakashi Sakamoto 
1040540de5STakashi Sakamoto struct fw_spkr {
1140540de5STakashi Sakamoto 	bool mute;
1240540de5STakashi Sakamoto 	s16 volume[6];
1340540de5STakashi Sakamoto 	s16 volume_min;
1440540de5STakashi Sakamoto 	s16 volume_max;
153e2f4570STakashi Sakamoto 
163e2f4570STakashi Sakamoto 	unsigned int mixer_channels;
173e2f4570STakashi Sakamoto 	u8 mute_fb_id;
183e2f4570STakashi Sakamoto 	u8 volume_fb_id;
1940540de5STakashi Sakamoto };
2040540de5STakashi Sakamoto 
21f3a0e32aSTakashi Sakamoto enum control_action { CTL_READ, CTL_WRITE };
22f3a0e32aSTakashi Sakamoto enum control_attribute {
23f3a0e32aSTakashi Sakamoto 	CTL_MIN		= 0x02,
24f3a0e32aSTakashi Sakamoto 	CTL_MAX		= 0x03,
25f3a0e32aSTakashi Sakamoto 	CTL_CURRENT	= 0x10,
26f3a0e32aSTakashi Sakamoto };
27f3a0e32aSTakashi Sakamoto 
avc_audio_feature_mute(struct fw_unit * unit,u8 fb_id,bool * value,enum control_action action)28eab8e4e4STakashi Sakamoto static int avc_audio_feature_mute(struct fw_unit *unit, u8 fb_id, bool *value,
29f3a0e32aSTakashi Sakamoto 				  enum control_action action)
30f3a0e32aSTakashi Sakamoto {
31f3a0e32aSTakashi Sakamoto 	u8 *buf;
32f3a0e32aSTakashi Sakamoto 	u8 response_ok;
33f3a0e32aSTakashi Sakamoto 	int err;
34f3a0e32aSTakashi Sakamoto 
35f3a0e32aSTakashi Sakamoto 	buf = kmalloc(11, GFP_KERNEL);
36f3a0e32aSTakashi Sakamoto 	if (!buf)
37f3a0e32aSTakashi Sakamoto 		return -ENOMEM;
38f3a0e32aSTakashi Sakamoto 
39f3a0e32aSTakashi Sakamoto 	if (action == CTL_READ) {
40f3a0e32aSTakashi Sakamoto 		buf[0] = 0x01;		/* AV/C, STATUS */
41f3a0e32aSTakashi Sakamoto 		response_ok = 0x0c;	/*       STABLE */
42f3a0e32aSTakashi Sakamoto 	} else {
43f3a0e32aSTakashi Sakamoto 		buf[0] = 0x00;		/* AV/C, CONTROL */
44f3a0e32aSTakashi Sakamoto 		response_ok = 0x09;	/*       ACCEPTED */
45f3a0e32aSTakashi Sakamoto 	}
46f3a0e32aSTakashi Sakamoto 	buf[1] = 0x08;			/* audio unit 0 */
47f3a0e32aSTakashi Sakamoto 	buf[2] = 0xb8;			/* FUNCTION BLOCK */
48f3a0e32aSTakashi Sakamoto 	buf[3] = 0x81;			/* function block type: feature */
49eab8e4e4STakashi Sakamoto 	buf[4] = fb_id;			/* function block ID */
50f3a0e32aSTakashi Sakamoto 	buf[5] = 0x10;			/* control attribute: current */
51f3a0e32aSTakashi Sakamoto 	buf[6] = 0x02;			/* selector length */
52f3a0e32aSTakashi Sakamoto 	buf[7] = 0x00;			/* audio channel number */
53f3a0e32aSTakashi Sakamoto 	buf[8] = 0x01;			/* control selector: mute */
54f3a0e32aSTakashi Sakamoto 	buf[9] = 0x01;			/* control data length */
55f3a0e32aSTakashi Sakamoto 	if (action == CTL_READ)
56f3a0e32aSTakashi Sakamoto 		buf[10] = 0xff;
57f3a0e32aSTakashi Sakamoto 	else
58f3a0e32aSTakashi Sakamoto 		buf[10] = *value ? 0x70 : 0x60;
59f3a0e32aSTakashi Sakamoto 
60eab8e4e4STakashi Sakamoto 	err = fcp_avc_transaction(unit, buf, 11, buf, 11, 0x3fe);
61f3a0e32aSTakashi Sakamoto 	if (err < 0)
62f3a0e32aSTakashi Sakamoto 		goto error;
63f3a0e32aSTakashi Sakamoto 	if (err < 11) {
64eab8e4e4STakashi Sakamoto 		dev_err(&unit->device, "short FCP response\n");
65f3a0e32aSTakashi Sakamoto 		err = -EIO;
66f3a0e32aSTakashi Sakamoto 		goto error;
67f3a0e32aSTakashi Sakamoto 	}
68f3a0e32aSTakashi Sakamoto 	if (buf[0] != response_ok) {
69eab8e4e4STakashi Sakamoto 		dev_err(&unit->device, "mute command failed\n");
70f3a0e32aSTakashi Sakamoto 		err = -EIO;
71f3a0e32aSTakashi Sakamoto 		goto error;
72f3a0e32aSTakashi Sakamoto 	}
73f3a0e32aSTakashi Sakamoto 	if (action == CTL_READ)
74f3a0e32aSTakashi Sakamoto 		*value = buf[10] == 0x70;
75f3a0e32aSTakashi Sakamoto 
76f3a0e32aSTakashi Sakamoto 	err = 0;
77f3a0e32aSTakashi Sakamoto 
78f3a0e32aSTakashi Sakamoto error:
79f3a0e32aSTakashi Sakamoto 	kfree(buf);
80f3a0e32aSTakashi Sakamoto 
81f3a0e32aSTakashi Sakamoto 	return err;
82f3a0e32aSTakashi Sakamoto }
83f3a0e32aSTakashi Sakamoto 
avc_audio_feature_volume(struct fw_unit * unit,u8 fb_id,s16 * value,unsigned int channel,enum control_attribute attribute,enum control_action action)84eab8e4e4STakashi Sakamoto static int avc_audio_feature_volume(struct fw_unit *unit, u8 fb_id, s16 *value,
85f3a0e32aSTakashi Sakamoto 				    unsigned int channel,
86f3a0e32aSTakashi Sakamoto 				    enum control_attribute attribute,
87f3a0e32aSTakashi Sakamoto 				    enum control_action action)
88f3a0e32aSTakashi Sakamoto {
89f3a0e32aSTakashi Sakamoto 	u8 *buf;
90f3a0e32aSTakashi Sakamoto 	u8 response_ok;
91f3a0e32aSTakashi Sakamoto 	int err;
92f3a0e32aSTakashi Sakamoto 
93f3a0e32aSTakashi Sakamoto 	buf = kmalloc(12, GFP_KERNEL);
94f3a0e32aSTakashi Sakamoto 	if (!buf)
95f3a0e32aSTakashi Sakamoto 		return -ENOMEM;
96f3a0e32aSTakashi Sakamoto 
97f3a0e32aSTakashi Sakamoto 	if (action == CTL_READ) {
98f3a0e32aSTakashi Sakamoto 		buf[0] = 0x01;		/* AV/C, STATUS */
99f3a0e32aSTakashi Sakamoto 		response_ok = 0x0c;	/*       STABLE */
100f3a0e32aSTakashi Sakamoto 	} else {
101f3a0e32aSTakashi Sakamoto 		buf[0] = 0x00;		/* AV/C, CONTROL */
102f3a0e32aSTakashi Sakamoto 		response_ok = 0x09;	/*       ACCEPTED */
103f3a0e32aSTakashi Sakamoto 	}
104f3a0e32aSTakashi Sakamoto 	buf[1] = 0x08;			/* audio unit 0 */
105f3a0e32aSTakashi Sakamoto 	buf[2] = 0xb8;			/* FUNCTION BLOCK */
106f3a0e32aSTakashi Sakamoto 	buf[3] = 0x81;			/* function block type: feature */
107eab8e4e4STakashi Sakamoto 	buf[4] = fb_id;			/* function block ID */
108f3a0e32aSTakashi Sakamoto 	buf[5] = attribute;		/* control attribute */
109f3a0e32aSTakashi Sakamoto 	buf[6] = 0x02;			/* selector length */
110f3a0e32aSTakashi Sakamoto 	buf[7] = channel;		/* audio channel number */
111f3a0e32aSTakashi Sakamoto 	buf[8] = 0x02;			/* control selector: volume */
112f3a0e32aSTakashi Sakamoto 	buf[9] = 0x02;			/* control data length */
113f3a0e32aSTakashi Sakamoto 	if (action == CTL_READ) {
114f3a0e32aSTakashi Sakamoto 		buf[10] = 0xff;
115f3a0e32aSTakashi Sakamoto 		buf[11] = 0xff;
116f3a0e32aSTakashi Sakamoto 	} else {
117f3a0e32aSTakashi Sakamoto 		buf[10] = *value >> 8;
118f3a0e32aSTakashi Sakamoto 		buf[11] = *value;
119f3a0e32aSTakashi Sakamoto 	}
120f3a0e32aSTakashi Sakamoto 
121eab8e4e4STakashi Sakamoto 	err = fcp_avc_transaction(unit, buf, 12, buf, 12, 0x3fe);
122f3a0e32aSTakashi Sakamoto 	if (err < 0)
123f3a0e32aSTakashi Sakamoto 		goto error;
124f3a0e32aSTakashi Sakamoto 	if (err < 12) {
125eab8e4e4STakashi Sakamoto 		dev_err(&unit->device, "short FCP response\n");
126f3a0e32aSTakashi Sakamoto 		err = -EIO;
127f3a0e32aSTakashi Sakamoto 		goto error;
128f3a0e32aSTakashi Sakamoto 	}
129f3a0e32aSTakashi Sakamoto 	if (buf[0] != response_ok) {
130eab8e4e4STakashi Sakamoto 		dev_err(&unit->device, "volume command failed\n");
131f3a0e32aSTakashi Sakamoto 		err = -EIO;
132f3a0e32aSTakashi Sakamoto 		goto error;
133f3a0e32aSTakashi Sakamoto 	}
134f3a0e32aSTakashi Sakamoto 	if (action == CTL_READ)
135f3a0e32aSTakashi Sakamoto 		*value = (buf[10] << 8) | buf[11];
136f3a0e32aSTakashi Sakamoto 
137f3a0e32aSTakashi Sakamoto 	err = 0;
138f3a0e32aSTakashi Sakamoto 
139f3a0e32aSTakashi Sakamoto error:
140f3a0e32aSTakashi Sakamoto 	kfree(buf);
141f3a0e32aSTakashi Sakamoto 
142f3a0e32aSTakashi Sakamoto 	return err;
143f3a0e32aSTakashi Sakamoto }
144f3a0e32aSTakashi Sakamoto 
spkr_mute_get(struct snd_kcontrol * control,struct snd_ctl_elem_value * value)14529aa09acSTakashi Sakamoto static int spkr_mute_get(struct snd_kcontrol *control,
146f3a0e32aSTakashi Sakamoto 			 struct snd_ctl_elem_value *value)
147f3a0e32aSTakashi Sakamoto {
148f3a0e32aSTakashi Sakamoto 	struct snd_oxfw *oxfw = control->private_data;
14940540de5STakashi Sakamoto 	struct fw_spkr *spkr = oxfw->spec;
150f3a0e32aSTakashi Sakamoto 
15140540de5STakashi Sakamoto 	value->value.integer.value[0] = !spkr->mute;
152f3a0e32aSTakashi Sakamoto 
153f3a0e32aSTakashi Sakamoto 	return 0;
154f3a0e32aSTakashi Sakamoto }
155f3a0e32aSTakashi Sakamoto 
spkr_mute_put(struct snd_kcontrol * control,struct snd_ctl_elem_value * value)15629aa09acSTakashi Sakamoto static int spkr_mute_put(struct snd_kcontrol *control,
157f3a0e32aSTakashi Sakamoto 			 struct snd_ctl_elem_value *value)
158f3a0e32aSTakashi Sakamoto {
159f3a0e32aSTakashi Sakamoto 	struct snd_oxfw *oxfw = control->private_data;
16040540de5STakashi Sakamoto 	struct fw_spkr *spkr = oxfw->spec;
161f3a0e32aSTakashi Sakamoto 	bool mute;
162f3a0e32aSTakashi Sakamoto 	int err;
163f3a0e32aSTakashi Sakamoto 
164f3a0e32aSTakashi Sakamoto 	mute = !value->value.integer.value[0];
165f3a0e32aSTakashi Sakamoto 
16640540de5STakashi Sakamoto 	if (mute == spkr->mute)
167f3a0e32aSTakashi Sakamoto 		return 0;
168f3a0e32aSTakashi Sakamoto 
1693e2f4570STakashi Sakamoto 	err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &mute,
1703e2f4570STakashi Sakamoto 				     CTL_WRITE);
171f3a0e32aSTakashi Sakamoto 	if (err < 0)
172f3a0e32aSTakashi Sakamoto 		return err;
17340540de5STakashi Sakamoto 	spkr->mute = mute;
174f3a0e32aSTakashi Sakamoto 
175f3a0e32aSTakashi Sakamoto 	return 1;
176f3a0e32aSTakashi Sakamoto }
177f3a0e32aSTakashi Sakamoto 
spkr_volume_info(struct snd_kcontrol * control,struct snd_ctl_elem_info * info)17829aa09acSTakashi Sakamoto static int spkr_volume_info(struct snd_kcontrol *control,
179f3a0e32aSTakashi Sakamoto 			    struct snd_ctl_elem_info *info)
180f3a0e32aSTakashi Sakamoto {
181f3a0e32aSTakashi Sakamoto 	struct snd_oxfw *oxfw = control->private_data;
18240540de5STakashi Sakamoto 	struct fw_spkr *spkr = oxfw->spec;
183f3a0e32aSTakashi Sakamoto 
184f3a0e32aSTakashi Sakamoto 	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
1853e2f4570STakashi Sakamoto 	info->count = spkr->mixer_channels;
18640540de5STakashi Sakamoto 	info->value.integer.min = spkr->volume_min;
18740540de5STakashi Sakamoto 	info->value.integer.max = spkr->volume_max;
188f3a0e32aSTakashi Sakamoto 
189f3a0e32aSTakashi Sakamoto 	return 0;
190f3a0e32aSTakashi Sakamoto }
191f3a0e32aSTakashi Sakamoto 
192f3a0e32aSTakashi Sakamoto static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 };
193f3a0e32aSTakashi Sakamoto 
spkr_volume_get(struct snd_kcontrol * control,struct snd_ctl_elem_value * value)19429aa09acSTakashi Sakamoto static int spkr_volume_get(struct snd_kcontrol *control,
195f3a0e32aSTakashi Sakamoto 			   struct snd_ctl_elem_value *value)
196f3a0e32aSTakashi Sakamoto {
197f3a0e32aSTakashi Sakamoto 	struct snd_oxfw *oxfw = control->private_data;
19840540de5STakashi Sakamoto 	struct fw_spkr *spkr = oxfw->spec;
199f3a0e32aSTakashi Sakamoto 	unsigned int i;
200f3a0e32aSTakashi Sakamoto 
2013e2f4570STakashi Sakamoto 	for (i = 0; i < spkr->mixer_channels; ++i)
20240540de5STakashi Sakamoto 		value->value.integer.value[channel_map[i]] = spkr->volume[i];
203f3a0e32aSTakashi Sakamoto 
204f3a0e32aSTakashi Sakamoto 	return 0;
205f3a0e32aSTakashi Sakamoto }
206f3a0e32aSTakashi Sakamoto 
spkr_volume_put(struct snd_kcontrol * control,struct snd_ctl_elem_value * value)20729aa09acSTakashi Sakamoto static int spkr_volume_put(struct snd_kcontrol *control,
208f3a0e32aSTakashi Sakamoto 			   struct snd_ctl_elem_value *value)
209f3a0e32aSTakashi Sakamoto {
210f3a0e32aSTakashi Sakamoto 	struct snd_oxfw *oxfw = control->private_data;
21140540de5STakashi Sakamoto 	struct fw_spkr *spkr = oxfw->spec;
212f3a0e32aSTakashi Sakamoto 	unsigned int i, changed_channels;
213f3a0e32aSTakashi Sakamoto 	bool equal_values = true;
214f3a0e32aSTakashi Sakamoto 	s16 volume;
215f3a0e32aSTakashi Sakamoto 	int err;
216f3a0e32aSTakashi Sakamoto 
2173e2f4570STakashi Sakamoto 	for (i = 0; i < spkr->mixer_channels; ++i) {
21840540de5STakashi Sakamoto 		if (value->value.integer.value[i] < spkr->volume_min ||
21940540de5STakashi Sakamoto 		    value->value.integer.value[i] > spkr->volume_max)
220f3a0e32aSTakashi Sakamoto 			return -EINVAL;
221f3a0e32aSTakashi Sakamoto 		if (value->value.integer.value[i] !=
222f3a0e32aSTakashi Sakamoto 		    value->value.integer.value[0])
223f3a0e32aSTakashi Sakamoto 			equal_values = false;
224f3a0e32aSTakashi Sakamoto 	}
225f3a0e32aSTakashi Sakamoto 
226f3a0e32aSTakashi Sakamoto 	changed_channels = 0;
2273e2f4570STakashi Sakamoto 	for (i = 0; i < spkr->mixer_channels; ++i)
228f3a0e32aSTakashi Sakamoto 		if (value->value.integer.value[channel_map[i]] !=
22940540de5STakashi Sakamoto 							spkr->volume[i])
230f3a0e32aSTakashi Sakamoto 			changed_channels |= 1 << (i + 1);
231f3a0e32aSTakashi Sakamoto 
232f3a0e32aSTakashi Sakamoto 	if (equal_values && changed_channels != 0)
233f3a0e32aSTakashi Sakamoto 		changed_channels = 1 << 0;
234f3a0e32aSTakashi Sakamoto 
2353e2f4570STakashi Sakamoto 	for (i = 0; i <= spkr->mixer_channels; ++i) {
236f3a0e32aSTakashi Sakamoto 		volume = value->value.integer.value[channel_map[i ? i - 1 : 0]];
237f3a0e32aSTakashi Sakamoto 		if (changed_channels & (1 << i)) {
238eab8e4e4STakashi Sakamoto 			err = avc_audio_feature_volume(oxfw->unit,
2393e2f4570STakashi Sakamoto 						  spkr->volume_fb_id, &volume,
240eab8e4e4STakashi Sakamoto 						  i, CTL_CURRENT, CTL_WRITE);
241f3a0e32aSTakashi Sakamoto 			if (err < 0)
242f3a0e32aSTakashi Sakamoto 				return err;
243f3a0e32aSTakashi Sakamoto 		}
244f3a0e32aSTakashi Sakamoto 		if (i > 0)
24540540de5STakashi Sakamoto 			spkr->volume[i - 1] = volume;
246f3a0e32aSTakashi Sakamoto 	}
247f3a0e32aSTakashi Sakamoto 
248f3a0e32aSTakashi Sakamoto 	return changed_channels != 0;
249f3a0e32aSTakashi Sakamoto }
250f3a0e32aSTakashi Sakamoto 
snd_oxfw_add_spkr(struct snd_oxfw * oxfw,bool is_lacie)2513e2f4570STakashi Sakamoto int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie)
252f3a0e32aSTakashi Sakamoto {
253f3a0e32aSTakashi Sakamoto 	static const struct snd_kcontrol_new controls[] = {
254f3a0e32aSTakashi Sakamoto 		{
255f3a0e32aSTakashi Sakamoto 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
256f3a0e32aSTakashi Sakamoto 			.name = "PCM Playback Switch",
257f3a0e32aSTakashi Sakamoto 			.info = snd_ctl_boolean_mono_info,
25829aa09acSTakashi Sakamoto 			.get = spkr_mute_get,
25929aa09acSTakashi Sakamoto 			.put = spkr_mute_put,
260f3a0e32aSTakashi Sakamoto 		},
261f3a0e32aSTakashi Sakamoto 		{
262f3a0e32aSTakashi Sakamoto 			.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
263f3a0e32aSTakashi Sakamoto 			.name = "PCM Playback Volume",
26429aa09acSTakashi Sakamoto 			.info = spkr_volume_info,
26529aa09acSTakashi Sakamoto 			.get = spkr_volume_get,
26629aa09acSTakashi Sakamoto 			.put = spkr_volume_put,
267f3a0e32aSTakashi Sakamoto 		},
268f3a0e32aSTakashi Sakamoto 	};
26940540de5STakashi Sakamoto 	struct fw_spkr *spkr;
270f3a0e32aSTakashi Sakamoto 	unsigned int i, first_ch;
271f3a0e32aSTakashi Sakamoto 	int err;
272f3a0e32aSTakashi Sakamoto 
27372bc8c43STakashi Sakamoto 	spkr = devm_kzalloc(&oxfw->card->card_dev, sizeof(struct fw_spkr),
27472bc8c43STakashi Sakamoto 			    GFP_KERNEL);
27572bc8c43STakashi Sakamoto 	if (!spkr)
27640540de5STakashi Sakamoto 		return -ENOMEM;
27740540de5STakashi Sakamoto 	oxfw->spec = spkr;
27840540de5STakashi Sakamoto 
2793e2f4570STakashi Sakamoto 	if (is_lacie) {
2803e2f4570STakashi Sakamoto 		spkr->mixer_channels = 1;
2813e2f4570STakashi Sakamoto 		spkr->mute_fb_id = 0x01;
2823e2f4570STakashi Sakamoto 		spkr->volume_fb_id = 0x01;
2833e2f4570STakashi Sakamoto 	} else {
2843e2f4570STakashi Sakamoto 		spkr->mixer_channels = 6;
2853e2f4570STakashi Sakamoto 		spkr->mute_fb_id = 0x01;
2863e2f4570STakashi Sakamoto 		spkr->volume_fb_id = 0x02;
2873e2f4570STakashi Sakamoto 	}
2883e2f4570STakashi Sakamoto 
2893e2f4570STakashi Sakamoto 	err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
2903e2f4570STakashi Sakamoto 				       &spkr->volume_min, 0, CTL_MIN, CTL_READ);
291f3a0e32aSTakashi Sakamoto 	if (err < 0)
292f3a0e32aSTakashi Sakamoto 		return err;
2933e2f4570STakashi Sakamoto 	err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
2943e2f4570STakashi Sakamoto 				       &spkr->volume_max, 0, CTL_MAX, CTL_READ);
295f3a0e32aSTakashi Sakamoto 	if (err < 0)
296f3a0e32aSTakashi Sakamoto 		return err;
297f3a0e32aSTakashi Sakamoto 
2983e2f4570STakashi Sakamoto 	err = avc_audio_feature_mute(oxfw->unit, spkr->mute_fb_id, &spkr->mute,
2993e2f4570STakashi Sakamoto 				     CTL_READ);
300f3a0e32aSTakashi Sakamoto 	if (err < 0)
301f3a0e32aSTakashi Sakamoto 		return err;
302f3a0e32aSTakashi Sakamoto 
3033e2f4570STakashi Sakamoto 	first_ch = spkr->mixer_channels == 1 ? 0 : 1;
3043e2f4570STakashi Sakamoto 	for (i = 0; i < spkr->mixer_channels; ++i) {
3053e2f4570STakashi Sakamoto 		err = avc_audio_feature_volume(oxfw->unit, spkr->volume_fb_id,
3063e2f4570STakashi Sakamoto 					       &spkr->volume[i], first_ch + i,
3073e2f4570STakashi Sakamoto 					       CTL_CURRENT, CTL_READ);
308f3a0e32aSTakashi Sakamoto 		if (err < 0)
309f3a0e32aSTakashi Sakamoto 			return err;
310f3a0e32aSTakashi Sakamoto 	}
311f3a0e32aSTakashi Sakamoto 
312f3a0e32aSTakashi Sakamoto 	for (i = 0; i < ARRAY_SIZE(controls); ++i) {
313f3a0e32aSTakashi Sakamoto 		err = snd_ctl_add(oxfw->card,
314f3a0e32aSTakashi Sakamoto 				  snd_ctl_new1(&controls[i], oxfw));
315f3a0e32aSTakashi Sakamoto 		if (err < 0)
316f3a0e32aSTakashi Sakamoto 			return err;
317f3a0e32aSTakashi Sakamoto 	}
318f3a0e32aSTakashi Sakamoto 
319f3a0e32aSTakashi Sakamoto 	return 0;
320f3a0e32aSTakashi Sakamoto }
321