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 "ac97.h" 50 51 status_t init_hardware(void); 52 status_t init_driver(void); 53 void uninit_driver(void); 54 const char ** publish_devices(void); 55 device_hooks * find_device(const char *); 56 57 pci_module_info *pci; 58 59 int32 num_cards; 60 auvia_dev cards[NUM_CARDS]; 61 int32 num_names; 62 char * names[NUM_CARDS*20+1]; 63 64 extern device_hooks multi_hooks; 65 66 /* Auvia Memory management */ 67 68 static auvia_mem * 69 auvia_mem_new(auvia_dev *card, size_t size) 70 { 71 auvia_mem *mem; 72 73 if ((mem = malloc(sizeof(*mem))) == NULL) 74 return (NULL); 75 76 mem->area = alloc_mem(&mem->phy_base, &mem->log_base, size, "auvia buffer"); 77 mem->size = size; 78 if (mem->area < B_OK) { 79 free(mem); 80 return NULL; 81 } 82 return mem; 83 } 84 85 static void 86 auvia_mem_delete(auvia_mem *mem) 87 { 88 if(mem->area > B_OK) 89 delete_area(mem->area); 90 free(mem); 91 } 92 93 static void * 94 auvia_mem_alloc(auvia_dev *card, size_t size) 95 { 96 auvia_mem *mem; 97 98 mem = auvia_mem_new(card, size); 99 if (mem == NULL) 100 return (NULL); 101 102 LIST_INSERT_HEAD(&(card->mems), mem, next); 103 104 return mem; 105 } 106 107 static void 108 auvia_mem_free(auvia_dev *card, void *ptr) 109 { 110 auvia_mem *mem; 111 112 LIST_FOREACH(mem, &card->mems, next) { 113 if (mem->log_base != ptr) 114 continue; 115 LIST_REMOVE(mem, next); 116 117 auvia_mem_delete(mem); 118 break; 119 } 120 } 121 122 /* Auvia stream functions */ 123 124 status_t 125 auvia_stream_set_audioparms(auvia_stream *stream, uint8 channels, 126 uint8 b16, uint32 sample_rate) 127 { 128 uint8 sample_size, frame_size; 129 LOG(("auvia_stream_set_audioparms\n")); 130 131 if ((stream->channels == channels) && 132 (stream->b16 == b16) && 133 (stream->sample_rate == sample_rate)) 134 return B_OK; 135 136 if(stream->buffer) 137 auvia_mem_free(stream->card, stream->buffer->log_base); 138 139 stream->b16 = b16; 140 stream->sample_rate = sample_rate; 141 stream->channels = channels; 142 143 sample_size = stream->b16 + 1; 144 frame_size = sample_size * stream->channels; 145 146 stream->buffer = auvia_mem_alloc(stream->card, stream->bufframes * frame_size * stream->bufcount); 147 148 stream->trigblk = 0; /* This shouldn't be needed */ 149 stream->blkmod = stream->bufcount; 150 stream->blksize = stream->bufframes * frame_size; 151 152 return B_OK; 153 } 154 155 status_t 156 auvia_stream_commit_parms(auvia_stream *stream) 157 { 158 int i; 159 uint32 *page; 160 uint32 value; 161 LOG(("auvia_stream_commit_parms\n")); 162 163 page = stream->dmaops_log_base; 164 165 for(i = 0; i < stream->bufcount; i++) { 166 page[2*i] = ((uint32)stream->buffer->phy_base) + 167 i * stream->blksize; 168 page[2*i + 1] = AUVIA_DMAOP_FLAG | stream->blksize; 169 } 170 171 page[2*stream->bufcount - 1] &= ~AUVIA_DMAOP_FLAG; 172 page[2*stream->bufcount - 1] |= AUVIA_DMAOP_EOL; 173 174 auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE, 175 (uint32)stream->dmaops_phy_base); 176 177 if(stream->use & AUVIA_USE_RECORD) 178 auvia_codec_write(&stream->card->config, AC97_PCM_L_R_ADC_RATE, (uint16)stream->sample_rate); 179 else 180 auvia_codec_write(&stream->card->config, AC97_PCM_FRONT_DAC_RATE, (uint16)stream->sample_rate); 181 182 if(IS_8233(&stream->card->config)) { 183 if(stream->base != AUVIA_8233_MP_BASE) { 184 value = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_8233_RP_RATEFMT); 185 value &= ~(AUVIA_8233_RATEFMT_48K | AUVIA_8233_RATEFMT_STEREO | AUVIA_8233_RATEFMT_16BIT); 186 if(stream->use & AUVIA_USE_PLAY) 187 value |= AUVIA_8233_RATEFMT_48K * (stream->sample_rate / 20) / (48000 / 20); 188 value |= (stream->channels == 2 ? AUVIA_8233_RATEFMT_STEREO : 0) 189 | (stream->b16 ? AUVIA_8233_RATEFMT_16BIT : 0); 190 auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_8233_RP_RATEFMT, value); 191 } else { 192 static const uint32 slottab[7] = {0, 0xff000011, 0xff000021, 0xff000521, 193 0xff004321, 0xff054321, 0xff654321}; 194 value = (stream->b16 ? AUVIA_8233_MP_FORMAT_16BIT : AUVIA_8233_MP_FORMAT_8BIT) 195 | ((stream->channels << 4) & AUVIA_8233_MP_FORMAT_CHANNEL_MASK) ; 196 auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_OFF_MP_FORMAT, value); 197 auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_8233_OFF_MP_STOP, 198 slottab[stream->channels]); 199 } 200 } 201 //auvia_codec_write(&stream->card->config, AC97_SPDIF_CONTROL, (uint16)stream->sample_rate); 202 203 return B_OK; 204 } 205 206 status_t 207 auvia_stream_get_nth_buffer(auvia_stream *stream, uint8 chan, uint8 buf, 208 char** buffer, size_t *stride) 209 { 210 uint8 sample_size, frame_size; 211 LOG(("auvia_stream_get_nth_buffer\n")); 212 213 sample_size = stream->b16 + 1; 214 frame_size = sample_size * stream->channels; 215 216 *buffer = stream->buffer->log_base + (buf * stream->bufframes * frame_size) 217 + chan * sample_size; 218 *stride = frame_size; 219 220 return B_OK; 221 } 222 223 static uint32 224 auvia_stream_curaddr(auvia_stream *stream) 225 { 226 uint32 addr; 227 if(IS_8233(&stream->card->config)) { 228 addr = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE); 229 TRACE(("stream_curaddr %p, phy_base %p\n", addr, (uint32)stream->dmaops_phy_base)); 230 return (addr - (uint32)stream->dmaops_phy_base - 4) / 8; 231 } else { 232 addr = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE); 233 TRACE(("stream_curaddr %p, phy_base %p\n", addr, (uint32)stream->dmaops_phy_base)); 234 return (addr - (uint32)stream->dmaops_phy_base - 8) / 8; 235 } 236 } 237 238 void 239 auvia_stream_start(auvia_stream *stream, void (*inth) (void *), void *inthparam) 240 { 241 LOG(("auvia_stream_start\n")); 242 243 stream->inth = inth; 244 stream->inthparam = inthparam; 245 246 stream->state |= AUVIA_STATE_STARTED; 247 248 if(IS_8233(&stream->card->config)) { 249 if(stream->base != AUVIA_8233_MP_BASE) { 250 auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_RP_DXS_LVOL, 0); 251 auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_RP_DXS_RVOL, 0); 252 } 253 auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL, 254 AUVIA_RPCTRL_START | AUVIA_RPCTRL_AUTOSTART | AUVIA_RPCTRL_STOP 255 | AUVIA_RPCTRL_EOL | AUVIA_RPCTRL_FLAG); 256 } else { 257 uint8 regvalue = (stream->channels > 1 ? AUVIA_RPMODE_STEREO : 0) 258 | (stream->b16 == 1 ? AUVIA_RPMODE_16BIT : 0) 259 | AUVIA_RPMODE_INTR_FLAG | AUVIA_RPMODE_INTR_EOL | AUVIA_RPMODE_AUTOSTART; 260 auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_MODE, regvalue); 261 auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START); 262 } 263 } 264 265 void 266 auvia_stream_halt(auvia_stream *stream) 267 { 268 LOG(("auvia_stream_halt\n")); 269 270 stream->state &= ~AUVIA_STATE_STARTED; 271 272 auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE); 273 } 274 275 auvia_stream * 276 auvia_stream_new(auvia_dev *card, uint8 use, uint32 bufframes, uint8 bufcount) 277 { 278 auvia_stream *stream; 279 cpu_status status; 280 LOG(("auvia_stream_new\n")); 281 282 stream = malloc(sizeof(auvia_stream)); 283 if (stream == NULL) 284 return (NULL); 285 stream->card = card; 286 stream->use = use; 287 stream->state = !AUVIA_STATE_STARTED; 288 stream->b16 = 0; 289 stream->sample_rate = 0; 290 stream->channels = 0; 291 stream->bufframes = bufframes; 292 stream->bufcount = bufcount; 293 stream->inth = NULL; 294 stream->inthparam = NULL; 295 stream->buffer = NULL; 296 stream->blksize = 0; 297 stream->trigblk = 0; 298 stream->blkmod = 0; 299 300 if(use & AUVIA_USE_PLAY) { 301 if(IS_8233(&card->config)) 302 stream->base = AUVIA_8233_MP_BASE; 303 //stream->base = AUVIA_PLAY_BASE; 304 else 305 stream->base = AUVIA_PLAY_BASE; 306 } else { 307 if(IS_8233(&card->config)) 308 stream->base = AUVIA_8233_RECORD_BASE; 309 else 310 stream->base = AUVIA_RECORD_BASE; 311 } 312 313 stream->frames_count = 0; 314 stream->real_time = 0; 315 stream->buffer_cycle = 0; 316 stream->update_needed = false; 317 318 /* allocate memory for our dma ops */ 319 stream->dmaops_area = alloc_mem(&stream->dmaops_phy_base, &stream->dmaops_log_base, 320 VIA_TABLE_SIZE, "auvia dmaops"); 321 322 if (stream->dmaops_area < B_OK) { 323 PRINT(("couldn't allocate memory\n")); 324 free(stream); 325 return NULL; 326 } 327 328 status = lock(); 329 LIST_INSERT_HEAD((&card->streams), stream, next); 330 unlock(status); 331 332 return stream; 333 } 334 335 void 336 auvia_stream_delete(auvia_stream *stream) 337 { 338 cpu_status status; 339 LOG(("auvia_stream_delete\n")); 340 341 auvia_stream_halt(stream); 342 343 auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE, 0); 344 345 if (stream->dmaops_area > B_OK) 346 delete_area(stream->dmaops_area); 347 348 if(stream->buffer) 349 auvia_mem_free(stream->card, stream->buffer->log_base); 350 351 status = lock(); 352 LIST_REMOVE(stream, next); 353 unlock(status); 354 355 free(stream); 356 } 357 358 /* Auvia interrupt */ 359 360 static int32 361 auvia_int(void *arg) 362 { 363 auvia_dev *card = arg; 364 bool gotone = false; 365 uint32 curblk; 366 auvia_stream *stream; 367 368 if(auvia_reg_read_32(&card->config, AUVIA_SGD_SHADOW) 369 & card->interrupt_mask) { 370 371 LIST_FOREACH(stream, &card->streams, next) 372 if(auvia_reg_read_8(&card->config, stream->base + AUVIA_RP_STAT) & AUVIA_RPSTAT_INTR) { 373 gotone = true; 374 //TRACE(("interrupt\n")); 375 376 curblk = auvia_stream_curaddr(stream); 377 TRACE(("RPSTAT_INTR at trigblk %lu, stream->trigblk %lu\n", curblk, stream->trigblk)); 378 if (curblk == stream->trigblk) { 379 //TRACE(("AUVIA_RPSTAT_INTR at trigblk %lu\n", curblk)); 380 381 if(stream->inth) 382 stream->inth(stream->inthparam); 383 384 stream->trigblk++; 385 stream->trigblk %= stream->blkmod; 386 } 387 388 auvia_reg_write_8(&card->config, stream->base + AUVIA_RP_STAT, AUVIA_RPSTAT_INTR); 389 } 390 } else { 391 TRACE(("SGD_SHADOW %x %x\n", card->interrupt_mask, auvia_reg_read_32(&card->config, AUVIA_SGD_SHADOW))); 392 } 393 394 if(gotone) 395 return B_INVOKE_SCHEDULER; 396 397 TRACE(("Got unhandled interrupt\n")); 398 return B_UNHANDLED_INTERRUPT; 399 } 400 401 /* Auvia driver functions */ 402 403 /* detect presence of our hardware */ 404 status_t 405 init_hardware(void) 406 { 407 int ix=0; 408 pci_info info; 409 status_t err = ENODEV; 410 411 LOG_CREATE(); 412 413 PRINT(("init_hardware()\n")); 414 415 if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci)) 416 return ENOSYS; 417 418 while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) { 419 if (info.vendor_id == VIATECH_VENDOR_ID && 420 (info.device_id == VIATECH_82C686_AC97_DEVICE_ID 421 || info.device_id == VIATECH_8233_AC97_DEVICE_ID 422 )) { 423 err = B_OK; 424 } 425 ix++; 426 } 427 428 put_module(B_PCI_MODULE_NAME); 429 430 return err; 431 } 432 433 static void 434 make_device_names( 435 auvia_dev * card) 436 { 437 sprintf(card->name, "audio/hmulti/auvia/%ld", card-cards+1); 438 names[num_names++] = card->name; 439 440 names[num_names] = NULL; 441 } 442 443 444 static status_t 445 auvia_init(auvia_dev * card) 446 { 447 uint32 pr; 448 449 pr = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, AUVIA_PCICONF_JUNK, 4); 450 PRINT(("AUVIA_PCICONF_JUNK before: %lx\n", pr)); 451 pr &= ~AUVIA_PCICONF_ENABLES; 452 pr |= AUVIA_PCICONF_ACLINKENAB | AUVIA_PCICONF_ACNOTRST | AUVIA_PCICONF_ACVSR | AUVIA_PCICONF_ACSGD; 453 pr &= ~(AUVIA_PCICONF_ACFM | AUVIA_PCICONF_ACSB); 454 (*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function, AUVIA_PCICONF_JUNK, 4, pr ); 455 snooze(100); 456 pr = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, AUVIA_PCICONF_JUNK, 4); 457 PRINT(("AUVIA_PCICONF_JUNK after: %lx\n", pr)); 458 459 if(IS_8233(&card->config)) { 460 card->interrupt_mask = 461 AUVIA_8233_SGD_STAT_FLAG_EOL | 462 AUVIA_8233_SGD_STAT_FLAG_EOL << 4 | 463 AUVIA_8233_SGD_STAT_FLAG_EOL << 8 | 464 AUVIA_8233_SGD_STAT_FLAG_EOL << 12 | 465 AUVIA_8233_SGD_STAT_FLAG_EOL << 16 | 466 AUVIA_8233_SGD_STAT_FLAG_EOL << 24 | 467 AUVIA_8233_SGD_STAT_FLAG_EOL << 28; 468 } else { 469 card->interrupt_mask = AUVIA_SGD_STAT_ALL | (AUVIA_SGD_STAT_ALL << 4); 470 } 471 472 473 /* Init streams list */ 474 LIST_INIT(&(card->streams)); 475 476 /* Init mems list */ 477 LIST_INIT(&(card->mems)); 478 479 return B_OK; 480 } 481 482 static status_t 483 auvia_setup(auvia_dev * card) 484 { 485 status_t err = B_OK; 486 unsigned char cmd; 487 488 PRINT(("auvia_setup(%p)\n", card)); 489 490 make_device_names(card); 491 492 card->config.subvendor_id = card->info.u.h0.subsystem_vendor_id; 493 card->config.subsystem_id = card->info.u.h0.subsystem_id; 494 card->config.nabmbar = card->info.u.h0.base_registers[0]; 495 card->config.irq = card->info.u.h0.interrupt_line; 496 card->config.type = 0; 497 if(card->info.device_id == VIATECH_82C686_AC97_DEVICE_ID) 498 card->config.type |= TYPE_686; 499 if(card->info.device_id == VIATECH_8233_AC97_DEVICE_ID) 500 card->config.type |= TYPE_8233; 501 502 PRINT(("%s deviceid = %#04x chiprev = %x model = %x enhanced at %lx\n", card->name, card->info.device_id, 503 card->info.revision, card->info.u.h0.subsystem_id, card->config.nabmbar)); 504 505 cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2); 506 PRINT(("PCI command before: %x\n", cmd)); 507 (*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2, cmd | PCI_command_io); 508 cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2); 509 PRINT(("PCI command after: %x\n", cmd)); 510 511 /* attach the codec */ 512 PRINT(("codec attach\n")); 513 ac97_attach(&card->config.ac97, (codec_reg_read)auvia_codec_read, 514 (codec_reg_write)auvia_codec_write, &card->config, 515 card->config.subvendor_id, card->config.subsystem_id); 516 517 PRINT(("installing interrupt : %lx\n", card->config.irq)); 518 install_io_interrupt_handler(card->config.irq, auvia_int, card, 0); 519 520 if ((err = auvia_init(card))) 521 return (err); 522 523 PRINT(("init_driver done\n")); 524 525 return err; 526 } 527 528 529 status_t 530 init_driver(void) 531 { 532 pci_info info; 533 int ix = 0; 534 535 num_cards = 0; 536 537 PRINT(("init_driver()\n")); 538 539 if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci)) 540 return ENOSYS; 541 542 while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) { 543 if (info.vendor_id == VIATECH_VENDOR_ID && 544 (info.device_id == VIATECH_82C686_AC97_DEVICE_ID 545 || info.device_id == VIATECH_8233_AC97_DEVICE_ID 546 )) { 547 if (num_cards == NUM_CARDS) { 548 PRINT(("Too many auvia cards installed!\n")); 549 break; 550 } 551 memset(&cards[num_cards], 0, sizeof(auvia_dev)); 552 cards[num_cards].info = info; 553 if (auvia_setup(&cards[num_cards])) { 554 PRINT(("Setup of auvia %ld failed\n", num_cards+1)); 555 } 556 else { 557 num_cards++; 558 } 559 } 560 ix++; 561 } 562 if (!num_cards) { 563 PRINT(("no cards\n")); 564 put_module(B_PCI_MODULE_NAME); 565 PRINT(("no suitable cards found\n")); 566 return ENODEV; 567 } 568 569 570 #if DEBUG 571 //add_debugger_command("auvia", auvia_debug, "auvia [card# (1-n)]"); 572 #endif 573 return B_OK; 574 } 575 576 577 static void 578 auvia_shutdown(auvia_dev *card) 579 { 580 PRINT(("shutdown(%p)\n", card)); 581 ac97_detach(card->config.ac97); 582 remove_io_interrupt_handler(card->config.irq, auvia_int, card); 583 } 584 585 586 void 587 uninit_driver(void) 588 { 589 int ix, cnt = num_cards; 590 num_cards = 0; 591 592 PRINT(("uninit_driver()\n")); 593 //remove_debugger_command("auvia", auvia_debug); 594 595 for (ix=0; ix<cnt; ix++) { 596 auvia_shutdown(&cards[ix]); 597 } 598 memset(&cards, 0, sizeof(cards)); 599 put_module(B_PCI_MODULE_NAME); 600 } 601 602 603 const char ** 604 publish_devices(void) 605 { 606 int ix = 0; 607 PRINT(("publish_devices()\n")); 608 609 for (ix=0; names[ix]; ix++) { 610 PRINT(("publish %s\n", names[ix])); 611 } 612 return (const char **)names; 613 } 614 615 616 device_hooks * 617 find_device(const char * name) 618 { 619 int ix; 620 621 PRINT(("find_device(%s)\n", name)); 622 623 for (ix=0; ix<num_cards; ix++) { 624 if (!strcmp(cards[ix].name, name)) { 625 return &multi_hooks; 626 } 627 } 628 PRINT(("find_device(%s) failed\n", name)); 629 return NULL; 630 } 631 632 int32 api_version = B_CUR_DRIVER_API_VERSION; 633