diff --git a/src/add-ons/kernel/drivers/audio/ac97/ich/ac97_multi.c b/src/add-ons/kernel/drivers/audio/ac97/ich/ac97_multi.c new file mode 100644 index 0000000000..5887f5fc3e --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ich/ac97_multi.c @@ -0,0 +1,361 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * 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 +#include +#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; +} + + diff --git a/src/add-ons/kernel/drivers/audio/ac97/ich/ac97_multi.h b/src/add-ons/kernel/drivers/audio/ac97/ich/ac97_multi.h new file mode 100644 index 0000000000..d562921279 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ich/ac97_multi.h @@ -0,0 +1,33 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * 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 diff --git a/src/add-ons/kernel/drivers/audio/ac97/ich/config.c b/src/add-ons/kernel/drivers/audio/ac97/ich/config.c new file mode 100644 index 0000000000..df24f9af42 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ich/config.c @@ -0,0 +1,272 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * 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 +#include +#include +#include + +//#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; +} + diff --git a/src/add-ons/kernel/drivers/audio/ac97/ich/config.h b/src/add-ons/kernel/drivers/audio/ac97/ich/config.h new file mode 100644 index 0000000000..eb0283b9f4 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ich/config.h @@ -0,0 +1,54 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * 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 diff --git a/src/add-ons/kernel/drivers/audio/ac97/ich/debug.c b/src/add-ons/kernel/drivers/audio/ac97/ich/debug.c new file mode 100644 index 0000000000..c1b4ab9370 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ich/debug.c @@ -0,0 +1,48 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * 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 +#include +#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 +} + diff --git a/src/add-ons/kernel/drivers/audio/ac97/ich/debug.h b/src/add-ons/kernel/drivers/audio/ac97/ich/debug.h new file mode 100644 index 0000000000..1bc4d02c88 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ich/debug.h @@ -0,0 +1,44 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * 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 diff --git a/src/add-ons/kernel/drivers/audio/ac97/ich/hardware.h b/src/add-ons/kernel/drivers/audio/ac97/ich/hardware.h new file mode 100644 index 0000000000..93f4a0594a --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ich/hardware.h @@ -0,0 +1,126 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * 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 diff --git a/src/add-ons/kernel/drivers/audio/ac97/ich/ich.c b/src/add-ons/kernel/drivers/audio/ac97/ich/ich.c new file mode 100644 index 0000000000..9116409283 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ich/ich.c @@ -0,0 +1,737 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * 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 +#include +#include +#include +#include +#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; +} diff --git a/src/add-ons/kernel/drivers/audio/ac97/ich/ich.h b/src/add-ons/kernel/drivers/audio/ac97/ich/ich.h new file mode 100644 index 0000000000..39284f76df --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ich/ich.h @@ -0,0 +1,72 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * 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 diff --git a/src/add-ons/kernel/drivers/audio/ac97/ich/io.c b/src/add-ons/kernel/drivers/audio/ac97/ich/io.c new file mode 100644 index 0000000000..42a3dd3f09 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ich/io.c @@ -0,0 +1,144 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * 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 +#include +#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); +} diff --git a/src/add-ons/kernel/drivers/audio/ac97/ich/io.h b/src/add-ons/kernel/drivers/audio/ac97/ich/io.h new file mode 100644 index 0000000000..f8c92474f1 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ich/io.h @@ -0,0 +1,43 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * 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 diff --git a/src/add-ons/kernel/drivers/audio/ac97/ich/util.c b/src/add-ons/kernel/drivers/audio/ac97/ich/util.c new file mode 100644 index 0000000000..8b67392ea8 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ich/util.c @@ -0,0 +1,86 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * 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 +#include +#include + +//#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; +} + diff --git a/src/add-ons/kernel/drivers/audio/ac97/ich/util.h b/src/add-ons/kernel/drivers/audio/ac97/ich/util.h new file mode 100644 index 0000000000..f4bdbb30b8 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ich/util.h @@ -0,0 +1,40 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * 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 + +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