ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / sound / sparc / amd7930.c
1 /*
2  * Driver for AMD7930 sound chips found on Sparcs.
3  * Copyright (C) 2002 David S. Miller <davem@redhat.com>
4  *
5  * Based entirely upon drivers/sbus/audio/amd7930.c which is:
6  * Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu)
7  *
8  * --- Notes from Thomas's original driver ---
9  * This is the lowlevel driver for the AMD7930 audio chip found on all
10  * sun4c machines and some sun4m machines.
11  *
12  * The amd7930 is actually an ISDN chip which has a very simple
13  * integrated audio encoder/decoder. When Sun decided on what chip to
14  * use for audio, they had the brilliant idea of using the amd7930 and
15  * only connecting the audio encoder/decoder pins.
16  *
17  * Thanks to the AMD engineer who was able to get us the AMD79C30
18  * databook which has all the programming information and gain tables.
19  *
20  * Advanced Micro Devices' Am79C30A is an ISDN/audio chip used in the
21  * SparcStation 1+.  The chip provides microphone and speaker interfaces
22  * which provide mono-channel audio at 8K samples per second via either
23  * 8-bit A-law or 8-bit mu-law encoding.  Also, the chip features an
24  * ISDN BRI Line Interface Unit (LIU), I.430 S/T physical interface,
25  * which performs basic D channel LAPD processing and provides raw
26  * B channel data.  The digital audio channel, the two ISDN B channels,
27  * and two 64 Kbps channels to the microprocessor are all interconnected
28  * via a multiplexer.
29  * --- End of notes from Thoamas's original driver ---
30  */
31
32 #include <linux/module.h>
33 #include <linux/kernel.h>
34 #include <linux/slab.h>
35 #include <linux/init.h>
36 #include <linux/interrupt.h>
37
38 #include <sound/driver.h>
39 #include <sound/core.h>
40 #include <sound/pcm.h>
41 #include <sound/info.h>
42 #include <sound/control.h>
43 #define SNDRV_GET_ID
44 #include <sound/initval.h>
45
46 #include <asm/io.h>
47 #include <asm/irq.h>
48 #include <asm/sbus.h>
49
50 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;      /* Index 0-MAX */
51 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;       /* ID for this card */
52 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;      /* Enable this card */
53
54 MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
55 MODULE_PARM_DESC(index, "Index value for Sun AMD7930 soundcard.");
56 MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
57 MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
58 MODULE_PARM_DESC(id, "ID string for Sun AMD7930 soundcard.");
59 MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
60 MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
61 MODULE_PARM_DESC(enable, "Enable Sun AMD7930 soundcard.");
62 MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
63 MODULE_AUTHOR("Thomas K. Dyas and David S. Miller");
64 MODULE_DESCRIPTION("Sun AMD7930");
65 MODULE_LICENSE("GPL");
66 MODULE_CLASSES("{sound}");
67 MODULE_DEVICES("{{Sun,AMD7930}}");
68
69 /* Device register layout.  */
70
71 /* Register interface presented to the CPU by the amd7930. */
72 #define AMD7930_CR      0x00UL          /* Command Register (W) */
73 #define AMD7930_IR      AMD7930_CR      /* Interrupt Register (R) */
74 #define AMD7930_DR      0x01UL          /* Data Register (R/W) */
75 #define AMD7930_DSR1    0x02UL          /* D-channel Status Register 1 (R) */
76 #define AMD7930_DER     0x03UL          /* D-channel Error Register (R) */
77 #define AMD7930_DCTB    0x04UL          /* D-channel Transmit Buffer (W) */
78 #define AMD7930_DCRB    AMD7930_DCTB    /* D-channel Receive Buffer (R) */
79 #define AMD7930_BBTB    0x05UL          /* Bb-channel Transmit Buffer (W) */
80 #define AMD7930_BBRB    AMD7930_BBTB    /* Bb-channel Receive Buffer (R) */
81 #define AMD7930_BCTB    0x06UL          /* Bc-channel Transmit Buffer (W) */
82 #define AMD7930_BCRB    AMD7930_BCTB    /* Bc-channel Receive Buffer (R) */
83 #define AMD7930_DSR2    0x07UL          /* D-channel Status Register 2 (R) */
84
85 /* Indirect registers in the Main Audio Processor. */
86 struct amd7930_map {
87         __u16   x[8];
88         __u16   r[8];
89         __u16   gx;
90         __u16   gr;
91         __u16   ger;
92         __u16   stgr;
93         __u16   ftgr;
94         __u16   atgr;
95         __u8    mmr1;
96         __u8    mmr2;
97 };
98
99 /* After an amd7930 interrupt, reading the Interrupt Register (ir)
100  * clears the interrupt and returns a bitmask indicating which
101  * interrupt source(s) require service.
102  */
103
104 #define AMR_IR_DTTHRSH                  0x01 /* D-channel xmit threshold */
105 #define AMR_IR_DRTHRSH                  0x02 /* D-channel recv threshold */
106 #define AMR_IR_DSRI                     0x04 /* D-channel packet status */
107 #define AMR_IR_DERI                     0x08 /* D-channel error */
108 #define AMR_IR_BBUF                     0x10 /* B-channel data xfer */
109 #define AMR_IR_LSRI                     0x20 /* LIU status */
110 #define AMR_IR_DSR2I                    0x40 /* D-channel buffer status */
111 #define AMR_IR_MLTFRMI                  0x80 /* multiframe or PP */
112
113 /* The amd7930 has "indirect registers" which are accessed by writing
114  * the register number into the Command Register and then reading or
115  * writing values from the Data Register as appropriate. We define the
116  * AMR_* macros to be the indirect register numbers and AM_* macros to
117  * be bits in whatever register is referred to.
118  */
119
120 /* Initialization */
121 #define AMR_INIT                        0x21
122 #define         AM_INIT_ACTIVE                  0x01
123 #define         AM_INIT_DATAONLY                0x02
124 #define         AM_INIT_POWERDOWN               0x03
125 #define         AM_INIT_DISABLE_INTS            0x04
126 #define AMR_INIT2                       0x20
127 #define         AM_INIT2_ENABLE_POWERDOWN       0x20
128 #define         AM_INIT2_ENABLE_MULTIFRAME      0x10
129
130 /* Line Interface Unit */
131 #define AMR_LIU_LSR                     0xA1
132 #define         AM_LIU_LSR_STATE                0x07
133 #define         AM_LIU_LSR_F3                   0x08
134 #define         AM_LIU_LSR_F7                   0x10
135 #define         AM_LIU_LSR_F8                   0x20
136 #define         AM_LIU_LSR_HSW                  0x40
137 #define         AM_LIU_LSR_HSW_CHG              0x80
138 #define AMR_LIU_LPR                     0xA2
139 #define AMR_LIU_LMR1                    0xA3
140 #define         AM_LIU_LMR1_B1_ENABL            0x01
141 #define         AM_LIU_LMR1_B2_ENABL            0x02
142 #define         AM_LIU_LMR1_F_DISABL            0x04
143 #define         AM_LIU_LMR1_FA_DISABL           0x08
144 #define         AM_LIU_LMR1_REQ_ACTIV           0x10
145 #define         AM_LIU_LMR1_F8_F3               0x20
146 #define         AM_LIU_LMR1_LIU_ENABL           0x40
147 #define AMR_LIU_LMR2                    0xA4
148 #define         AM_LIU_LMR2_DECHO               0x01
149 #define         AM_LIU_LMR2_DLOOP               0x02
150 #define         AM_LIU_LMR2_DBACKOFF            0x04
151 #define         AM_LIU_LMR2_EN_F3_INT           0x08
152 #define         AM_LIU_LMR2_EN_F8_INT           0x10
153 #define         AM_LIU_LMR2_EN_HSW_INT          0x20
154 #define         AM_LIU_LMR2_EN_F7_INT           0x40
155 #define AMR_LIU_2_4                     0xA5
156 #define AMR_LIU_MF                      0xA6
157 #define AMR_LIU_MFSB                    0xA7
158 #define AMR_LIU_MFQB                    0xA8
159
160 /* Multiplexor */
161 #define AMR_MUX_MCR1                    0x41
162 #define AMR_MUX_MCR2                    0x42
163 #define AMR_MUX_MCR3                    0x43
164 #define         AM_MUX_CHANNEL_B1               0x01
165 #define         AM_MUX_CHANNEL_B2               0x02
166 #define         AM_MUX_CHANNEL_Ba               0x03
167 #define         AM_MUX_CHANNEL_Bb               0x04
168 #define         AM_MUX_CHANNEL_Bc               0x05
169 #define         AM_MUX_CHANNEL_Bd               0x06
170 #define         AM_MUX_CHANNEL_Be               0x07
171 #define         AM_MUX_CHANNEL_Bf               0x08
172 #define AMR_MUX_MCR4                    0x44
173 #define         AM_MUX_MCR4_ENABLE_INTS         0x08
174 #define         AM_MUX_MCR4_REVERSE_Bb          0x10
175 #define         AM_MUX_MCR4_REVERSE_Bc          0x20
176 #define AMR_MUX_1_4                     0x45
177
178 /* Main Audio Processor */
179 #define AMR_MAP_X                       0x61
180 #define AMR_MAP_R                       0x62
181 #define AMR_MAP_GX                      0x63
182 #define AMR_MAP_GR                      0x64
183 #define AMR_MAP_GER                     0x65
184 #define AMR_MAP_STGR                    0x66
185 #define AMR_MAP_FTGR_1_2                0x67
186 #define AMR_MAP_ATGR_1_2                0x68
187 #define AMR_MAP_MMR1                    0x69
188 #define         AM_MAP_MMR1_ALAW                0x01
189 #define         AM_MAP_MMR1_GX                  0x02
190 #define         AM_MAP_MMR1_GR                  0x04
191 #define         AM_MAP_MMR1_GER                 0x08
192 #define         AM_MAP_MMR1_X                   0x10
193 #define         AM_MAP_MMR1_R                   0x20
194 #define         AM_MAP_MMR1_STG                 0x40
195 #define         AM_MAP_MMR1_LOOPBACK            0x80
196 #define AMR_MAP_MMR2                    0x6A
197 #define         AM_MAP_MMR2_AINB                0x01
198 #define         AM_MAP_MMR2_LS                  0x02
199 #define         AM_MAP_MMR2_ENABLE_DTMF         0x04
200 #define         AM_MAP_MMR2_ENABLE_TONEGEN      0x08
201 #define         AM_MAP_MMR2_ENABLE_TONERING     0x10
202 #define         AM_MAP_MMR2_DISABLE_HIGHPASS    0x20
203 #define         AM_MAP_MMR2_DISABLE_AUTOZERO    0x40
204 #define AMR_MAP_1_10                    0x6B
205 #define AMR_MAP_MMR3                    0x6C
206 #define AMR_MAP_STRA                    0x6D
207 #define AMR_MAP_STRF                    0x6E
208 #define AMR_MAP_PEAKX                   0x70
209 #define AMR_MAP_PEAKR                   0x71
210 #define AMR_MAP_15_16                   0x72
211
212 /* Data Link Controller */
213 #define AMR_DLC_FRAR_1_2_3              0x81
214 #define AMR_DLC_SRAR_1_2_3              0x82
215 #define AMR_DLC_TAR                     0x83
216 #define AMR_DLC_DRLR                    0x84
217 #define AMR_DLC_DTCR                    0x85
218 #define AMR_DLC_DMR1                    0x86
219 #define         AMR_DLC_DMR1_DTTHRSH_INT        0x01
220 #define         AMR_DLC_DMR1_DRTHRSH_INT        0x02
221 #define         AMR_DLC_DMR1_TAR_ENABL          0x04
222 #define         AMR_DLC_DMR1_EORP_INT           0x08
223 #define         AMR_DLC_DMR1_EN_ADDR1           0x10
224 #define         AMR_DLC_DMR1_EN_ADDR2           0x20
225 #define         AMR_DLC_DMR1_EN_ADDR3           0x40
226 #define         AMR_DLC_DMR1_EN_ADDR4           0x80
227 #define         AMR_DLC_DMR1_EN_ADDRS           0xf0
228 #define AMR_DLC_DMR2                    0x87
229 #define         AMR_DLC_DMR2_RABRT_INT          0x01
230 #define         AMR_DLC_DMR2_RESID_INT          0x02
231 #define         AMR_DLC_DMR2_COLL_INT           0x04
232 #define         AMR_DLC_DMR2_FCS_INT            0x08
233 #define         AMR_DLC_DMR2_OVFL_INT           0x10
234 #define         AMR_DLC_DMR2_UNFL_INT           0x20
235 #define         AMR_DLC_DMR2_OVRN_INT           0x40
236 #define         AMR_DLC_DMR2_UNRN_INT           0x80
237 #define AMR_DLC_1_7                     0x88
238 #define AMR_DLC_DRCR                    0x89
239 #define AMR_DLC_RNGR1                   0x8A
240 #define AMR_DLC_RNGR2                   0x8B
241 #define AMR_DLC_FRAR4                   0x8C
242 #define AMR_DLC_SRAR4                   0x8D
243 #define AMR_DLC_DMR3                    0x8E
244 #define         AMR_DLC_DMR3_VA_INT             0x01
245 #define         AMR_DLC_DMR3_EOTP_INT           0x02
246 #define         AMR_DLC_DMR3_LBRP_INT           0x04
247 #define         AMR_DLC_DMR3_RBA_INT            0x08
248 #define         AMR_DLC_DMR3_LBT_INT            0x10
249 #define         AMR_DLC_DMR3_TBE_INT            0x20
250 #define         AMR_DLC_DMR3_RPLOST_INT         0x40
251 #define         AMR_DLC_DMR3_KEEP_FCS           0x80
252 #define AMR_DLC_DMR4                    0x8F
253 #define         AMR_DLC_DMR4_RCV_1              0x00
254 #define         AMR_DLC_DMR4_RCV_2              0x01
255 #define         AMR_DLC_DMR4_RCV_4              0x02
256 #define         AMR_DLC_DMR4_RCV_8              0x03
257 #define         AMR_DLC_DMR4_RCV_16             0x01
258 #define         AMR_DLC_DMR4_RCV_24             0x02
259 #define         AMR_DLC_DMR4_RCV_30             0x03
260 #define         AMR_DLC_DMR4_XMT_1              0x00
261 #define         AMR_DLC_DMR4_XMT_2              0x04
262 #define         AMR_DLC_DMR4_XMT_4              0x08
263 #define         AMR_DLC_DMR4_XMT_8              0x0c
264 #define         AMR_DLC_DMR4_XMT_10             0x08
265 #define         AMR_DLC_DMR4_XMT_14             0x0c
266 #define         AMR_DLC_DMR4_IDLE_MARK          0x00
267 #define         AMR_DLC_DMR4_IDLE_FLAG          0x10
268 #define         AMR_DLC_DMR4_ADDR_BOTH          0x00
269 #define         AMR_DLC_DMR4_ADDR_1ST           0x20
270 #define         AMR_DLC_DMR4_ADDR_2ND           0xa0
271 #define         AMR_DLC_DMR4_CR_ENABLE          0x40
272 #define AMR_DLC_12_15                   0x90
273 #define AMR_DLC_ASR                     0x91
274 #define AMR_DLC_EFCR                    0x92
275 #define         AMR_DLC_EFCR_EXTEND_FIFO        0x01
276 #define         AMR_DLC_EFCR_SEC_PKT_INT        0x02
277
278 #define AMR_DSR1_VADDR                  0x01
279 #define AMR_DSR1_EORP                   0x02
280 #define AMR_DSR1_PKT_IP                 0x04
281 #define AMR_DSR1_DECHO_ON               0x08
282 #define AMR_DSR1_DLOOP_ON               0x10
283 #define AMR_DSR1_DBACK_OFF              0x20
284 #define AMR_DSR1_EOTP                   0x40
285 #define AMR_DSR1_CXMT_ABRT              0x80
286
287 #define AMR_DSR2_LBRP                   0x01
288 #define AMR_DSR2_RBA                    0x02
289 #define AMR_DSR2_RPLOST                 0x04
290 #define AMR_DSR2_LAST_BYTE              0x08
291 #define AMR_DSR2_TBE                    0x10
292 #define AMR_DSR2_MARK_IDLE              0x20
293 #define AMR_DSR2_FLAG_IDLE              0x40
294 #define AMR_DSR2_SECOND_PKT             0x80
295
296 #define AMR_DER_RABRT                   0x01
297 #define AMR_DER_RFRAME                  0x02
298 #define AMR_DER_COLLISION               0x04
299 #define AMR_DER_FCS                     0x08
300 #define AMR_DER_OVFL                    0x10
301 #define AMR_DER_UNFL                    0x20
302 #define AMR_DER_OVRN                    0x40
303 #define AMR_DER_UNRN                    0x80
304
305 /* Peripheral Port */
306 #define AMR_PP_PPCR1                    0xC0
307 #define AMR_PP_PPSR                     0xC1
308 #define AMR_PP_PPIER                    0xC2
309 #define AMR_PP_MTDR                     0xC3
310 #define AMR_PP_MRDR                     0xC3
311 #define AMR_PP_CITDR0                   0xC4
312 #define AMR_PP_CIRDR0                   0xC4
313 #define AMR_PP_CITDR1                   0xC5
314 #define AMR_PP_CIRDR1                   0xC5
315 #define AMR_PP_PPCR2                    0xC8
316 #define AMR_PP_PPCR3                    0xC9
317
318 typedef struct snd_amd7930 {
319         spinlock_t              lock;
320         unsigned long           regs;
321         u32                     flags;
322 #define AMD7930_FLAG_PLAYBACK   0x00000001
323 #define AMD7930_FLAG_CAPTURE    0x00000002
324
325         struct amd7930_map      map;
326
327         snd_card_t              *card;
328         snd_pcm_t               *pcm;
329         snd_pcm_substream_t     *playback_substream;
330         snd_pcm_substream_t     *capture_substream;
331
332         /* Playback/Capture buffer state. */
333         unsigned char           *p_orig, *p_cur;
334         int                     p_left;
335         unsigned char           *c_orig, *c_cur;
336         int                     c_left;
337
338         int                     rgain;
339         int                     pgain;
340         int                     mgain;
341
342         struct sbus_dev         *sdev;
343         unsigned int            irq;
344         unsigned int            regs_size;
345         struct snd_amd7930      *next;
346 } amd7930_t;
347 #define chip_t amd7930_t
348
349 static amd7930_t *amd7930_list;
350
351 /* Idle the AMD7930 chip.  The amd->lock is not held.  */
352 static __inline__ void amd7930_idle(amd7930_t *amd)
353 {
354         unsigned long flags;
355
356         spin_lock_irqsave(&amd->lock, flags);
357         sbus_writeb(AMR_INIT, amd->regs + AMD7930_CR);
358         sbus_writeb(0, amd->regs + AMD7930_DR);
359         spin_unlock_irqrestore(&amd->lock, flags);
360 }
361
362 /* Enable chip interrupts.  The amd->lock is not held.  */
363 static __inline__ void amd7930_enable_ints(amd7930_t *amd)
364 {
365         unsigned long flags;
366
367         spin_lock_irqsave(&amd->lock, flags);
368         sbus_writeb(AMR_INIT, amd->regs + AMD7930_CR);
369         sbus_writeb(AM_INIT_ACTIVE, amd->regs + AMD7930_DR);
370         spin_unlock_irqrestore(&amd->lock, flags);
371 }
372
373 /* Disable chip interrupts.  The amd->lock is not held.  */
374 static __inline__ void amd7930_disable_ints(amd7930_t *amd)
375 {
376         unsigned long flags;
377
378         spin_lock_irqsave(&amd->lock, flags);
379         sbus_writeb(AMR_INIT, amd->regs + AMD7930_CR);
380         sbus_writeb(AM_INIT_ACTIVE | AM_INIT_DISABLE_INTS, amd->regs + AMD7930_DR);
381         spin_unlock_irqrestore(&amd->lock, flags);
382 }
383
384 /* Commit amd7930_map settings to the hardware.
385  * The amd->lock is held and local interrupts are disabled.
386  */
387 static void __amd7930_write_map(amd7930_t *amd)
388 {
389         struct amd7930_map *map = &amd->map;
390
391         sbus_writeb(AMR_MAP_GX, amd->regs + AMD7930_CR);
392         sbus_writeb(((map->gx >> 0) & 0xff), amd->regs + AMD7930_DR);
393         sbus_writeb(((map->gx >> 8) & 0xff), amd->regs + AMD7930_DR);
394
395         sbus_writeb(AMR_MAP_GR, amd->regs + AMD7930_CR);
396         sbus_writeb(((map->gr >> 0) & 0xff), amd->regs + AMD7930_DR);
397         sbus_writeb(((map->gr >> 8) & 0xff), amd->regs + AMD7930_DR);
398
399         sbus_writeb(AMR_MAP_STGR, amd->regs + AMD7930_CR);
400         sbus_writeb(((map->stgr >> 0) & 0xff), amd->regs + AMD7930_DR);
401         sbus_writeb(((map->stgr >> 8) & 0xff), amd->regs + AMD7930_DR);
402
403         sbus_writeb(AMR_MAP_GER, amd->regs + AMD7930_CR);
404         sbus_writeb(((map->ger >> 0) & 0xff), amd->regs + AMD7930_DR);
405         sbus_writeb(((map->ger >> 8) & 0xff), amd->regs + AMD7930_DR);
406
407         sbus_writeb(AMR_MAP_MMR1, amd->regs + AMD7930_CR);
408         sbus_writeb(map->mmr1, amd->regs + AMD7930_DR);
409
410         sbus_writeb(AMR_MAP_MMR2, amd->regs + AMD7930_CR);
411         sbus_writeb(map->mmr2, amd->regs + AMD7930_DR);
412 }
413
414 /* gx, gr & stg gains.  this table must contain 256 elements with
415  * the 0th being "infinity" (the magic value 9008).  The remaining
416  * elements match sun's gain curve (but with higher resolution):
417  * -18 to 0dB in .16dB steps then 0 to 12dB in .08dB steps.
418  */
419 static __const__ __u16 gx_coeff[256] = {
420         0x9008, 0x8b7c, 0x8b51, 0x8b45, 0x8b42, 0x8b3b, 0x8b36, 0x8b33,
421         0x8b32, 0x8b2a, 0x8b2b, 0x8b2c, 0x8b25, 0x8b23, 0x8b22, 0x8b22,
422         0x9122, 0x8b1a, 0x8aa3, 0x8aa3, 0x8b1c, 0x8aa6, 0x912d, 0x912b,
423         0x8aab, 0x8b12, 0x8aaa, 0x8ab2, 0x9132, 0x8ab4, 0x913c, 0x8abb,
424         0x9142, 0x9144, 0x9151, 0x8ad5, 0x8aeb, 0x8a79, 0x8a5a, 0x8a4a,
425         0x8b03, 0x91c2, 0x91bb, 0x8a3f, 0x8a33, 0x91b2, 0x9212, 0x9213,
426         0x8a2c, 0x921d, 0x8a23, 0x921a, 0x9222, 0x9223, 0x922d, 0x9231,
427         0x9234, 0x9242, 0x925b, 0x92dd, 0x92c1, 0x92b3, 0x92ab, 0x92a4,
428         0x92a2, 0x932b, 0x9341, 0x93d3, 0x93b2, 0x93a2, 0x943c, 0x94b2,
429         0x953a, 0x9653, 0x9782, 0x9e21, 0x9d23, 0x9cd2, 0x9c23, 0x9baa,
430         0x9bde, 0x9b33, 0x9b22, 0x9b1d, 0x9ab2, 0xa142, 0xa1e5, 0x9a3b,
431         0xa213, 0xa1a2, 0xa231, 0xa2eb, 0xa313, 0xa334, 0xa421, 0xa54b,
432         0xada4, 0xac23, 0xab3b, 0xaaab, 0xaa5c, 0xb1a3, 0xb2ca, 0xb3bd,
433         0xbe24, 0xbb2b, 0xba33, 0xc32b, 0xcb5a, 0xd2a2, 0xe31d, 0x0808,
434         0x72ba, 0x62c2, 0x5c32, 0x52db, 0x513e, 0x4cce, 0x43b2, 0x4243,
435         0x41b4, 0x3b12, 0x3bc3, 0x3df2, 0x34bd, 0x3334, 0x32c2, 0x3224,
436         0x31aa, 0x2a7b, 0x2aaa, 0x2b23, 0x2bba, 0x2c42, 0x2e23, 0x25bb,
437         0x242b, 0x240f, 0x231a, 0x22bb, 0x2241, 0x2223, 0x221f, 0x1a33,
438         0x1a4a, 0x1acd, 0x2132, 0x1b1b, 0x1b2c, 0x1b62, 0x1c12, 0x1c32,
439         0x1d1b, 0x1e71, 0x16b1, 0x1522, 0x1434, 0x1412, 0x1352, 0x1323,
440         0x1315, 0x12bc, 0x127a, 0x1235, 0x1226, 0x11a2, 0x1216, 0x0a2a,
441         0x11bc, 0x11d1, 0x1163, 0x0ac2, 0x0ab2, 0x0aab, 0x0b1b, 0x0b23,
442         0x0b33, 0x0c0f, 0x0bb3, 0x0c1b, 0x0c3e, 0x0cb1, 0x0d4c, 0x0ec1,
443         0x079a, 0x0614, 0x0521, 0x047c, 0x0422, 0x03b1, 0x03e3, 0x0333,
444         0x0322, 0x031c, 0x02aa, 0x02ba, 0x02f2, 0x0242, 0x0232, 0x0227,
445         0x0222, 0x021b, 0x01ad, 0x0212, 0x01b2, 0x01bb, 0x01cb, 0x01f6,
446         0x0152, 0x013a, 0x0133, 0x0131, 0x012c, 0x0123, 0x0122, 0x00a2,
447         0x011b, 0x011e, 0x0114, 0x00b1, 0x00aa, 0x00b3, 0x00bd, 0x00ba,
448         0x00c5, 0x00d3, 0x00f3, 0x0062, 0x0051, 0x0042, 0x003b, 0x0033,
449         0x0032, 0x002a, 0x002c, 0x0025, 0x0023, 0x0022, 0x001a, 0x0021,
450         0x001b, 0x001b, 0x001d, 0x0015, 0x0013, 0x0013, 0x0012, 0x0012,
451         0x000a, 0x000a, 0x0011, 0x0011, 0x000b, 0x000b, 0x000c, 0x000e,
452 };
453
454 static __const__ __u16 ger_coeff[] = {
455         0x431f, /* 5. dB */
456         0x331f, /* 5.5 dB */
457         0x40dd, /* 6. dB */
458         0x11dd, /* 6.5 dB */
459         0x440f, /* 7. dB */
460         0x411f, /* 7.5 dB */
461         0x311f, /* 8. dB */
462         0x5520, /* 8.5 dB */
463         0x10dd, /* 9. dB */
464         0x4211, /* 9.5 dB */
465         0x410f, /* 10. dB */
466         0x111f, /* 10.5 dB */
467         0x600b, /* 11. dB */
468         0x00dd, /* 11.5 dB */
469         0x4210, /* 12. dB */
470         0x110f, /* 13. dB */
471         0x7200, /* 14. dB */
472         0x2110, /* 15. dB */
473         0x2200, /* 15.9 dB */
474         0x000b, /* 16.9 dB */
475         0x000f  /* 18. dB */
476 };
477 #define NR_GER_COEFFS (sizeof(ger_coeff) / sizeof(ger_coeff[0]))
478
479 /* Update amd7930_map settings and program them into the hardware.
480  * The amd->lock is held and local interrupts are disabled.
481  */
482 static void __amd7930_update_map(amd7930_t *amd)
483 {
484         struct amd7930_map *map = &amd->map;
485         int level;
486
487         map->gx = gx_coeff[amd->rgain];
488         map->stgr = gx_coeff[amd->mgain];
489         level = (amd->pgain * (256 + NR_GER_COEFFS)) >> 8;
490         if (level >= 256) {
491                 map->ger = ger_coeff[level - 256];
492                 map->gr = gx_coeff[255];
493         } else {
494                 map->ger = ger_coeff[0];
495                 map->gr = gx_coeff[level];
496         }
497         __amd7930_write_map(amd);
498 }
499
500 static irqreturn_t snd_amd7930_interrupt(int irq, void *dev_id, struct pt_regs *regs)
501 {
502         amd7930_t *amd = dev_id;
503         unsigned int elapsed;
504         u8 ir;
505
506         spin_lock(&amd->lock);
507
508         elapsed = 0;
509
510         ir = sbus_readb(amd->regs + AMD7930_IR);
511         if (ir & AMR_IR_BBUF) {
512                 u8 byte;
513
514                 if (amd->flags & AMD7930_FLAG_PLAYBACK) {
515                         if (amd->p_left > 0) {
516                                 byte = *(amd->p_cur++);
517                                 amd->p_left--;
518                                 sbus_writeb(byte, amd->regs + AMD7930_BBTB);
519                                 if (amd->p_left == 0)
520                                         elapsed |= AMD7930_FLAG_PLAYBACK;
521                         } else
522                                 sbus_writeb(0, amd->regs + AMD7930_BBTB);
523                 } else if (amd->flags & AMD7930_FLAG_CAPTURE) {
524                         byte = sbus_readb(amd->regs + AMD7930_BBRB);
525                         if (amd->c_left > 0) {
526                                 *(amd->c_cur++) = byte;
527                                 amd->c_left--;
528                                 if (amd->c_left == 0)
529                                         elapsed |= AMD7930_FLAG_CAPTURE;
530                         }
531                 }
532         }
533         spin_unlock(&amd->lock);
534
535         if (elapsed & AMD7930_FLAG_PLAYBACK)
536                 snd_pcm_period_elapsed(amd->playback_substream);
537         else
538                 snd_pcm_period_elapsed(amd->capture_substream);
539
540         return IRQ_HANDLED;
541 }
542
543 static int snd_amd7930_trigger(amd7930_t *amd, unsigned int flag, int cmd)
544 {
545         unsigned long flags;
546         int result = 0;
547
548         spin_lock_irqsave(&amd->lock, flags);
549         if (cmd == SNDRV_PCM_TRIGGER_START) {
550                 if (!(amd->flags & flag)) {
551                         amd->flags |= flag;
552
553                         /* Enable B channel interrupts.  */
554                         sbus_writeb(AMR_MUX_MCR4, amd->regs + AMD7930_CR);
555                         sbus_writeb(AM_MUX_MCR4_ENABLE_INTS, amd->regs + AMD7930_DR);
556                 }
557         } else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
558                 if (amd->flags & flag) {
559                         amd->flags &= ~flag;
560
561                         /* Disable B channel interrupts.  */
562                         sbus_writeb(AMR_MUX_MCR4, amd->regs + AMD7930_CR);
563                         sbus_writeb(0, amd->regs + AMD7930_DR);
564                 }
565         } else {
566                 result = -EINVAL;
567         }
568         spin_unlock_irqrestore(&amd->lock, flags);
569
570         return result;
571 }
572
573 static int snd_amd7930_playback_trigger(snd_pcm_substream_t * substream,
574                                         int cmd)
575 {
576         amd7930_t *amd = snd_pcm_substream_chip(substream);
577         return snd_amd7930_trigger(amd, AMD7930_FLAG_PLAYBACK, cmd);
578 }
579
580 static int snd_amd7930_capture_trigger(snd_pcm_substream_t * substream,
581                                        int cmd)
582 {
583         amd7930_t *amd = snd_pcm_substream_chip(substream);
584         return snd_amd7930_trigger(amd, AMD7930_FLAG_CAPTURE, cmd);
585 }
586
587 static int snd_amd7930_playback_prepare(snd_pcm_substream_t * substream)
588 {
589         amd7930_t *amd = snd_pcm_substream_chip(substream);
590         snd_pcm_runtime_t *runtime = substream->runtime;
591         unsigned int size = snd_pcm_lib_buffer_bytes(substream);
592         unsigned long flags;
593         u8 new_mmr1;
594
595         spin_lock_irqsave(&amd->lock, flags);
596
597         amd->flags |= AMD7930_FLAG_PLAYBACK;
598
599         /* Setup the pseudo-dma transfer pointers.  */
600         amd->p_orig = amd->p_cur = runtime->dma_area;
601         amd->p_left = size;
602
603         /* Put the chip into the correct encoding format.  */
604         new_mmr1 = amd->map.mmr1;
605         if (runtime->format == SNDRV_PCM_FORMAT_A_LAW)
606                 new_mmr1 |= AM_MAP_MMR1_ALAW;
607         else
608                 new_mmr1 &= ~AM_MAP_MMR1_ALAW;
609         if (new_mmr1 != amd->map.mmr1) {
610                 amd->map.mmr1 = new_mmr1;
611                 __amd7930_update_map(amd);
612         }
613
614         spin_unlock_irqrestore(&amd->lock, flags);
615
616         return 0;
617 }
618
619 static int snd_amd7930_capture_prepare(snd_pcm_substream_t * substream)
620 {
621         amd7930_t *amd = snd_pcm_substream_chip(substream);
622         snd_pcm_runtime_t *runtime = substream->runtime;
623         unsigned int size = snd_pcm_lib_buffer_bytes(substream);
624         unsigned long flags;
625         u8 new_mmr1;
626
627         spin_lock_irqsave(&amd->lock, flags);
628
629         amd->flags |= AMD7930_FLAG_CAPTURE;
630
631         /* Setup the pseudo-dma transfer pointers.  */
632         amd->c_orig = amd->c_cur = runtime->dma_area;
633         amd->c_left = size;
634
635         /* Put the chip into the correct encoding format.  */
636         new_mmr1 = amd->map.mmr1;
637         if (runtime->format == SNDRV_PCM_FORMAT_A_LAW)
638                 new_mmr1 |= AM_MAP_MMR1_ALAW;
639         else
640                 new_mmr1 &= ~AM_MAP_MMR1_ALAW;
641         if (new_mmr1 != amd->map.mmr1) {
642                 amd->map.mmr1 = new_mmr1;
643                 __amd7930_update_map(amd);
644         }
645
646         spin_unlock_irqrestore(&amd->lock, flags);
647
648         return 0;
649 }
650
651 static snd_pcm_uframes_t snd_amd7930_playback_pointer(snd_pcm_substream_t * substream)
652 {
653         amd7930_t *amd = snd_pcm_substream_chip(substream);
654         size_t ptr;
655
656         if (!(amd->flags & AMD7930_FLAG_PLAYBACK))
657                 return 0;
658         ptr = amd->p_cur - amd->p_orig;
659         return bytes_to_frames(substream->runtime, ptr);
660 }
661
662 static snd_pcm_uframes_t snd_amd7930_capture_pointer(snd_pcm_substream_t * substream)
663 {
664         amd7930_t *amd = snd_pcm_substream_chip(substream);
665         size_t ptr;
666
667         if (!(amd->flags & AMD7930_FLAG_CAPTURE))
668                 return 0;
669
670         ptr = amd->c_cur - amd->c_orig;
671         return bytes_to_frames(substream->runtime, ptr);
672 }
673
674 /* Playback and capture have identical properties.  */
675 static snd_pcm_hardware_t snd_amd7930_pcm_hw =
676 {
677         .info                   = (SNDRV_PCM_INFO_MMAP |
678                                    SNDRV_PCM_INFO_MMAP_VALID |
679                                    SNDRV_PCM_INFO_INTERLEAVED |
680                                    SNDRV_PCM_INFO_BLOCK_TRANSFER |
681                                    SNDRV_PCM_INFO_HALF_DUPLEX),
682         .formats                = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
683         .rates                  = SNDRV_PCM_RATE_8000,
684         .rate_min               = 8000,
685         .rate_max               = 8000,
686         .channels_min           = 1,
687         .channels_max           = 1,
688         .buffer_bytes_max       = (64*1024),
689         .period_bytes_min       = 1,
690         .period_bytes_max       = (64*1024),
691         .periods_min            = 1,
692         .periods_max            = 1024,
693 };
694
695 static int snd_amd7930_playback_open(snd_pcm_substream_t * substream)
696 {
697         amd7930_t *amd = snd_pcm_substream_chip(substream);
698         snd_pcm_runtime_t *runtime = substream->runtime;
699
700         amd->playback_substream = substream;
701         runtime->hw = snd_amd7930_pcm_hw;
702         return 0;
703 }
704
705 static int snd_amd7930_capture_open(snd_pcm_substream_t * substream)
706 {
707         amd7930_t *amd = snd_pcm_substream_chip(substream);
708         snd_pcm_runtime_t *runtime = substream->runtime;
709
710         amd->capture_substream = substream;
711         runtime->hw = snd_amd7930_pcm_hw;
712         return 0;
713 }
714
715 static int snd_amd7930_playback_close(snd_pcm_substream_t * substream)
716 {
717         amd7930_t *amd = snd_pcm_substream_chip(substream);
718
719         amd->playback_substream = NULL;
720         return 0;
721 }
722
723 static int snd_amd7930_capture_close(snd_pcm_substream_t * substream)
724 {
725         amd7930_t *amd = snd_pcm_substream_chip(substream);
726
727         amd->capture_substream = NULL;
728         return 0;
729 }
730
731 static int snd_amd7930_hw_params(snd_pcm_substream_t * substream,
732                                     snd_pcm_hw_params_t * hw_params)
733 {
734         return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
735 }
736
737 static int snd_amd7930_hw_free(snd_pcm_substream_t * substream)
738 {
739         return snd_pcm_lib_free_pages(substream);
740 }
741
742 static snd_pcm_ops_t snd_amd7930_playback_ops = {
743         .open           =       snd_amd7930_playback_open,
744         .close          =       snd_amd7930_playback_close,
745         .ioctl          =       snd_pcm_lib_ioctl,
746         .hw_params      =       snd_amd7930_hw_params,
747         .hw_free        =       snd_amd7930_hw_free,
748         .prepare        =       snd_amd7930_playback_prepare,
749         .trigger        =       snd_amd7930_playback_trigger,
750         .pointer        =       snd_amd7930_playback_pointer,
751 };
752
753 static snd_pcm_ops_t snd_amd7930_capture_ops = {
754         .open           =       snd_amd7930_capture_open,
755         .close          =       snd_amd7930_capture_close,
756         .ioctl          =       snd_pcm_lib_ioctl,
757         .hw_params      =       snd_amd7930_hw_params,
758         .hw_free        =       snd_amd7930_hw_free,
759         .prepare        =       snd_amd7930_capture_prepare,
760         .trigger        =       snd_amd7930_capture_trigger,
761         .pointer        =       snd_amd7930_capture_pointer,
762 };
763
764 static void snd_amd7930_pcm_free(snd_pcm_t *pcm)
765 {
766         amd7930_t *amd = snd_magic_cast(amd7930_t, pcm->private_data, return);
767
768         amd->pcm = NULL;
769         snd_pcm_lib_preallocate_free_for_all(pcm);
770 }
771
772 static int __init snd_amd7930_pcm(amd7930_t *amd)
773 {
774         snd_pcm_t *pcm;
775         int err;
776
777         if ((err = snd_pcm_new(amd->card,
778                                /* ID */             "sun_amd7930",
779                                /* device */         0,
780                                /* playback count */ 1,
781                                /* capture count */  1, &pcm)) < 0)
782                 return err;
783         snd_assert(pcm != NULL, return -EINVAL);
784
785         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_amd7930_playback_ops);
786         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_amd7930_capture_ops);
787
788         pcm->private_data = amd;
789         pcm->private_free = snd_amd7930_pcm_free;
790         pcm->info_flags = 0;
791         strcpy(pcm->name, amd->card->shortname);
792         amd->pcm = pcm;
793
794         snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
795                                               snd_dma_continuous_data(GFP_KERNEL),
796                                               64*1024, 64*1024);
797
798         return 0;
799 }
800
801 #define VOLUME_MONITOR  0
802 #define VOLUME_CAPTURE  1
803 #define VOLUME_PLAYBACK 2
804
805 static int snd_amd7930_info_volume(snd_kcontrol_t *kctl, snd_ctl_elem_info_t *uinfo)
806 {
807         int type = kctl->private_value;
808
809         snd_assert(type == VOLUME_MONITOR ||
810                    type == VOLUME_CAPTURE ||
811                    type == VOLUME_PLAYBACK, return -EINVAL);
812         (void) type;
813
814         uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
815         uinfo->count = 1;
816         uinfo->value.integer.min = 0;
817         uinfo->value.integer.max = 255;
818
819         return 0;
820 }
821
822 static int snd_amd7930_get_volume(snd_kcontrol_t *kctl, snd_ctl_elem_value_t *ucontrol)
823 {
824         amd7930_t *amd = snd_kcontrol_chip(kctl);
825         int type = kctl->private_value;
826         int *swval;
827
828         snd_assert(type == VOLUME_MONITOR ||
829                    type == VOLUME_CAPTURE ||
830                    type == VOLUME_PLAYBACK, return -EINVAL);
831
832         switch (type) {
833         case VOLUME_MONITOR:
834                 swval = &amd->mgain;
835                 break;
836         case VOLUME_CAPTURE:
837                 swval = &amd->rgain;
838                 break;
839         case VOLUME_PLAYBACK:
840         default:
841                 swval = &amd->pgain;
842                 break;
843         };
844
845         ucontrol->value.integer.value[0] = *swval;
846
847         return 0;
848 }
849
850 static int snd_amd7930_put_volume(snd_kcontrol_t *kctl, snd_ctl_elem_value_t *ucontrol)
851 {
852         amd7930_t *amd = snd_kcontrol_chip(kctl);
853         unsigned long flags;
854         int type = kctl->private_value;
855         int *swval, change;
856
857         snd_assert(type == VOLUME_MONITOR ||
858                    type == VOLUME_CAPTURE ||
859                    type == VOLUME_PLAYBACK, return -EINVAL);
860
861         switch (type) {
862         case VOLUME_MONITOR:
863                 swval = &amd->mgain;
864                 break;
865         case VOLUME_CAPTURE:
866                 swval = &amd->rgain;
867                 break;
868         case VOLUME_PLAYBACK:
869         default:
870                 swval = &amd->pgain;
871                 break;
872         };
873
874         spin_lock_irqsave(&amd->lock, flags);
875
876         if (*swval != ucontrol->value.integer.value[0]) {
877                 *swval = ucontrol->value.integer.value[0];
878                 __amd7930_update_map(amd);
879                 change = 1;
880         } else
881                 change = 0;
882
883         spin_unlock_irqrestore(&amd->lock, flags);
884
885         return change;
886 }
887
888 static snd_kcontrol_new_t amd7930_controls[] __initdata = {
889         {
890                 .iface          =       SNDRV_CTL_ELEM_IFACE_MIXER,
891                 .name           =       "Monitor Volume",
892                 .index          =       0,
893                 .info           =       snd_amd7930_info_volume,
894                 .get            =       snd_amd7930_get_volume,
895                 .put            =       snd_amd7930_put_volume,
896                 .private_value  =       VOLUME_MONITOR,
897         },
898         {
899                 .iface          =       SNDRV_CTL_ELEM_IFACE_MIXER,
900                 .name           =       "Capture Volume",
901                 .index          =       0,
902                 .info           =       snd_amd7930_info_volume,
903                 .get            =       snd_amd7930_get_volume,
904                 .put            =       snd_amd7930_put_volume,
905                 .private_value  =       VOLUME_CAPTURE,
906         },
907         {
908                 .iface          =       SNDRV_CTL_ELEM_IFACE_MIXER,
909                 .name           =       "Playback Volume",
910                 .index          =       0,
911                 .info           =       snd_amd7930_info_volume,
912                 .get            =       snd_amd7930_get_volume,
913                 .put            =       snd_amd7930_put_volume,
914                 .private_value  =       VOLUME_PLAYBACK,
915         },
916 };
917
918 #define NUM_AMD7930_CONTROLS (sizeof(amd7930_controls)/sizeof(snd_kcontrol_new_t))
919
920 static int __init snd_amd7930_mixer(amd7930_t *amd)
921 {
922         snd_card_t *card;
923         int idx, err;
924
925         snd_assert(amd != NULL && amd->card != NULL, return -EINVAL);
926
927         card = amd->card;
928         strcpy(card->mixername, card->shortname);
929
930         for (idx = 0; idx < NUM_AMD7930_CONTROLS; idx++) {
931                 if ((err = snd_ctl_add(card,
932                                        snd_ctl_new1(&amd7930_controls[idx], amd))) < 0)
933                         return err;
934         }
935
936         return 0;
937 }
938
939 static int snd_amd7930_free(amd7930_t *amd)
940 {
941         amd7930_idle(amd);
942
943         if (amd->irq)
944                 free_irq(amd->irq, amd);
945
946         if (amd->regs)
947                 sbus_iounmap(amd->regs, amd->regs_size);
948
949         snd_magic_kfree(amd);
950
951         return 0;
952 }
953
954 static int snd_amd7930_dev_free(snd_device_t *device)
955 {
956         amd7930_t *amd = snd_magic_cast(amd7930_t, device->device_data, return -ENXIO);
957
958         return snd_amd7930_free(amd);
959 }
960
961 static snd_device_ops_t snd_amd7930_dev_ops = {
962         .dev_free       =       snd_amd7930_dev_free,
963 };
964
965 static int __init snd_amd7930_create(snd_card_t *card,
966                                      struct sbus_dev *sdev,
967                                      struct resource *rp,
968                                      unsigned int reg_size,
969                                      struct linux_prom_irqs *irq_prop,
970                                      int dev,
971                                      amd7930_t **ramd)
972 {
973         unsigned long flags;
974         amd7930_t *amd;
975         int err;
976
977         *ramd = NULL;
978         amd = snd_magic_kcalloc(amd7930_t, 0, GFP_KERNEL);
979         if (amd == NULL)
980                 return -ENOMEM;
981
982         spin_lock_init(&amd->lock);
983         amd->card = card;
984         amd->sdev = sdev;
985         amd->regs_size = reg_size;
986
987         amd->regs = sbus_ioremap(rp, 0, amd->regs_size, "amd7930");
988         if (!amd->regs) {
989                 snd_printk("amd7930-%d: Unable to map chip registers.\n", dev);
990                 return -EIO;
991         }
992
993         amd7930_idle(amd);
994
995         if (request_irq(irq_prop->pri, snd_amd7930_interrupt,
996                         SA_INTERRUPT | SA_SHIRQ, "amd7930", amd)) {
997                 snd_printk("amd7930-%d: Unable to grab IRQ %s\n",
998                            dev,
999                            __irq_itoa(irq_prop->pri));
1000                 snd_amd7930_free(amd);
1001                 return -EBUSY;
1002         }
1003         amd->irq = irq_prop->pri;
1004
1005         amd7930_enable_ints(amd);
1006
1007         spin_lock_irqsave(&amd->lock, flags);
1008
1009         amd->rgain = 128;
1010         amd->pgain = 200;
1011         amd->mgain = 0;
1012
1013         memset(&amd->map, 0, sizeof(amd->map));
1014         amd->map.mmr1 = (AM_MAP_MMR1_GX | AM_MAP_MMR1_GER |
1015                          AM_MAP_MMR1_GR | AM_MAP_MMR1_STG);
1016         amd->map.mmr2 = (AM_MAP_MMR2_LS | AM_MAP_MMR2_AINB);
1017
1018         __amd7930_update_map(amd);
1019
1020         /* Always MUX audio (Ba) to channel Bb. */
1021         sbus_writeb(AMR_MUX_MCR1, amd->regs + AMD7930_CR);
1022         sbus_writeb(AM_MUX_CHANNEL_Ba | (AM_MUX_CHANNEL_Bb << 4),
1023                     amd->regs + AMD7930_DR);
1024
1025         spin_unlock_irqrestore(&amd->lock, flags);
1026
1027         if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
1028                                   amd, &snd_amd7930_dev_ops)) < 0) {
1029                 snd_amd7930_free(amd);
1030                 return err;
1031         }
1032
1033         *ramd = amd;
1034         return 0;
1035 }
1036
1037 static int __init amd7930_attach(int prom_node, struct sbus_dev *sdev)
1038 {
1039         static int dev;
1040         struct linux_prom_registers reg_prop;
1041         struct linux_prom_irqs irq_prop;
1042         struct resource res, *rp;
1043         snd_card_t *card;
1044         amd7930_t *amd;
1045         int err;
1046
1047         if (dev >= SNDRV_CARDS)
1048                 return -ENODEV;
1049         if (!enable[dev]) {
1050                 dev++;
1051                 return -ENOENT;
1052         }
1053
1054         err = prom_getproperty(prom_node, "intr",
1055                                (char *) &irq_prop, sizeof(irq_prop));
1056         if (err < 0) {
1057                 snd_printk("amd7930-%d: Firmware node lacks IRQ property.\n", dev);
1058                 return -ENODEV;
1059         }
1060
1061         err = prom_getproperty(prom_node, "reg",
1062                                (char *) &reg_prop, sizeof(reg_prop));
1063         if (err < 0) {
1064                 snd_printk("amd7930-%d: Firmware node lacks register property.\n", dev);
1065                 return -ENODEV;
1066         }
1067
1068         if (sdev) {
1069                 rp = &sdev->resource[0];
1070         } else {
1071                 rp = &res;
1072                 rp->start = reg_prop.phys_addr;
1073                 rp->end = rp->start + reg_prop.reg_size - 1;
1074                 rp->flags = IORESOURCE_IO | (reg_prop.which_io & 0xff);
1075         }
1076
1077         card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
1078         if (card == NULL)
1079                 return -ENOMEM;
1080
1081         strcpy(card->driver, "AMD7930");
1082         strcpy(card->shortname, "Sun AMD7930");
1083         sprintf(card->longname, "%s at 0x%02lx:0x%08lx, irq %s",
1084                 card->shortname,
1085                 rp->flags & 0xffL,
1086                 rp->start,
1087                 __irq_itoa(irq_prop.pri));
1088
1089         if ((err = snd_amd7930_create(card, sdev, rp, reg_prop.reg_size,
1090                                           &irq_prop, dev, &amd)) < 0)
1091                 goto out_err;
1092
1093         if ((err = snd_amd7930_pcm(amd)) < 0)
1094                 goto out_err;
1095
1096         if ((err = snd_amd7930_mixer(amd)) < 0)
1097                 goto out_err;
1098
1099         if ((err = snd_card_register(card)) < 0)
1100                 goto out_err;
1101
1102         amd->next = amd7930_list;
1103         amd7930_list = amd;
1104
1105         dev++;
1106         return 0;
1107
1108 out_err:
1109         snd_card_free(card);
1110         return err;
1111 }
1112
1113 static int __init amd7930_init(void)
1114 {
1115         struct sbus_bus *sbus;
1116         struct sbus_dev *sdev;
1117         int node, found;
1118
1119         found = 0;
1120
1121         /* Try to find the sun4c "audio" node first. */
1122         node = prom_getchild(prom_root_node);
1123         node = prom_searchsiblings(node, "audio");
1124         if (node && amd7930_attach(node, NULL) == 0)
1125                 found++;
1126
1127         /* Probe each SBUS for amd7930 chips. */
1128         for_all_sbusdev(sdev, sbus) {
1129                 if (!strcmp(sdev->prom_name, "audio")) {
1130                         if (amd7930_attach(sdev->prom_node, sdev) == 0)
1131                                 found++;
1132                 }
1133         }
1134
1135         return (found > 0) ? 0 : -EIO;
1136 }
1137
1138 static void __exit amd7930_exit(void)
1139 {
1140         amd7930_t *p = amd7930_list;
1141
1142         while (p != NULL) {
1143                 amd7930_t *next = p->next;
1144
1145                 snd_card_free(p->card);
1146
1147                 p = next;
1148         }
1149
1150         amd7930_list = NULL;
1151 }
1152
1153 module_init(amd7930_init);
1154 module_exit(amd7930_exit);
1155
1156 #ifndef MODULE
1157
1158 /* format is: snd-sun-amd7930=index,id,enable */
1159
1160 static int __init alsa_card_sun_amd7930_setup(char *str)
1161 {
1162         static unsigned __initdata nr_dev = 0;
1163
1164         if (nr_dev >= SNDRV_CARDS)
1165                 return 0;
1166         (void)(get_option(&str,&index[nr_dev]) == 2 &&
1167                get_option(&str,&id[nr_dev]) == 2 &&
1168                get_id(&str,&enable[nr_dev]) == 2);
1169         nr_dev++;
1170         return 1;
1171 }
1172
1173 __setup("snd-sun-amd7930=", alsa_card_sun_amd7930_setup);
1174
1175 #endif /* ifndef MODULE */