1/* 2 * SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver. 3 * Copyright (c) 2002, 2008-2011 S.Zharski <imker@gmx.li> 4 * Distributed under the terms of the MIT license. 5 * 6 * Copyright for ali5451 support: 7 * (c) 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com). 8 */ 9 10 11#include "Device.h" 12 13#include <string.h> 14 15#include "Settings.h" 16#include "Registers.h" 17 18 19Device::Device(Device::Info &DeviceInfo, pci_info &PCIInfo) 20 : 21 fStatus(B_ERROR), 22 fPCIInfo(PCIInfo), 23 fInfo(DeviceInfo), 24 fIOBase(0), 25 fInterruptsNest(0), 26 fBuffersReadySem(-1), 27 fMixer(this), 28 fPlaybackStream(this, false), 29 fRecordStream(this, true) 30{ 31 B_INITIALIZE_SPINLOCK(&fHWSpinlock); 32 33 fStatus = _ReserveDeviceOnBus(true); 34 if (fStatus != B_OK) 35 return; // InitCheck will handle the rest 36 37 uint32 cmdRegister = gPCI->read_pci_config(PCIInfo.bus, 38 PCIInfo.device, PCIInfo.function, PCI_command, 2); 39 TRACE("cmdRegister:%#010x\n", cmdRegister); 40 cmdRegister |= PCI_command_io | PCI_command_memory | PCI_command_master; 41 gPCI->write_pci_config(PCIInfo.bus, PCIInfo.device, 42 PCIInfo.function, PCI_command, 2, cmdRegister); 43 44 fIOBase = PCIInfo.u.h0.base_registers[0]; 45 TRACE("fIOBase:%#010x\n", fIOBase); 46 47 fStatus = B_OK; 48} 49 50 51Device::~Device() 52{ 53 fMixer.Free(); 54 _ReserveDeviceOnBus(false); 55 56 if (fBuffersReadySem > B_OK) { 57 delete_sem(fBuffersReadySem); 58 } 59} 60 61 62void 63Device::_ResetCard(uint32 resetMask, uint32 releaseMask) 64{ 65 // disable Legacy Control 66 gPCI->write_pci_config(fPCIInfo.bus, fPCIInfo.device, 67 fPCIInfo.function, 0x40, 4, 0); 68 uint32 cmdReg = gPCI->read_pci_config(fPCIInfo.bus, fPCIInfo.device, 69 fPCIInfo.function, 0x44, 4); 70 gPCI->write_pci_config(fPCIInfo.bus, fPCIInfo.device, 71 fPCIInfo.function, 0x44, 4, cmdReg & 0xffff0000); 72 snooze(100); 73 74 // audio engine reset 75 cmdReg = gPCI->read_pci_config(fPCIInfo.bus, fPCIInfo.device, 76 fPCIInfo.function, 0x44, 4); 77 gPCI->write_pci_config(fPCIInfo.bus, fPCIInfo.device, 78 fPCIInfo.function, 0x44, 4, cmdReg | resetMask); 79 snooze(100); 80 81 // release reset 82 cmdReg = gPCI->read_pci_config(fPCIInfo.bus, fPCIInfo.device, 83 fPCIInfo.function, 0x44, 4); 84 gPCI->write_pci_config(fPCIInfo.bus, fPCIInfo.device, 85 fPCIInfo.function, 0x44, 4, cmdReg & ~releaseMask); 86 snooze(100); 87} 88 89 90status_t 91Device::Setup() 92{ 93 cpu_status cp = 0; 94 uint32 channelsIndex = ChIndexMidEna | ChIndexEndEna; 95 96 switch (HardwareId()) { 97 case SiS7018: 98 _ResetCard(0x000c0000, 0x00040000); 99 100 cp = Lock(); 101 102 WritePCI32(RegSiSCodecGPIO, 0x00000000); 103 WritePCI32(RegSiSCodecStatus, SiSCodecResetOff); 104 channelsIndex |= ChIndexSiSEnaB; 105 106 Unlock(cp); 107 break; 108 109 case ALi5451: 110 _ResetCard(0x000c0000, 0x00040000); 111 112 cp = Lock(); 113 WritePCI32(RegALiDigiMixer, ALiDigiMixerPCMIn); 114 WritePCI32(RegALiVolumeA, 0); 115 Unlock(cp); 116 break; 117 118 case TridentNX: 119 _ResetCard(0x00010000, 0x00010000); 120 121 cp = Lock(); 122 WritePCI32(RegNXCodecStatus, NXCodecStatusDAC1ON); 123 Unlock(cp); 124 break; 125 126 case TridentDX: 127 _ResetCard(0x00040000, 0x00040000); 128 129 cp = Lock(); 130 WritePCI32(RegCodecStatus, CodecStatusDACON); 131 Unlock(cp); 132 break; 133 } 134 135 // clear channels status 136 WritePCI32(RegStopA, 0xffffffff); 137 WritePCI32(RegStopB, 0xffffffff); 138 139 // disable channels interrupt 140 WritePCI32(RegEnaINTA, 0x00000000); 141 WritePCI32(RegEnaINTB, 0x00000000); 142 143 // enable loop interrupts 144 WritePCI32(RegChIndex, channelsIndex); 145 146 fRecordStream.Init(); 147 fPlaybackStream.Init(); 148 149 fBuffersReadySem = create_sem(0, DRIVER_NAME "_buffers_ready"); 150 151 fMixer.Init(); 152 153 return B_OK; 154} 155 156 157status_t 158Device::Open(uint32 flags) 159{ 160 TRACE("flags:%x\n", flags); 161 162 if (atomic_add(&fInterruptsNest, 1) == 0) { 163 install_io_interrupt_handler(fPCIInfo.u.h0.interrupt_line, 164 InterruptHandler, this, 0); 165 TRACE("Interrupt handler installed at line %d.\n", 166 fPCIInfo.u.h0.interrupt_line); 167 } 168 169 status_t status = fRecordStream.Start(); 170 if (status != B_OK) { 171 ERROR("Error of starting record stream:%#010x\n", status); 172 } 173 174 status = fPlaybackStream.Start(); 175 if (status != B_OK) { 176 ERROR("Error of starting playback stream:%#010x\n", status); 177 } 178 179 return B_OK; 180} 181 182 183status_t 184Device::Close() 185{ 186 TRACE("closed!\n"); 187 188 status_t status = fPlaybackStream.Stop(); 189 if (status != B_OK) { 190 ERROR("Error of stopping playback stream:%#010x\n", status); 191 } 192 193 status = fRecordStream.Stop(); 194 if (status != B_OK) { 195 ERROR("Error of stopping record stream:%#010x\n", status); 196 } 197 198 if (atomic_add(&fInterruptsNest, -1) == 1) { 199 remove_io_interrupt_handler(fPCIInfo.u.h0.interrupt_line, 200 InterruptHandler, this); 201 TRACE("Interrupt handler at line %d uninstalled.\n", 202 fPCIInfo.u.h0.interrupt_line); 203 } 204 205 return B_OK; 206} 207 208 209status_t 210Device::Free() 211{ 212 TRACE("freed\n"); 213 return B_OK; 214} 215 216 217status_t 218Device::Read(uint8 *buffer, size_t *numBytes) 219{ 220 *numBytes = 0; 221 return B_IO_ERROR; 222} 223 224 225status_t 226Device::Write(const uint8 *buffer, size_t *numBytes) 227{ 228 *numBytes = 0; 229 return B_IO_ERROR; 230} 231 232 233status_t 234Device::Control(uint32 op, void *buffer, size_t length) 235{ 236 switch (op) { 237 case B_MULTI_GET_DESCRIPTION: 238 return _MultiGetDescription((multi_description*)buffer); 239 240 case B_MULTI_GET_EVENT_INFO: 241 TRACE(("B_MULTI_GET_EVENT_INFO\n")); 242 return B_ERROR; 243 244 case B_MULTI_SET_EVENT_INFO: 245 TRACE(("B_MULTI_SET_EVENT_INFO\n")); 246 return B_ERROR; 247 248 case B_MULTI_GET_EVENT: 249 TRACE(("B_MULTI_GET_EVENT\n")); 250 return B_ERROR; 251 252 case B_MULTI_GET_ENABLED_CHANNELS: 253 return _MultiGetEnabledChannels((multi_channel_enable*)buffer); 254 255 case B_MULTI_SET_ENABLED_CHANNELS: 256 return _MultiSetEnabledChannels((multi_channel_enable*)buffer); 257 258 case B_MULTI_GET_GLOBAL_FORMAT: 259 return _MultiGetGlobalFormat((multi_format_info*)buffer); 260 261 case B_MULTI_SET_GLOBAL_FORMAT: 262 return _MultiSetGlobalFormat((multi_format_info*)buffer); 263 264 case B_MULTI_GET_CHANNEL_FORMATS: 265 TRACE(("B_MULTI_GET_CHANNEL_FORMATS\n")); 266 return B_ERROR; 267 268 case B_MULTI_SET_CHANNEL_FORMATS: 269 TRACE(("B_MULTI_SET_CHANNEL_FORMATS\n")); 270 return B_ERROR; 271 272 case B_MULTI_GET_MIX: 273 return _MultiGetMix((multi_mix_value_info *)buffer); 274 275 case B_MULTI_SET_MIX: 276 return _MultiSetMix((multi_mix_value_info *)buffer); 277 278 case B_MULTI_LIST_MIX_CHANNELS: 279 TRACE(("B_MULTI_LIST_MIX_CHANNELS\n")); 280 return B_ERROR; 281 282 case B_MULTI_LIST_MIX_CONTROLS: 283 return _MultiListMixControls((multi_mix_control_info*)buffer); 284 285 case B_MULTI_LIST_MIX_CONNECTIONS: 286 TRACE(("B_MULTI_LIST_MIX_CONNECTIONS\n")); 287 return B_ERROR; 288 289 case B_MULTI_GET_BUFFERS: 290 return _MultiGetBuffers((multi_buffer_list*)buffer); 291 292 case B_MULTI_SET_BUFFERS: 293 TRACE(("B_MULTI_SET_BUFFERS\n")); 294 return B_ERROR; 295 296 case B_MULTI_SET_START_TIME: 297 TRACE(("B_MULTI_SET_START_TIME\n")); 298 return B_ERROR; 299 300 case B_MULTI_BUFFER_EXCHANGE: 301 return _MultiBufferExchange((multi_buffer_info*)buffer); 302 303 case B_MULTI_BUFFER_FORCE_STOP: 304 TRACE(("B_MULTI_BUFFER_FORCE_STOP\n")); 305 return B_ERROR; 306 307 default: 308 ERROR("Unhandled IOCTL catched: %#010x\n", op); 309 } 310 311 return B_DEV_INVALID_IOCTL; 312} 313 314 315status_t 316Device::_MultiGetDescription(multi_description *multiDescription) 317{ 318 multi_channel_info channel_descriptions[] = { 319 { 0, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, 320 { 1, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 }, 321 { 2, B_MULTI_INPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, 322 { 3, B_MULTI_INPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 }, 323 { 4, B_MULTI_OUTPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 324 B_CHANNEL_MINI_JACK_STEREO }, 325 { 5, B_MULTI_OUTPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 326 B_CHANNEL_MINI_JACK_STEREO }, 327 { 6, B_MULTI_INPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 328 B_CHANNEL_MINI_JACK_STEREO }, 329 { 7, B_MULTI_INPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 330 B_CHANNEL_MINI_JACK_STEREO }, 331 }; 332 333 multi_description Description; 334 if (user_memcpy(&Description, 335 multiDescription, sizeof(multi_description)) != B_OK) 336 return B_BAD_ADDRESS; 337 338 Description.interface_version = B_CURRENT_INTERFACE_VERSION; 339 Description.interface_minimum = B_CURRENT_INTERFACE_VERSION; 340 341 strlcpy(Description.friendly_name, fInfo.Name(), 342 sizeof(Description.friendly_name)); 343 344 strlcpy(Description.vendor_info, "Haiku.Inc.", 345 sizeof(Description.vendor_info)); 346 347 Description.output_channel_count = 2; 348 Description.input_channel_count = 2; 349 Description.output_bus_channel_count = 2; 350 Description.input_bus_channel_count = 2; 351 Description.aux_bus_channel_count = 0; 352 353 Description.output_rates = fMixer.OutputRates(); 354 Description.input_rates = fMixer.InputRates(); 355 356 Description.output_formats = fMixer.OutputFormats(); 357 Description.input_formats = fMixer.InputFormats(); 358 359 Description.min_cvsr_rate = 0; 360 Description.max_cvsr_rate = 0; 361 362 Description.lock_sources = B_MULTI_LOCK_INTERNAL; 363 Description.timecode_sources = 0; 364 Description.interface_flags 365 = B_MULTI_INTERFACE_PLAYBACK | B_MULTI_INTERFACE_RECORD; 366 Description.start_latency = 3000; 367 368 Description.control_panel[0] = '\0'; 369 370 if (user_memcpy(multiDescription, 371 &Description, sizeof(multi_description)) != B_OK) 372 return B_BAD_ADDRESS; 373 374 if (Description.request_channel_count 375 >= (int)(B_COUNT_OF(channel_descriptions))) { 376 if (user_memcpy(multiDescription->channels, 377 &channel_descriptions, sizeof(channel_descriptions)) != B_OK) 378 return B_BAD_ADDRESS; 379 } 380 381 return B_OK; 382} 383 384 385status_t 386Device::_MultiGetEnabledChannels(multi_channel_enable *Enable) 387{ 388 B_SET_CHANNEL(Enable->enable_bits, 0, true); 389 B_SET_CHANNEL(Enable->enable_bits, 1, true); 390 B_SET_CHANNEL(Enable->enable_bits, 2, true); 391 B_SET_CHANNEL(Enable->enable_bits, 3, true); 392 Enable->lock_source = B_MULTI_LOCK_INTERNAL; 393 return B_OK; 394} 395 396 397status_t 398Device::_MultiSetEnabledChannels(multi_channel_enable *Enable) 399{ 400 TRACE("0:%s\n", B_TEST_CHANNEL(Enable->enable_bits, 0) ? "en" : "dis"); 401 TRACE("1:%s\n", B_TEST_CHANNEL(Enable->enable_bits, 1) ? "en" : "dis"); 402 TRACE("2:%s\n", B_TEST_CHANNEL(Enable->enable_bits, 2) ? "en" : "dis"); 403 TRACE("3:%s\n", B_TEST_CHANNEL(Enable->enable_bits, 3) ? "en" : "dis"); 404 return B_OK; 405} 406 407 408status_t 409Device::_MultiGetGlobalFormat(multi_format_info *Format) 410{ 411 fPlaybackStream.GetFormat(Format); 412 fRecordStream.GetFormat(Format); 413 414 return B_OK; 415} 416 417 418status_t 419Device::_MultiSetGlobalFormat(multi_format_info *Format) 420{ 421 status_t status = fPlaybackStream.SetFormat(Format->output, 422 fMixer.OutputFormats(), fMixer.OutputRates()); 423 if (status != B_OK) 424 return status; 425 426 return fRecordStream.SetFormat(Format->input, 427 fMixer.InputFormats(), fMixer.InputRates()); 428} 429 430 431status_t 432Device::_MultiListMixControls(multi_mix_control_info* Info) 433{ 434 return fMixer.ListMixControls(Info); 435} 436 437 438status_t 439Device::_MultiGetMix(multi_mix_value_info *Info) 440{ 441 return fMixer.GetMix(Info); 442} 443 444 445status_t 446Device::_MultiSetMix(multi_mix_value_info *Info) 447{ 448 return fMixer.SetMix(Info); 449} 450 451 452status_t 453Device::_MultiGetBuffers(multi_buffer_list* List) 454{ 455 fPlaybackStream.GetBuffers(List->flags, List->return_playback_buffers, 456 List->return_playback_channels, List->return_playback_buffer_size, 457 List->playback_buffers); 458 459 fRecordStream.GetBuffers(List->flags, List->return_record_buffers, 460 List->return_record_channels, List->return_record_buffer_size, 461 List->record_buffers); 462 return B_OK; 463} 464 465 466status_t 467Device::_MultiBufferExchange(multi_buffer_info* bufferInfo) 468{ 469 multi_buffer_info BufferInfo; 470 if (user_memcpy(&BufferInfo, bufferInfo, sizeof(multi_buffer_info)) != B_OK) { 471 return B_BAD_ADDRESS; 472 } 473 474 status_t status = B_NO_INIT; 475 476 if (!fRecordStream.IsActive()) { 477 status = fRecordStream.Start(); 478 if (status != B_OK) { 479 ERROR("Error of starting record stream:%#010x\n", status); 480 return status; 481 } 482 } 483 484 if (!fPlaybackStream.IsActive()) { 485 status = fPlaybackStream.Start(); 486 if (status != B_OK) { 487 ERROR("Error of starting playback stream:%#010x\n", status); 488 return status; 489 } 490 } 491 492 status = acquire_sem_etc(fBuffersReadySem, 1, 493 B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, 50000); 494 if (status == B_TIMED_OUT) { 495 ERROR("Timeout during buffers exchange.\n"); 496 } 497 498 cpu_status cst = Lock(); 499 500 fRecordStream.ExchangeBuffers(BufferInfo.recorded_real_time, 501 BufferInfo.recorded_frames_count, BufferInfo.record_buffer_cycle); 502 503 fPlaybackStream.ExchangeBuffers(BufferInfo.played_real_time, 504 BufferInfo.played_frames_count, BufferInfo.playback_buffer_cycle); 505 506 Unlock(cst); 507 508 if (user_memcpy(bufferInfo, &BufferInfo, sizeof(multi_buffer_info)) != B_OK) { 509 return B_BAD_ADDRESS; 510 } 511 512 return B_OK; 513} 514 515 516int32 517Device::InterruptHandler(void *interruptParam) 518{ 519 Device *device = reinterpret_cast<Device*>(interruptParam); 520 if (device == 0) { 521 ERROR("Invalid parameter in the interrupt handler.\n"); 522 return B_HANDLED_INTERRUPT; 523 } 524 525 bool wasHandled = false; 526 527 acquire_spinlock(&device->fHWSpinlock); 528 529 uint32 mask = device->ReadPCI32(RegMiscINT); 530 if (mask & 0x00000020) { 531 wasHandled = device->fRecordStream.InterruptHandler(); 532 wasHandled = device->fPlaybackStream.InterruptHandler() || wasHandled; 533 } 534 535 release_spinlock(&device->fHWSpinlock); 536 537 return wasHandled ? B_INVOKE_SCHEDULER : B_UNHANDLED_INTERRUPT; 538} 539 540 541void 542Device::SignalReadyBuffers() 543{ 544 release_sem_etc(fBuffersReadySem, 1, B_DO_NOT_RESCHEDULE); 545} 546 547 548status_t 549Device::_ReserveDeviceOnBus(bool reserve) 550{ 551 status_t result = B_NO_INIT; 552 if (reserve) { 553 result = gPCI->reserve_device(fPCIInfo.bus, fPCIInfo.device, 554 fPCIInfo.function, DRIVER_NAME, this); 555 if (result != B_OK) 556 ERROR("Unable to reserve PCI device %d:%d:%d on bus:%#010x\n", 557 fPCIInfo.bus, fPCIInfo.device, fPCIInfo.function, result); 558 } else { 559 result = gPCI->unreserve_device(fPCIInfo.bus, fPCIInfo.device, 560 fPCIInfo.function, DRIVER_NAME, this); 561 } 562 563 return result; 564} 565 566 567uint8 568Device::ReadPCI8(int offset) 569{ 570 return gPCI->read_io_8(fIOBase + offset); 571} 572 573 574uint16 575Device::ReadPCI16(int offset) 576{ 577 return gPCI->read_io_16(fIOBase + offset); 578} 579 580 581uint32 582Device::ReadPCI32(int offset) 583{ 584 return gPCI->read_io_32(fIOBase + offset); 585} 586 587 588void 589Device::WritePCI8(int offset, uint8 value) 590{ 591 gPCI->write_io_8(fIOBase + offset, value); 592} 593 594 595void 596Device::WritePCI16(int offset, uint16 value) 597{ 598 gPCI->write_io_16(fIOBase + offset, value); 599} 600 601 602void 603Device::WritePCI32(int offset, uint32 value) 604{ 605 gPCI->write_io_32(fIOBase + offset, value); 606} 607 608 609cpu_status 610Device::Lock() 611{ 612 cpu_status st = disable_interrupts(); 613 acquire_spinlock(&fHWSpinlock); 614 return st; 615} 616 617 618void 619Device::Unlock(cpu_status st) 620{ 621 release_spinlock(&fHWSpinlock); 622 restore_interrupts(st); 623} 624 625 626