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