xref: /haiku/src/add-ons/kernel/drivers/audio/ac97/auvia/auvia.c (revision 1d9d47fc72028bb71b5f232a877231e59cfe2438)
1 /*
2  * Auvia BeOS Driver for Via VT82xx Southbridge audio
3  *
4  * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr)
5 
6  * This code is derived from software contributed to The NetBSD Foundation
7  * by Tyler C. Sarna
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the NetBSD
20  *	Foundation, Inc. and its contributors.
21  * 4. Neither the name of The NetBSD Foundation nor the names of its
22  *    contributors may be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 #include <KernelExport.h>
39 #include <PCI.h>
40 #include <string.h>
41 #include <stdio.h>
42 #include "auvia.h"
43 #include "debug.h"
44 #include "config.h"
45 #include "util.h"
46 #include "io.h"
47 #include <fcntl.h>
48 #include <unistd.h>
49 #include <malloc.h>
50 #include "ac97.h"
51 
52 status_t init_hardware(void);
53 status_t init_driver(void);
54 void uninit_driver(void);
55 const char ** publish_devices(void);
56 device_hooks * find_device(const char *);
57 
58 static char pci_name[] = B_PCI_MODULE_NAME;
59 pci_module_info	*pci;
60 
61 int32 num_cards;
62 auvia_dev cards[NUM_CARDS];
63 int32 num_names;
64 char * names[NUM_CARDS*20+1];
65 
66 extern device_hooks multi_hooks;
67 
68 /* Auvia Memory management */
69 
70 static auvia_mem *
71 auvia_mem_new(auvia_dev *card, size_t size)
72 {
73 	auvia_mem *mem;
74 
75 	if ((mem = malloc(sizeof(*mem))) == NULL)
76 		return (NULL);
77 
78 	mem->area = alloc_mem(&mem->phy_base, &mem->log_base, size, "auvia buffer");
79 	mem->size = size;
80 	if (mem->area < B_OK) {
81 		free(mem);
82 		return NULL;
83 	}
84 	return mem;
85 }
86 
87 static void
88 auvia_mem_delete(auvia_mem *mem)
89 {
90 	if(mem->area > B_OK)
91 		delete_area(mem->area);
92 	free(mem);
93 }
94 
95 static void *
96 auvia_mem_alloc(auvia_dev *card, size_t size)
97 {
98 	auvia_mem *mem;
99 
100 	mem = auvia_mem_new(card, size);
101 	if (mem == NULL)
102 		return (NULL);
103 
104 	LIST_INSERT_HEAD(&(card->mems), mem, next);
105 
106 	return mem;
107 }
108 
109 static void
110 auvia_mem_free(auvia_dev *card, void *ptr)
111 {
112 	auvia_mem 		*mem;
113 
114 	LIST_FOREACH(mem, &card->mems, next) {
115 		if (mem->log_base != ptr)
116 			continue;
117 		LIST_REMOVE(mem, next);
118 
119 		auvia_mem_delete(mem);
120 		break;
121 	}
122 }
123 
124 /*	Auvia stream functions */
125 
126 status_t
127 auvia_stream_set_audioparms(auvia_stream *stream, uint8 channels,
128      uint8 b16, uint32 sample_rate)
129 {
130 	uint8 			sample_size, frame_size;
131 	LOG(("auvia_stream_set_audioparms\n"));
132 
133 	if ((stream->channels == channels) &&
134 		(stream->b16 == b16) &&
135 		(stream->sample_rate == sample_rate))
136 		return B_OK;
137 
138 	if(stream->buffer)
139 		auvia_mem_free(stream->card, stream->buffer->log_base);
140 
141 	stream->b16 = b16;
142 	stream->sample_rate = sample_rate;
143 	stream->channels = channels;
144 
145 	sample_size = stream->b16 + 1;
146 	frame_size = sample_size * stream->channels;
147 
148 	stream->buffer = auvia_mem_alloc(stream->card, stream->bufframes * frame_size * stream->bufcount);
149 
150 	stream->trigblk = 0;	/* This shouldn't be needed */
151 	stream->blkmod = stream->bufcount;
152 	stream->blksize = stream->bufframes * frame_size;
153 
154 	return B_OK;
155 }
156 
157 status_t
158 auvia_stream_commit_parms(auvia_stream *stream)
159 {
160 	int             i;
161 	uint32      	*page;
162 	uint32			value;
163 	LOG(("auvia_stream_commit_parms\n"));
164 
165 	page = stream->dmaops_log_base;
166 
167 	for(i=0; i<stream->bufcount; i++) {
168 		page[2*i] = ((uint32)stream->buffer->phy_base) +
169 			i * stream->blksize;
170 		page[2*i + 1] = AUVIA_DMAOP_FLAG | stream->blksize;
171 	}
172 
173 	page[2*stream->bufcount - 1] &= ~AUVIA_DMAOP_FLAG;
174 	page[2*stream->bufcount - 1] |= AUVIA_DMAOP_EOL;
175 
176 	auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE,
177 		(uint32)stream->dmaops_phy_base);
178 
179 	if(stream->use & AUVIA_USE_RECORD)
180 		auvia_codec_write(&stream->card->config, AC97_PCM_LR_ADC_RATE, (uint16)stream->sample_rate);
181 	else
182 		auvia_codec_write(&stream->card->config, AC97_PCM_FRONT_DAC_RATE, (uint16)stream->sample_rate);
183 
184 	if(IS_8233(&stream->card->config)) {
185 		if(stream->base != AUVIA_8233_MP_BASE) {
186 			value = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_8233_RP_RATEFMT);
187 			value &= ~(AUVIA_8233_RATEFMT_48K | AUVIA_8233_RATEFMT_STEREO | AUVIA_8233_RATEFMT_16BIT);
188 			if(stream->use & AUVIA_USE_PLAY)
189 				value |= AUVIA_8233_RATEFMT_48K * (stream->sample_rate / 20) / (48000 / 20);
190 			value |= (stream->channels == 2 ? AUVIA_8233_RATEFMT_STEREO : 0)
191 				| (stream->b16 ? AUVIA_8233_RATEFMT_16BIT : 0);
192 			auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_8233_RP_RATEFMT, value);
193 		} else {
194 			static const uint32 slottab[7] = {0, 0xff000011, 0xff000021, 0xff000521,
195 											0xff004321, 0xff054321, 0xff654321};
196 			value = (stream->b16 ? AUVIA_8233_MP_FORMAT_16BIT : AUVIA_8233_MP_FORMAT_8BIT)
197 				| ((stream->channels << 4) & AUVIA_8233_MP_FORMAT_CHANNEL_MASK) ;
198 			auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_OFF_MP_FORMAT, value);
199 			auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_8233_OFF_MP_STOP,
200 				slottab[stream->channels]);
201 		}
202 	}
203 	//auvia_codec_write(&stream->card->config, AC97_SPDIF_CONTROL, (uint16)stream->sample_rate);
204 
205 	return B_OK;
206 }
207 
208 status_t
209 auvia_stream_get_nth_buffer(auvia_stream *stream, uint8 chan, uint8 buf,
210 					char** buffer, size_t *stride)
211 {
212 	uint8 			sample_size, frame_size;
213 	LOG(("auvia_stream_get_nth_buffer\n"));
214 
215 	sample_size = stream->b16 + 1;
216 	frame_size = sample_size * stream->channels;
217 
218 	*buffer = stream->buffer->log_base + (buf * stream->bufframes * frame_size)
219 		+ chan * sample_size;
220 	*stride = frame_size;
221 
222 	return B_OK;
223 }
224 
225 static uint32
226 auvia_stream_curaddr(auvia_stream *stream)
227 {
228 	uint32 addr;
229 	if(IS_8233(&stream->card->config)) {
230 		addr = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE);
231 		TRACE(("stream_curaddr %p, phy_base %p\n", addr, (uint32)stream->dmaops_phy_base));
232 		return (addr - (uint32)stream->dmaops_phy_base - 4) / 8;
233 	} else {
234 		addr = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE);
235 		TRACE(("stream_curaddr %p, phy_base %p\n", addr, (uint32)stream->dmaops_phy_base));
236 		return (addr - (uint32)stream->dmaops_phy_base - 8) / 8;
237 	}
238 }
239 
240 void
241 auvia_stream_start(auvia_stream *stream, void (*inth) (void *), void *inthparam)
242 {
243 	LOG(("auvia_stream_start\n"));
244 
245 	stream->inth = inth;
246 	stream->inthparam = inthparam;
247 
248 	stream->state |= AUVIA_STATE_STARTED;
249 
250 	if(IS_8233(&stream->card->config)) {
251 		if(stream->base != AUVIA_8233_MP_BASE) {
252 			auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_RP_DXS_LVOL, 0);
253 			auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_RP_DXS_RVOL, 0);
254 		}
255 		auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL,
256 			AUVIA_RPCTRL_START | AUVIA_RPCTRL_AUTOSTART | AUVIA_RPCTRL_STOP
257 			| AUVIA_RPCTRL_EOL | AUVIA_RPCTRL_FLAG);
258 	} else {
259 		uint8 regvalue = (stream->channels > 1 ? AUVIA_RPMODE_STEREO : 0)
260 			| (stream->b16 == 1 ? AUVIA_RPMODE_16BIT : 0)
261 			| AUVIA_RPMODE_INTR_FLAG | AUVIA_RPMODE_INTR_EOL | AUVIA_RPMODE_AUTOSTART;
262 		auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_MODE, regvalue);
263 		auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START);
264 	}
265 }
266 
267 void
268 auvia_stream_halt(auvia_stream *stream)
269 {
270 	LOG(("auvia_stream_halt\n"));
271 
272 	stream->state &= ~AUVIA_STATE_STARTED;
273 
274 	auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE);
275 }
276 
277 auvia_stream *
278 auvia_stream_new(auvia_dev *card, uint8 use, uint32 bufframes, uint8 bufcount)
279 {
280 	auvia_stream *stream;
281 	cpu_status status;
282 	LOG(("auvia_stream_new\n"));
283 
284 	stream = malloc(sizeof(auvia_stream));
285 	if (stream == NULL)
286 		return (NULL);
287 	stream->card = card;
288 	stream->use = use;
289 	stream->state = !AUVIA_STATE_STARTED;
290 	stream->b16 = 0;
291 	stream->sample_rate = 0;
292 	stream->channels = 0;
293 	stream->bufframes = bufframes;
294 	stream->bufcount = bufcount;
295 	stream->inth = NULL;
296 	stream->inthparam = NULL;
297 	stream->buffer = NULL;
298 	stream->blksize = 0;
299 	stream->trigblk = 0;
300 	stream->blkmod = 0;
301 
302 	if(use & AUVIA_USE_PLAY) {
303 		if(IS_8233(&card->config))
304 			stream->base = AUVIA_8233_MP_BASE;
305 			//stream->base = AUVIA_PLAY_BASE;
306 		else
307 			stream->base = AUVIA_PLAY_BASE;
308 	} else {
309 		if(IS_8233(&card->config))
310 			stream->base = AUVIA_8233_RECORD_BASE;
311 		else
312 			stream->base = AUVIA_RECORD_BASE;
313 	}
314 
315 	stream->frames_count = 0;
316 	stream->real_time = 0;
317 	stream->update_needed = false;
318 
319 	/* allocate memory for our dma ops */
320 	stream->dmaops_area = alloc_mem(&stream->dmaops_phy_base, &stream->dmaops_log_base,
321 		VIA_TABLE_SIZE, "auvia dmaops");
322 
323 	if (stream->dmaops_area < B_OK) {
324 		PRINT(("couldn't allocate memory\n"));
325 		free(stream);
326 		return NULL;
327 	}
328 
329 	status = lock();
330 	LIST_INSERT_HEAD((&card->streams), stream, next);
331 	unlock(status);
332 
333 	return stream;
334 }
335 
336 void
337 auvia_stream_delete(auvia_stream *stream)
338 {
339 	cpu_status status;
340 	LOG(("auvia_stream_delete\n"));
341 
342 	auvia_stream_halt(stream);
343 
344 	auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE, 0);
345 
346 	if (stream->dmaops_area > B_OK)
347 		delete_area(stream->dmaops_area);
348 
349 	if(stream->buffer)
350 		auvia_mem_free(stream->card, stream->buffer->log_base);
351 
352 	status = lock();
353 	LIST_REMOVE(stream, next);
354 	unlock(status);
355 
356 	free(stream);
357 }
358 
359 /* Auvia interrupt */
360 
361 static int32
362 auvia_int(void *arg)
363 {
364 	auvia_dev	 *card = arg;
365 	bool gotone = false;
366 	uint32       curblk;
367 	auvia_stream *stream;
368 
369 	if(auvia_reg_read_32(&card->config, AUVIA_SGD_SHADOW)
370 		& card->interrupt_mask) {
371 
372 		LIST_FOREACH(stream, &card->streams, next)
373 			if(auvia_reg_read_8(&card->config, stream->base + AUVIA_RP_STAT) & AUVIA_RPSTAT_INTR) {
374 				gotone = true;
375 				//TRACE(("interrupt\n"));
376 
377 				curblk = auvia_stream_curaddr(stream);
378 				TRACE(("RPSTAT_INTR at trigblk %lu, stream->trigblk %lu\n", curblk, stream->trigblk));
379 				if (curblk == stream->trigblk) {
380 					//TRACE(("AUVIA_RPSTAT_INTR at trigblk %lu\n", curblk));
381 
382 					if(stream->inth)
383 						stream->inth(stream->inthparam);
384 
385 					stream->trigblk++;
386 					stream->trigblk %= stream->blkmod;
387 				}
388 
389 				auvia_reg_write_8(&card->config, stream->base + AUVIA_RP_STAT, AUVIA_RPSTAT_INTR);
390 			}
391 	} else {
392 		TRACE(("SGD_SHADOW %x %x\n", card->interrupt_mask, auvia_reg_read_32(&card->config, AUVIA_SGD_SHADOW)));
393 	}
394 
395 	if(gotone)
396 		return B_INVOKE_SCHEDULER;
397 
398 	TRACE(("Got unhandled interrupt\n"));
399 	return B_UNHANDLED_INTERRUPT;
400 }
401 
402 /*	Auvia driver functions */
403 
404 /* detect presence of our hardware */
405 status_t
406 init_hardware(void)
407 {
408 	int ix=0;
409 	pci_info info;
410 	status_t err = ENODEV;
411 
412 	LOG_CREATE();
413 
414 	PRINT(("init_hardware()\n"));
415 
416 	if (get_module(pci_name, (module_info **)&pci))
417 		return ENOSYS;
418 
419 	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
420 		if (info.vendor_id == VIATECH_VENDOR_ID &&
421 			(info.device_id == VIATECH_82C686_AC97_DEVICE_ID
422 			|| info.device_id == VIATECH_8233_AC97_DEVICE_ID
423 			)) {
424 			err = B_OK;
425 		}
426 		ix++;
427 	}
428 
429 	put_module(pci_name);
430 
431 	return err;
432 }
433 
434 static void
435 make_device_names(
436 	auvia_dev * card)
437 {
438 	sprintf(card->name, "audio/hmulti/auvia/%ld", card-cards+1);
439 	names[num_names++] = card->name;
440 
441 	names[num_names] = NULL;
442 }
443 
444 
445 static status_t
446 auvia_init(auvia_dev * card)
447 {
448 	uint32 pr;
449 
450 	pr = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, AUVIA_PCICONF_JUNK, 4);
451 	PRINT(("AUVIA_PCICONF_JUNK before: %lx\n", pr));
452 	pr &= ~AUVIA_PCICONF_ENABLES;
453 	pr |= AUVIA_PCICONF_ACLINKENAB | AUVIA_PCICONF_ACNOTRST | AUVIA_PCICONF_ACVSR | AUVIA_PCICONF_ACSGD;
454 	pr &= ~(AUVIA_PCICONF_ACFM | AUVIA_PCICONF_ACSB);
455 	(*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function, AUVIA_PCICONF_JUNK, 4, pr );
456 	snooze(100);
457 	pr = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, AUVIA_PCICONF_JUNK, 4);
458 	PRINT(("AUVIA_PCICONF_JUNK after: %lx\n", pr));
459 
460 	if(IS_8233(&card->config)) {
461 		card->interrupt_mask =
462 			AUVIA_8233_SGD_STAT_FLAG_EOL |
463 			AUVIA_8233_SGD_STAT_FLAG_EOL << 4 |
464 			AUVIA_8233_SGD_STAT_FLAG_EOL << 8 |
465 			AUVIA_8233_SGD_STAT_FLAG_EOL << 12 |
466 			AUVIA_8233_SGD_STAT_FLAG_EOL << 16 |
467 			AUVIA_8233_SGD_STAT_FLAG_EOL << 24 |
468 			AUVIA_8233_SGD_STAT_FLAG_EOL << 28;
469 	} else {
470 		card->interrupt_mask = AUVIA_SGD_STAT_ALL | (AUVIA_SGD_STAT_ALL << 4);
471 	}
472 
473 
474 	/* Init streams list */
475 	LIST_INIT(&(card->streams));
476 
477 	/* Init mems list */
478 	LIST_INIT(&(card->mems));
479 
480 	return B_OK;
481 }
482 
483 static status_t
484 auvia_setup(auvia_dev * card)
485 {
486 	status_t err = B_OK;
487 	unsigned char cmd;
488 
489 	PRINT(("auvia_setup(%p)\n", card));
490 
491 	make_device_names(card);
492 
493 	card->config.nabmbar = card->info.u.h0.base_registers[0];
494 	card->config.irq = card->info.u.h0.interrupt_line;
495 	card->config.type = 0;
496 	if(card->info.device_id == VIATECH_82C686_AC97_DEVICE_ID)
497 		card->config.type |= TYPE_686;
498 	if(card->info.device_id == VIATECH_8233_AC97_DEVICE_ID)
499 		card->config.type |= TYPE_8233;
500 
501 	PRINT(("%s deviceid = %#04x chiprev = %x model = %x enhanced at %lx\n", card->name, card->info.device_id,
502 		card->info.revision, card->info.u.h0.subsystem_id, card->config.nabmbar));
503 
504 	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2);
505 	PRINT(("PCI command before: %x\n", cmd));
506 	(*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2, cmd | PCI_command_io);
507 	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2);
508 	PRINT(("PCI command after: %x\n", cmd));
509 
510 	/* reset the codec */
511 	PRINT(("codec reset\n"));
512 	auvia_codec_write(&card->config, 0x00, 0x0000);
513 	snooze(50000); // 50 ms
514 
515 	ac97_init(&card->config);
516 	ac97_amp_enable(&card->config, true);
517 
518 	PRINT(("codec vendor id      = %#08lx\n",ac97_get_vendor_id(&card->config)));
519 	PRINT(("codec description     = %s\n",ac97_get_vendor_id_description(&card->config)));
520 	PRINT(("codec 3d enhancement = %s\n",ac97_get_3d_stereo_enhancement(&card->config)));
521 
522 	PRINT(("installing interrupt : %lx\n", card->config.irq));
523 	install_io_interrupt_handler(card->config.irq, auvia_int, card, 0);
524 
525 	/*PRINT(("codec master output = %#04x\n",auvia_codec_read(&card->config, 0x02)));
526 	PRINT(("codec aux output    = %#04x\n",auvia_codec_read(&card->config, 0x04)));
527 	PRINT(("codec mono output   = %#04x\n",auvia_codec_read(&card->config, 0x06)));
528 	PRINT(("codec pcm output    = %#04x\n",auvia_codec_read(&card->config, 0x18)));
529 	PRINT(("codec line in	    = %#04x\n",auvia_codec_read(&card->config, 0x10)));
530 	PRINT(("codec record line in= %#04x\n",auvia_codec_read(&card->config, 0x1a)));
531 	PRINT(("codec record gain   = %#04x\n",auvia_codec_read(&card->config, 0x1c)));*/
532 
533 	PRINT(("writing codec registers\n"));
534 	// TODO : to move with AC97
535 	/* enable master output */
536 	auvia_codec_write(&card->config, AC97_MASTER_VOLUME, 0x0000);
537 	/* enable aux output */
538 	auvia_codec_write(&card->config, AC97_AUX_OUT_VOLUME, 0x0000);
539 	/* enable mono output */
540 	//auvia_codec_write(&card->config, AC97_MONO_VOLUME, 0x0004);
541 	/* enable pcm output */
542 	auvia_codec_write(&card->config, AC97_PCM_OUT_VOLUME, 0x0808);
543 	/* enable line in */
544 	//auvia_codec_write(&card->config, AC97_LINE_IN_VOLUME, 0x8808);
545 	/* set record line in */
546 	auvia_codec_write(&card->config, AC97_RECORD_SELECT, 0x0404);
547 	/* set record gain */
548 	//auvia_codec_write(&card->config, AC97_RECORD_GAIN, 0x0000);
549 
550 	PRINT(("codec master output = %#04x\n",auvia_codec_read(&card->config, AC97_MASTER_VOLUME)));
551 	PRINT(("codec aux output    = %#04x\n",auvia_codec_read(&card->config, AC97_AUX_OUT_VOLUME)));
552 	PRINT(("codec mono output   = %#04x\n",auvia_codec_read(&card->config, AC97_MONO_VOLUME)));
553 	PRINT(("codec pcm output    = %#04x\n",auvia_codec_read(&card->config, AC97_PCM_OUT_VOLUME)));
554 	PRINT(("codec line in	    = %#04x\n",auvia_codec_read(&card->config, AC97_LINE_IN_VOLUME)));
555 	PRINT(("codec record line in= %#04x\n",auvia_codec_read(&card->config, AC97_RECORD_SELECT)));
556 	PRINT(("codec record gain   = %#04x\n",auvia_codec_read(&card->config, AC97_RECORD_GAIN)));
557 
558 	if ((err = auvia_init(card)))
559 		return (err);
560 
561 	PRINT(("init_driver done\n"));
562 
563 	return err;
564 }
565 
566 
567 status_t
568 init_driver(void)
569 {
570 	int ix=0;
571 
572 	pci_info info;
573 	num_cards = 0;
574 
575 	PRINT(("init_driver()\n"));
576 	load_driver_symbols("auvia");
577 
578 	if (get_module(pci_name, (module_info **) &pci))
579 		return ENOSYS;
580 
581 	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
582 		if (info.vendor_id == VIATECH_VENDOR_ID &&
583 			(info.device_id == VIATECH_82C686_AC97_DEVICE_ID
584 			|| info.device_id == VIATECH_8233_AC97_DEVICE_ID
585 			)) {
586 			if (num_cards == NUM_CARDS) {
587 				PRINT(("Too many auvia cards installed!\n"));
588 				break;
589 			}
590 			memset(&cards[num_cards], 0, sizeof(auvia_dev));
591 			cards[num_cards].info = info;
592 			if (auvia_setup(&cards[num_cards])) {
593 				PRINT(("Setup of auvia %ld failed\n", num_cards+1));
594 			}
595 			else {
596 				num_cards++;
597 			}
598 		}
599 		ix++;
600 	}
601 	if (!num_cards) {
602 		PRINT(("no cards\n"));
603 		put_module(pci_name);
604 		PRINT(("no suitable cards found\n"));
605 		return ENODEV;
606 	}
607 
608 
609 #if DEBUG
610 	//add_debugger_command("auvia", auvia_debug, "auvia [card# (1-n)]");
611 #endif
612 	return B_OK;
613 }
614 
615 
616 static void
617 auvia_shutdown(auvia_dev *card)
618 {
619 	PRINT(("shutdown(%p)\n", card));
620 	remove_io_interrupt_handler(card->config.irq, auvia_int, card);
621 }
622 
623 
624 void
625 uninit_driver(void)
626 {
627 	int ix, cnt = num_cards;
628 	num_cards = 0;
629 
630 	PRINT(("uninit_driver()\n"));
631 	//remove_debugger_command("auvia", auvia_debug);
632 
633 	for (ix=0; ix<cnt; ix++) {
634 		auvia_shutdown(&cards[ix]);
635 	}
636 	memset(&cards, 0, sizeof(cards));
637 	put_module(pci_name);
638 }
639 
640 
641 const char **
642 publish_devices(void)
643 {
644 	int ix = 0;
645 	PRINT(("publish_devices()\n"));
646 
647 	for (ix=0; names[ix]; ix++) {
648 		PRINT(("publish %s\n", names[ix]));
649 	}
650 	return (const char **)names;
651 }
652 
653 
654 device_hooks *
655 find_device(const char * name)
656 {
657 	int ix;
658 
659 	PRINT(("find_device(%s)\n", name));
660 
661 	for (ix=0; ix<num_cards; ix++) {
662 		if (!strcmp(cards[ix].name, name)) {
663 			return &multi_hooks;
664 		}
665 	}
666 	PRINT(("find_device(%s) failed\n", name));
667 	return NULL;
668 }
669 
670 int32	api_version = B_CUR_DRIVER_API_VERSION;
671