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:
parent
e24ab46c4e
commit
af9bf37038
361
src/add-ons/kernel/drivers/audio/ac97/ich/ac97_multi.c
Normal file
361
src/add-ons/kernel/drivers/audio/ac97/ich/ac97_multi.c
Normal 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;
|
||||
}
|
||||
|
||||
|
33
src/add-ons/kernel/drivers/audio/ac97/ich/ac97_multi.h
Normal file
33
src/add-ons/kernel/drivers/audio/ac97/ich/ac97_multi.h
Normal 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
|
272
src/add-ons/kernel/drivers/audio/ac97/ich/config.c
Normal file
272
src/add-ons/kernel/drivers/audio/ac97/ich/config.c
Normal 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;
|
||||
}
|
||||
|
54
src/add-ons/kernel/drivers/audio/ac97/ich/config.h
Normal file
54
src/add-ons/kernel/drivers/audio/ac97/ich/config.h
Normal 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
|
48
src/add-ons/kernel/drivers/audio/ac97/ich/debug.c
Normal file
48
src/add-ons/kernel/drivers/audio/ac97/ich/debug.c
Normal 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
|
||||
}
|
||||
|
44
src/add-ons/kernel/drivers/audio/ac97/ich/debug.h
Normal file
44
src/add-ons/kernel/drivers/audio/ac97/ich/debug.h
Normal 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
|
126
src/add-ons/kernel/drivers/audio/ac97/ich/hardware.h
Normal file
126
src/add-ons/kernel/drivers/audio/ac97/ich/hardware.h
Normal 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
|
737
src/add-ons/kernel/drivers/audio/ac97/ich/ich.c
Normal file
737
src/add-ons/kernel/drivers/audio/ac97/ich/ich.c
Normal 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;
|
||||
}
|
72
src/add-ons/kernel/drivers/audio/ac97/ich/ich.h
Normal file
72
src/add-ons/kernel/drivers/audio/ac97/ich/ich.h
Normal 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
|
144
src/add-ons/kernel/drivers/audio/ac97/ich/io.c
Normal file
144
src/add-ons/kernel/drivers/audio/ac97/ich/io.c
Normal 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);
|
||||
}
|
43
src/add-ons/kernel/drivers/audio/ac97/ich/io.h
Normal file
43
src/add-ons/kernel/drivers/audio/ac97/ich/io.h
Normal 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
|
86
src/add-ons/kernel/drivers/audio/ac97/ich/util.c
Normal file
86
src/add-ons/kernel/drivers/audio/ac97/ich/util.c
Normal 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;
|
||||
}
|
||||
|
40
src/add-ons/kernel/drivers/audio/ac97/ich/util.h
Normal file
40
src/add-ons/kernel/drivers/audio/ac97/ich/util.h
Normal 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
|
Loading…
Reference in New Issue
Block a user