1 /* $NetBSD: am7930.c,v 1.49 2005/12/11 12:21:25 christos Exp $ */
4 * Copyright (c) 1995 Rolf Grossmann
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Rolf Grossmann.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * Front-end attachment independent layer for AMD 79c30
35 * audio driver. No ISDN support.
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: am7930.c,v 1.49 2005/12/11 12:21:25 christos Exp $");
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/errno.h>
47 #include <sys/ioctl.h>
48 #include <sys/device.h>
52 #include <machine/autoconf.h>
55 #include <sys/audioio.h>
56 #include <dev/audio_if.h>
58 #include <dev/ic/am7930reg.h>
59 #include <dev/ic/am7930var.h>
63 #define DPRINTF(x) if (am7930debug) printf x
69 /* The following tables stolen from former (4.4Lite's) sys/sparc/bsd_audio.c */
72 * gx, gr & stg gains. this table must contain 256 elements with
73 * the 0th being "infinity" (the magic value 9008). The remaining
74 * elements match sun's gain curve (but with higher resolution):
75 * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
77 static const uint16_t gx_coeff
[256] = {
78 0x9008, 0x8e7c, 0x8e51, 0x8e45, 0x8d42, 0x8d3b, 0x8c36, 0x8c33,
79 0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
80 0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
81 0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb,
82 0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a,
83 0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213,
84 0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231,
85 0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4,
86 0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2,
87 0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa,
88 0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b,
89 0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b,
90 0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd,
91 0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808,
92 0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243,
93 0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224,
94 0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb,
95 0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33,
96 0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32,
97 0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323,
98 0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a,
99 0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23,
100 0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1,
101 0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333,
102 0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227,
103 0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6,
104 0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2,
105 0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba,
106 0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033,
107 0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021,
108 0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012,
109 0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
113 * second stage play gain.
115 static const uint16_t ger_coeff
[] = {
127 0x111f, /* 10.5 dB */
129 0x00dd, /* 11.5 dB */
134 0x2200, /* 15.9 dB */
135 0x000b, /* 16.9 dB */
137 #define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
142 * Reset chip and set boot-time softc defaults.
145 am7930_init(struct am7930_softc
*sc
, int flag
)
148 DPRINTF(("am7930_init()\n"));
150 /* set boot defaults */
154 sc
->sc_out_port
= AUDIOAMD_SPEAKER_VOL
;
157 /* disable sample interrupts */
158 AM7930_IWRITE(sc
, AM7930_IREG_MUX_MCR4
, 0);
160 /* initialise voice and data, and disable interrupts */
161 AM7930_IWRITE(sc
, AM7930_IREG_INIT
,
162 AM7930_INIT_PMS_ACTIVE
| AM7930_INIT_INT_DISABLE
);
164 if (flag
== AUDIOAMD_DMA_MODE
) {
166 /* configure PP for serial (SBP) mode */
167 AM7930_IWRITE(sc
, AM7930_IREG_PP_PPCR1
, AM7930_PPCR1_SBP
);
170 * Initialise the MUX unit - route the MAP to the PP
172 AM7930_IWRITE(sc
, AM7930_IREG_MUX_MCR1
,
173 (AM7930_MCRCHAN_BA
<< 4) | AM7930_MCRCHAN_BD
);
174 AM7930_IWRITE(sc
, AM7930_IREG_MUX_MCR2
, AM7930_MCRCHAN_NC
);
175 AM7930_IWRITE(sc
, AM7930_IREG_MUX_MCR3
, AM7930_MCRCHAN_NC
);
180 * Initialize the MUX unit. We use MCR3 to route the MAP
181 * through channel Bb. MCR1 and MCR2 are unused.
182 * Setting the INT enable bit in MCR4 will generate an
183 * interrupt on each converted audio sample.
185 AM7930_IWRITE(sc
, AM7930_IREG_MUX_MCR1
, 0);
186 AM7930_IWRITE(sc
, AM7930_IREG_MUX_MCR2
, 0);
187 AM7930_IWRITE(sc
, AM7930_IREG_MUX_MCR3
,
188 (AM7930_MCRCHAN_BB
<< 4) | AM7930_MCRCHAN_BA
);
189 AM7930_IWRITE(sc
, AM7930_IREG_MUX_MCR4
,
190 AM7930_MCR4_INT_ENABLE
);
196 am7930_open(void *addr
, int flags
)
198 struct am7930_softc
*sc
;
201 DPRINTF(("sa_open: unit %p\n", sc
));
202 sc
->sc_glue
->onopen(sc
);
203 DPRINTF(("saopen: ok -> sc=%p\n",sc
));
208 am7930_close(void *addr
)
210 struct am7930_softc
*sc
;
213 DPRINTF(("sa_close: sc=%p\n", sc
));
214 sc
->sc_glue
->onclose(sc
);
215 DPRINTF(("sa_close: closed.\n"));
219 * XXX should be extended to handle a few of the more common formats.
222 am7930_set_params(void *addr
, int setmode
, int usemode
, audio_params_t
*p
,
223 audio_params_t
*r
, stream_filter_list_t
*pfil
, stream_filter_list_t
*rfil
)
226 struct am7930_softc
*sc
;
229 if ((usemode
& AUMODE_PLAY
) == AUMODE_PLAY
) {
230 if (p
->sample_rate
< 7500 || p
->sample_rate
> 8500 ||
231 p
->encoding
!= AUDIO_ENCODING_ULAW
||
235 p
->sample_rate
= 8000;
236 if (sc
->sc_glue
->output_conv
!= NULL
) {
238 hw
.encoding
= AUDIO_ENCODING_NONE
;
239 hw
.precision
*= sc
->sc_glue
->factor
;
240 pfil
->append(pfil
, sc
->sc_glue
->output_conv
, &hw
);
243 if ((usemode
& AUMODE_RECORD
) == AUMODE_RECORD
) {
244 if (r
->sample_rate
< 7500 || r
->sample_rate
> 8500 ||
245 r
->encoding
!= AUDIO_ENCODING_ULAW
||
249 r
->sample_rate
= 8000;
250 if (sc
->sc_glue
->input_conv
!= NULL
) {
252 hw
.encoding
= AUDIO_ENCODING_NONE
;
253 hw
.precision
*= sc
->sc_glue
->factor
;
254 pfil
->append(rfil
, sc
->sc_glue
->input_conv
, &hw
);
262 am7930_query_encoding(void *addr
, struct audio_encoding
*fp
)
266 strcpy(fp
->name
, AudioEmulaw
);
267 fp
->encoding
= AUDIO_ENCODING_ULAW
;
279 am7930_round_blocksize(void *addr
, int blk
,
280 int mode
, const audio_params_t
*param
)
286 am7930_commit_settings(void *addr
)
288 struct am7930_softc
*sc
;
289 uint16_t ger
, gr
, gx
, stgr
;
293 DPRINTF(("sa_commit.\n"));
295 gx
= gx_coeff
[sc
->sc_rlevel
];
296 stgr
= gx_coeff
[sc
->sc_mlevel
];
298 level
= (sc
->sc_plevel
* (256 + NGER
)) >> 8;
300 ger
= ger_coeff
[level
- 256];
304 gr
= gx_coeff
[level
];
309 mmr2
= AM7930_IREAD(sc
, AM7930_IREG_MAP_MMR2
);
310 if (sc
->sc_out_port
== AUDIOAMD_SPEAKER_VOL
)
311 mmr2
|= AM7930_MMR2_LS
;
313 mmr2
&= ~AM7930_MMR2_LS
;
314 AM7930_IWRITE(sc
, AM7930_IREG_MAP_MMR2
, mmr2
);
316 mmr3
= AM7930_IREAD(sc
, AM7930_IREG_MAP_MMR3
);
318 mmr3
|= AM7930_MMR3_MUTE
;
320 mmr3
&= ~AM7930_MMR3_MUTE
;
321 AM7930_IWRITE(sc
, AM7930_IREG_MAP_MMR3
, mmr3
);
323 AM7930_IWRITE(sc
, AM7930_IREG_MAP_MMR1
,
324 AM7930_MMR1_GX
| AM7930_MMR1_GER
|
325 AM7930_MMR1_GR
| AM7930_MMR1_STG
);
327 AM7930_IWRITE16(sc
, AM7930_IREG_MAP_GX
, gx
);
328 AM7930_IWRITE16(sc
, AM7930_IREG_MAP_STG
, stgr
);
329 AM7930_IWRITE16(sc
, AM7930_IREG_MAP_GR
, gr
);
330 AM7930_IWRITE16(sc
, AM7930_IREG_MAP_GER
, ger
);
338 am7930_halt_output(void *addr
)
340 struct am7930_softc
*sc
;
343 /* XXX only halt, if input is also halted ?? */
344 AM7930_IWRITE(sc
, AM7930_IREG_INIT
,
345 AM7930_INIT_PMS_ACTIVE
| AM7930_INIT_INT_DISABLE
);
350 am7930_halt_input(void *addr
)
352 struct am7930_softc
*sc
;
355 /* XXX only halt, if output is also halted ?? */
356 AM7930_IWRITE(sc
, AM7930_IREG_INIT
,
357 AM7930_INIT_PMS_ACTIVE
| AM7930_INIT_INT_DISABLE
);
362 * XXX chip is full-duplex, but really attach-dependent.
363 * For now we know of no half-duplex attachments.
366 am7930_get_props(void *addr
)
368 return AUDIO_PROP_FULLDUPLEX
;
372 * Attach-dependent channel set/query
375 am7930_set_port(void *addr
, mixer_ctrl_t
*cp
)
377 struct am7930_softc
*sc
;
379 DPRINTF(("am7930_set_port: port=%d", cp
->dev
));
381 if (cp
->dev
== AUDIOAMD_RECORD_SOURCE
||
382 cp
->dev
== AUDIOAMD_MONITOR_OUTPUT
||
383 cp
->dev
== AUDIOAMD_MIC_MUTE
) {
384 if (cp
->type
!= AUDIO_MIXER_ENUM
)
386 } else if (cp
->type
!= AUDIO_MIXER_VALUE
||
387 cp
->un
.value
.num_channels
!= 1) {
392 case AUDIOAMD_MIC_VOL
:
393 sc
->sc_rlevel
= cp
->un
.value
.level
[AUDIO_MIXER_LEVEL_MONO
];
395 case AUDIOAMD_SPEAKER_VOL
:
396 case AUDIOAMD_HEADPHONES_VOL
:
397 sc
->sc_plevel
= cp
->un
.value
.level
[AUDIO_MIXER_LEVEL_MONO
];
399 case AUDIOAMD_MONITOR_VOL
:
400 sc
->sc_mlevel
= cp
->un
.value
.level
[AUDIO_MIXER_LEVEL_MONO
];
402 case AUDIOAMD_RECORD_SOURCE
:
403 if (cp
->un
.ord
!= AUDIOAMD_MIC_VOL
)
406 case AUDIOAMD_MIC_MUTE
:
407 sc
->sc_mic_mute
= cp
->un
.ord
;
409 case AUDIOAMD_MONITOR_OUTPUT
:
410 if (cp
->un
.ord
!= AUDIOAMD_SPEAKER_VOL
&&
411 cp
->un
.ord
!= AUDIOAMD_HEADPHONES_VOL
)
413 sc
->sc_out_port
= cp
->un
.ord
;
423 am7930_get_port(void *addr
, mixer_ctrl_t
*cp
)
425 struct am7930_softc
*sc
;
427 DPRINTF(("am7930_get_port: port=%d\n", cp
->dev
));
429 if (cp
->dev
== AUDIOAMD_RECORD_SOURCE
||
430 cp
->dev
== AUDIOAMD_MONITOR_OUTPUT
||
431 cp
->dev
== AUDIOAMD_MIC_MUTE
) {
432 if (cp
->type
!= AUDIO_MIXER_ENUM
)
434 } else if (cp
->type
!= AUDIO_MIXER_VALUE
||
435 cp
->un
.value
.num_channels
!= 1) {
440 case AUDIOAMD_MIC_VOL
:
441 cp
->un
.value
.level
[AUDIO_MIXER_LEVEL_MONO
] = sc
->sc_rlevel
;
443 case AUDIOAMD_SPEAKER_VOL
:
444 case AUDIOAMD_HEADPHONES_VOL
:
445 cp
->un
.value
.level
[AUDIO_MIXER_LEVEL_MONO
] = sc
->sc_plevel
;
447 case AUDIOAMD_MONITOR_VOL
:
448 cp
->un
.value
.level
[AUDIO_MIXER_LEVEL_MONO
] = sc
->sc_mlevel
;
450 case AUDIOAMD_RECORD_SOURCE
:
451 cp
->un
.ord
= AUDIOAMD_MIC_VOL
;
453 case AUDIOAMD_MIC_MUTE
:
454 cp
->un
.ord
= sc
->sc_mic_mute
;
456 case AUDIOAMD_MONITOR_OUTPUT
:
457 cp
->un
.ord
= sc
->sc_out_port
;
468 * Define mixer control facilities.
471 am7930_query_devinfo(void *addr
, mixer_devinfo_t
*dip
)
474 DPRINTF(("am7930_query_devinfo()\n"));
477 case AUDIOAMD_MIC_VOL
:
478 dip
->type
= AUDIO_MIXER_VALUE
;
479 dip
->mixer_class
= AUDIOAMD_INPUT_CLASS
;
480 dip
->prev
= AUDIO_MIXER_LAST
;
481 dip
->next
= AUDIOAMD_MIC_MUTE
;
482 strcpy(dip
->label
.name
, AudioNmicrophone
);
483 dip
->un
.v
.num_channels
= 1;
484 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
486 case AUDIOAMD_SPEAKER_VOL
:
487 dip
->type
= AUDIO_MIXER_VALUE
;
488 dip
->mixer_class
= AUDIOAMD_OUTPUT_CLASS
;
489 dip
->prev
= dip
->next
= AUDIO_MIXER_LAST
;
490 strcpy(dip
->label
.name
, AudioNspeaker
);
491 dip
->un
.v
.num_channels
= 1;
492 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
494 case AUDIOAMD_HEADPHONES_VOL
:
495 dip
->type
= AUDIO_MIXER_VALUE
;
496 dip
->mixer_class
= AUDIOAMD_OUTPUT_CLASS
;
497 dip
->prev
= dip
->next
= AUDIO_MIXER_LAST
;
498 strcpy(dip
->label
.name
, AudioNheadphone
);
499 dip
->un
.v
.num_channels
= 1;
500 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
502 case AUDIOAMD_MONITOR_VOL
:
503 dip
->type
= AUDIO_MIXER_VALUE
;
504 dip
->mixer_class
= AUDIOAMD_MONITOR_CLASS
;
505 dip
->prev
= dip
->next
= AUDIO_MIXER_LAST
;
506 strcpy(dip
->label
.name
, AudioNmonitor
);
507 dip
->un
.v
.num_channels
= 1;
508 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
510 case AUDIOAMD_RECORD_SOURCE
:
511 dip
->type
= AUDIO_MIXER_ENUM
;
512 dip
->mixer_class
= AUDIOAMD_RECORD_CLASS
;
513 dip
->next
= dip
->prev
= AUDIO_MIXER_LAST
;
514 strcpy(dip
->label
.name
, AudioNsource
);
515 dip
->un
.e
.num_mem
= 1;
516 strcpy(dip
->un
.e
.member
[0].label
.name
, AudioNmicrophone
);
517 dip
->un
.e
.member
[0].ord
= AUDIOAMD_MIC_VOL
;
519 case AUDIOAMD_MONITOR_OUTPUT
:
520 dip
->type
= AUDIO_MIXER_ENUM
;
521 dip
->mixer_class
= AUDIOAMD_MONITOR_CLASS
;
522 dip
->next
= dip
->prev
= AUDIO_MIXER_LAST
;
523 strcpy(dip
->label
.name
, AudioNoutput
);
524 dip
->un
.e
.num_mem
= 2;
525 strcpy(dip
->un
.e
.member
[0].label
.name
, AudioNspeaker
);
526 dip
->un
.e
.member
[0].ord
= AUDIOAMD_SPEAKER_VOL
;
527 strcpy(dip
->un
.e
.member
[1].label
.name
, AudioNheadphone
);
528 dip
->un
.e
.member
[1].ord
= AUDIOAMD_HEADPHONES_VOL
;
530 case AUDIOAMD_MIC_MUTE
:
531 dip
->type
= AUDIO_MIXER_ENUM
;
532 dip
->mixer_class
= AUDIOAMD_INPUT_CLASS
;
533 dip
->prev
= AUDIOAMD_MIC_VOL
;
534 dip
->next
= AUDIO_MIXER_LAST
;
535 strcpy(dip
->label
.name
, AudioNmute
);
536 dip
->un
.e
.num_mem
= 2;
537 strcpy(dip
->un
.e
.member
[0].label
.name
, AudioNoff
);
538 dip
->un
.e
.member
[0].ord
= 0;
539 strcpy(dip
->un
.e
.member
[1].label
.name
, AudioNon
);
540 dip
->un
.e
.member
[1].ord
= 1;
542 case AUDIOAMD_INPUT_CLASS
:
543 dip
->type
= AUDIO_MIXER_CLASS
;
544 dip
->mixer_class
= AUDIOAMD_INPUT_CLASS
;
545 dip
->next
= dip
->prev
= AUDIO_MIXER_LAST
;
546 strcpy(dip
->label
.name
, AudioCinputs
);
548 case AUDIOAMD_OUTPUT_CLASS
:
549 dip
->type
= AUDIO_MIXER_CLASS
;
550 dip
->mixer_class
= AUDIOAMD_OUTPUT_CLASS
;
551 dip
->next
= dip
->prev
= AUDIO_MIXER_LAST
;
552 strcpy(dip
->label
.name
, AudioCoutputs
);
554 case AUDIOAMD_RECORD_CLASS
:
555 dip
->type
= AUDIO_MIXER_CLASS
;
556 dip
->mixer_class
= AUDIOAMD_RECORD_CLASS
;
557 dip
->next
= dip
->prev
= AUDIO_MIXER_LAST
;
558 strcpy(dip
->label
.name
, AudioCrecord
);
560 case AUDIOAMD_MONITOR_CLASS
:
561 dip
->type
= AUDIO_MIXER_CLASS
;
562 dip
->mixer_class
= AUDIOAMD_MONITOR_CLASS
;
563 dip
->next
= dip
->prev
= AUDIO_MIXER_LAST
;
564 strcpy(dip
->label
.name
, AudioCmonitor
);
571 DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip
->label
.name
));