Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | /* Reset the mixer to the currently saved settings. */ | |
116 | int | |
117 | ac97_reset (struct ac97_hwint *dev) | |
118 | { | |
119 | int x; | |
120 | ||
121 | if (dev->reset_device (dev)) | |
122 | return -1; | |
123 | ||
124 | /* Now set the registers back to their last-written values. */ | |
125 | for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) { | |
126 | int regnum = mixerRegs[x].ac97_regnum; | |
127 | int value = dev->last_written_mixer_values [regnum / 2]; | |
128 | if (value >= 0) | |
129 | ac97_put_register (dev, regnum, value); | |
130 | } | |
131 | return 0; | |
132 | } | |
133 | ||
134 | /* Return the contents of register REG; use the cache if the value in it | |
135 | is valid. Returns a negative error code on failure. */ | |
136 | static int | |
137 | ac97_get_register (struct ac97_hwint *dev, u8 reg) | |
138 | { | |
139 | if (reg > 127 || (reg & 1)) | |
140 | return -EINVAL; | |
141 | ||
142 | /* See if it's in the cache, or if it's just plain invalid. */ | |
143 | switch (dev->last_written_mixer_values[reg / 2]) { | |
144 | case AC97_REG_UNSUPPORTED: | |
145 | return -EINVAL; | |
146 | break; | |
147 | case AC97_REGVAL_UNKNOWN: | |
148 | dev->last_written_mixer_values[reg / 2] = dev->read_reg (dev, reg); | |
149 | break; | |
150 | default: | |
151 | break; | |
152 | } | |
153 | return dev->last_written_mixer_values[reg / 2]; | |
154 | } | |
155 | ||
156 | /* Write VALUE to AC97 register REG, and cache its value in the last-written | |
157 | cache. Returns a negative error code on failure, or 0 on success. */ | |
158 | int | |
159 | ac97_put_register (struct ac97_hwint *dev, u8 reg, u16 value) | |
160 | { | |
161 | if (reg > 127 || (reg & 1)) | |
162 | return -EINVAL; | |
163 | ||
164 | if (dev->last_written_mixer_values[reg / 2] == AC97_REG_UNSUPPORTED) | |
165 | return -EINVAL; | |
166 | else { | |
167 | int res = dev->write_reg (dev, reg, value); | |
168 | if (res >= 0) { | |
169 | dev->last_written_mixer_values[reg / 2] = value; | |
170 | return 0; | |
171 | } | |
172 | else | |
173 | return res; | |
174 | } | |
175 | } | |
176 | ||
177 | /* Scale VALUE (a value fro 0 to MAXVAL) to a value from 0-100. If | |
178 | IS_STEREO is set, VALUE is a stereo value; the left channel value | |
179 | is in the lower 8 bits, and the right channel value is in the upper | |
180 | 8 bits. | |
181 | ||
182 | A negative error code is returned on failure, or the unsigned | |
183 | scaled value on success. */ | |
184 | ||
185 | static int | |
186 | ac97_scale_to_oss_val (int value, int maxval, int is_stereo, int inv) | |
187 | { | |
188 | /* Muted? */ | |
189 | if (value & AC97_MUTE) | |
190 | return 0; | |
191 | ||
192 | if (is_stereo) | |
193 | return (ac97_scale_to_oss_val (value & 255, maxval, 0, inv) << 8) | |
194 | | (ac97_scale_to_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0); | |
195 | else { | |
196 | int i; | |
197 | ||
198 | /* Inverted. */ | |
199 | if (inv) | |
200 | value = maxval - value; | |
201 | ||
202 | i = (value * 100 + (maxval / 2)) / maxval; | |
203 | if (i > 100) | |
204 | i = 100; | |
205 | if (i < 0) | |
206 | i = 0; | |
207 | return i; | |
208 | } | |
209 | } | |
210 | ||
211 | static int | |
212 | ac97_scale_from_oss_val (int value, int maxval, int is_stereo, int inv) | |
213 | { | |
214 | if (is_stereo) | |
215 | return (ac97_scale_from_oss_val (value & 255, maxval, 0, inv) << 8) | |
216 | | (ac97_scale_from_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0); | |
217 | else { | |
218 | int i = ((value & 255) * maxval + 50) / 100; | |
219 | if (inv) | |
220 | i = maxval - i; | |
221 | if (i < 0) | |
222 | i = 0; | |
223 | if (i > maxval) | |
224 | i = maxval; | |
225 | return i; | |
226 | } | |
227 | } | |
228 | ||
229 | static int | |
230 | ac97_set_mixer (struct ac97_hwint *dev, int oss_channel, u16 oss_value) | |
231 | { | |
232 | int scaled_value; | |
233 | struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel); | |
234 | int result; | |
235 | ||
236 | if (channel == NULL) | |
237 | return -ENODEV; | |
238 | if (! ac97_is_valid_channel (dev, channel)) | |
239 | return -ENODEV; | |
240 | scaled_value = ac97_scale_from_oss_val (oss_value, channel->maxval, | |
241 | channel->is_stereo, | |
242 | channel->is_inverted); | |
243 | if (scaled_value < 0) | |
244 | return scaled_value; | |
245 | ||
246 | if (channel->regmask != 0) { | |
247 | int mv; | |
248 | ||
249 | int oldval = ac97_get_register (dev, channel->ac97_regnum); | |
250 | if (oldval < 0) | |
251 | return oldval; | |
252 | ||
253 | for (mv = channel->regmask; ! (mv & 1); mv >>= 1) | |
254 | scaled_value <<= 1; | |
255 | ||
256 | scaled_value &= channel->regmask; | |
257 | scaled_value |= (oldval & ~channel->regmask); | |
258 | } | |
259 | result = ac97_put_register (dev, channel->ac97_regnum, scaled_value); | |
260 | if (result == 0) | |
261 | dev->last_written_OSS_values[oss_channel] = oss_value; | |
262 | return result; | |
263 | } | |
264 | ||
265 | static int | |
266 | ac97_get_mixer_scaled (struct ac97_hwint *dev, int oss_channel) | |
267 | { | |
268 | struct ac97_chn_desc *channel = ac97_find_chndesc (dev, oss_channel); | |
269 | int regval; | |
270 | ||
271 | if (channel == NULL) | |
272 | return -ENODEV; | |
273 | ||
274 | if (! ac97_is_valid_channel (dev, channel)) | |
275 | return -ENODEV; | |
276 | ||
277 | regval = ac97_get_register (dev, channel->ac97_regnum); | |
278 | ||
279 | if (regval < 0) | |
280 | return regval; | |
281 | ||
282 | if (channel->regmask != 0) { | |
283 | int mv; | |
284 | ||
285 | regval &= channel->regmask; | |
286 | ||
287 | for (mv = channel->regmask; ! (mv & 1); mv >>= 1) | |
288 | regval >>= 1; | |
289 | } | |
290 | return ac97_scale_to_oss_val (regval, channel->maxval, | |
291 | channel->is_stereo, | |
292 | channel->is_inverted); | |
293 | } | |
294 | ||
295 | static int | |
296 | ac97_get_recmask (struct ac97_hwint *dev) | |
297 | { | |
298 | int recReg = ac97_get_register (dev, AC97_RECORD_SELECT); | |
299 | ||
300 | if (recReg < 0) | |
301 | return recReg; | |
302 | else { | |
303 | int x; | |
304 | for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) { | |
305 | if (mixerRegs[x].recordNum == (recReg & 7)) | |
306 | return mixerRegs[x].oss_mask; | |
307 | } | |
308 | return -ENODEV; | |
309 | } | |
310 | } | |
311 | ||
312 | static int | |
313 | ac97_set_recmask (struct ac97_hwint *dev, int oss_recmask) | |
314 | { | |
315 | int x; | |
316 | ||
317 | if (oss_recmask == 0) | |
318 | oss_recmask = SOUND_MIXER_MIC; | |
319 | ||
320 | for (x = 0; mixerRegs[x].ac97_regnum >= 0; x++) { | |
321 | if ((mixerRegs[x].recordNum >= 0) | |
322 | && (oss_recmask & mixerRegs[x].oss_mask)) | |
323 | break; | |
324 | } | |
325 | if (mixerRegs[x].ac97_regnum < 0) | |
326 | return -ENODEV; | |
327 | else { | |
328 | int regval = (mixerRegs[x].recordNum << 8) | mixerRegs[x].recordNum; | |
329 | int res = ac97_put_register (dev, AC97_RECORD_SELECT, regval); | |
330 | if (res == 0) | |
331 | return ac97_get_recmask (dev); | |
332 | else | |
333 | return res; | |
334 | } | |
335 | } | |
336 | ||
337 | /* Set the mixer DEV to the list of values in VALUE_LIST. Return 0 on | |
338 | success, or a negative error code. */ | |
339 | int | |
340 | ac97_set_values (struct ac97_hwint *dev, | |
341 | struct ac97_mixer_value_list *value_list) | |
342 | { | |
343 | int x; | |
344 | ||
345 | for (x = 0; value_list[x].oss_channel != -1; x++) { | |
346 | int chnum = value_list[x].oss_channel; | |
347 | struct ac97_chn_desc *chent = ac97_find_chndesc (dev, chnum); | |
348 | if (chent != NULL) { | |
349 | u16 val; | |
350 | int res; | |
351 | ||
352 | if (chent->is_stereo) | |
353 | val = (value_list[x].value.stereo.right << 8) | |
354 | | value_list[x].value.stereo.left; | |
355 | else { | |
356 | /* We do this so the returned value looks OK in the | |
357 | mixer app. It's not necessary otherwise. */ | |
358 | val = (value_list[x].value.mono << 8) | |
359 | | value_list[x].value.mono; | |
360 | } | |
361 | res = ac97_set_mixer (dev, chnum, val); | |
362 | if (res < 0) | |
363 | return res; | |
364 | } | |
365 | else | |
366 | return -ENODEV; | |
367 | } | |
368 | return 0; | |
369 | } | |
370 | ||
371 | int | |
372 | ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd, void __user *arg) | |
373 | { | |
374 | int ret; | |
375 | ||
376 | switch (cmd) { | |
377 | case SOUND_MIXER_READ_RECSRC: | |
378 | ret = ac97_get_recmask (dev); | |
379 | break; | |
380 | ||
381 | case SOUND_MIXER_WRITE_RECSRC: | |
382 | { | |
383 | if (get_user (ret, (int __user *) arg)) | |
384 | ret = -EFAULT; | |
385 | else | |
386 | ret = ac97_set_recmask (dev, ret); | |
387 | } | |
388 | break; | |
389 | ||
390 | case SOUND_MIXER_READ_CAPS: | |
391 | ret = SOUND_CAP_EXCL_INPUT; | |
392 | break; | |
393 | ||
394 | case SOUND_MIXER_READ_DEVMASK: | |
395 | ret = dev->mixer_devmask; | |
396 | break; | |
397 | ||
398 | case SOUND_MIXER_READ_RECMASK: | |
399 | ret = dev->mixer_recmask; | |
400 | break; | |
401 | ||
402 | case SOUND_MIXER_READ_STEREODEVS: | |
403 | ret = dev->mixer_stereomask; | |
404 | break; | |
405 | ||
406 | default: | |
407 | /* Read or write request. */ | |
408 | ret = -EINVAL; | |
409 | if (_IOC_TYPE (cmd) == 'M') { | |
410 | int dir = _SIOC_DIR (cmd); | |
411 | int channel = _IOC_NR (cmd); | |
412 | ||
413 | if (channel >= 0 && channel < SOUND_MIXER_NRDEVICES) { | |
414 | ret = 0; | |
415 | if (dir & _SIOC_WRITE) { | |
416 | int val; | |
417 | if (get_user (val, (int __user *) arg) == 0) | |
418 | ret = ac97_set_mixer (dev, channel, val); | |
419 | else | |
420 | ret = -EFAULT; | |
421 | } | |
422 | if (ret >= 0 && (dir & _SIOC_READ)) { | |
423 | if (dev->last_written_OSS_values[channel] | |
424 | == AC97_REGVAL_UNKNOWN) | |
425 | dev->last_written_OSS_values[channel] | |
426 | = ac97_get_mixer_scaled (dev, channel); | |
427 | ret = dev->last_written_OSS_values[channel]; | |
428 | } | |
429 | } | |
430 | } | |
431 | break; | |
432 | } | |
433 | ||
434 | if (ret < 0) | |
435 | return ret; | |
436 | else | |
437 | return put_user(ret, (int __user *) arg); | |
438 | } | |
439 | ||
440 | EXPORT_SYMBOL(ac97_init); | |
441 | EXPORT_SYMBOL(ac97_set_values); | |
442 | EXPORT_SYMBOL(ac97_put_register); | |
443 | EXPORT_SYMBOL(ac97_mixer_ioctl); | |
444 | EXPORT_SYMBOL(ac97_reset); | |
445 | MODULE_LICENSE("GPL"); | |
446 | ||
447 | \f | |
448 | /* | |
449 | * Local variables: | |
450 | * c-basic-offset: 4 | |
451 | * End: | |
452 | */ |