xref: /haiku/src/add-ons/kernel/drivers/audio/ac97/sis7018/Device.cpp (revision 68ea01249e1e2088933cb12f9c28d4e5c5d1c9ef)
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 
19 Device::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 
51 Device::~Device()
52 {
53 	fMixer.Free();
54 	_ReserveDeviceOnBus(false);
55 
56 	if (fBuffersReadySem > B_OK) {
57 		delete_sem(fBuffersReadySem);
58 	}
59 }
60 
61 
62 void
63 Device::_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 
90 status_t
91 Device::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 
157 status_t
158 Device::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 
183 status_t
184 Device::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 
209 status_t
210 Device::Free()
211 {
212 	TRACE("freed\n");
213 	return B_OK;
214 }
215 
216 
217 status_t
218 Device::Read(uint8 *buffer, size_t *numBytes)
219 {
220 	*numBytes = 0;
221 	return B_IO_ERROR;
222 }
223 
224 
225 status_t
226 Device::Write(const uint8 *buffer, size_t *numBytes)
227 {
228 	*numBytes = 0;
229 	return B_IO_ERROR;
230 }
231 
232 
233 status_t
234 Device::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 
315 status_t
316 Device::_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 
385 status_t
386 Device::_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 
397 status_t
398 Device::_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 
408 status_t
409 Device::_MultiGetGlobalFormat(multi_format_info *Format)
410 {
411 	fPlaybackStream.GetFormat(Format);
412 	fRecordStream.GetFormat(Format);
413 
414 	return B_OK;
415 }
416 
417 
418 status_t
419 Device::_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 
431 status_t
432 Device::_MultiListMixControls(multi_mix_control_info* Info)
433 {
434 	return fMixer.ListMixControls(Info);
435 }
436 
437 
438 status_t
439 Device::_MultiGetMix(multi_mix_value_info *Info)
440 {
441 	return fMixer.GetMix(Info);
442 }
443 
444 
445 status_t
446 Device::_MultiSetMix(multi_mix_value_info *Info)
447 {
448 	return fMixer.SetMix(Info);
449 }
450 
451 
452 status_t
453 Device::_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 
466 status_t
467 Device::_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 
516 int32
517 Device::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 
541 void
542 Device::SignalReadyBuffers()
543 {
544 	release_sem_etc(fBuffersReadySem, 1, B_DO_NOT_RESCHEDULE);
545 }
546 
547 
548 status_t
549 Device::_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 
567 uint8
568 Device::ReadPCI8(int offset)
569 {
570 	return gPCI->read_io_8(fIOBase + offset);
571 }
572 
573 
574 uint16
575 Device::ReadPCI16(int offset)
576 {
577 	return gPCI->read_io_16(fIOBase + offset);
578 }
579 
580 
581 uint32
582 Device::ReadPCI32(int offset)
583 {
584 	return gPCI->read_io_32(fIOBase + offset);
585 }
586 
587 
588 void
589 Device::WritePCI8(int offset, uint8 value)
590 {
591 	gPCI->write_io_8(fIOBase + offset, value);
592 }
593 
594 
595 void
596 Device::WritePCI16(int offset, uint16 value)
597 {
598 	gPCI->write_io_16(fIOBase + offset, value);
599 }
600 
601 
602 void
603 Device::WritePCI32(int offset, uint32 value)
604 {
605 	gPCI->write_io_32(fIOBase + offset, value);
606 }
607 
608 
609 cpu_status
610 Device::Lock()
611 {
612 	cpu_status st = disable_interrupts();
613 	acquire_spinlock(&fHWSpinlock);
614 	return st;
615 }
616 
617 
618 void
619 Device::Unlock(cpu_status st)
620 {
621 	release_spinlock(&fHWSpinlock);
622 	restore_interrupts(st);
623 }
624 
625 
626