⛏️ index : haiku.git

/*
 *	SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
 *	Copyright (c) 2002, 2008-2011 S.Zharski <imker@gmx.li>
 *	Distributed under the terms of the MIT license.
 *
 *	Copyright for ali5451 support:
 *		(c) 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
 */


#include "Stream.h"

#include <memory.h>

#include "Device.h"
#include "Registers.h"
#include "Settings.h"


Stream::Stream(Device *device, bool isInput)
		:	
		fDevice(device),
		fStatus(B_NO_INIT),
		fIsInput(isInput),
		fIsActive(false),
		fHWChannel(isInput ? 0 : 1),
		fBuffersArea(-1),
		fBuffersAreaSize(0),
		fBufferSamplesCount(0),
		fBuffersAddress(0),
		fBuffersPhysAddress(0),
		fRealTime(0),
		fFramesCount(0),
		fBufferCycle(fIsInput ? 1 :0)
{
	fFormat.format = B_FMT_16BIT;
	fFormat.rate = B_SR_48000;
	fFormat.cvsr = _DecodeRate(fFormat.rate);
	memset(fFormat._reserved_, 0, sizeof(fFormat._reserved_));
}


Stream::~Stream()
{
	Free();
}


uint32
Stream::_HWId()
{
	return fDevice->HardwareId();
}


status_t
Stream::Init()
{
	if (fStatus == B_OK)
		Free();

	fHWChannel = fIsInput ? 0 : 1;

	if (_HWId() == SiS7018)
			fHWChannel += 0x20; // bank B optimized for PCM
	else if (_HWId() == ALi5451 && fIsInput)
			fHWChannel = 31;
	
	// assume maximal possible buffers size
	fBuffersAreaSize = 1024; // samples
	fBuffersAreaSize *= 2 * 2 * 2; // stereo + 16-bit samples + 2 buffers
	fBuffersAreaSize = (fBuffersAreaSize + (B_PAGE_SIZE - 1)) &~ (B_PAGE_SIZE - 1);
	fBuffersArea = create_area(
			(fIsInput) ? DRIVER_NAME "_record_area" : DRIVER_NAME "_playback_area",
				&fBuffersAddress, B_ANY_KERNEL_ADDRESS, fBuffersAreaSize,
				B_32_BIT_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
	if (fBuffersArea < 0) {
		ERROR("Error of creating %#lx-bytes size buffer area:%#010x\n",
												fBuffersAreaSize, fBuffersArea);
		fStatus = fBuffersArea;
		return fStatus;
	}

	physical_entry PhysEntry;
	get_memory_map(fBuffersAddress, fBuffersAreaSize, &PhysEntry, 1);

	fBuffersPhysAddress = PhysEntry.address;

	TRACE("Created area id %d with size: %#x at address %#x[phys:%#lx]\n",
		fBuffersArea, fBuffersAreaSize, fBuffersAddress, fBuffersPhysAddress);

	// back to samples - half of buffer for 16-bit stereo data
	fBufferSamplesCount = fBuffersAreaSize / (2 * 2 * 2);

	fStatus = B_OK;
	return fStatus;
}


void
Stream::Free()
{
	delete_area(fBuffersArea);
	fStatus = B_NO_INIT;
}


uint32
Stream::_DecodeRate(uint32 rate)
{
	switch(rate) {
		case B_SR_8000: return 8000;
		case B_SR_11025: return 11025;
		case B_SR_12000: return 12000;
		case B_SR_16000: return 16000;
		case B_SR_22050: return 22050;
		case B_SR_24000: return 24000;
		case B_SR_32000: return 32000;
		case B_SR_44100: return 44100;
		case B_SR_48000: return 48000;
	}

	ERROR("Rate:%x is not supported. Fall to default 48000\n", rate);
	return 48000;
}


void	
Stream::GetFormat(multi_format_info *Format)	
{	
	if (fIsInput) {	
		Format->input_latency = 0;	
		Format->input = fFormat;	
	} else {	
		Format->output_latency = 0;	
		Format->output = fFormat;	
	}	
}	


status_t
Stream::SetFormat(_multi_format& format, uint32 formats, uint32 rates)
{
	if (fFormat.rate == format.rate && fFormat.format == format.format)
		return B_OK;

	if ((format.rate & rates) == 0 || (format.format & formats) == 0) {
		ERROR("Unsupported data format:%x or rate:%x. Ignore.\n",
					format.format, format.rate);
		return B_ERROR;
	}
	
	fFormat = format;
	fFormat.cvsr = _DecodeRate(fFormat.rate);
	
	fBufferSamplesCount = fBuffersAreaSize / (2 * 2);
	switch (fFormat.format) {
		default:
			ERROR("Unsupported data format:%x. 16 bit assumed.\n", fFormat.format);
		case B_FMT_16BIT:
			fBufferSamplesCount /= 2;
			break;
		case B_FMT_8BIT_S:
		case B_FMT_8BIT_U:
			break;
	}

	TRACE("Format:%#x;Rate:%#x;cvsr:%.2f\n",
			fFormat.format, fFormat.rate, fFormat.cvsr);
	
	// stop the stream - it will be rewaked during next exchnage buffers call
	Stop();

	return B_OK;
}


void
Stream::GetBuffers(uint32& Flags, int32& BuffersCount, int32& ChannelsCount,
						uint32& BufferSize, buffer_desc** Buffers)
{
	Flags |= fIsInput ? B_MULTI_BUFFER_RECORD : B_MULTI_BUFFER_PLAYBACK;
	BuffersCount = 2;
	ChannelsCount = 2;
	BufferSize = fBufferSamplesCount;

	uint32 stride = 4;
	if (fFormat.format == B_FMT_8BIT_S || fFormat.format == B_FMT_8BIT_U) {
		stride = 2;
	}
		// [b][c] init buffers
	Buffers[0][0].base
		= Buffers[1][0].base
		= Buffers[0][1].base
		= Buffers[1][1].base = (char*)fBuffersAddress;

	Buffers[0][0].stride
		= Buffers[1][0].stride
		= Buffers[0][1].stride
		= Buffers[1][1].stride = stride;

	// shift pair of second part of buffers
	Buffers[1][0].base += BufferSize * stride;
	Buffers[1][1].base += BufferSize * stride;

	// shift right channel buffers
	Buffers[0][1].base += stride / 2;
	Buffers[1][1].base += stride / 2;

	TRACE("%s buffers:\n", fIsInput ? "input" : "output");
	TRACE("1: %#010x %#010x\n", Buffers[0][0].base, Buffers[0][1].base);
	TRACE("2: %#010x %#010x\n", Buffers[1][0].base, Buffers[1][1].base);
}


status_t
Stream::Start()
{
	if (!fIsInput)
		fDevice->Mixer().SetOutputRate(fFormat.cvsr);

	uint32 CSO = 0;
	uint32 LBA = uint32(fBuffersPhysAddress) & 0x3fffffff;
	uint32 ESO = ((fBufferSamplesCount * 2) - 1) & 0xffff;
	uint32 Delta = fIsInput ? ((48000 << 12) / uint32(fFormat.cvsr)) & 0xffff
							: ((uint32(fFormat.cvsr) << 12) / 48000) & 0xffff;
	uint32 DeltaESO = (ESO << 16) | Delta;
	uint32 FMControlEtc = fIsInput ? 0 : (0x03 << 14);
	uint32 ControlEtc =  1 << 14 | 1 << 12; // stereo data + loop enabled
	
	switch (fFormat.format) {
		case B_FMT_16BIT:
			ControlEtc |= (1 << 15); // 16 bit
		case B_FMT_8BIT_S:
			ControlEtc |= (1 << 13); // signed
			break;
	}

	switch (_HWId()) {
		case TridentDX:
			FMControlEtc |= (0x7f << 7) < 0x7f;
			break;
		case TridentNX:
			CSO = Delta << 24;
			DeltaESO = ((Delta << 16) & 0xff000000) | (ESO & 0x00ffffff);
			FMControlEtc |= (0x7f << 7) < 0x7f;
			break;
		case SiS7018:
			FMControlEtc = fIsInput ? (0x8a80 << 16) : FMControlEtc;
			break;
	}

	cpu_status cst = fDevice->Lock();

	// select used channel
	uint32 ChIntReg = fDevice->ReadPCI32(RegChIndex) & ~0x3f;
	ChIntReg |= (fHWChannel & 0x3f);
	fDevice->WritePCI32(RegChIndex, ChIntReg);

	fDevice->WritePCI32(RegCSOAlphaFMS, CSO);
	fDevice->WritePCI32(RegLBA_PPTR, LBA);
	fDevice->WritePCI32(RegDeltaESO, DeltaESO);
	fDevice->WritePCI32(RegRVolCVolFMC, FMControlEtc);
	fDevice->WritePCI32(RegGVSelVolCtrl, ControlEtc);
	fDevice->WritePCI32(RegEBuf1, 0);
	fDevice->WritePCI32(RegEBuf2, 0);

	if (fIsInput) {
		uint32 reg = 0;
		switch (_HWId()) {
			case ALi5451:
				reg = fDevice->ReadPCI32(RegALiDigiMixer);
				fDevice->WritePCI32(RegALiDigiMixer, reg | (1 << _HWVoice()));
				break;
			case TridentDX:
				reg = fDevice->ReadPCI8(RegCodecStatus);
				fDevice->WritePCI8(RegCodecStatus,
						reg | CodecStatusADCON | CodecStatusSBCtrl);
				// enable and set record channel
				fDevice->WritePCI8(RegRecChannel, 0x80 | _HWVoice());
				break;
			case TridentNX:
				reg = fDevice->ReadPCI16(RegMiscINT);
				fDevice->WritePCI8(RegMiscINT, reg | 0x1000);
				// enable and set record channel
				fDevice->WritePCI8(RegRecChannel, 0x80 | _HWVoice());
				break;
		}
	}

	// enable INT for current channel
	uint32 ChIntMask = fDevice->ReadPCI32(_UseBankB() ? RegEnaINTB : RegEnaINTA);
	ChIntMask |= 1 << _HWVoice();
	fDevice->WritePCI32(_UseBankB() ? RegAddrINTB : RegAddrINTA, 1 << _HWVoice());
	fDevice->WritePCI32(_UseBankB() ? RegEnaINTB : RegEnaINTA, ChIntMask);

	// start current channel
	fDevice->WritePCI32(_UseBankB() ? RegStartB : RegStartA, 1 << _HWVoice());
	fIsActive = true;

	fDevice->Unlock(cst);

	TRACE("%s:CSO:%#x;LBA:%#x;ESO:%#x;Delta:%#x;FM:%#x:Ctrl:%#x;CIR:%#x\n",
		fIsInput ? "Rec" : "Play", CSO, LBA, ESO, Delta, FMControlEtc,
			ControlEtc, ChIntReg);
	
	return B_OK;
}


status_t
Stream::Stop()
{
	if (!fIsActive)
		return B_OK;

	cpu_status cst = fDevice->Lock();

	// stop current channel
	fDevice->WritePCI32(_UseBankB() ? RegStopB : RegStopA, 1 << _HWVoice());
	fIsActive = false;
	
	if (_HWId() == ALi5451 && fIsInput) {
		uint32 reg = fDevice->ReadPCI32(RegALiDigiMixer);
		fDevice->WritePCI32(RegALiDigiMixer, reg & ~(1 << _HWVoice()));
	}

	fDevice->Unlock(cst);

	TRACE("%s:OK\n", fIsInput ? "Rec" : "Play");
	
	fBufferCycle = fIsInput ? 1 : 0;
	
	return B_OK;
}


bool
Stream::InterruptHandler()
{
	uint32 SignaledMask = fDevice->ReadPCI32(
							_UseBankB() ? RegAddrINTB : RegAddrINTA);
	uint32 ChannelMask = 1 << _HWVoice();
	if ((SignaledMask & ChannelMask) == 0) {
		return false;
	}

	// first clear signalled channel bit
	fDevice->WritePCI32(_UseBankB() ? RegAddrINTB : RegAddrINTA, ChannelMask);

	fRealTime = system_time();
	fFramesCount += fBufferSamplesCount;
	fBufferCycle = (fBufferCycle + 1) % 2;

	fDevice->SignalReadyBuffers();

	return true;
}


void
Stream::ExchangeBuffers(bigtime_t& RealTime,
								bigtime_t& FramesCount, int32& BufferCycle)
{
	RealTime = fRealTime;
	FramesCount = fFramesCount;
	BufferCycle = fBufferCycle;
}