• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
oxfw_mute_command(struct snd_oxfw * oxfw,bool * value,enum control_action action)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 
oxfw_volume_command(struct snd_oxfw * oxfw,s16 * value,unsigned int channel,enum control_attribute attribute,enum control_action action)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 
oxfw_mute_get(struct snd_kcontrol * control,struct snd_ctl_elem_value * value)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 
oxfw_mute_put(struct snd_kcontrol * control,struct snd_ctl_elem_value * value)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 
oxfw_volume_info(struct snd_kcontrol * control,struct snd_ctl_elem_info * info)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 
oxfw_volume_get(struct snd_kcontrol * control,struct snd_ctl_elem_value * value)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 
oxfw_volume_put(struct snd_kcontrol * control,struct snd_ctl_elem_value * value)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 
snd_oxfw_create_mixer(struct snd_oxfw * oxfw)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 }
284