BeOS Sound Driver for Intel ICH AC'97 Link interface

Inital checkin of version 1.0, as published on BeBits.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@70 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
beveloper 2002-07-11 00:12:08 +00:00
parent e24ab46c4e
commit af9bf37038
13 changed files with 2060 additions and 0 deletions

View File

@ -0,0 +1,361 @@
/*
* BeOS Driver for Intel ICH AC'97 Link interface
*
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <OS.h>
#include <MediaDefs.h>
#include "multi_audio.h"
//#define DEBUG 1
#include "debug.h"
#include "ich.h"
#include "util.h"
/*
*
* XXX!!! ALL "MIX" ioctls are not implemented by the BeOS R5, get never called :-(
* The concept described by B_MULTI_SET_BUFFERS is impossible to implement
* B_MULTI_GET_BUFFERS can not be handled as efficient as envisioned by the API creators
*
*/
static status_t get_control(multi_mix_control * control)
{
return B_ERROR;
}
static status_t list_mix_controls(multi_mix_control_info * data)
{
return B_ERROR;
}
static status_t list_mix_connections(multi_mix_connection_info * data)
{
return B_ERROR;
}
static status_t list_mix_channels(multi_mix_channel_info *data)
{
return B_ERROR;
}
multi_channel_info chans[] = {
{ 0, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
{ 1, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
{ 2, B_MULTI_INPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
{ 3, B_MULTI_INPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
{ 4, B_MULTI_OUTPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO },
{ 5, B_MULTI_OUTPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO },
{ 6, B_MULTI_INPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO },
{ 7, B_MULTI_INPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO },
};
status_t get_description(multi_description *data)
{
data->interface_version = B_CURRENT_INTERFACE_VERSION;
data->interface_minimum = B_CURRENT_INTERFACE_VERSION;
strcpy(data->friendly_name,"AC97 (ICH)");
strcpy(data->vendor_info,"Marcus Overhagen");
data->output_channel_count = 2;
data->input_channel_count = 2;
data->output_bus_channel_count = 2;
data->input_bus_channel_count = 2;
data->aux_bus_channel_count = 0;
// for each channel, starting with the first output channel,
// then the second, third..., followed by the first input
// channel, second, third, ..., followed by output bus
// channels and input bus channels and finally auxillary channels,
TRACE(("request_channel_count = %d\n",data->request_channel_count));
if (data->request_channel_count >= (int)(sizeof(chans) / sizeof(chans[0]))) {
TRACE(("copying data\n"));
memcpy(data->channels,&chans,sizeof(chans));
}
data->output_rates = B_SR_48000;// | B_SR_44100 | B_SR_CVSR;
data->input_rates = B_SR_48000;// | B_SR_44100 | B_SR_CVSR;
data->min_cvsr_rate = 0;
data->max_cvsr_rate = 48000;
data->output_formats = B_FMT_16BIT;
data->input_formats = B_FMT_16BIT;
data->lock_sources = B_MULTI_LOCK_INTERNAL;
data->timecode_sources = 0;
data->interface_flags = B_MULTI_INTERFACE_PLAYBACK | B_MULTI_INTERFACE_RECORD;
data->start_latency = 3000;
strcpy(data->control_panel,"");
return B_OK;
}
status_t get_enabled_channels(multi_channel_enable *data)
{
B_SET_CHANNEL(data->enable_bits, 0, true);
B_SET_CHANNEL(data->enable_bits, 1, true);
B_SET_CHANNEL(data->enable_bits, 2, true);
B_SET_CHANNEL(data->enable_bits, 3, true);
data->lock_source = B_MULTI_LOCK_INTERNAL;
/*
uint32 lock_source;
int32 lock_data;
uint32 timecode_source;
uint32 * connectors;
*/
return B_OK;
}
status_t get_global_format(multi_format_info *data)
{
data->output_latency = 0;
data->input_latency = 0;
data->timecode_kind = 0;
data->input.rate = B_SR_48000;
data->input.cvsr = 48000;
data->input.format = B_FMT_16BIT;
data->output.rate = B_SR_48000;
data->output.cvsr = 48000;
data->output.format = B_FMT_16BIT;
return B_OK;
}
status_t get_buffers(multi_buffer_list *data)
{
TRACE(("flags = %#x\n",data->flags));
TRACE(("request_playback_buffers = %#x\n",data->request_playback_buffers));
TRACE(("request_playback_channels = %#x\n",data->request_playback_channels));
TRACE(("request_playback_buffer_size = %#x\n",data->request_playback_buffer_size));
TRACE(("request_record_buffers = %#x\n",data->request_record_buffers));
TRACE(("request_record_channels = %#x\n",data->request_record_channels));
TRACE(("request_record_buffer_size = %#x\n",data->request_record_buffer_size));
if (data->request_playback_buffers < 2 ||
data->request_playback_channels < 2 ||
data->request_record_buffers < 2 ||
data->request_record_channels < 2) {
TRACE(("not enough channels/buffers\n"));
}
ASSERT(BUFFER_COUNT == 2);
// data->flags = B_MULTI_BUFFER_PLAYBACK | B_MULTI_BUFFER_RECORD; // XXX ???
data->flags = 0;
data->return_playback_buffers = 2; /* playback_buffers[b][] */
data->return_playback_channels = 2; /* playback_buffers[][c] */
data->return_playback_buffer_size = BUFFER_SIZE / 4; /* frames */
/* buffer 0, left channel */
data->playback_buffers[0][0].base = chan_po->userbuffer[0]; /* pointer to first sample for channel for buffer */
data->playback_buffers[0][0].stride = 4; /* offset to next sample */
/* buffer 0, right channel */
data->playback_buffers[0][1].base = ((char*)chan_po->userbuffer[0]) + 2; /* pointer to first sample for channel for buffer */
data->playback_buffers[0][1].stride = 4; /* offset to next sample */
/* buffer 1, left channel */
data->playback_buffers[1][0].base = chan_po->userbuffer[1]; /* pointer to first sample for channel for buffer */
data->playback_buffers[1][0].stride = 4; /* offset to next sample */
/* buffer 1, right channel */
data->playback_buffers[1][1].base = ((char*)chan_po->userbuffer[1]) + 2; /* pointer to first sample for channel for buffer */
data->playback_buffers[1][1].stride = 4; /* offset to next sample */
data->return_record_buffers = 2;
data->return_record_channels = 2;
data->return_record_buffer_size = BUFFER_SIZE / 4; /* frames */
/* buffer 0, left channel */
data->record_buffers[0][0].base = chan_pi->userbuffer[0]; /* pointer to first sample for channel for buffer */
data->record_buffers[0][0].stride = 4; /* offset to next sample */
/* buffer 0, right channel */
data->record_buffers[0][1].base = chan_pi->userbuffer[0] + 2; /* pointer to first sample for channel for buffer */
data->record_buffers[0][1].stride = 4; /* offset to next sample */
/* buffer 1, left channel */
data->record_buffers[1][0].base = chan_pi->userbuffer[1]; /* pointer to first sample for channel for buffer */
data->record_buffers[1][0].stride = 4; /* offset to next sample */
/* buffer 1, right channel */
data->record_buffers[1][1].base = chan_pi->userbuffer[1] + 2; /* pointer to first sample for channel for buffer */
data->record_buffers[1][1].stride = 4; /* offset to next sample */
return B_OK;
}
status_t buffer_exchange(multi_buffer_info *data)
{
cpu_status status;
void *backbuffer;
/*
static int next_input = 0;
*/
/*
* Only playback works for now, recording seems to be broken, but I don't know why
*/
if (!chan_po->running)
start_chan(chan_po);
/*
if (!chan_pi->running)
start_chan(chan_pi);
*/
/* do playback */
acquire_sem(chan_po->buffer_ready_sem);
status = lock();
backbuffer = (void *)chan_po->backbuffer;
data->played_real_time = chan_po->played_real_time;
data->played_frames_count = chan_po->played_frames_count;
unlock(status);
memcpy(backbuffer, chan_po->userbuffer[data->playback_buffer_cycle], BUFFER_SIZE);
/* do record */
data->record_buffer_cycle = 0;
data->recorded_frames_count = 0;
/*
if (B_OK == acquire_sem_etc(chan_pi->buffer_ready_sem, 1, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT,200)) {
status = lock();
backbuffer = (void *)chan_pi->backbuffer;
data->recorded_real_time = chan_pi->played_real_time;
unlock(status);
data->recorded_frames_count = BUFFER_SIZE / 4;
data->record_buffer_cycle = next_input;
next_input = (next_input + 1) % 2;
memcpy(chan_pi->userbuffer[data->record_buffer_cycle],backbuffer, BUFFER_SIZE);
} else {
data->record_buffer_cycle = next_input;
data->recorded_frames_count = 0;
}
*/
return B_OK;
/*
if (data->flags & B_MULTI_BUFFER_PLAYBACK) {
dprintf("B_MULTI_BUFFER_PLAYBACK\n");
}
if (data->flags & B_MULTI_BUFFER_RECORD) {
dprintf("B_MULTI_BUFFER_RECORD\n");
data->recorded_real_time = 0;
data->recorded_frames_count = 0;
data->record_buffer_cycle = 0;
}
if (data->flags & B_MULTI_BUFFER_METERING) {
dprintf("B_MULTI_BUFFER_METERING\n");
data->meter_channel_count = 0;
// data->meters_peak
// data->meters_average
}
if (data->flags & B_MULTI_BUFFER_TIMECODE) {
dprintf("B_MULTI_BUFFER_TIMECODE\n");
data->hours = 0;
data->minutes = 0;
data->seconds = 0;
data->tc_frames = 0;
data->at_frame_delta = 0;
}
return B_OK;
*/
}
status_t buffer_force_stop()
{
return B_OK;
}
status_t multi_control(void *cookie, uint32 op, void *data, size_t length)
{
switch (op) {
case B_MULTI_GET_DESCRIPTION:
TRACE(("B_MULTI_GET_DESCRIPTION\n"));
return get_description((multi_description *)data);
case B_MULTI_GET_EVENT_INFO:
TRACE(("B_MULTI_GET_EVENT_INFO\n"));
return B_ERROR;
case B_MULTI_SET_EVENT_INFO:
TRACE(("B_MULTI_SET_EVENT_INFO\n"));
return B_ERROR;
case B_MULTI_GET_EVENT:
TRACE(("B_MULTI_GET_EVENT\n"));
return B_ERROR;
case B_MULTI_GET_ENABLED_CHANNELS:
TRACE(("B_MULTI_GET_ENABLED_CHANNELS\n"));
return get_enabled_channels((multi_channel_enable *)data);
case B_MULTI_SET_ENABLED_CHANNELS:
TRACE(("B_MULTI_SET_ENABLED_CHANNELS\n"));
return B_OK; break;
case B_MULTI_GET_GLOBAL_FORMAT:
TRACE(("B_MULTI_GET_GLOBAL_FORMAT\n"));
return get_global_format((multi_format_info *)data);
case B_MULTI_SET_GLOBAL_FORMAT:
TRACE(("B_MULTI_SET_GLOBAL_FORMAT\n"));
return B_OK; /* XXX BUG! we *MUST* return B_OK, returning B_ERROR will prevent
* BeOS to accept the format returned in B_MULTI_GET_GLOBAL_FORMAT
*/
case B_MULTI_GET_CHANNEL_FORMATS:
TRACE(("B_MULTI_GET_CHANNEL_FORMATS\n"));
return B_ERROR;
case B_MULTI_SET_CHANNEL_FORMATS: /* only implemented if possible */
TRACE(("B_MULTI_SET_CHANNEL_FORMATS\n"));
return B_ERROR;
case B_MULTI_GET_MIX:
TRACE(("B_MULTI_GET_MIX\n"));
return B_ERROR;
case B_MULTI_SET_MIX:
TRACE(("B_MULTI_SET_MIX\n"));
return B_ERROR;
case B_MULTI_LIST_MIX_CHANNELS:
TRACE(("B_MULTI_LIST_MIX_CHANNELS\n"));
return list_mix_channels((multi_mix_channel_info *)data);
case B_MULTI_LIST_MIX_CONTROLS:
TRACE(("B_MULTI_LIST_MIX_CONTROLS\n"));
return list_mix_controls((multi_mix_control_info *)data);
case B_MULTI_LIST_MIX_CONNECTIONS:
TRACE(("B_MULTI_LIST_MIX_CONNECTIONS\n"));
return list_mix_connections((multi_mix_connection_info *)data);
case B_MULTI_GET_BUFFERS: /* Fill out the struct for the first time; doesn't start anything. */
TRACE(("B_MULTI_GET_BUFFERS\n"));
return get_buffers(data);
case B_MULTI_SET_BUFFERS: /* Set what buffers to use, if the driver supports soft buffers. */
TRACE(("B_MULTI_SET_BUFFERS\n"));
return B_ERROR; /* we do not support soft buffers */
case B_MULTI_SET_START_TIME: /* When to actually start */
TRACE(("B_MULTI_SET_START_TIME\n"));
return B_ERROR;
case B_MULTI_BUFFER_EXCHANGE: /* stop and go are derived from this being called */
// dprintf("B_MULTI_BUFFER_EXCHANGE\n");
return buffer_exchange((multi_buffer_info *)data);
case B_MULTI_BUFFER_FORCE_STOP: /* force stop of playback */
TRACE(("B_MULTI_BUFFER_FORCE_STOP\n"));
return buffer_force_stop();
}
TRACE(("ERROR: unknown multi_control %#x\n",op));
return B_ERROR;
}

View File

@ -0,0 +1,33 @@
/*
* BeOS Driver for Intel ICH AC'97 Link interface
*
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _AC97_MULTI_H_
#define _AC97_MULTI_H_
status_t multi_control(void *cookie, uint32 op, void *data, size_t length);
#endif

View File

@ -0,0 +1,272 @@
/*
* BeOS Driver for Intel ICH AC'97 Link interface
*
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <KernelExport.h>
#include <config_manager.h>
#include <PCI.h>
#include <OS.h>
//#define DEBUG 2
#include "debug.h"
#include "config.h"
#include "ich.h"
device_config c;
device_config *config = &c;
status_t find_pci_pin_irq(uint8 pin, uint8 *irq);
/*
* search for the ICH AC97 controller, and initialize the global config
* XXX multiple controllers not supported
*/
status_t probe_device()
{
pci_module_info *pcimodule;
config_manager_for_driver_module_info *configmodule;
struct device_info info;
uint64 cookie;
status_t result;
struct device_info *dinfo;
struct pci_info *pciinfo;
uint32 value;
if (get_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME,(module_info **)&configmodule) < 0) {
dprintf(DRIVER_NAME " ERROR: couldn't load config manager module\n");
return B_ERROR;
}
if (get_module(B_PCI_MODULE_NAME,(module_info **)&pcimodule) < 0) {
dprintf(DRIVER_NAME " ERROR: couldn't load pci module\n");
put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME);
return B_ERROR;
}
config->name = 0;
config->nambar = 0;
config->nabmbar = 0;
config->mmbar = 0;
config->mbbar = 0;
config->irq = 0;
config->sample_size = 2;
config->swap_reg = false;
config->type = 0;
config->log_mmbar = 0;
config->log_mbbar = 0;
config->area_mmbar = -1;
config->area_mbbar = -1;
result = B_ERROR;
cookie = 0;
while (configmodule->get_next_device_info(B_PCI_BUS, &cookie, &info, sizeof(info)) == B_OK) {
if (info.config_status != B_OK)
continue;
dinfo = (struct device_info *) malloc(info.size);
if (dinfo == 0)
break;
if (configmodule->get_device_info_for(cookie, dinfo, info.size) != B_OK) {
free(dinfo);
break;
}
pciinfo = (struct pci_info *)((char *)dinfo + info.bus_dependent_info_offset);
if (pciinfo->vendor_id == 0x8086 && pciinfo->device_id == 0x7195) {
config->name = "Intel 82443MX";
} else if (pciinfo->vendor_id == 0x8086 && pciinfo->device_id == 0x2415) { /* verified */
config->name = "Intel 82801AA (ICH)";
} else if (pciinfo->vendor_id == 0x8086 && pciinfo->device_id == 0x2425) { /* verified */
config->name = "Intel 82801AB (ICH0)";
} else if (pciinfo->vendor_id == 0x8086 && pciinfo->device_id == 0x2445) { /* verified */
config->name = "Intel 82801BA (ICH2), Intel 82801BAM (ICH2-M)";
} else if (pciinfo->vendor_id == 0x8086 && pciinfo->device_id == 0x2485) { /* verified */
config->name = "Intel 82801CA (ICH3-S), Intel 82801CAM (ICH3-M)";
} else if (pciinfo->vendor_id == 0x8086 && pciinfo->device_id == 0x24C5) { /* verified */
config->name = "Intel 82801DB (ICH4)";
config->type = TYPE_ICH4;
} else if (pciinfo->vendor_id == 0x1039 && pciinfo->device_id == 0x7012) { /* verified */
config->name = "SiS SI7012";
config->swap_reg = true;
config->sample_size = 1;
} else if (pciinfo->vendor_id == 0x10DE && pciinfo->device_id == 0x01B1) {
config->name = "Nvidia nForce AC97";
} else if (pciinfo->vendor_id == 0x1022 && pciinfo->device_id == 0x764d) {
config->name = "AMD AMD8111";
} else if (pciinfo->vendor_id == 0x1022 && pciinfo->device_id == 0x7445) {
config->name = "AMD AMD768";
} else {
free(dinfo);
continue;
}
TRACE(("found %s\n",config->name));
TRACE(("revision = %d\n",pciinfo->revision));
#if DEBUG
TRACE(("bus = %#x, device = %#x, function = %#x\n",pciinfo->bus, pciinfo->device, pciinfo->function));
value = pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x00, 2);
TRACE(("VID = %#04x\n",value));
value = pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x02, 2);
TRACE(("DID = %#04x\n",value));
value = pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x08, 1);
TRACE(("RID = %#02x\n",value));
value = pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x04, 2);
TRACE(("PCICMD = %#04x\n",value));
value = pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x06, 2);
TRACE(("PCISTS = %#04x\n",value));
value = pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x10, 4);
TRACE(("NAMBAR = %#08x\n",value));
value = pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x14, 4);
TRACE(("NABMBAR = %#08x\n",value));
value = pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x3c, 1);
TRACE(("INTR_LN = %#02x\n",value));
value = pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x3d, 1);
TRACE(("INTR_PN = %#02x\n",value));
#endif
/*
* for ICH4 enable memory mapped IO and busmaster access,
* for old ICHs enable programmed IO and busmaster access
*/
value = pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, PCI_PCICMD, 2);
if (config->type & TYPE_ICH4)
value |= PCI_PCICMD_MSE | PCI_PCICMD_BME;
else
value |= PCI_PCICMD_IOS | PCI_PCICMD_BME;
pcimodule->write_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x04, 2, value);
#if DEBUG
value = pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, PCI_PCICMD, 2);
TRACE(("PCICMD = %#04x\n",value));
#endif
config->irq = pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x3C, 1);
if (config->irq == 0 || config->irq == 0xff) {
// workaround: even if no irq is configured, we may be able to find the correct one
uint8 pin;
uint8 irq;
pin = pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x3d, 1);
TRACE(("IRQ not assigned to pin %d\n",pin));
TRACE(("Searching for IRQ...\n"));
if (B_OK == find_pci_pin_irq(pin, &irq)) {
TRACE(("Assigning IRQ %d to pin %d\n",irq,pin));
config->irq = irq;
} else {
config->irq = 0; // always 0, not 0xff if no irq assigned
}
}
if (config->type & TYPE_ICH4) {
// memory mapped access
config->mmbar = 0xfffffffe & pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x18, 4);
config->mbbar = 0xfffffffe & pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x1C, 4);
} else {
// pio access
config->nambar = 0xfffffffe & pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x10, 4);
config->nabmbar = 0xfffffffe & pcimodule->read_pci_config(pciinfo->bus, pciinfo->device, pciinfo->function, 0x14, 4);
}
TRACE(("irq = %d\n",config->irq));
TRACE(("nambar = %#08x\n",config->nambar));
TRACE(("nabmbar = %#08x\n",config->nabmbar));
TRACE(("mmbar = %#08x\n",config->mmbar));
TRACE(("mbbar = %#08x\n",config->mbbar));
free(dinfo);
result = B_OK;
}
if (result != B_OK)
TRACE(("probe_device() hardware not found\n"));
//config->irq = 0;
if (config->irq == 0) {
dprintf(DRIVER_NAME ": WARNING: no interrupt configured\n");
/*
* we can continue without an interrupt, as another
* workaround to handle this is also implemented
*/
}
/* the ICH4 uses memory mapped IO */
if ((config->type & TYPE_ICH4) != 0 && ((config->mmbar == 0) || (config->mbbar == 0))) {
dprintf(DRIVER_NAME " ERROR: memory mapped IO not configured\n");
result = B_ERROR;
}
/* all other ICHs use programmed IO */
if ((config->type & TYPE_ICH4) == 0 && ((config->nambar == 0) || (config->nabmbar == 0))) {
dprintf(DRIVER_NAME " ERROR: IO space not configured\n");
result = B_ERROR;
}
put_module(B_PCI_MODULE_NAME);
put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME);
return result;
}
/*
* This is another ugly workaround. If no irq has been assigned
* to our card, we try to find another card that uses the same
* interrupt pin, but has an irq assigned, and use it.
*/
status_t find_pci_pin_irq(uint8 pin, uint8 *irq)
{
pci_module_info *module;
struct pci_info info;
status_t result;
long index;
if (get_module(B_PCI_MODULE_NAME,(module_info **)&module) < 0) {
dprintf(DRIVER_NAME " ERROR: couldn't load pci module\n");
return B_ERROR;
}
result = B_ERROR;
for (index = 0; B_OK == module->get_nth_pci_info(index, &info); index++) {
uint8 pciirq = module->read_pci_config(info.bus, info.device, info.function, PCI_interrupt_line, 1);
uint8 pcipin = module->read_pci_config(info.bus, info.device, info.function, PCI_interrupt_pin, 1);
TRACE(("pin %d, irq %d\n",pcipin,pciirq));
if (pcipin == pin && pciirq != 0 && pciirq != 0xff) {
*irq = pciirq;
result = B_OK;
break;
}
}
#if DEBUG
if (result != B_OK) {
TRACE(("Couldn't find IRQ for pin %d\n",pin));
}
#endif
put_module(B_PCI_MODULE_NAME);
return result;
}

View File

@ -0,0 +1,54 @@
/*
* BeOS Driver for Intel ICH AC'97 Link interface
*
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _CONFIG_H_
#define _CONFIG_H_
typedef struct
{
const char *name;
uint32 nambar;
uint32 nabmbar;
uint32 irq;
uint32 type;
int sample_size;
bool swap_reg;
uint32 mmbar; // ich4
uint32 mbbar; // ich4
void * log_mmbar; // ich4
void * log_mbbar; // ich4
area_id area_mmbar; // ich4
area_id area_mbbar; // ich4
} device_config;
extern device_config *config;
status_t probe_device();
#define TYPE_ICH4 0x01
#endif

View File

@ -0,0 +1,48 @@
/*
* BeOS Driver for Intel ICH AC'97 Link interface
*
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <stdio.h>
#include <OS.h>
#include "debug.h"
#include "ich.h"
void debug_printf(const char *text,...)
{
char buf[1024];
va_list ap;
va_start(ap,text);
vsprintf(buf,text,ap);
va_end(ap);
dprintf(DRIVER_NAME ": %s",buf);
#if DEBUG > 1
snooze(150000);
#endif
}

View File

@ -0,0 +1,44 @@
/*
* BeOS Driver for Intel ICH AC'97 Link interface
*
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _DEBUG_H_
#define _DEBUG_H_
#ifndef DEBUG
#define DEBUG 0
#endif
#if DEBUG > 0
#define TRACE(a) debug_printf a
#define ASSERT(a) if (a) {} else TRACE(("ASSERT failed! file = %s, line = %d\n",__FILE__,__LINE__))
void debug_printf(const char *text,...);
#else
#define TRACE(a) ((void)(0))
#define ASSERT(a) ((void)(0))
#endif
#endif

View File

@ -0,0 +1,126 @@
/*
* BeOS Driver for Intel ICH AC'97 Link interface
*
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _HARDWARE_H_
#define _HARDWARE_H_
/* Native Audio Bus Master Control Registers */
enum ICH_GLOBAL_REGISTER
{
ICH_REG_GLOB_CNT = 0x2C,
ICH_REG_GLOB_STA = 0x30,
ICH_REG_ACC_SEMA = 0x34
};
enum ICH_X_REGISTER_BASE /* base addresses for the following offsets */
{
ICH_REG_PI_BASE = 0x00,
ICH_REG_PO_BASE = 0x10,
ICH_REG_MC_BASE = 0x20
};
enum ICH_X_REGISTER_OFFSETS /* add base address to get the PI, PO or MC reg */
{
ICH_REG_X_BDBAR = 0x00,
ICH_REG_X_CIV = 0x04,
ICH_REG_X_LVI = 0x05,
ICH_REG_X_SR = 0x06,
ICH_REG_X_PICB = 0x08,
ICH_REG_X_PIV = 0x0A,
ICH_REG_X_CR = 0x0B
};
/* ICH_REG_X_SR (Status Register) Bits */
enum REG_X_SR_BITS
{
SR_DCH = 0x0001,
SR_CELV = 0x0002,
SR_LVBCI = 0x0004,
SR_BCIS = 0x0008,
SR_FIFOE = 0x0010
};
/* ICH_REG_X_CR (Control Register) Bits */
enum REG_X_CR_BITS
{
CR_RPBM = 0x01,
CR_RR = 0x02,
CR_LVBIE = 0x04,
CR_FEIE = 0x08,
CR_IOCE = 0x10
};
/* ICH_REG_GLOB_CNT (Global Control Register) Bits */
enum REG_GLOB_CNT_BITS
{
CNT_GIE = 0x01,
CNT_COLD = 0x02,
CNT_WARM = 0x04,
CNT_SHUT = 0x08,
CNT_PRIE = 0x10,
CNT_SRIE = 0x20
};
/* ICH_REG_GLOB_STA (Global Status Register) Bits */
enum REG_GLOB_STA_BITS
{
STA_GSCI = 0x00000001,
STA_MIINT = 0x00000002,
STA_MOINT = 0x00000004,
STA_PIINT = 0x00000020,
STA_POINT = 0x00000040,
STA_MINT = 0x00000080,
STA_PCR = 0x00000100,
STA_SCR = 0x00000200,
STA_PRI = 0x00000400,
STA_SRI = 0x00000800,
STA_RCS = 0x00008000,
STA_AD3 = 0x00010000,
STA_MD3 = 0x00020000,
STA_INTMASK = (STA_MIINT | STA_MOINT | STA_PIINT | STA_POINT | STA_MINT | STA_PRI | STA_SRI)
};
#define ICH_BD_COUNT 32
/* The Hardware Buffer Descriptor */
typedef struct {
volatile uint32 buffer;
volatile uint16 length;
volatile uint16 flags;
#define ICH_BDC_FLAG_IOC 0x8000
} ich_bd;
/* PCI Configuration Space */
#define PCI_PCICMD 0x04
#define PCI_PCICMD_IOS 0x01
#define PCI_PCICMD_MSE 0x02
#define PCI_PCICMD_BME 0x04
#define PCI_CFG 0x41
#define PCI_CFG_IOSE 0x01
#endif

View File

@ -0,0 +1,737 @@
/*
* BeOS Driver for Intel ICH AC'97 Link interface
*
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#define VERSION "Version 1.0, Copyright (c) 2002 Marcus Overhagen, compiled on " ## __DATE__ ## " " ## __TIME__ ## "\n"
//#define DEBUG 1
#include <KernelExport.h>
#include <Drivers.h>
#include <Errors.h>
#include <OS.h>
#include <malloc.h>
#include "debug.h"
#include "config.h"
#include "io.h"
#include "ich.h"
#include "util.h"
#include "ac97_multi.h"
int32 api_version = B_CUR_DRIVER_API_VERSION;
volatile bool int_thread_exit = false;
thread_id int_thread_id = -1;
ich_chan * chan_pi = 0;
ich_chan * chan_po = 0;
ich_chan * chan_mc = 0;
int32 ihc_int(void *data);
int32 ihc_test_int(void *check);
bool interrupt_test();
void init_chan(ich_chan *chan);
void start_chan(ich_chan *chan);
void reset_chan(ich_chan *chan);
#if DEBUG > 2
void dump_chan(ich_chan *chan)
{
int i;
TRACE(("chan->regbase = %#08x\n",chan->regbase));
TRACE(("chan->buffer_ready_sem = %#08x\n",chan->buffer_ready_sem));
TRACE(("chan->buffer_log_base = %#08x\n",chan->buffer_log_base));
TRACE(("chan->buffer_phy_base = %#08x\n",chan->buffer_phy_base));
TRACE(("chan->bd_phy_base = %#08x\n",chan->bd_phy_base));
TRACE(("chan->bd_log_base = %#08x\n",chan->bd_log_base));
for (i = 0; i < ICH_BD_COUNT; i++) {
TRACE(("chan->buffer[%d] = %#08x\n",i,chan->buffer[i]));
}
for (i = 0; i < ICH_BD_COUNT; i++) {
TRACE(("chan->bd[%d] = %#08x\n",i,chan->bd[i]));
TRACE(("chan->bd[%d]->buffer = %#08x\n",i,chan->bd[i]->buffer));
TRACE(("chan->bd[%d]->length = %#08x\n",i,chan->bd[i]->length));
TRACE(("chan->bd[%d]->flags = %#08x\n",i,chan->bd[i]->flags));
}
}
#endif
void init_chan(ich_chan *chan)
{
int i;
ASSERT(BUFFER_COUNT <= ICH_BD_COUNT);
TRACE(("init chan\n"));
chan->lastindex = 0;
chan->played_frames_count = 0;
chan->played_real_time = 0;
chan->running = false;
for (i = 0; i < BUFFER_COUNT; i++) {
chan->userbuffer[i] = ((char *)chan->userbuffer_base) + i * BUFFER_SIZE;
}
for (i = 0; i < ICH_BD_COUNT; i++) {
chan->buffer[i] = ((char *)chan->buffer_log_base) + (i % BUFFER_COUNT) * BUFFER_SIZE;
chan->bd[i] = (ich_bd *) (((char *)chan->bd_log_base) + i * sizeof(ich_bd));
chan->bd[i]->buffer = ((uint32)chan->buffer_phy_base) + (i % BUFFER_COUNT) * BUFFER_SIZE;
chan->bd[i]->length = BUFFER_SIZE / config->sample_size;
chan->bd[i]->flags = ICH_BDC_FLAG_IOC;
}
// set physical buffer descriptor base address
ich_reg_write_32(chan->regbase, (uint32)chan->bd_phy_base);
TRACE(("init chan finished\n"));
}
void start_chan(ich_chan *chan)
{
int32 civ;
if (chan->running)
return;
#if DEBUG > 2
dump_chan(chan);
#endif
civ = ich_reg_read_8(chan->regbase + ICH_REG_X_CIV);
chan->lastindex = civ;
chan->played_frames_count = -BUFFER_FRAMES_COUNT; /* gets bumped up to 0 in the first interrupt before playback starts */
chan->played_real_time = 0;
// step 1: clear status bits
ich_reg_write_16(chan->regbase + ICH_REG_X_SR, SR_FIFOE | SR_BCIS | SR_LVBCI);
// step 2: prepare buffer transfer
ich_reg_write_8(chan->regbase + ICH_REG_X_LVI, (civ + 28) % ICH_BD_COUNT);
// step 3: enable interrupts & busmaster transfer
ich_reg_write_8(chan->regbase + ICH_REG_X_CR, CR_RPBM | CR_LVBIE | CR_IOCE);
chan->running = true;
}
void reset_chan(ich_chan *chan)
{
int i, cr;
ich_reg_write_8(chan->regbase + ICH_REG_X_CR, 0);
snooze(10000); // 10 ms
chan->running = false;
ich_reg_write_8(chan->regbase + ICH_REG_X_CR, CR_RR);
for (i = 0; i < 10000; i++) {
cr = ich_reg_read_8(chan->regbase + ICH_REG_X_CR);
if (cr == 0) {
TRACE(("channel reset finished, %d\n",i));
return;
}
snooze(1);
}
TRACE(("channel reset failed after 10ms\n"));
}
bool interrupt_test()
{
bool *testresult;
bool result;
bigtime_t duration;
int i;
TRACE(("testing if interrupt is working\n"));
// our stack is not mapped in interrupt context, we must use malloc
// to have a valid pointer inside the interrupt handler
testresult = malloc(sizeof(bool));
*testresult = false; // assume it's not working
install_io_interrupt_handler(config->irq, ihc_test_int, testresult, 0);
// clear status bits
ich_reg_write_16(chan_po->regbase + ICH_REG_X_SR, SR_FIFOE | SR_BCIS | SR_LVBCI);
// start transfer of 1 buffer
ich_reg_write_8(chan_po->regbase + ICH_REG_X_LVI, (ich_reg_read_8(chan_po->regbase + ICH_REG_X_LVI) + 1) % ICH_BD_COUNT);
// enable interrupts & busmaster transfer
ich_reg_write_8(chan_po->regbase + ICH_REG_X_CR, CR_RPBM | CR_LVBIE | CR_IOCE);
// the interrupt handler will set *testresult to true
duration = system_time();
for (i = 0; i < 500; i++) {
if (*testresult)
break;
else
snooze(1000); // 1 ms
}
duration = system_time() - duration;
// disable interrupts & busmaster transfer
ich_reg_write_8(chan_po->regbase + ICH_REG_X_CR, 0);
snooze(25000);
remove_io_interrupt_handler(config->irq, ihc_test_int, testresult);
result = *testresult;
free(testresult);
#if DEBUG
if (result)
TRACE(("got interrupt after %Lu us\n",duration));
else
TRACE(("no interrupt, timeout after %Lu us\n",duration));
#endif
return result;
}
void chan_free_resources()
{
if (chan_po) {
if (chan_po->buffer_ready_sem > B_OK)
delete_sem(chan_po->buffer_ready_sem);
if (chan_po->bd_area > B_OK)
delete_area(chan_po->bd_area);
if (chan_po->buffer_area > B_OK)
delete_area(chan_po->buffer_area);
if (chan_po->userbuffer_area > B_OK)
delete_area(chan_po->userbuffer_area);
free(chan_po);
}
if (chan_pi) {
if (chan_pi->buffer_ready_sem > B_OK)
delete_sem(chan_pi->buffer_ready_sem);
if (chan_pi->bd_area > B_OK)
delete_area(chan_pi->bd_area);
if (chan_pi->buffer_area > B_OK)
delete_area(chan_pi->buffer_area);
if (chan_pi->userbuffer_area > B_OK)
delete_area(chan_pi->userbuffer_area);
free(chan_pi);
}
if (chan_mc) {
if (chan_mc->buffer_ready_sem > B_OK)
delete_sem(chan_mc->buffer_ready_sem);
if (chan_mc->bd_area > B_OK)
delete_area(chan_mc->bd_area);
if (chan_mc->buffer_area > B_OK)
delete_area(chan_mc->buffer_area);
if (chan_mc->userbuffer_area > B_OK)
delete_area(chan_mc->userbuffer_area);
free(chan_mc);
}
}
status_t map_io_memory()
{
if ((config->type & TYPE_ICH4) == 0)
return B_OK;
ASSERT(config->log_mmbar == 0);
ASSERT(config->log_mbbar == 0);
ASSERT(config->mmbar != 0);
ASSERT(config->mbbar != 0);
config->area_mmbar = map_physical_memory("ich_ac97 mmbar io",(void *)config->mmbar, B_PAGE_SIZE, B_ANY_KERNEL_BLOCK_ADDRESS, B_READ_AREA | B_WRITE_AREA, &config->log_mmbar);
if (config->area_mmbar <= B_OK) {
TRACE(("mapping of mmbar io failed\n"));
return B_ERROR;
}
config->area_mbbar = map_physical_memory("ich_ac97 mbbar io",(void *)config->mbbar, B_PAGE_SIZE, B_ANY_KERNEL_BLOCK_ADDRESS, B_READ_AREA | B_WRITE_AREA, &config->log_mbbar);
if (config->area_mbbar <= B_OK) {
TRACE(("mapping of mbbar io failed\n"));
delete_area(config->area_mmbar);
config->area_mmbar = -1;
return B_ERROR;
}
}
status_t unmap_io_memory()
{
status_t rv;
if ((config->type & TYPE_ICH4) == 0)
return B_OK;
rv = delete_area(config->area_mmbar);
rv |= delete_area(config->area_mbbar);
return rv;
}
int32 int_thread(void *data)
{
cpu_status status;
while (!int_thread_exit) {
status = disable_interrupts();
ihc_int(data);
restore_interrupts(status);
snooze(1500);
}
return 0;
}
status_t
init_hardware(void)
{
if (B_OK == probe_device()) {
dprintf("ALL YOUR BASE ARE BELONG TO US\n");
return B_OK;
}
return B_ERROR;
}
status_t
init_driver(void)
{
status_t rv;
bigtime_t start;
TRACE(("init_driver\n"));
TRACE((VERSION));
ASSERT(sizeof(ich_bd) == 8);
rv = probe_device();
if (rv != B_OK) {
TRACE(("hardware not found\n"));
return B_ERROR;
}
dprintf("ich-ac97: " VERSION "\n");
dprintf("ich-ac97: found %s, IRQ = %d, NAMBAR = %#X, NABMBAR = %#X, MMBAR = %#X, MBBAR = %#X\n",config->name,config->irq,config->nambar,config->nabmbar,config->mmbar,config->mbbar);
/* before doing anything else, map the IO memory */
rv = map_io_memory();
if (rv != B_OK) {
dprintf("ich-ac97: mapping of memory IO space failed\n");
return B_ERROR;
}
TRACE(("GLOB_CNT = %#08x\n",ich_reg_read_32(ICH_REG_GLOB_CNT)));
TRACE(("GLOB_STA = %#08x\n",ich_reg_read_32(ICH_REG_GLOB_STA)));
TRACE(("PO ICH_REG_X_LVI = %d\n",ich_reg_read_8(ICH_REG_X_LVI)));
/* do a cold reset */
TRACE(("cold reset\n"));
ich_reg_write_32(ICH_REG_GLOB_CNT, 0);
snooze(50000); // 50 ms
ich_reg_write_32(ICH_REG_GLOB_CNT, CNT_COLD | CNT_PRIE);
TRACE(("cold reset finished\n"));
rv = ich_reg_read_32(ICH_REG_GLOB_CNT);
if ((rv & CNT_COLD) == 0) {
TRACE(("cold reset failed\n"));
}
/* wait until a codec is ready */
start = system_time();
while ((system_time() - start) < 1000000) {
rv = ich_reg_read_32(ICH_REG_GLOB_STA);
if ((rv & STA_PCR) != 0)
break;
snooze(50000);
}
if ((rv & STA_PCR)) {
TRACE(("primary codec ready after %Ld us\n",(system_time() - start)));
} else {
TRACE(("primary codec not ready after %Ld us\n",(system_time() - start)));
}
while ((system_time() - start) < 1000000) {
rv = ich_reg_read_32(ICH_REG_GLOB_STA);
if ((rv & STA_SCR) != 0)
break;
snooze(50000);
}
if ((rv & STA_SCR)) {
TRACE(("secondary codec ready after %Ld us\n",(system_time() - start)));
} else {
TRACE(("secondary codec not ready after %Ld us\n",(system_time() - start)));
}
if ((rv & STA_PCR | STA_SCR) == 0) {
dprintf("ich-ac97: compatible chipset found, but no codec ready!\n");
unmap_io_memory();
return B_ERROR;
}
if ((rv & STA_PCR) == 0 && (rv & STA_SCR) != 0) {
TRACE(("using secondary codec!\n"));
config->nambar += 0x80;
}
/* allocate memory for channel info struct */
chan_pi = (ich_chan *) malloc(sizeof(ich_chan));
chan_po = (ich_chan *) malloc(sizeof(ich_chan));
chan_mc = (ich_chan *) malloc(sizeof(ich_chan));
if (0 == chan_pi || 0 == chan_po || 0 == chan_mc) {
dprintf("ich-ac97: couldn't allocate memory for channel descriptors!\n");
chan_free_resources();
unmap_io_memory();
return B_ERROR;
}
/* allocate memory for buffer descriptors */
chan_po->bd_area = alloc_mem(&chan_po->bd_phy_base, &chan_po->bd_log_base, ICH_BD_COUNT * sizeof(ich_bd), "ich_ac97 po buf desc");
chan_pi->bd_area = alloc_mem(&chan_pi->bd_phy_base, &chan_pi->bd_log_base, ICH_BD_COUNT * sizeof(ich_bd), "ich_ac97 pi buf desc");
chan_mc->bd_area = alloc_mem(&chan_mc->bd_phy_base, &chan_mc->bd_log_base, ICH_BD_COUNT * sizeof(ich_bd), "ich_ac97 mc buf desc");
/* allocate memory buffers */
chan_po->buffer_area = alloc_mem(&chan_po->buffer_phy_base, &chan_po->buffer_log_base, BUFFER_COUNT * BUFFER_SIZE, "ich_ac97 po buf");
chan_pi->buffer_area = alloc_mem(&chan_pi->buffer_phy_base, &chan_pi->buffer_log_base, BUFFER_COUNT * BUFFER_SIZE, "ich_ac97 pi buf");
chan_mc->buffer_area = alloc_mem(&chan_mc->buffer_phy_base, &chan_mc->buffer_log_base, BUFFER_COUNT * BUFFER_SIZE, "ich_ac97 mc buf");
/* allocate memory exported userland buffers */
chan_po->userbuffer_area = alloc_mem(NULL, &chan_po->userbuffer_base, BUFFER_COUNT * BUFFER_SIZE, "ich_ac97 po user buf");
chan_pi->userbuffer_area = alloc_mem(NULL, &chan_pi->userbuffer_base, BUFFER_COUNT * BUFFER_SIZE, "ich_ac97 pi user buf");
chan_mc->userbuffer_area = alloc_mem(NULL, &chan_mc->userbuffer_base, BUFFER_COUNT * BUFFER_SIZE, "ich_ac97 mc user buf");
if ( chan_po->bd_area < B_OK || chan_po->buffer_area < B_OK || chan_po->userbuffer_area < B_OK ||
chan_pi->bd_area < B_OK || chan_pi->buffer_area < B_OK || chan_pi->userbuffer_area < B_OK ||
chan_mc->bd_area < B_OK || chan_mc->buffer_area < B_OK || chan_mc->userbuffer_area < B_OK) {
dprintf("ich-ac97: couldn't allocate memory for DMA buffers!\n");
chan_free_resources();
unmap_io_memory();
return B_ERROR;
}
/* allocate all semaphores */
chan_po->buffer_ready_sem = create_sem(0,"po buffer ready"); /* pcm out buffer available */
chan_pi->buffer_ready_sem = create_sem(0,"pi buffer ready"); /* 0 available pcm in buffers */
chan_mc->buffer_ready_sem = create_sem(0,"mc buffer ready"); /* 0 available mic in buffers */
if (chan_po->buffer_ready_sem < B_OK || chan_pi->buffer_ready_sem < B_OK || chan_mc->buffer_ready_sem < B_OK) {
dprintf("ich-ac97: couldn't semaphores!\n");
chan_free_resources();
unmap_io_memory();
return B_ERROR;
}
/* setup channel register base address */
chan_pi->regbase = ICH_REG_PI_BASE;
chan_po->regbase = ICH_REG_PO_BASE;
chan_mc->regbase = ICH_REG_MC_BASE;
/* reset the (primary?) codec */
TRACE(("codec reset\n"));
ich_codec_write(0x00, 0x0000);
snooze(50000); // 50 ms
TRACE(("reading codec\n"));
TRACE(("codec = %#04x\n",ich_codec_read(0x00)));
/* reset all channels */
reset_chan(chan_pi);
reset_chan(chan_po);
reset_chan(chan_mc);
/* init channels */
init_chan(chan_pi);
init_chan(chan_po);
init_chan(chan_mc);
/* first test if interrupts are working, on some Laptops they don't work :-( */
if (config->irq != 0 && false == interrupt_test()) {
TRACE(("interrupt not working, using a kernel thread for polling\n"));
config->irq = 0; /* don't use interrupts */
}
/* install interrupt or polling thread */
if (config->irq != 0) {
install_io_interrupt_handler(config->irq,ihc_int,0,0);
} else {
int_thread_id = spawn_kernel_thread(int_thread, "ich_ac97 interrupt poller", B_REAL_TIME_PRIORITY, 0);
resume_thread(int_thread_id);
}
/* enable master output */
ich_codec_write(0x02, 0x0000);
/* enable pcm output */
ich_codec_write(0x18, 0x0404);
#if 0
/* enable pcm input */
ich_codec_write(0x10, 0x0000);
/* enable mic input */
/* ich_codec_write(0x0E, 0x0000); */
/* select pcm input record */
ich_codec_write(0x1A, 4 | (4 << 8));
/* enable PCM record */
ich_codec_write(0x1C, 0x0000);
/* enable mic record */
/* ich_codec_write(0x1E, 0x0000); */
#endif
TRACE(("init_driver finished!\n"));
return B_OK;
}
void
uninit_driver(void)
{
TRACE(("uninit_driver()\n"));
#if DEBUG
if (chan_po) TRACE(("chan_po frames_count = %Ld\n",chan_po->played_frames_count));
if (chan_pi) TRACE(("chan_pi frames_count = %Ld\n",chan_pi->played_frames_count));
if (chan_mc) TRACE(("chan_mc frames_count = %Ld\n",chan_mc->played_frames_count));
#endif
/* reset all channels */
if (chan_pi)
reset_chan(chan_pi);
if (chan_po)
reset_chan(chan_po);
if (chan_mc)
reset_chan(chan_mc);
snooze(50000);
/* remove the interrupt handler or thread */
if (config->irq != 0) {
remove_io_interrupt_handler(config->irq,ihc_int,0);
} else if (int_thread_id != -1) {
status_t exit_value;
int_thread_exit = true;
wait_for_thread(int_thread_id, &exit_value);
}
/* invalidate hardware buffer descriptor base addresses */
ich_reg_write_32(ICH_REG_PI_BASE, 0);
ich_reg_write_32(ICH_REG_PO_BASE, 0);
ich_reg_write_32(ICH_REG_MC_BASE, 0);
/* free allocated channel semaphores and memory */
chan_free_resources();
/* the very last thing to do is unmap the io memory */
unmap_io_memory();
TRACE(("uninit_driver() finished\n"));
}
int32 ihc_test_int(void *check)
{
/*
* This interrupt handler is used once to test if interrupt handling is working.
* If it gets executed and the interrupt is from pcm-out, it will set the bool
* pointed to by check to true.
*/
uint32 sta = ich_reg_read_32(ICH_REG_GLOB_STA);
uint16 sr = ich_reg_read_16(chan_po->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR));
if ((sta & STA_INTMASK) == 0)
return B_UNHANDLED_INTERRUPT;
if ((sta & STA_POINT) && (sr & SR_BCIS)) // pcm-out buffer completed
*(bool *)check = true; // notify
sr &= SR_FIFOE | SR_BCIS | SR_LVBCI;
ich_reg_write_16(chan_po->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR),sr);
return B_HANDLED_INTERRUPT;
}
int32 ihc_int(void *unused)
{
uint32 sta;
sta = ich_reg_read_32(ICH_REG_GLOB_STA);
if ((sta & STA_INTMASK) == 0)
return B_UNHANDLED_INTERRUPT;
if (sta & (STA_PRI | STA_SRI)) {
/* ignore and clear resume interrupt(s) */
ich_reg_write_32(ICH_REG_GLOB_STA, sta & (STA_PRI | STA_SRI));
}
if (sta & STA_POINT) { // pcm-out
uint16 sr = ich_reg_read_16(chan_po->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR));
sr &= SR_FIFOE | SR_BCIS | SR_LVBCI;
if (sr & SR_BCIS) { // a buffer completed
int32 count;
int32 civ;
/* shedule playback of the next buffers */
civ = ich_reg_read_8(chan_po->regbase + ICH_REG_X_CIV);
ich_reg_write_8(chan_po->regbase + ICH_REG_X_LVI, (civ + 28) % ICH_BD_COUNT);
/* calculate played buffer count since last interrupt */
if (civ <= chan_po->lastindex)
count = civ + ICH_BD_COUNT - chan_po->lastindex;
else
count = civ - chan_po->lastindex;
if (count != 1)
dprintf(DRIVER_NAME ": lost %d po interrupts\n",count - 1);
acquire_spinlock(&slock);
chan_po->played_real_time = system_time();
chan_po->played_frames_count += count * BUFFER_FRAMES_COUNT;
chan_po->lastindex = civ;
chan_po->backbuffer = chan_po->buffer[(civ + 1) % ICH_BD_COUNT];
release_spinlock(&slock);
get_sem_count(chan_po->buffer_ready_sem, &count);
if (count <= 0)
release_sem_etc(chan_po->buffer_ready_sem,1,B_DO_NOT_RESCHEDULE);
}
ich_reg_write_16(chan_po->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR),sr);
}
if (sta & STA_PIINT) { // pcm-in
uint16 sr = ich_reg_read_16(chan_pi->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR));
sr &= SR_FIFOE | SR_BCIS | SR_LVBCI;
if (sr & SR_BCIS) { // a buffer completed
int32 count;
int32 civ;
/* shedule record of the next buffers */
civ = ich_reg_read_8(chan_pi->regbase + ICH_REG_X_CIV);
ich_reg_write_8(chan_pi->regbase + ICH_REG_X_LVI, (civ + 28) % ICH_BD_COUNT);
/* calculate recorded buffer count since last interrupt */
if (civ <= chan_pi->lastindex)
count = civ + ICH_BD_COUNT - chan_pi->lastindex;
else
count = civ - chan_pi->lastindex;
if (count != 1)
dprintf(DRIVER_NAME ": lost %d pi interrupts\n",count - 1);
acquire_spinlock(&slock);
chan_pi->played_real_time = system_time();
chan_pi->played_frames_count += count * BUFFER_FRAMES_COUNT;
chan_pi->lastindex = civ;
chan_pi->backbuffer = chan_pi->buffer[(ICH_BD_COUNT - (civ - 1)) % ICH_BD_COUNT];
release_spinlock(&slock);
get_sem_count(chan_pi->buffer_ready_sem, &count);
if (count <= 0)
release_sem_etc(chan_pi->buffer_ready_sem,1,B_DO_NOT_RESCHEDULE);
}
ich_reg_write_16(chan_pi->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR),sr);
}
if (sta & STA_MINT) { // mic-in
uint16 sr = ich_reg_read_16(chan_mc->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR));
sr &= SR_FIFOE | SR_BCIS | SR_LVBCI;
if (sr & SR_BCIS) { // a buffer completed
release_sem_etc(chan_mc->buffer_ready_sem,1,B_DO_NOT_RESCHEDULE);
}
ich_reg_write_16(chan_mc->regbase + (config->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR),sr);
}
return B_HANDLED_INTERRUPT;
}
static status_t
ihc_open(const char *name, uint32 flags, void** cookie)
{
TRACE(("open()\n"));
return B_OK;
}
static status_t
ihc_close(void* cookie)
{
TRACE(("close()\n"));
return B_OK;
}
static status_t
ihc_free(void* cookie)
{
return B_OK;
}
static status_t
ihc_control(void* cookie, uint32 op, void* arg, size_t len)
{
return multi_control(cookie,op,arg,len);
}
static status_t
ihc_read(void* cookie, off_t position, void *buf, size_t* num_bytes)
{
*num_bytes = 0; /* tell caller nothing was read */
return B_IO_ERROR;
}
static status_t
ihc_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
{
*num_bytes = 0; /* tell caller nothing was written */
return B_IO_ERROR;
}
/* -----
null-terminated array of device names supported by this driver
----- */
static const char *ihc_name[] = {
"audio/multi/ich_ac97/1",
NULL
};
/* -----
function pointers for the device hooks entry points
----- */
device_hooks ihc_hooks = {
ihc_open, /* -> open entry point */
ihc_close, /* -> close entry point */
ihc_free, /* -> free cookie */
ihc_control, /* -> control entry point */
ihc_read, /* -> read entry point */
ihc_write, /* -> write entry point */
NULL, /* start select */
NULL, /* stop select */
NULL, /* scatter-gather read from the device */
NULL /* scatter-gather write to the device */
};
/* ----------
publish_devices - return a null-terminated array of devices
supported by this driver.
----- */
const char**
publish_devices()
{
return ihc_name;
}
/* ----------
find_device - return ptr to device hooks structure for a
given device name
----- */
device_hooks*
find_device(const char* name)
{
return &ihc_hooks;
}

View File

@ -0,0 +1,72 @@
/*
* BeOS Driver for Intel ICH AC'97 Link interface
*
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _ICH_H_
#define _ICH_H_
#include "hardware.h"
#define DRIVER_NAME "ich_ac97"
#define BUFFER_SIZE 512
#define BUFFER_COUNT 2
#define BUFFER_FRAMES_COUNT (BUFFER_SIZE / 4)
/* the software channel descriptor */
typedef struct {
uint32 regbase;
volatile int32 lastindex;
volatile int64 played_frames_count;
volatile bigtime_t played_real_time;
volatile bool running;
volatile void *backbuffer;
sem_id buffer_ready_sem;
void *buffer_log_base;
void *buffer_phy_base;
void *buffer[ICH_BD_COUNT];
void *bd_phy_base;
void *bd_log_base;
ich_bd *bd[ICH_BD_COUNT];
/*
* a second set of buffers, exported to the multiaudio api (bad idea)
*/
void *userbuffer[BUFFER_COUNT];
void *userbuffer_base;
area_id buffer_area;
area_id userbuffer_area;
area_id bd_area;
} ich_chan;
extern ich_chan * chan_pi;
extern ich_chan * chan_po;
extern ich_chan * chan_mc;
#endif

View File

@ -0,0 +1,144 @@
/*
* BeOS Driver for Intel ICH AC'97 Link interface
*
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <KernelExport.h>
#include <OS.h>
#include "debug.h"
#include "io.h"
#include "ich.h"
#include "config.h"
status_t ich_codec_wait();
uint8
ich_reg_read_8(int regno)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63);
if (config->type & TYPE_ICH4)
return *(uint8 *)(((char *)config->log_mbbar) + regno);
else
return read_io_8(config->nabmbar + regno);
}
uint16
ich_reg_read_16(int regno)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63);
if (config->type & TYPE_ICH4)
return *(uint16 *)(((char *)config->log_mbbar) + regno);
else
return read_io_16(config->nabmbar + regno);
}
uint32
ich_reg_read_32(int regno)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63);
if (config->type & TYPE_ICH4)
return *(uint32 *)(((char *)config->log_mbbar) + regno);
else
return read_io_32(config->nabmbar + regno);
}
void
ich_reg_write_8(int regno, uint8 value)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63);
if (config->type & TYPE_ICH4)
*(uint8 *)(((char *)config->log_mbbar) + regno) = value;
else
write_io_8(config->nabmbar + regno, value);
}
void
ich_reg_write_16(int regno, uint16 value)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63);
if (config->type & TYPE_ICH4)
*(uint16 *)(((char *)config->log_mbbar) + regno) = value;
else
write_io_16(config->nabmbar + regno, value);
}
void
ich_reg_write_32(int regno, uint32 value)
{
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63);
if (config->type & TYPE_ICH4)
*(uint32 *)(((char *)config->log_mbbar) + regno) = value;
else
write_io_32(config->nabmbar + regno, value);
}
status_t
ich_codec_wait()
{
int i;
for (i = 0; i < 1100; i++) {
if ((ich_reg_read_8(ICH_REG_ACC_SEMA) & 0x01) == 0)
return B_OK;
if (i > 100)
snooze(1);
}
return B_TIMED_OUT;
}
uint16
ich_codec_read(int regno)
{
status_t rv;
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 511) || regno <= 255);
rv = ich_codec_wait();
if (rv != B_OK)
dprintf(DRIVER_NAME " semaphore timeout reading register %#x\n",regno);
if (config->type & TYPE_ICH4)
return *(uint16 *)(((char *)config->log_mmbar) + regno);
else
return read_io_16(config->nambar + regno);
}
void
ich_codec_write(int regno, uint16 value)
{
status_t rv;
ASSERT(regno >= 0);
ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 511) || regno <= 255);
rv = ich_codec_wait();
if (rv != B_OK)
dprintf(DRIVER_NAME " semaphore timeout writing register %#x\n",regno);
if (config->type & TYPE_ICH4)
*(uint16 *)(((char *)config->log_mmbar) + regno) = value;
else
write_io_16(config->nambar + regno, value);
}

View File

@ -0,0 +1,43 @@
/*
* BeOS Driver for Intel ICH AC'97 Link interface
*
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _IO_H_
#define _IO_H_
uint8 ich_reg_read_8(int regno);
uint16 ich_reg_read_16(int regno);
uint32 ich_reg_read_32(int regno);
void ich_reg_write_8(int regno, uint8 value);
void ich_reg_write_16(int regno, uint16 value);
void ich_reg_write_32(int regno, uint32 value);
uint16 ich_codec_read(int regno);
void ich_codec_write(int regno, uint16 value);
#endif

View File

@ -0,0 +1,86 @@
/*
* BeOS Driver for Intel ICH AC'97 Link interface
*
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <Errors.h>
#include <OS.h>
#include <string.h>
//#define DEBUG 2
#include "debug.h"
#include "util.h"
spinlock slock = 0;
cpu_status lock()
{
cpu_status status = disable_interrupts();
acquire_spinlock(&slock);
return status;
}
void unlock(cpu_status status)
{
release_spinlock(&slock);
restore_interrupts(status);
}
uint32 round_to_pagesize(uint32 size)
{
return (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
}
area_id alloc_mem(void **phy, void **log, size_t size, const char *name)
{
physical_entry pe;
void * logadr;
area_id areaid;
status_t rv;
TRACE(("allocating %d bytes for %s\n",size,name));
size = round_to_pagesize(size);
areaid = create_area(name, &logadr, B_ANY_KERNEL_ADDRESS,size,B_FULL_LOCK | B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
if (areaid < B_OK) {
TRACE(("couldn't allocate area %s\n",name));
return B_ERROR;
}
rv = get_memory_map(logadr,size,&pe,1);
if (rv < B_OK) {
delete_area(areaid);
TRACE(("couldn't map %s\n",name));
return B_ERROR;
}
memset(logadr,0,size);
if (log)
*log = logadr;
if (phy)
*phy = pe.address;
TRACE(("area = %d, size = %d, log = %#08X, phy = %#08X\n",areaid,size,logadr,pe.address));
return areaid;
}

View File

@ -0,0 +1,40 @@
/*
* BeOS Driver for Intel ICH AC'97 Link interface
*
* Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _UTIL_H_
#define _UTIL_H_
#include <KernelExport.h>
area_id alloc_mem(void **phy, void **log, size_t size, const char *name);
cpu_status lock();
void unlock(cpu_status status);
extern spinlock slock;
#endif