Source to iokit/Drivers/audio/drvPPCBurgundy/PPCBurgundy.cpp
/*
* Copyright (c) 1998 Apple Computer, Inc. All rights reserved.
*
* Hardware independent (relatively) code for the Burgundy Controller
*
* HISTORY
*
*
*/
#include <IOKit/assert.h>
#include <IOKit/system.h>
#include <IOKit/IOLib.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/ppc/IODBDMA.h>
#include "PPCBurgundy.h"
#include "burgundy_hw.h"
/*
* Prototyes for the "very private methods" at the end of this
* file. They provide access to the burgundy registers:
*/
static void writeSoundControlReg( volatile UInt8*, int);
static int readCodecSenseLines( volatile UInt8*);
static int readCodecReg( volatile UInt8*, int);
static void writeCodecReg( volatile UInt8*, int, int);
#define super IOAudioBus
OSDefineMetaClassAndStructors( PPCBurgundy, IOAudioBus )
/* ==============
* Public Methods
* ============== */
bool
PPCBurgundy::init(OSDictionary * properties)
{
if (!super::init(properties)) {
#ifdef DEBUGMODE
IOLog( "PPCBurgundy::init (this = 0x%08lx) super fails\n", (UInt32)this);
#endif
return false;
}
if(!properties){
#ifdef DEBUGMODE
IOLog( "PPCBurgundy::init (this = 0x%08lx) Need to know where Burgundy is !!\n", (UInt32)this);
#endif
return false; // Need to know where Burgundy is!
}
// This invalidate the cache of the machine architecture
machineType = kMachineTypeUnknown;
// Nulls the pointers for the arrays we allocate:
fInputComponents = NULL;
fOutputComponents = NULL;
sound = NULL;
// We start with a clean status register mirror so first read will
// set the driver in the correct mode:
lastStatusRegister = 0L;
#ifdef DEBUGMODE
IOLog( "PPCBurgundy::init (this = 0x%08lx)\n", (UInt32)this);
#endif
return true;
}
void
PPCBurgundy::free()
{
// Releases the sound:
if(sound != NULL)
sound->release();
sound = NULL;
super::free();
}
IOService*
PPCBurgundy::probe(IOService* provider, SInt32* score)
{
IOService *myService = this;
// We CAN fail the tye check:
super::probe(provider, score);
*score = kIODefaultProbeScore;
// finds and cashes the hardware we are running on:
int mType = findHostingHardware(provider);
// If it one of the machine I know supports
// burgundy I'm going to increment the score
if ((mType == kMachineTypeYosemite) || (mType == kMachineTypeiMac)) {
// One score increment because this is a burgundy-hosting motherboard
*score = *score + 1;
}
// Figure out if this is a bugundy and if it is a bordeaux
sound = NULL;
isBordeaux = false;
// This is likely to be the case that this is perch
if (implementsBurgundy(IORegistryEntry::fromPath("/perch", gIODTPlane))) {
*score = *score + 1;
myService = this;
}
else if ((sound = provider->childFromPath("sound", gIODTPlane)) != NULL) {
// IOLog("PPCBurgundy::probe looking for sound\n");
if (implementsBurgundy(sound)) {
*score = *score + 1;
myService = this;
}
}
if (myService != NULL) {
IOLog("Found Burgundy compatible chip %s\n", (isBordeaux ? "and Bordeaux card": "NON Bordeaux card"));
}
return (myService);
}
#define kNumDMAStreams 2
bool
PPCBurgundy::start(IOService* provider)
{
// Gets the base for the burgundy registers:
IOMemoryMap *map;
int i;
if( !super::start(provider))
return (false);
map = provider->mapDeviceMemoryWithIndex(kAudioDMAdeviceInt);
if(!map) {
return false;
}
ioBaseBurgundy = (UInt8 *)map->getVirtualAddress();
if (!AllocateStreams(kNumDMAStreams))
return false;
map = provider->mapDeviceMemoryWithIndex(kAudioDMAtxInt);
if(!map) {
return false;
}
DefineStream(kAudioDMAOutputStream, kOutput, outputFrameRate(), (IODBDMAChannelRegisters*)map->getVirtualAddress());
map = provider->mapDeviceMemoryWithIndex(kAudioDMArxInt);
if(!map) {
return false;
}
DefineStream(kAudioDMAInputStream, kInput, inputFrameRate(), (IODBDMAChannelRegisters*)map->getVirtualAddress());
// Creates the array of input and output components
numInputComponents = numberOfOutputComponents();
numOutputComponents = numberOfInputComponents();
fInputComponents = (IOAudioComponentImplPtr*)IOMalloc(sizeof(IOAudioComponentImplPtr) * numInputComponents);
if (fInputComponents == NULL) {
// Free the DMA and exit:
FreeStreams();
return false;
}
fOutputComponents = (IOAudioComponentImplPtr*)IOMalloc(sizeof(IOAudioComponentImplPtr) * numOutputComponents);
if (fOutputComponents == NULL) {
// Free the input components
IOFree(fInputComponents, sizeof(IOAudioComponentImplPtr) * numInputComponents);
fInputComponents = NULL;
numInputComponents = 0;
// Free the DMA and exit:
FreeStreams();
return false;
}
// Initalizes all the components:
for (i = 0; i < numInputComponents; i++)
fInputComponents[i] = NULL;
for (i = 0; i < numOutputComponents; i++)
fOutputComponents[i] = NULL;
// Initializes the sound control register:
soundControlRegister = ( kSoundCtlReg_InSubFrame0 | \
kSoundCtlReg_OutSubFrame0 | \
kSoundCtlReg_Rate_44100 );
writeSoundControlReg(ioBaseBurgundy, soundControlRegister);
// Set Input Source A gain to default
writeCodecReg( ioBaseBurgundy, kGASALReg, kGAS_Default_Gain );
writeCodecReg( ioBaseBurgundy, kGASARReg, kGAS_Default_Gain );
writeCodecReg( ioBaseBurgundy, kVGA2Reg, 0x44 );
writeCodecReg( ioBaseBurgundy, kGAS2LReg, 255 );
writeCodecReg( ioBaseBurgundy, kGAS2RReg, 255 );
// Set mixer 1 input to Input Source A (Serial-in Subframe 0)
writeCodecReg( ioBaseBurgundy, kMX1Reg, kMX1Reg_Select_ISAL | kMX1Reg_Select_ISAR );
writeCodecReg( ioBaseBurgundy, kSDInReg, kSDInReg_ASA_From_SF0 );
// Set mixer 0-3 outputs to default gain
writeCodecReg( ioBaseBurgundy, kMXEQ1LReg, kMXEQ_Default_Gain );
writeCodecReg( ioBaseBurgundy, kMXEQ1RReg, kMXEQ_Default_Gain );
writeCodecReg( ioBaseBurgundy, kMXEQ2LReg, kMXEQ_Default_Gain );
writeCodecReg( ioBaseBurgundy, kMXEQ2RReg, kMXEQ_Default_Gain );
writeCodecReg( ioBaseBurgundy, kMXEQ3LReg, kMXEQ_Default_Gain );
writeCodecReg( ioBaseBurgundy, kMXEQ3RReg, kMXEQ_Default_Gain );
// Set Output Source 0 gain to default
writeCodecReg( ioBaseBurgundy, kGAP0LReg, kGAP_Default_Gain );
writeCodecReg( ioBaseBurgundy, kGAP0RReg, kGAP_Default_Gain );
startWorkLoop();
return true;
}
void
PPCBurgundy::stop(IOService *provider)
{
// Releases all DMA streams:
FreeStreams();
// Releases the input components
if (fInputComponents != NULL) {
IOFree(fInputComponents, sizeof(IOAudioComponentImplPtr) * numInputComponents);
fInputComponents = NULL;
numInputComponents = 0;
}
// Releases the output components
if (fOutputComponents != NULL) {
IOFree(fOutputComponents, sizeof(IOAudioComponentImplPtr) * numOutputComponents);
fOutputComponents = NULL;
numOutputComponents = 0;
}
}
void
PPCBurgundy::CreateAudioTopology(IOCommandQueue *queue)
{
IOAudioComponentImpl *mixerIn;
IOAudioComponentImpl *mixerOut;
IOAudioComponentImpl *microphone;
IOAudioComponentImpl *lineout;
IOAudioComponentImpl *linein;
IOAudioComponentImpl *headphones;
IOAudioComponentImpl *cd;
IOAudioComponentImpl *speaker;
IOAudioComponentImpl *passtru;
AudioStreamIndex outputStream;
AudioStreamIndex inputStream;
if (!fStreams)
return;
outputStream = firstStreamAfter(kOutput, 0);
inputStream = firstStreamAfter(kInput, 0);
if ((outputStream == kInvalidStreamIndex) || (inputStream == kInvalidStreamIndex)) {
IOLog("PPCBurgundy::CreateAudioTopology called without available DMA streams\n");
return;
}
else
IOLog("PPCBurgundy::CreateAudioTopology input stream is %d output is %d\n", inputStream, outputStream);
mixerOut = buildComponentAndAttach(GetStream(outputStream), NULL, componentDictionaryMixerOut(), queue);
headphones = buildComponentAndAttach(mixerOut, NULL, componentDictionaryHeadphones(), queue);
speaker = buildComponentAndAttach(mixerOut, NULL, componentDictionarySpeaker(), queue);
lineout = buildComponentAndAttach(mixerOut, NULL, componentDictionaryLineout(), queue);
mixerIn = buildComponentAndAttach(NULL, GetStream(inputStream), componentDictionaryMixerIn(), queue);
cd = buildComponentAndAttach(NULL, NULL, componentDictionaryCD(), queue);
linein = buildComponentAndAttach(NULL, NULL, componentDictionaryLinein(), queue);
microphone = buildComponentAndAttach(NULL, mixerIn, componentDictionaryMicrophone(), queue);
passtru = buildComponentAndAttach(mixerIn, mixerOut, componentDictionaryPassThru(), queue);
buildComponentAndAttach(mixerIn, mixerOut, componentDictionaryPassThru(), queue);
fInputComponents[kCD] = buildComponentAndAttach(NULL, mixerIn, componentDictionaryCD(), queue);
// FIXME: the component connection could be done better than so
if (fInputComponents != NULL) {
fInputComponents[kMicroPhone] = microphone;
if (numInputComponents > 1)
fInputComponents[kCD] = cd;
if (numInputComponents > 2)
fInputComponents[kLineIn] = linein;
}
if (fOutputComponents != NULL) {
fOutputComponents[kSpeaker] = speaker;
if (numOutputComponents > 1)
fOutputComponents[kHeadphones] = headphones;
if (numOutputComponents > 2)
fOutputComponents[kLineOut] = lineout;
}
// Sets ups the burgundy as required by this machine wiring
// configuration:
if (!dependentSetup())
IOLog("PPCBurgundy::CreateAudioTopology burgundy setup failed\n");
}
void
PPCBurgundy::DoClockTick(IOTimerEventSource *t)
{
checkStatusRegister();
super::DoClockTick(t);
}
void
PPCBurgundy::calculateTickInterval(AbsoluteTime *tickInterval)
{
AbsoluteTime maxInterval;
// We want to check the status at least one a second
nanoseconds_to_absolutetime(NSEC_PER_SEC, &maxInterval);
if(CMP_ABSOLUTETIME(&maxInterval, tickInterval) < 0) {
*tickInterval = maxInterval;
}
super::calculateTickInterval(tickInterval);
}
IOReturn
PPCBurgundy::SetControl(UInt16 id, int val)
{
IOReturn res = kIOReturnSuccess;
switch(id) {
case kSpeakerMute:
muteInternalSpeaker(val != 0);
muteHeadphones(val == 0);
break;
case kHeadphonesMute:
muteHeadphones(val != 0);
muteInternalSpeaker(val == 0);
break;
case kMicrophoneMute:
setMicInput(val == 0);
break;
case kSpeakerVolLeft:
volumeInternalSpeakerLeft(val);
break;
case kSpeakerVolRight:
volumeInternalSpeakerRight(val);
break;
case kHeadphonesVolLeft:
volumeHeadphonesLeft(val);
break;
case kHeadphonesVolRight:
volumeHeadphonesRight(val);
break;
case kCDMute:
muteCDLine(val != 0);
break;
case kMixerInVolLeft:
volumeMixerInLeft(val);
break;
case kMixerInVolRight:
volumeMixerInRight(val);
break;
case kPassThruVolLeft:
break;
case kPassThruVolRight:
break;
case kPassThruMute:
break;
default:
res = kIOReturnUnsupported;
}
return res;
}
/* ===============
* Private Methods
* =============== */
// --------------------------------------------------------------------------
// Method: implementsBurgundy
//
// Purpose:
// Attempts to discover if the device implements burgundy:
bool
PPCBurgundy::implementsBurgundy(IORegistryEntry *device)
{
if (device != NULL) {
OSData *s = NULL;
//IOLog("Matching burgundy compatibility with %s\n", device->getName());
s = OSDynamicCast(OSData, device->getProperty("compatible"));
if (s != NULL) {
if(s->isEqualTo("burgundy", sizeof("burgundy")-1)) {
return true;
}
else if(s->isEqualTo("DVD-Video and Audio/Video", sizeof("DVD-Video and Audio/Video")-1)) {
// also sets the isBordeaux flag
isBordeaux = true;
return true;
}
else {
//IOLog("PPCBurgundy::probe sound is not burgundy compatible.\n");
}
}
else {
//IOLog("PPCBurgundy::probe sound does not have a compatible property.\n");
}
}
else{
//IOLog("PPCBurgundy::probe this hardware does not have a sound chip\n");
}
return false;
}
// --------------------------------------------------------------------------
// Method: findHostingHardware
//
// Purpose:
// Finds the kind of hardware we are running on. Since all the older machines
// do not have all the information about how burgundy is wired, knowing which
// machine we are running on helps to set up the driver correctly.
// The method recognizes more machines than the ones that use bugundy (e.g.
// "'AAPL,9500'" has an AWACS).
int
PPCBurgundy::findHostingHardware(IOService *provider)
{
IOService *topProvider = NULL;
// If we already found the machine the driver is running on
// just returns the value previously found:
if ((machineType >= kMachineTypeUnknown) ||
(machineType == 0)) {
// if the argument is missing I've to get from myself:
if (provider == NULL)
provider = getProvider();
// See if we find the top of the tree (with the machine type)
// iterating all the way up:
while (provider != NULL) {
topProvider = provider;
provider = topProvider->getProvider();
}
// by default the hardware is unknown:
machineType = kMachineTypeUnknown;
if (topProvider != NULL) {
if (IODTMatchNubWithKeys(topProvider, "'AAPL,9500'"))
machineType = kMachine9500;
if (IODTMatchNubWithKeys(topProvider, "'AAPL,Gossamer'"))
machineType = kMachineGenericGossamer;
if (IODTMatchNubWithKeys(topProvider, "'AAPL,PowerMac G3'"))
machineType = kMachineTypeSilk;
else if (IODTMatchNubWithKeys(topProvider, "'AAPL,PowerBook1998'"))
machineType = kMachineTypeWallstreet;
else if (IODTMatchNubWithKeys(topProvider, "'iMac,1'"))
machineType = kMachineTypeiMac;
else if (IODTMatchNubWithKeys(topProvider, "('PowerMac1,1', 'PowerMac1,2')"))
machineType = kMachineTypeYosemite;
else if (IODTMatchNubWithKeys(topProvider, "'PowerMac3,1'"))
machineType = kMachineTypeSawtooth;
else if (IODTMatchNubWithKeys(topProvider, "'PowerBook1,1'"))
machineType = kMachineType101;
else if (IODTMatchNubWithKeys(topProvider, "'PowerBook2,1'"))
machineType = kMachineTypeiBook;
else
IOLog("PPCBurgundy::findHostingHardware unknown machine type\n");
}
else
IOLog("PPCBurgundy::findHostingHardware misses the top provider\n");
}
//Just to ease up the debugging
//IOLog("PPCBurgundy::findHostingHardware found %d\n", machineType);
return (machineType);
}
// --------------------------------------------------------------------------
// Method: numberOfInputComponents
//
// Purpose:
// returns the number of components for the input lines
// usually are 2 one is the microphone and the other is
// microphone jack.
#define kCommonNumberOfInputComponents 2
int
PPCBurgundy::numberOfInputComponents()
{
if(sound) {
OSData *t;
t = OSDynamicCast(OSData, sound->getProperty("#-detects"));
if (t != NULL) {
return *(UInt32*)(t->getBytesNoCopy());
}
}
return kCommonNumberOfInputComponents;
}
// --------------------------------------------------------------------------
// Method: numberOfOutputComponents
//
// Purpose:
// returns the number of components for the output lines
// usually are 2 one is the internal speacker and the other
// is the headphone line.
#define kCommonNumberOfOutputComponents 2
int
PPCBurgundy::numberOfOutputComponents()
{
if(sound) {
OSData *t;
t = OSDynamicCast(OSData, sound->getProperty("#-outputs"));
if (t != NULL) {
return *(UInt32*)(t->getBytesNoCopy());
}
}
return kCommonNumberOfOutputComponents;
}
// --------------------------------------------------------------------------
// Method: inputFrameRate
//
// Purpose:
// returns the input frame rate as in the registry, if it is
// not found in the registry, it returns the default value.
#define kCommonInputFrameRate 44100
UInt16
PPCBurgundy::inputFrameRate()
{
if(sound) {
OSData *t;
t = OSDynamicCast(OSData, sound->getProperty("input-frame-rates"));
if (t != NULL) {
UInt16 fr = *(UInt32*)(t->getBytesNoCopy());
#ifdef DEBUGMODE
IOLog( "PPCBurgundy::inputFrameRate = %d\n", fr);
#endif
return fr;
}
}
return kCommonInputFrameRate;
}
// --------------------------------------------------------------------------
// Method: outputFrameRate
//
// Purpose:
// returns the output frame rate as in the registry, if it is
// not found in the registry, it returns the default value.
#define kCommonOutputFrameRate 44100
UInt16
PPCBurgundy::outputFrameRate()
{
if(sound) {
OSData *t;
t = OSDynamicCast(OSData, sound->getProperty("output-frame-rates"));
if (t != NULL) {
UInt16 fr = *(UInt32*)(t->getBytesNoCopy());
#ifdef DEBUGMODE
IOLog( "PPCBurgundy::outputFrameRate = %d\n", fr);
#endif
return fr;
}
}
return kCommonOutputFrameRate;
}
// --------------------------------------------------------------------------
// Method(s): componentDictionaryXXXXXX
//
// Purpose:
// the next N methods return the strings for the component dictionaries
// for each kind of component. As stated in the AWACS driver, these should
// end in the driver resources.
// NOTE: (This is important !!!) the soundControls enum in the header file is
// bounded to the following stings in this way:
// for each component the control Id in the string must have the same value
// of the corrispondent soundControls enum item. For example the sound control
// enum item kHeadphonesVolLeft has value 4, so in sHeadphones the ID of the
// volume left control must be 4.
char*
PPCBurgundy::componentDictionarySpeaker()
{
static const char *sSpeaker =
"{
'Type' = 'Speaker';
'Channels' = 2:8;
'Master' = 1:8;
'Controls' = {
'MuteAll' = {
'Type' = 'Mute';
'Id' = 0:16;
'Val' = 0:8;
'Min' = 0:8;
'Max' = 1:8;
'Chan' = 0:8;
};
'VolumeLeft' = {
'Type' = 'Volume';
'Id' = 1:16;
'Val' = 65535:16;
'Min' = 0:16;
'Max' = 65535:16;
'Chan' = 1:8;
};
'VolumeRight' = {
'Type' = 'Volume';
'Id' = 2:16;
'Val' = 65535:16;
'Min' = 0:16;
'Max' = 65535:16;
'Chan' = 2:8;
};
};
}";
return (char*)sSpeaker;
}
char*
PPCBurgundy::componentDictionaryHeadphones()
{
static const char *sHeadphones =
"{
'Type' = 'Headphones';
'Channels' = 2:8;
'Master' = 1:8;
'Controls' = {
'MuteAll' = {
'Type' = 'Mute';
'Id' = 3:16;
'Val' = 0:8;
'Min' = 0:8;
'Max' = 1:8;
'Chan' = 0:8;
};
'VolumeLeft' = {
'Type' = 'Volume';
'Id' = 4:16;
'Val' = 65535:16;
'Min' = 0:16;
'Max' = 65535:16;
'Chan' = 1:8;
};
'VolumeRight' = {
'Type' = 'Volume';
'Id' = 5:16;
'Val' = 65535:16;
'Min' = 0:16;
'Max' = 65535:16;
'Chan' = 2:8;
};
};
'Inputs' = {
'Jack' = {
'Val' = 0:8;
'Min' = 0:8;
'Max' = 1:8;
};
};
}";
return (char*)sHeadphones;
}
char*
PPCBurgundy::componentDictionaryMicrophone()
{
static const char *sMicrophone =
"{
'Type' = 'Microphone';
'Channels' = 2:8;
'Controls' = {
'MuteAll' = {
'Type' = 'Mute';
'Id' = 6:16;
'Val' = 0:8;
'Min' = 0:8;
'Max' = 1:8;
'Chan' = 0:8;
};
};
'Inputs' = {
'Jack' = {
'Val' = 0:8;
'Min' = 0:8;
'Max' = 1:8;
};
};
}";
return (char*)sMicrophone;
}
char*
PPCBurgundy::componentDictionaryLinein()
{
static const char *sLinein =
"{
'Type' = 'LineIn';
'Channels' = 2:8;
'Controls' = {
'MuteAll' = {
'Type' = 'Mute';
'Id' = 14:16;
'Val' = 0:8;
'Min' = 0:8;
'Max' = 1:8;
'Chan' = 0:8;
};
};
'Inputs' = {
'Jack' = {
'Val' = 0:8;
'Min' = 0:8;
'Max' = 1:8;
};
};
}";
return (char*)sLinein;
}
char*
PPCBurgundy::componentDictionaryLineout()
{
static const char *sLineout =
"{
'Type' = 'LineOut';
'Channels' = 2:8;
'Controls' = {
'MuteAll' = {
'Type' = 'Mute';
'Id' = 13:16;
'Val' = 0:8;
'Min' = 0:8;
'Max' = 1:8;
'Chan' = 0:8;
};
};
'Inputs' = {
'Jack' = {
'Val' = 0:8;
'Min' = 0:8;
'Max' = 1:8;
};
};
}";
return (char*)sLineout;
}
char*
PPCBurgundy::componentDictionaryCD()
{
static const char *sCD =
"{
'Type' = 'CD';
'Channels' = 2:8;
'Controls' = {
'MuteAll' = {
'Type' = 'Mute';
'Id' = 7:16;
'Val' = 0:8;
'Min' = 0:8;
'Max' = 1:8;
'Chan' = 0:8;
};
};
}";
return (char*)sCD;
}
char*
PPCBurgundy::componentDictionaryMixerIn()
{
static const char *sMixerIn =
"{
'Type' = 'Mixer';
'Channels' = 2:8;
'Controls' = {
'VolumeLeft' = {
'Type' = 'Volume';
'Id' = 8:16;
'Val' = 65535:16;
'Min' = 0:16;
'Max' = 65535:16;
'Chan' = 1:8;
};
'VolumeRight' = {
'Type' = 'Volume';
'Id' = 9:16;
'Val' = 65535:16;
'Min' = 0:16;
'Max' = 65535:16;
'Chan' = 2:8;
};
};
}";
return (char*)sMixerIn;
}
char*
PPCBurgundy::componentDictionaryMixerOut()
{
static const char *sMixerOut =
"{
'Type' = 'Mixer';
'Channels' = 2:8;
}";
return (char*)sMixerOut;
}
char*
PPCBurgundy::componentDictionaryPassThru()
{
static const char *sPassThru =
"{
'Type' = 'Feature';
'Channels' = 2:8;
'Controls' = {
'MuteAll' = {
'Type' = 'Mute';
'Id' = 12:16;
'Val' = 1:8;
'Min' = 0:8;
'Max' = 1:8;
'Chan' = 0:8;
};
'VolumeLeft' = {
'Type' = 'Volume';
'Id' = 10:16;
'Val' = 65535:16;
'Min' = 0:16;
'Max' = 65535:16;
'Chan' = 1:8;
};
'VolumeRight' = {
'Type' = 'Volume';
'Id' = 11:16;
'Val' = 65535:16;
'Min' = 0:16;
'Max' = 65535:16;
'Chan' = 2:8;
};
};
}";
return (char*)sPassThru;
}
// --------------------------------------------------------------------------
// Method: dependentSetup
//
// Purpose:
// this handles the setup of the burgundy chip for each kind of
// hosting hardware.
bool
PPCBurgundy::dependentSetup()
{
UInt32 OSReg = 0;
//
// G3-Gossamer DVD-card assignments (Bordeaux)
//
// Port 17 - Internal speaker
// Port 15 - Rear panel RCA Out
// Port 16 - Rear panel MiniJack Out
//
// Yosemite assignments
//
// Port 17 - Internal speaker
// Port 14 - Rear panel MiniJack Out
//
writeCodecReg( ioBaseBurgundy, kOutputLvlPort17Reg, kOutputLvl_Default );
switch (findHostingHardware(NULL))
{
case kMachineTypeSilk:
// G3-Gossamer DVD-card
assert(isBordeaux);
writeCodecReg( ioBaseBurgundy, kOutputLvlPort15Reg, kOutputLvl_Default );
writeCodecReg( ioBaseBurgundy, kOutputLvlPort16Reg, kOutputLvl_Default );
writeCodecReg( ioBaseBurgundy, kGAP1LReg, kGAP_Default_Gain );
writeCodecReg( ioBaseBurgundy, kGAP1RReg, kGAP_Default_Gain );
currentOutputMuteReg = kOutputMuteReg_Port15L | kOutputMuteReg_Port15R |
kOutputMuteReg_Port16L | kOutputMuteReg_Port16R |
kOutputMuteReg_Port17M;
OSReg = kOSReg_OS0_Select_MXO1 | kOSReg_OS1_Select_MXO1;
break;
case kMachineTypeiMac:
case kMachineTypeYosemite:
// Yosemite: (FIXME as soon as newwolds works I should move this in the newworld handling
// this should be done also for the others switch statements in this file)
writeCodecReg( ioBaseBurgundy, kOutputLvlPort14Reg, kOutputLvl_Default );
currentOutputMuteReg = kOutputMuteReg_Port14L | kOutputMuteReg_Port14R |
kOutputMuteReg_Port17M;
OSReg = kOSReg_OS0_Select_MXO1;
break;
default:
// FIXME: this may be a newworld, so the registry holds
// all we need to know about how the burgundy is wired:
// Add here the code to interpret it.
break;
}
writeCodecReg( ioBaseBurgundy, kOSReg, OSReg );
writeCodecReg( ioBaseBurgundy, kOutputMuteReg, currentOutputMuteReg );
writeCodecReg( ioBaseBurgundy, kOutputCtl0Reg, kOutputCtl0Reg_OutCtl1_High );
// while this acts on the analog volume controls
writeCodecReg( ioBaseBurgundy, kOutputLvlPort13Reg, 0x00);
writeCodecReg( ioBaseBurgundy, kOutputLvlPort14Reg, 0x00);
writeCodecReg( ioBaseBurgundy, kOutputLvlPort15Reg, 0x00);
writeCodecReg( ioBaseBurgundy, kOutputLvlPort16Reg, 0x00);
writeCodecReg( ioBaseBurgundy, kOutputLvlPort17Reg, 0x00);
// and writes the digital volumes:
writeCodecReg( ioBaseBurgundy, kGAP0LReg, 0xFF);
writeCodecReg( ioBaseBurgundy, kGAP1LReg, 0xFF);
writeCodecReg( ioBaseBurgundy, kGAP2LReg, 0xFF);
writeCodecReg( ioBaseBurgundy, kGAP3LReg, 0xFF);
writeCodecReg( ioBaseBurgundy, kGAP0RReg, 0xFF);
writeCodecReg( ioBaseBurgundy, kGAP1RReg, 0xFF);
writeCodecReg( ioBaseBurgundy, kGAP2RReg, 0xFF);
writeCodecReg( ioBaseBurgundy, kGAP3RReg, 0xFF);
return true;
}
// --------------------------------------------------------------------------
// Method: checkStatusRegister
//
// Purpose:
// if the argument is true mutes the internal speaker, otherwise
// it "unmutes" it.
void
PPCBurgundy::checkStatusRegister()
{
if (burgundyStatusChanged()) {
int i;
OSNumber * num = OSNumber::withNumber(lastStatusRegister, 32);
setProperty("Status", num);
num->release();
#ifdef DEBUGMODE
IOLog("Status changes in: 0x%08lx\n", lastStatusRegister);
#endif
// Something changed, act on it:
// For each sense line we call the action attached
// to thar sense line:
for (i = 0; i < numInputComponents; i++)
if (fInputComponents[i] != NULL)
fInputComponents[i]->Set(gInputsSym, gJackSym, inputComponentStatus(i));
for (i = 0; i < numOutputComponents; i++)
if (fOutputComponents[i] != NULL)
fOutputComponents[i]->Set(gInputsSym, gJackSym, outputComponentStatus(i));
}
}
// --------------------------------------------------------------------------
// Method: inputComponentStatus
//
// Purpose:
// checks the status of the jack line of the ith output component:
bool
PPCBurgundy::inputComponentStatus(int index)
{
switch(index)
{
case kMicroPhone: // check the microphone
return microphoneInserted();
case kCD: // the cd input is not associated to a jack
return false;
case kLineIn: // the RCA line does not have a jack
return false;
}
return false; // by deafult there is no jack
}
// --------------------------------------------------------------------------
// Method: outputComponentStatus
//
// Purpose:
// checks the status of the jack line of the ith output component:
bool
PPCBurgundy::outputComponentStatus(int index)
{
switch(index)
{
case kSpeaker:
return false; // the internal speaker does not have a jack
case kHeadphones: // check the headphones:
return headphonesInserted();
case kLineOut:
return false; // the RCA line does not have a jack
}
return false; // by deafult there is no jack
}
// --------------------------------------------------------------------------
// Method: muteInternalSpeaker
//
// Purpose:
// if the argument is true mutes the internal speaker, otherwise
// it "unmutes" it.
void
PPCBurgundy::muteInternalSpeaker(bool mute)
{
UInt32 outputSourceMask = 0;
switch (findHostingHardware(NULL))
{
case kMachineTypeSilk:
// G3-Gossamer DVD-card
assert(isBordeaux);
outputSourceMask = kOutputMuteReg_Port17M;
break;
case kMachineTypeiMac:
// iMac: (FIXME as soon as newwolds works I should move this in the newworld handling)
outputSourceMask = kOutputMuteReg_Port14L | kOutputMuteReg_Port14R;
break;
case kMachineTypeYosemite:
outputSourceMask = kOutputMuteReg_Port17M;
break;
default:
// FIXME: this may be a newworld, so the registry holds
// all we need to know about how the burgundy is wired:
// Add here the code to interpret it.
break;
}
if (mute)
currentOutputMuteReg &= ~outputSourceMask;
else
currentOutputMuteReg |= outputSourceMask;
writeCodecReg(ioBaseBurgundy, kOutputMuteReg, currentOutputMuteReg);
}
// --------------------------------------------------------------------------
// Method: muteRCAOutput
//
// Purpose:
// if the argument is true mutes the rear panel RCA, otherwise
// it "unmutes" it.
void
PPCBurgundy::muteRCAOutput(bool mute)
{
UInt32 outputSourceMask = 0;
switch (findHostingHardware(NULL))
{
case kMachineTypeSilk:
// G3-Gossamer DVD-card
assert(isBordeaux);
outputSourceMask = kOutputMuteReg_Port15L | kOutputMuteReg_Port15R;
break;
case kMachineTypeYosemite:
// Yosemite does not have an RCA
outputSourceMask = 0;
break;
default:
// FIXME: this may be a newworld, so the registry holds
// all we need to know about how the burgundy is wired:
// Add here the code to interpret it.
outputSourceMask = 0;
break;
}
if (mute)
currentOutputMuteReg &= ~outputSourceMask;
else
currentOutputMuteReg |= outputSourceMask;
writeCodecReg(ioBaseBurgundy, kOutputMuteReg, currentOutputMuteReg);
}
// --------------------------------------------------------------------------
// Method: muteHeadphones
//
// Purpose:
// if the argument is true mutes the rear panel mini jack, otherwise
// it "unmutes" it.
void
PPCBurgundy::muteHeadphones(bool mute)
{
UInt32 outputSourceMask = 0;
switch (findHostingHardware(NULL))
{
case kMachineTypeSilk:
// G3-Gossamer DVD-card
assert(isBordeaux);
outputSourceMask = kOutputMuteReg_Port16L | kOutputMuteReg_Port16R;
break;
case kMachineTypeiMac:
// iMac: (FIXME I do not know how this is wired, as soon as I have the iMac motherboard I should fix it)
outputSourceMask |= kOutputMuteReg_Port15L | kOutputMuteReg_Port15R;
outputSourceMask |= kOutputMuteReg_Port16L | kOutputMuteReg_Port16R;
outputSourceMask |= kOutputMuteReg_Port13M | kOutputMuteReg_Port17M;
break;
case kMachineTypeYosemite:
// Yosemite: (FIXME as soon as newwolds works I should move this in the newworld handling)
outputSourceMask = kOutputMuteReg_Port14L | kOutputMuteReg_Port14R;
break;
default:
// FIXME: this may be a newworld, so the registry holds
// all we need to know about how the burgundy is wired:
// Add here the code to interpret it.
break;
}
if (mute)
currentOutputMuteReg &= ~outputSourceMask;
else
currentOutputMuteReg |= outputSourceMask;
writeCodecReg(ioBaseBurgundy, kOutputMuteReg, currentOutputMuteReg);
}
// --------------------------------------------------------------------------
// Method: muteCDLine
//
// Purpose:
// if the argument is true mutes the line of the cd player
void
PPCBurgundy::muteCDLine(bool mute)
{
// FIXME: Figure out how this is wired up:
}
// --------------------------------------------------------------------------
// Method: setMicInput
//
// Purpose:
// selects the microphone as input source (if bool = true)
void
PPCBurgundy::setMicInput(bool mic)
{
UInt32 reg;
#ifdef DEBUGMODE
IOLog( "PPCBurgundy::setMicInput: %s\n", (mic ? "True" : "False"));
#endif
if (mic) {
reg = readCodecReg( ioBaseBurgundy, kMux2Reg );
reg |= kMux2Reg_Mux2L_SelectPort6L | kMux2Reg_Mux2R_SelectPort6R;
writeCodecReg( ioBaseBurgundy, kMux2Reg, reg );
reg = readCodecReg( ioBaseBurgundy, kMX3Reg );
reg |= kMX3Reg_Select_IS2L | kMX3Reg_Select_IS2R;
writeCodecReg( ioBaseBurgundy, kMX3Reg, reg);
reg = readCodecReg( ioBaseBurgundy, kOSReg );
reg |= kOSReg_OSE_Select_MXO3;
writeCodecReg( ioBaseBurgundy, kOSReg, reg );
reg = readCodecReg( ioBaseBurgundy, kSDOutReg );
reg |= kSDOutReg_OSE_To_SF0;
writeCodecReg( ioBaseBurgundy, kSDOutReg, reg );
}
else {
reg = readCodecReg( ioBaseBurgundy, kMux2Reg );
reg &= ~(kMux2Reg_Mux2L_SelectPort6L | kMux2Reg_Mux2R_SelectPort6R);
writeCodecReg( ioBaseBurgundy, kMux2Reg, reg );
reg = readCodecReg( ioBaseBurgundy, kMX3Reg );
reg &= ~(kMX3Reg_Select_IS2L | kMX3Reg_Select_IS2R);
writeCodecReg( ioBaseBurgundy, kMX3Reg, reg);
reg = readCodecReg( ioBaseBurgundy, kOSReg );
reg &= ~kOSReg_OSE_Select_MXO3;
writeCodecReg( ioBaseBurgundy, kOSReg, reg );
reg = readCodecReg( ioBaseBurgundy, kSDOutReg );
reg &= ~kSDOutReg_OSE_To_SF0;
writeCodecReg( ioBaseBurgundy, kSDOutReg, reg );
}
}
// --------------------------------------------------------------------------
// Method: burgundyStatusChanged()
//
// Purpose:
// returns true if something changed in the status register:.
bool
PPCBurgundy::burgundyStatusChanged()
{
UInt32 currentStatusRegister = readCodecSenseLines(ioBaseBurgundy);
bool returnValue = (currentStatusRegister != lastStatusRegister);
lastStatusRegister = currentStatusRegister;
return (returnValue);
}
// --------------------------------------------------------------------------
// Method: headphonesInserted
//
// Purpose:
// returns true if the headphones are inserted.
bool
PPCBurgundy::headphonesInserted()
{
UInt32 codecSense;
UInt32 inputSourceMask = 0;
switch (findHostingHardware(NULL))
{
case kMachineTypeSilk:
// G3-Gossamer DVD-card
assert(isBordeaux);
inputSourceMask = kCodecStatusReg_Sense_Headphones;
break;
case kMachineTypeiMac:
// iMac: (FIXME as soon as newwolds works I should move this in the newworld handling)
inputSourceMask = kCodecStatusReg_Sense_Headphones | kCodecStatusReg_Sense_Headphones2 | kCodecStatusReg_Sense_Bit;
break;
case kMachineTypeYosemite:
// Yosemite: (FIXME as soon as newwolds works I should move this in the newworld handling)
inputSourceMask = kCodecStatusReg_Sense_Headphones;
break;
default:
// FIXME: this may be a newworld, so the registry holds
// all we need to know about how the burgundy is wired:
// Add here the code to interpret it.
break;
}
codecSense = readCodecSenseLines(ioBaseBurgundy);
#ifdef DEBUGMODE
IOLog( "PPCBurgundy::headphonesInserted = %d\n\r", (codecSense & inputSourceMask) != 0);
#endif
return (codecSense & inputSourceMask) != 0;
}
// --------------------------------------------------------------------------
// Method: microphoneInserted
//
// Purpose:
// returns true if the microphone is inserted.
bool
PPCBurgundy::microphoneInserted()
{
UInt32 codecSense;
UInt32 inputSourceMask = 0;
switch (findHostingHardware(NULL))
{
case kMachineTypeSilk:
// G3-Gossamer DVD-card
assert(isBordeaux);
inputSourceMask = kCodecStatusReg_Sense_Mic;
break;
case kMachineTypeiMac:
// iMac: (FIXME as soon as newwolds works I should move this in the newworld handling)
inputSourceMask = kCodecStatusReg_Sense_Mic;
break;
case kMachineTypeYosemite:
// Yosemite: (FIXME as soon as newwolds works I should move this in the newworld handling)
inputSourceMask = kCodecStatusReg_Sense_Mic;
break;
default:
// FIXME: this may be a newworld, so the registry holds
// all we need to know about how the burgundy is wired:
// Add here the code to interpret it.
break;
}
codecSense = readCodecSenseLines(ioBaseBurgundy);
#ifdef DEBUGMODE
IOLog( "PPCBurgundy::microphoneInserted = %d\n\r", (codecSense & inputSourceMask) != 0);
#endif
// FIXME: This should not be necessary, but since it does not work by default
// I'll add this call myself:
setMicInput((codecSense & inputSourceMask) != 0);
return (codecSense & inputSourceMask) != 0;
}
// --------------------------------------------------------------------------
// Method: scaleVolume
//
// Purpose:
// Scale the volume to the range used by burgundy:
// note, this considers that the volume can range from
// 0 to 65536 as in the control strings above (e.g. componentDictionaryMixerIn)
UInt8
PPCBurgundy::scaleVolume(int vol)
{
int output;
output = vol / 4096;
if ( output > 15 )
output = 15;
if (output < 0 )
output = 0;
#ifdef DEBUGMODE
IOLog("Volume input: %d, output (scaled) %d\n", vol, output);
#endif
return (UInt8)output;
}
// --------------------------------------------------------------------------
// Method: volumeHeadphonesLeft & volumeHeadphonesRight
//
// Purpose:
// sets the volume for the left and right channel of the internal
// headphones.
void
PPCBurgundy::volumeHeadphonesLeft(int vol)
{
// FIXME: This sets all the output volumes, but we may wish to have
// a more discriminating volume handling. As soon as I know for sure
// how burgundy is wired we can do it. For now this should be fine.
volumeAllLeft(vol);
}
void
PPCBurgundy::volumeHeadphonesRight(int vol)
{
// FIXME: This sets all the output volumes, but we may wish to have
// a more discriminating volume handling. As soon as I know for sure
// how burgundy is wired we can do it. For now this should be fine.
volumeAllRight(vol);
}
// --------------------------------------------------------------------------
// Method: volumeInternalSpeakerLeft & volumeInternalSpeakerRight
//
// Purpose:
// sets the volume for the left and right channel of the internal
// speaker.
void
PPCBurgundy::volumeInternalSpeakerLeft(int vol)
{
// FIXME: This sets all the output volumes, but we may wish to have
// a more discriminating volume handling. As soon as I know for sure
// how burgundy is wired we can do it. For now this should be fine.
volumeAllLeft(vol);
}
void
PPCBurgundy::volumeInternalSpeakerRight(int vol)
{
// FIXME: This sets all the output volumes, but we may wish to have
// a more discriminating volume handling. As soon as I know for sure
// how burgundy is wired we can do it. For now this should be fine.
volumeAllRight(vol);
}
// --------------------------------------------------------------------------
// Method: volumeCDLineLeft & volumeCDLineRight
//
// Purpose:
// sets the volume for the left and right channel of the CD
// line.
void
PPCBurgundy::volumeCDLineLeft(int vol)
{
// FIXME: This sets all the output volumes, but we may wish to have
// a more discriminating volume handling. As soon as I know for sure
// how burgundy is wired we can do it. For now this should be fine.
volumeAllLeft(vol);
}
void
PPCBurgundy::volumeCDLineRight(int vol)
{
// FIXME: This sets all the output volumes, but we may wish to have
// a more discriminating volume handling. As soon as I know for sure
// how burgundy is wired we can do it. For now this should be fine.
volumeAllRight(vol);
}
// --------------------------------------------------------------------------
// Method: volumeRCAOutputLeft & volumeRCAOutputRight
//
// Purpose:
// sets the volume for the left and right channel of the RCA
// line.
void
PPCBurgundy::volumeRCAOutputLeft(int vol)
{
// FIXME: This sets all the output volumes, but we may wish to have
// a more discriminating volume handling. As soon as I know for sure
// how burgundy is wired we can do it. For now this should be fine.
volumeAllLeft(vol);
}
void
PPCBurgundy::volumeRCAOutputRight(int vol)
{
// FIXME: This sets all the output volumes, but we may wish to have
// a more discriminating volume handling. As soon as I know for sure
// how burgundy is wired we can do it. For now this should be fine.
volumeAllRight(vol);
}
// --------------------------------------------------------------------------
// Method: volumeAllRight & volumeAllLeft
//
// Purpose:
// sets the volume to all the output ports. This is because I do not
// really know how burgundy is wired up. (and because however the
// OS has only one control for the volume).
void
PPCBurgundy::volumeAllLeft(int vol)
{
UInt8 newVol = scaleVolume(vol);
// This changes all the digital volume controls (used only to mute and unmute the oputput)
if (newVol > 0) {
writeCodecReg( ioBaseBurgundy, kGAP0LReg, 0xFF);
writeCodecReg( ioBaseBurgundy, kGAP1LReg, 0xFF);
writeCodecReg( ioBaseBurgundy, kGAP2LReg, 0xFF);
writeCodecReg( ioBaseBurgundy, kGAP3LReg, 0xFF);
}
else {
writeCodecReg( ioBaseBurgundy, kGAP0LReg, 0x00);
writeCodecReg( ioBaseBurgundy, kGAP1LReg, 0x00);
writeCodecReg( ioBaseBurgundy, kGAP2LReg, 0x00);
writeCodecReg( ioBaseBurgundy, kGAP3LReg, 0x00);
}
// while this acts on the analog volume controls
newVol = 15 - newVol;
writeCodecReg( ioBaseBurgundy, kOutputLvlPort13Reg, (readCodecReg(ioBaseBurgundy, kOutputLvlPort13Reg) & 0xF0) | (newVol & 0x0F));
writeCodecReg( ioBaseBurgundy, kOutputLvlPort14Reg, (readCodecReg(ioBaseBurgundy, kOutputLvlPort14Reg) & 0xF0) | (newVol & 0x0F));
writeCodecReg( ioBaseBurgundy, kOutputLvlPort15Reg, (readCodecReg(ioBaseBurgundy, kOutputLvlPort15Reg) & 0xF0) | (newVol & 0x0F));
writeCodecReg( ioBaseBurgundy, kOutputLvlPort16Reg, (readCodecReg(ioBaseBurgundy, kOutputLvlPort16Reg) & 0xF0) | (newVol & 0x0F));
writeCodecReg( ioBaseBurgundy, kOutputLvlPort17Reg, (readCodecReg(ioBaseBurgundy, kOutputLvlPort17Reg) & 0xF0) | (newVol & 0x0F));
}
void
PPCBurgundy::volumeAllRight(int vol)
{
UInt8 newVol = scaleVolume(vol);
// This changes all the digital volume controls (used only to mute and unmute the oputput)
if (newVol > 0) {
writeCodecReg( ioBaseBurgundy, kGAP0RReg, 0xFF);
writeCodecReg( ioBaseBurgundy, kGAP1RReg, 0xFF);
writeCodecReg( ioBaseBurgundy, kGAP2RReg, 0xFF);
writeCodecReg( ioBaseBurgundy, kGAP3RReg, 0xFF);
}
else {
writeCodecReg( ioBaseBurgundy, kGAP0RReg, 0x00);
writeCodecReg( ioBaseBurgundy, kGAP1RReg, 0x00);
writeCodecReg( ioBaseBurgundy, kGAP2RReg, 0x00);
writeCodecReg( ioBaseBurgundy, kGAP3RReg, 0x00);
}
// while this acts on the analog volume controls
newVol = (15 - newVol) << 4;
writeCodecReg( ioBaseBurgundy, kOutputLvlPort13Reg, (readCodecReg(ioBaseBurgundy, kOutputLvlPort13Reg) & 0x0F) | (newVol & 0xF0));
writeCodecReg( ioBaseBurgundy, kOutputLvlPort14Reg, (readCodecReg(ioBaseBurgundy, kOutputLvlPort14Reg) & 0x0F) | (newVol & 0xF0));
writeCodecReg( ioBaseBurgundy, kOutputLvlPort15Reg, (readCodecReg(ioBaseBurgundy, kOutputLvlPort15Reg) & 0x0F) | (newVol & 0xF0));
writeCodecReg( ioBaseBurgundy, kOutputLvlPort16Reg, (readCodecReg(ioBaseBurgundy, kOutputLvlPort16Reg) & 0x0F) | (newVol & 0xF0));
writeCodecReg( ioBaseBurgundy, kOutputLvlPort17Reg, (readCodecReg(ioBaseBurgundy, kOutputLvlPort17Reg) & 0x0F) | (newVol & 0xF0));
}
// --------------------------------------------------------------------------
// Method: volumeMixerInLeft
//
// Purpose:
// sets the gain for the input left line
void
PPCBurgundy::volumeMixerInLeft(int vol)
{
UInt8 left = scaleVolume(vol);
writeCodecReg( ioBaseBurgundy, kVGA2Reg, 0x44 );
writeCodecReg( ioBaseBurgundy, kGAS2LReg, left );
}
// --------------------------------------------------------------------------
// Method: volumeMixerInRight
//
// Purpose:
// sets the gain for the input right line
void
PPCBurgundy::volumeMixerInRight(int vol)
{
UInt8 right = scaleVolume(vol);
writeCodecReg( ioBaseBurgundy, kVGA2Reg, 0x44 );
writeCodecReg( ioBaseBurgundy, kGAS2RReg, right );
}
/* =============================================================
* VERY Private Methods used to access to the burgundy registers
* ============================================================= */
static void
writeCodecReg( volatile UInt8 *ioBaseBurgundy, int regInfo, int value )
{
u_int32_t regBits;
u_int32_t regValue;
u_int32_t i;
volatile u_int32_t CodecControlReg;
struct _reg
{
UInt8 unused0;
UInt8 size;
UInt8 addr;
UInt8 offset;
} *reg = (struct _reg *)®Info;
regBits = (kCodecCtlReg_Write | kCodecCtlReg_Reset)
| ((u_int32_t) reg->addr * kCodecCtlReg_Addr_Bit)
| ((u_int32_t)(reg->size + reg->offset - 1) * kCodecCtlReg_LastByte_Bit);
for ( i=0; i < reg->size; i++ )
{
regValue = regBits | ((u_int32_t)(reg->offset + i) * kCodecCtlReg_CurrentByte_Bit) | (value & 0xFF);
OSWriteLittleInt32(ioBaseBurgundy, kCodecCtlReg, regValue );
eieio();
#ifdef DEBUGMODE
IOLog( "PPCSound(burgundy): CodecWrite = %08x\n\r", regValue );
#endif
value >>= 8;
regBits &= ~kCodecCtlReg_Reset;
do
{
CodecControlReg = OSReadLittleInt32( ioBaseBurgundy, kCodecCtlReg );
eieio();
}
while ( CodecControlReg & kCodecCtlReg_Busy );
}
}
static int
readCodecReg( volatile UInt8 *ioBaseBurgundy, int regInfo )
{
u_int32_t regBits;
u_int32_t regValue = 0;
u_int32_t i,j;
int value;
u_int32_t byteCounter;
u_int32_t currentCounter;
volatile u_int32_t CodecControlReg;
volatile u_int32_t CodecStatusReg;
struct _reg
{
UInt8 unused0;
UInt8 size;
UInt8 addr;
UInt8 offset;
} *reg = (struct _reg *)®Info;
value = 0;
regBits = (kCodecCtlReg_Reset)
| ((u_int32_t) reg->addr * kCodecCtlReg_Addr_Bit)
| ((u_int32_t)(reg->size + reg->offset - 1) * kCodecCtlReg_LastByte_Bit);
CodecStatusReg = OSReadLittleInt32( ioBaseBurgundy, kCodecStatusReg );
eieio();
byteCounter = ((CodecStatusReg & kCodecStatusReg_ByteCounter_Mask) / kCodecStatusReg_ByteCounter_Bit + 1) & 0x03;
for ( i=0; i < reg->size; i++ )
{
regValue = regBits | ((u_int32_t)(reg->offset + i) * kCodecCtlReg_CurrentByte_Bit);
OSWriteLittleInt32(ioBaseBurgundy, kCodecCtlReg, regValue );
eieio();
regBits &= ~kCodecCtlReg_Reset;
do
{
CodecControlReg = OSReadLittleInt32( ioBaseBurgundy, kCodecCtlReg );
eieio();
}
while ( CodecControlReg & kCodecCtlReg_Busy );
j=0;
do
{
CodecStatusReg = OSReadLittleInt32( ioBaseBurgundy, kCodecStatusReg );
eieio();
currentCounter = ((CodecStatusReg & kCodecStatusReg_ByteCounter_Mask) / kCodecStatusReg_ByteCounter_Bit) & 0x03;
}
while ( (byteCounter != currentCounter) && (j++ < 1000));
byteCounter++;
IODelay(10);
CodecStatusReg = OSReadLittleInt32( ioBaseBurgundy, kCodecStatusReg );
value |= ((CodecStatusReg & kCodecStatusReg_Data_Mask) / kCodecStatusReg_Data_Bit) << 8*i;
}
#ifdef DEBUGMODE
IOLog( "PPCSound(burgundy): CodecRead = %08x %08x\n\r", regValue, value );
#endif
return value;
}
static int
readCodecSenseLines( volatile UInt8 *ioBaseBurgundy )
{
return ((OSReadLittleInt32( ioBaseBurgundy, kCodecStatusReg ) / kCodecStatusReg_Sense_Bit) & kCodecStatusReg_Sense_Mask);
}
static void
writeSoundControlReg( volatile UInt8 *ioBaseBurgundy, int value )
{
#ifdef DEBUGMODE
IOLog( "PPCSound(burgundy): SoundControlReg = %08x\n", value);
#endif
OSWriteLittleInt32( ioBaseBurgundy, kSoundCtlReg, value );
eieio();
}