diff options
Diffstat (limited to 'sound/oss/ac97.c')
-rw-r--r-- | sound/oss/ac97.c | 432 |
1 files changed, 0 insertions, 432 deletions
diff --git a/sound/oss/ac97.c b/sound/oss/ac97.c deleted file mode 100644 index 72cf4ed77937..000000000000 --- a/sound/oss/ac97.c +++ /dev/null | |||
@@ -1,432 +0,0 @@ | |||
1 | #include <linux/module.h> | ||
2 | #include <linux/kernel.h> | ||
3 | #include <linux/init.h> | ||
4 | #include "ac97.h" | ||
5 | |||
6 | /* Flag for mono controls. */ | ||
7 | #define MO 0 | ||
8 | /* And for stereo. */ | ||
9 | #define ST 1 | ||
10 | |||
11 | /* Whether or not the bits in the channel are inverted. */ | ||
12 | #define INV 1 | ||
13 | #define NINV 0 | ||
14 | |||
15 | static struct ac97_chn_desc { | ||
16 | int ac97_regnum; | ||
17 | int oss_channel; | ||
18 | int maxval; | ||
19 | int is_stereo; | ||
20 | int oss_mask; | ||
21 | int recordNum; | ||
22 | u16 regmask; | ||
23 | int is_inverted; | ||
24 | } mixerRegs[] = { | ||
25 | { AC97_MASTER_VOL_STEREO, SOUND_MIXER_VOLUME, 0x3f, ST, SOUND_MASK_VOLUME, 5, 0x0000, INV }, | ||
26 | { AC97_MASTER_VOL_MONO, SOUND_MIXER_PHONEOUT, 0x3f, MO, SOUND_MASK_PHONEOUT, 6, 0x0000, INV }, | ||
27 | { AC97_MASTER_TONE, SOUND_MIXER_TREBLE, 0x0f, MO, SOUND_MASK_TREBLE, -1, 0x00ff, INV }, | ||
28 | { AC97_MASTER_TONE, SOUND_MIXER_BASS, 0x0f, MO, SOUND_MASK_BASS, -1, 0xff00, INV }, | ||
29 | { AC97_PCBEEP_VOL, SOUND_MIXER_SPEAKER, 0x0f, MO, SOUND_MASK_SPEAKER, -1, 0x001e, INV }, | ||
30 | { AC97_PHONE_VOL, SOUND_MIXER_PHONEIN, 0x1f, MO, SOUND_MASK_PHONEIN, 7, 0x0000, INV }, | ||
31 | { AC97_MIC_VOL, SOUND_MIXER_MIC, 0x1f, MO, SOUND_MASK_MIC, 0, 0x0000, INV }, | ||
32 | { AC97_LINEIN_VOL, SOUND_MIXER_LINE, 0x1f, ST, SOUND_MASK_LINE, 4, 0x0000, INV }, | ||
33 | { AC97_CD_VOL, SOUND_MIXER_CD, 0x1f, ST, SOUND_MASK_CD, 1, 0x0000, INV }, | ||
34 | { AC97_VIDEO_VOL, SOUND_MIXER_VIDEO, 0x1f, ST, SOUND_MASK_VIDEO, 2, 0x0000, INV }, | ||
35 | { AC97_AUX_VOL, SOUND_MIXER_LINE1, 0x1f, ST, SOUND_MASK_LINE1, 3, 0x0000, INV }, | ||
36 | { AC97_PCMOUT_VOL, SOUND_MIXER_PCM, 0x1f, ST, SOUND_MASK_PCM, -1, 0x0000, INV }, | ||
37 | { AC97_RECORD_GAIN, SOUND_MIXER_IGAIN, 0x0f, ST, SOUND_MASK_IGAIN, -1, 0x0000, NINV }, | ||
38 | { -1, -1, 0xff, 0, 0, -1, 0x0000, 0 }, | ||
39 | }; | ||
40 | |||
41 | static struct ac97_chn_desc * | ||
42 | ac97_find_chndesc (struct ac97_hwint *dev, int oss_channel) | ||
43 | { | ||
44 | int x; | ||
45 | |||
46 | for (x = 0; mixerRegs[x].oss_channel != -1; x++) { | ||
47 | if (mixerRegs[x].oss_channel == oss_channel) | ||
48 | return mixerRegs + x; | ||
49 | } | ||
50 | |||
51 | return NULL; | ||
52 | } | ||
53 | |||
54 | static inline int | ||
55 | ac97_is_valid_channel (struct ac97_hwint *dev, struct ac97_chn_desc *chn) | ||
56 | { | ||
57 | return (dev->last_written_mixer_values[chn->ac97_regnum / 2] | ||
58 | != AC97_REG_UNSUPPORTED); | ||
59 | } | ||
60 | |||
61 | int | ||
62 | ac97_init (struct ac97_hwint *dev) | ||
63 | { | ||
64 | int x; | ||
65 | int reg0; | ||
66 | |||
67 | /* Clear out the arrays of cached values. */ | ||
68 | for (x = 0; x < AC97_REG_CNT; x++) | ||
69 | dev->last_written_mixer_values[x] = AC97_REGVAL_UNKNOWN; | ||
70 | |||
71 | for (x = 0; x < SOUND_MIXER_NRDEVICES; x++) | ||
72 | dev->last_written_OSS_values[x] = AC97_REGVAL_UNKNOWN; | ||
73 | |||
74 | /* Clear the device masks. */ | ||
75 | dev->mixer_devmask = 0; | ||
76 | dev->mixer_stereomask = 0; | ||
77 | dev->mixer_recmask = 0; | ||
78 | |||
79 | /* ??? Do a "standard reset" via register 0? */ | ||
80 | |||
81 | /* Hardware-dependent reset. */ | ||
82 | if (dev->reset_device (dev)) | ||
83 | return -1; | ||
84 | |||
85 | /* Check the mixer device capabilities. */ | ||
86 | reg0 = dev->read_reg (dev, AC97_RESET); | ||
87 | |||
88 | if (reg0 < 0) | ||
89 | return -1; | ||
90 | |||
91 | /* Check for support for treble/bass controls. */ | ||
92 | if (! (reg0 & 4)) { | ||
93 | dev->last_written_mixer_values[AC97_MASTER_TONE / 2] | ||
94 | = AC97_REG_UNSUPPORTED; | ||
95 | } | ||
96 | |||
97 | /* ??? There may be other tests here? */ | ||
98 | |||
99 | /* Fill in the device masks. */ | ||
100 | for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) { | ||
101 | if (ac97_is_valid_channel (dev, mixerRegs + x)) { | ||
102 | dev->mixer_devmask |= mixerRegs[x].oss_mask; | ||
103 | |||
104 | if (mixerRegs[x].is_stereo) | ||
105 | dev->mixer_stereomask |= mixerRegs[x].oss_mask; | ||
106 | |||
107 | if (mixerRegs[x].recordNum != -1) | ||
108 | dev->mixer_recmask |= mixerRegs[x].oss_mask; | ||
109 | } | ||
110 | } | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | /* Return the contents of register REG; use the cache if the value in it | ||
116 | is valid. Returns a negative error code on failure. */ | ||
117 | static int | ||
118 | ac97_get_register (struct ac97_hwint *dev, u8 reg) | ||
119 | { | ||
120 | if (reg > 127 || (reg & 1)) | ||
121 | return -EINVAL; | ||
122 | |||
123 | /* See if it's in the cache, or if it's just plain invalid. */ | ||
124 | switch (dev->last_written_mixer_values[reg / 2]) { | ||
125 | case AC97_REG_UNSUPPORTED: | ||
126 | return -EINVAL; | ||
127 | break; | ||
128 | case AC97_REGVAL_UNKNOWN: | ||
129 | dev->last_written_mixer_values[reg / 2] = dev->read_reg (dev, reg); | ||
130 | break; | ||
131 | default: | ||
132 | break; | ||
133 | } | ||
134 | return dev->last_written_mixer_values[reg / 2]; | ||
135 | } | ||
136 | |||
137 | /* Write VALUE to AC97 register REG, and cache its value in the last-written | ||
138 | cache. Returns a negative error code on failure, or 0 on success. */ | ||
139 | int | ||
140 | ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value) | ||
141 | { | ||
142 | if (reg > 127 || (reg & 1)) | ||
143 | return -EINVAL; | ||
144 | |||
145 | if (dev->last_written_mixer_values[reg / 2] == AC97_REG_UNSUPPORTED) | ||
146 | return -EINVAL; | ||
147 | else { | ||
148 | int res = dev->write_reg (dev, reg, value); | ||
149 | if (res >= 0) { | ||
150 | dev->last_written_mixer_values[reg / 2] = value; | ||
151 | return 0; | ||
152 | } | ||
153 | else | ||
154 | return res; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | /* Scale VALUE (a value fro 0 to MAXVAL) to a value from 0-100. If | ||
159 | IS_STEREO is set, VALUE is a stereo value; the left channel value | ||
160 | is in the lower 8 bits, and the right channel value is in the upper | ||
161 | 8 bits. | ||
162 | |||
163 | A negative error code is returned on failure, or the unsigned | ||
164 | scaled value on success. */ | ||
165 | |||
166 | static int | ||
167 | ac97_scale_to_oss_val (int value, int maxval, int is_stereo, int inv) | ||
168 | { | ||
169 | /* Muted? */ | ||
170 | if (value & AC97_MUTE) | ||
171 | return 0; | ||
172 | |||
173 | if (is_stereo) | ||
174 | return (ac97_scale_to_oss_val (value & 255, maxval, 0, inv) << 8) | ||
175 | | (ac97_scale_to_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0); | ||
176 | else { | ||
177 | int i; | ||
178 | |||
179 | /* Inverted. */ | ||
180 | if (inv) | ||
181 | value = maxval - value; | ||
182 | |||
183 | i = (value * 100 + (maxval / 2)) / maxval; | ||
184 | if (i > 100) | ||
185 | i = 100; | ||
186 | if (i < 0) | ||
187 | i = 0; | ||
188 | return i; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | static int | ||
193 | ac97_scale_from_oss_val (int value, int maxval, int is_stereo, int inv) | ||
194 | { | ||
195 | if (is_stereo) | ||
196 | return (ac97_scale_from_oss_val (value & 255, maxval, 0, inv) << 8) | ||
197 | | (ac97_scale_from_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0); | ||
198 | else { | ||
199 | int i = ((value & 255) * maxval + 50) / 100; | ||
200 | if (inv) | ||
201 | i = maxval - i; | ||
202 | if (i < 0) | ||
203 | i = 0; | ||
204 | if (i > maxval) | ||
205 | i = maxval; | ||
206 | return i; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | static int | ||
211 | ac97_set_mixer (struct ac97_hwint *dev, int oss_channel, u16 oss_value) | ||
212 | { | ||
213 | int scaled_value; | ||
214 | struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel); | ||
215 | int result; | ||
216 | |||
217 | if (channel == NULL) | ||
218 | return -ENODEV; | ||
219 | if (! ac97_is_valid_channel (dev, channel)) | ||
220 | return -ENODEV; | ||
221 | scaled_value = ac97_scale_from_oss_val (oss_value, channel->maxval, | ||
222 | channel->is_stereo, | ||
223 | channel->is_inverted); | ||
224 | if (scaled_value < 0) | ||
225 | return scaled_value; | ||
226 | |||
227 | if (channel->regmask != 0) { | ||
228 | int mv; | ||
229 | |||
230 | int oldval = ac97_get_register (dev, channel->ac97_regnum); | ||
231 | if (oldval < 0) | ||
232 | return oldval; | ||
233 | |||
234 | for (mv = channel->regmask; ! (mv & 1); mv >>= 1) | ||
235 | scaled_value <<= 1; | ||
236 | |||
237 | scaled_value &= channel->regmask; | ||
238 | scaled_value |= (oldval & ~channel->regmask); | ||
239 | } | ||
240 | result = ac97_put_register (dev, channel->ac97_regnum, scaled_value); | ||
241 | if (result == 0) | ||
242 | dev->last_written_OSS_values[oss_channel] = oss_value; | ||
243 | return result; | ||
244 | } | ||
245 | |||
246 | static int | ||
247 | ac97_get_mixer_scaled (struct ac97_hwint *dev, int oss_channel) | ||
248 | { | ||
249 | struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel); | ||
250 | int regval; | ||
251 | |||
252 | if (channel == NULL) | ||
253 | return -ENODEV; | ||
254 | |||
255 | if (! ac97_is_valid_channel (dev, channel)) | ||
256 | return -ENODEV; | ||
257 | |||
258 | regval = ac97_get_register (dev, channel->ac97_regnum); | ||
259 | |||
260 | if (regval < 0) | ||
261 | return regval; | ||
262 | |||
263 | if (channel->regmask != 0) { | ||
264 | int mv; | ||
265 | |||
266 | regval &= channel->regmask; | ||
267 | |||
268 | for (mv = channel->regmask; ! (mv & 1); mv >>= 1) | ||
269 | regval >>= 1; | ||
270 | } | ||
271 | return ac97_scale_to_oss_val (regval, channel->maxval, | ||
272 | channel->is_stereo, | ||
273 | channel->is_inverted); | ||
274 | } | ||
275 | |||
276 | static int | ||
277 | ac97_get_recmask (struct ac97_hwint *dev) | ||
278 | { | ||
279 | int recReg = ac97_get_register (dev, AC97_RECORD_SELECT); | ||
280 | |||
281 | if (recReg < 0) | ||
282 | return recReg; | ||
283 | else { | ||
284 | int x; | ||
285 | for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) { | ||
286 | if (mixerRegs[x].recordNum == (recReg & 7)) | ||
287 | return mixerRegs[x].oss_mask; | ||
288 | } | ||
289 | return -ENODEV; | ||
290 | } | ||
291 | } | ||
292 | |||
293 | static int | ||
294 | ac97_set_recmask (struct ac97_hwint *dev, int oss_recmask) | ||
295 | { | ||
296 | int x; | ||
297 | |||
298 | if (oss_recmask == 0) | ||
299 | oss_recmask = SOUND_MIXER_MIC; | ||
300 | |||
301 | for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) { | ||
302 | if ((mixerRegs[x].recordNum >= 0) | ||
303 | && (oss_recmask & mixerRegs[x].oss_mask)) | ||
304 | break; | ||
305 | } | ||
306 | if (mixerRegs[x].ac97_regnum < 0) | ||
307 | return -ENODEV; | ||
308 | else { | ||
309 | int regval = (mixerRegs[x].recordNum << 8) | mixerRegs[x].recordNum; | ||
310 | int res = ac97_put_register (dev, AC97_RECORD_SELECT, regval); | ||
311 | if (res == 0) | ||
312 | return ac97_get_recmask (dev); | ||
313 | else | ||
314 | return res; | ||
315 | } | ||
316 | } | ||
317 | |||
318 | /* Set the mixer DEV to the list of values in VALUE_LIST. Return 0 on | ||
319 | success, or a negative error code. */ | ||
320 | int | ||
321 | ac97_set_values (struct ac97_hwint *dev, | ||
322 | struct ac97_mixer_value_list *value_list) | ||
323 | { | ||
324 | int x; | ||
325 | |||
326 | for (x = 0; value_list[x].oss_channel != -1; x++) { | ||
327 | int chnum = value_list[x].oss_channel; | ||
328 | struct ac97_chn_desc *chent = ac97_find_chndesc (dev, chnum); | ||
329 | if (chent != NULL) { | ||
330 | u16 val; | ||
331 | int res; | ||
332 | |||
333 | if (chent->is_stereo) | ||
334 | val = (value_list[x].value.stereo.right << 8) | ||
335 | | value_list[x].value.stereo.left; | ||
336 | else { | ||
337 | /* We do this so the returned value looks OK in the | ||
338 | mixer app. It's not necessary otherwise. */ | ||
339 | val = (value_list[x].value.mono << 8) | ||
340 | | value_list[x].value.mono; | ||
341 | } | ||
342 | res = ac97_set_mixer (dev, chnum, val); | ||
343 | if (res < 0) | ||
344 | return res; | ||
345 | } | ||
346 | else | ||
347 | return -ENODEV; | ||
348 | } | ||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | int | ||
353 | ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, void __user *arg) | ||
354 | { | ||
355 | int ret; | ||
356 | |||
357 | switch (cmd) { | ||
358 | case SOUND_MIXER_READ_RECSRC: | ||
359 | ret = ac97_get_recmask (dev); | ||
360 | break; | ||
361 | |||
362 | case SOUND_MIXER_WRITE_RECSRC: | ||
363 | { | ||
364 | if (get_user (ret, (int __user *) arg)) | ||
365 | ret = -EFAULT; | ||
366 | else | ||
367 | ret = ac97_set_recmask (dev, ret); | ||
368 | } | ||
369 | break; | ||
370 | |||
371 | case SOUND_MIXER_READ_CAPS: | ||
372 | ret = SOUND_CAP_EXCL_INPUT; | ||
373 | break; | ||
374 | |||
375 | case SOUND_MIXER_READ_DEVMASK: | ||
376 | ret = dev->mixer_devmask; | ||
377 | break; | ||
378 | |||
379 | case SOUND_MIXER_READ_RECMASK: | ||
380 | ret = dev->mixer_recmask; | ||
381 | break; | ||
382 | |||
383 | case SOUND_MIXER_READ_STEREODEVS: | ||
384 | ret = dev->mixer_stereomask; | ||
385 | break; | ||
386 | |||
387 | default: | ||
388 | /* Read or write request. */ | ||
389 | ret = -EINVAL; | ||
390 | if (_IOC_TYPE (cmd) == 'M') { | ||
391 | int dir = _SIOC_DIR (cmd); | ||
392 | int channel = _IOC_NR (cmd); | ||
393 | |||
394 | if (channel >= 0 && channel < SOUND_MIXER_NRDEVICES) { | ||
395 | ret = 0; | ||
396 | if (dir & _SIOC_WRITE) { | ||
397 | int val; | ||
398 | if (get_user (val, (int __user *) arg) == 0) | ||
399 | ret = ac97_set_mixer (dev, channel, val); | ||
400 | else | ||
401 | ret = -EFAULT; | ||
402 | } | ||
403 | if (ret >= 0 && (dir & _SIOC_READ)) { | ||
404 | if (dev->last_written_OSS_values[channel] | ||
405 | == AC97_REGVAL_UNKNOWN) | ||
406 | dev->last_written_OSS_values[channel] | ||
407 | = ac97_get_mixer_scaled (dev, channel); | ||
408 | ret = dev->last_written_OSS_values[channel]; | ||
409 | } | ||
410 | } | ||
411 | } | ||
412 | break; | ||
413 | } | ||
414 | |||
415 | if (ret < 0) | ||
416 | return ret; | ||
417 | else | ||
418 | return put_user(ret, (int __user *) arg); | ||
419 | } | ||
420 | |||
421 | EXPORT_SYMBOL(ac97_init); | ||
422 | EXPORT_SYMBOL(ac97_set_values); | ||
423 | EXPORT_SYMBOL(ac97_put_register); | ||
424 | EXPORT_SYMBOL(ac97_mixer_ioctl); | ||
425 | MODULE_LICENSE("GPL"); | ||
426 | |||
427 | |||
428 | /* | ||
429 | * Local variables: | ||
430 | * c-basic-offset: 4 | ||
431 | * End: | ||
432 | */ | ||