diff --git a/src/add-ons/kernel/drivers/audio/ac97/Jamfile b/src/add-ons/kernel/drivers/audio/ac97/Jamfile index 405a62bb6f..d5ea1de416 100644 --- a/src/add-ons/kernel/drivers/audio/ac97/Jamfile +++ b/src/add-ons/kernel/drivers/audio/ac97/Jamfile @@ -1,5 +1,6 @@ SubDir HAIKU_TOP src add-ons kernel drivers audio ac97 ; +SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 ali5451 ; SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 auich ; SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 auvia ; SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 es1370 ; diff --git a/src/add-ons/kernel/drivers/audio/ac97/ali5451/Jamfile b/src/add-ons/kernel/drivers/audio/ac97/ali5451/Jamfile new file mode 100644 index 0000000000..79c6288a99 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ali5451/Jamfile @@ -0,0 +1,12 @@ +SubDir HAIKU_TOP src add-ons kernel drivers audio ac97 ali5451 ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +UsePrivateHeaders media ; + +KernelAddon ali5451 : + driver.c + ali_multi.c + ali_hardware.c + util.c +; diff --git a/src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_hardware.c b/src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_hardware.c new file mode 100644 index 0000000000..09cf8b3ab1 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_hardware.c @@ -0,0 +1,404 @@ +/* + * Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com). + * + * All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + + +#include +#include +#include +#include + +#include "queue.h" +#include "driver.h" +#include "util.h" +#include "ali_hardware.h" +#include "debug.h" + + +#define ALI_5451_V02 0x02 +#define ALI_AC97_CODEC_ID 0x41445374 +#define ALI_AC97_CAPS_HEAD 0x10 + +// ALI registers + +#define ALI_MPUR0 0x20 +#define ALI_MPUR1 0x21 +#define ALI_MPUR2 0x22 +#define ALI_MPUR3 0x23 + +#define ALI_ACR0 0x40 +#define ALI_ACR1 0x44 +#define ALI_ACR2 0x48 + +#define ALI_START_A 0x80 +#define ALI_STOP_A 0x84 + +#define ALI_AIN_A 0x98 +#define ALI_LFO_A 0xa0 + #define ALI_LFO_A_ENDLP_IE 0x1000 + #define ALI_LFO_A_MIDLP_IE 0x2000 +#define ALI_AINTEN_A 0xa4 +#define ALI_VOL_A 0xa8 +#define ALI_STIMER 0xc8 +#define ALI_T_DIGIMIXER 0xd4 +#define ALI_AIN_B 0xd8 + +#define ALI_CSO_ALPHA_FMS 0xe0 +#define ALI_PPTR_LBA 0xe4 +#define ALI_ESO_DELTA 0xe8 +#define ALI_FMC_RVOL_CVOL 0xec +#define ALI_GVSEL_MISC 0xf0 +#define ALI_EBUF1 0xf4 +#define ALI_EBUF2 0xf8 + +extern pci_module_info *gPCI; + + +/* card i/o */ + + +static uint32 +io_read32(ali_dev *card, ulong offset) +{ + return (*gPCI->read_io_32)(card->io_base+offset); +} + + +static uint8 +io_read8(ali_dev *card, ulong offset) +{ + return (*gPCI->read_io_8)(card->io_base+offset); +} + + +static void +io_write32(ali_dev *card, ulong offset, uint32 value) +{ + (*gPCI->write_io_32)(card->io_base+offset, value); +} + + +static void +io_write16(ali_dev *card, ulong offset, uint16 value) +{ + (*gPCI->write_io_16)(card->io_base+offset, value); +} + + +static void +io_write8(ali_dev *card, ulong offset, uint8 value) +{ + (*gPCI->write_io_8)(card->io_base+offset, value); +} + + +/* codec i/o */ + + +static bool +ac97_is_ready(ali_dev *card, uint32 offset) +{ + uint8 cnt = 200; + uint32 val; + + while (cnt--) { + val = io_read32(card, offset); + if (!(val & 0x8000)) + return true; + spin(1); + } + + io_write32(card, offset, val & ~0x8000); + TRACE("ac97 not ready\n"); + return false; +} + + +static bool +ac97_is_stimer_ready(ali_dev *card) +{ + uint8 cnt = 200; + uint32 t1, t2; + + t1 = io_read32(card, ALI_STIMER); + + while (cnt--) { + t2 = io_read32(card, ALI_STIMER); + if (t2 != t1) + return true; + spin(1); + } + + TRACE("ac97 stimer not ready\n"); + return false; +} + + +uint16 +ac97_read(ali_dev *card, uint8 reg) +{ + uint32 ac_reg; + + ac_reg = (card->info.revision == ALI_5451_V02)?ALI_ACR0:ALI_ACR1; + + if (!ac97_is_ready(card, ac_reg) || !ac97_is_stimer_ready(card)) + return 0; + + io_write16(card, ac_reg, (uint16) reg | 0x8000); + + if (!ac97_is_stimer_ready(card) || !ac97_is_ready(card, ac_reg)) + return 0; + + return (uint16) (io_read32(card, ac_reg) >> 16); +} + + +void +ac97_write(ali_dev *card, uint8 reg, uint16 val) +{ + uint32 dw_val; + + if (!ac97_is_ready(card, ALI_ACR0) || !ac97_is_stimer_ready(card)) + return; + + dw_val = reg | 0x8000 | (uint32) val << 16; + if (card->info.revision == ALI_5451_V02) + dw_val |= 0x0100; + + io_write32(card, ALI_ACR0, dw_val); +} + + +static bool +ac97_init(ali_dev *card) +{ + uint16 capabilities; + uint32 id; + + // ac97 reset + ac97_write(card, AC97_REG_POWER, 0); + ac97_write(card, AC97_REG_RESET, 0); + snooze(100000); + ac97_write(card, AC97_REG_POWER, 0); + ac97_write(card, AC97_REG_GEN, 0); + + capabilities = ac97_read(card, AC97_REG_RESET); + TRACE("ac97 capabilities: %04x\n", capabilities & 0x3ff); + + id = (uint32) ac97_read(card, AC97_REG_ID1) << 16 + | ac97_read(card, AC97_REG_ID2); + TRACE("ac97 id: %08x\n", (uint) id); + if (id != ALI_AC97_CODEC_ID) + return false; + + if ((capabilities & ALI_AC97_CAPS_HEAD) != 0) + ac97_write(card, AC97_JACK_SENSE, (uint16) 1 << 10); + // enable headphone jack sense + + // some sane default values + ac97_write(card, AC97_MIX_MASTER, 0x707); + ac97_write(card, AC97_MIX_PCM, 0x707); + ac97_write(card, AC97_MIX_HEADPHONE, 0x707); + + return true; +} + + +static bool +hardware_reset(ali_dev *card) +{ + uint32 cmd_reg; + uint16 val; + uint8 bval; + + // pci reset + cmd_reg = (*gPCI->read_pci_config)(card->info.bus, card->info.device, + card->info.function, 0x44, 4); + (*gPCI->write_pci_config)(card->info.bus, card->info.device, + card->info.function, 0x44, 4, cmd_reg | 0x08000000); + snooze(500); + cmd_reg = (*gPCI->read_pci_config)(card->info.bus, card->info.device, + card->info.function, 0x44, 4); + (*gPCI->write_pci_config)(card->info.bus, card->info.device, + card->info.function, 0x44, 4, cmd_reg & 0xfffbffff); + snooze(5000); + + bval = io_read8(card, ALI_ACR2); + bval |= 2; + io_write8(card, ALI_ACR2, bval); + snooze(5000); + bval = io_read8(card, ALI_ACR2); + bval &= 0xfd; + io_write8(card, ALI_ACR2, bval); + snooze(15000); + + cmd_reg = 200; + while (cmd_reg--) { + val = ac97_read(card, AC97_REG_POWER); + if ((val & 0xf) == 0xf) + break; + snooze(5000); + } + if (cmd_reg == 0xffffffff) { + TRACE("hardware reset: not ready\n"); + return false; + } + + return true; +} + + +status_t +hardware_init(ali_dev *card) +{ + if (!hardware_reset(card)) + return B_ERROR; + + LOCK(card->lock_hw); + + io_write32(card, ALI_AINTEN_A, 0); + // disable voice interrupts + io_write32(card, ALI_AIN_A, 0xffffffff); + // clr voice interrupt flags + io_write32(card, ALI_VOL_A, 0); + // master & wave volumes + io_write8(card, ALI_MPUR2, 0x10); + + io_write32(card, ALI_LFO_A, ALI_LFO_A_ENDLP_IE | ALI_LFO_A_MIDLP_IE); + + UNLOCK(card->lock_hw); + + if (!ac97_init(card)) + return B_ERROR; + + TRACE("hardware init ok\n"); + return B_OK; +} + + +void +hardware_terminate(ali_dev *card) +{ + LOCK(card->lock_hw); + + io_write32(card, ALI_AINTEN_A, 0); + // disable voice interrupts + io_write32(card, ALI_AIN_A, 0xffffffff); + // clr voice interrupt flags + + UNLOCK(card->lock_hw); +} + + +uint32 +ali_read_int(ali_dev *card) +{ + return io_read32(card, ALI_AIN_A); +} + + +/* voices handling */ + + +static uint16 +ali_delta_from_rate(uint32 rate, bool rec) +{ + uint32 delta; + + if (rate < 8000) + rate = 8000; + if (48000 < rate) + rate = 48000; + + switch (rate) { + case 8000: delta = (rec?0x6000:0x2ab); break; + case 44100: delta = (rec?0x116a:0xeb3); break; + case 48000: delta = 0x1000; break; + default: + delta = (rec?((48000 << 12) / rate):(((rate << 12) + rate) / 48000)); + } + + return (uint16) delta; +} + + +static uint32 +ali_control_mode(ali_stream *stream) +{ + uint32 ctrl = 1; + + if (util_format_is_signed(stream->format.format)) + ctrl |= 2; + if (1 < stream->channels) + ctrl |= 4; + if (util_format_to_sample_size(stream->format.format) == 2) + ctrl |= 8; + + return ctrl; +} + + +bool +ali_voice_start(ali_stream *stream) +{ + LOCK(stream->card->lock_hw); + + // setup per-voice registers + + io_write8(stream->card, ALI_LFO_A, stream->used_voice); + // select voice + + io_write32(stream->card, ALI_CSO_ALPHA_FMS, 0); + // cso = 0, alpha = 0 + io_write32(stream->card, ALI_PPTR_LBA, (uint32) stream->buffer->phy_base); + // pptr & lba + io_write32(stream->card, ALI_ESO_DELTA, + ((uint32) stream->buf_frames * stream->buf_count - 1) << 16 + | ali_delta_from_rate(stream->format.rate, stream->rec)); + // eso & delta + + io_write32(stream->card, ALI_GVSEL_MISC, + ali_control_mode(stream) << 12); + // gvsel=0, pan=0, vol=0, ctrl, ec=0 + + // reset envelopes + io_write32(stream->card, ALI_EBUF1, 0); + io_write32(stream->card, ALI_EBUF2, 0); + + UNLOCK(stream->card->lock_hw); + + io_write32(stream->card, ALI_AIN_A, (uint32) 1 << stream->used_voice); + // clr voice interrupt + io_write32(stream->card, ALI_AINTEN_A, stream->card->used_voices); + // enable interupt for voice + + io_write32(stream->card, ALI_START_A, (uint32) 1 << stream->used_voice); + // start voice + + if (stream->rec) + io_write32(stream->card, ALI_T_DIGIMIXER, + (uint32) 1 << stream->used_voice); + // enable special channel for recording + + return true; +} + + +void +ali_voice_stop(ali_stream *stream) +{ + io_write32(stream->card, ALI_STOP_A, (uint32) 1 << stream->used_voice); + + if (stream->rec) + io_write32(stream->card, ALI_T_DIGIMIXER, 0); +} + + +void +ali_voice_clr_int(ali_stream *stream) +{ + io_write32(stream->card, ALI_AIN_A, (uint32) 1 << stream->used_voice); +} diff --git a/src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_hardware.h b/src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_hardware.h new file mode 100644 index 0000000000..594ee56e4a --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_hardware.h @@ -0,0 +1,47 @@ +/* + * Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com). + * + * All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + + +#ifndef _ALI_HARDWARE_H +#define _ALI_HARDWARE_H + +// AC97 codec registers + +#define AC97_REG_RESET 0x00 +#define AC97_MIX_MASTER 0x02 +#define AC97_MIX_HEADPHONE 0x04 +#define AC97_MIX_MONO 0x06 +#define AC97_MIX_PHONE 0x0c +#define AC97_MIX_MIC 0x0e +#define AC97_MIX_LINEIN 0x10 +#define AC97_MIX_CD 0x12 +#define AC97_MIX_AUX 0x16 +#define AC97_MIX_PCM 0x18 +#define AC97_MIX_RECORDSELECT 0x1a +#define AC97_MIX_RECORDGAIN 0x1c +#define AC97_REG_GEN 0x20 +#define AC97_REG_POWER 0x26 +#define AC97_FRONT_DAC_SR 0x2c +#define AC97_ADC_RATE 0x32 +#define AC97_JACK_SENSE 0x72 +#define AC97_REG_ID1 0x7c +#define AC97_REG_ID2 0x7e + + +uint16 ac97_read(ali_dev *card, uint8 reg); +void ac97_write(ali_dev *card, uint8 reg, uint16 val); + +status_t hardware_init(ali_dev *card); +void hardware_terminate(ali_dev *card); + +uint32 ali_read_int(ali_dev *card); + +bool ali_voice_start(ali_stream *stream); +void ali_voice_stop(ali_stream *stream); +void ali_voice_clr_int(ali_stream *stream); + +#endif // _ALI_HARDWARE_H diff --git a/src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_multi.c b/src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_multi.c new file mode 100644 index 0000000000..d7de416724 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_multi.c @@ -0,0 +1,576 @@ +/* + * Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com). + * + * All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + + +#include +#include +#include +#include +#include + +#include "queue.h" +#include "driver.h" +#include "util.h" +#include "ali_hardware.h" +#include "ali_multi.h" +#include "debug.h" + + +#define ALI_SUPPORTED_OUTPUT_CHANNELS 2 +#define ALI_SUPPORTED_INPUT_CHANNELS 2 + +#define ALI_VALID_OUTPUT_SAMPLE_RATES (B_SR_8000 | B_SR_11025 | B_SR_12000 | \ + B_SR_16000 | B_SR_22050 | B_SR_24000 | B_SR_32000 | B_SR_44100 | B_SR_48000) +#define ALI_VALID_OUTPUT_FORMATS (B_FMT_16BIT | B_FMT_8BIT_S | B_FMT_8BIT_U) +#define ALI_VALID_INPUT_SAMPLE_RATES (B_SR_48000) +#define ALI_VALID_INPUT_FORMATS ALI_VALID_OUTPUT_FORMATS + +#define ALI_MIXER_GRANULARITY 1.5f + +#define ALI_MIX_OUTPUT_ID 0x000 +#define ALI_MIX_INPUT_ID 0x100 + +#define ALI_MIX_GROUP_ID 1 +#define ALI_MIX_MUTE_ID 2 +#define ALI_MIX_LEFT_ID 3 +#define ALI_MIX_RIGHT_ID 4 +#define ALI_MIX_MONO_ID ALI_MIX_RIGHT_ID +#define ALI_MIX_MIC_BOOST_ID 5 +#define ALI_MIX_INPUT_SELECTOR_ID 6 +#define ALI_MIX_RECORD_INPUT 15 + +#define ALI_MIX_FT_STEREO 1 +#define ALI_MIX_FT_MUTE 2 +#define ALI_MIX_FT_MICBST 4 +#define ALI_MIX_FT_GAIN 8 +#define ALI_MIX_FT_INSEL 16 + +typedef struct { + int32 string_id; + const char *name; + uint8 reg; + float min_gain; + float max_gain; + uint8 features; +} ali_ac97_mixer_info; + +#define ALI_OUTPUT_MIXERS 7 +#define ALI_INPUT_MIXERS 1 +#define ALI_ALL_MIXERS (ALI_OUTPUT_MIXERS + ALI_INPUT_MIXERS) + +static const ali_ac97_mixer_info ali_ac97_mixers[ALI_ALL_MIXERS] = { + {S_MASTER, NULL, AC97_MIX_MASTER, -46.5f, 0.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE}, + {S_WAVE, NULL, AC97_MIX_PCM, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE}, + {S_HEADPHONE, NULL, AC97_MIX_HEADPHONE, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE}, + {S_CD, NULL, AC97_MIX_CD, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE}, + {S_LINE, NULL, AC97_MIX_LINEIN, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE}, + {S_MIC, NULL, AC97_MIX_MIC, -34.5f, 12.0f, ALI_MIX_FT_MUTE | ALI_MIX_FT_MICBST}, + {S_AUX, NULL, AC97_MIX_AUX, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE}, + {S_null, "Record", AC97_MIX_RECORDGAIN, 0.0f, 22.5f, + ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE | ALI_MIX_FT_GAIN | ALI_MIX_FT_INSEL}, +}; + +typedef struct { + int32 string_id; + const char *name; +} ali_ac97_mux_input_info; + +#define ALI_MUX_INPUTS 7 + +static const ali_ac97_mux_input_info ali_ac97_mux_inputs[ALI_MUX_INPUTS] = { + {S_MIC, NULL}, + {S_CD, NULL}, + {S_MUTE, NULL}, + {S_AUX, NULL}, + {S_LINE, NULL}, + {S_STEREO_MIX, NULL}, + {S_MONO_MIX, NULL}, +}; + +static const multi_channel_info channel_descriptions[] = { + { 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 }, +}; + + +static status_t +get_description(void *cookie, multi_description *data) +{ + data->interface_version = B_CURRENT_INTERFACE_VERSION; + data->interface_minimum = B_CURRENT_INTERFACE_VERSION; + + strcpy(data->friendly_name, "ALI M5451"); + strcpy(data->vendor_info, "Krzysztof Ćwiertnia"); + + data->output_channel_count = ALI_SUPPORTED_OUTPUT_CHANNELS; + data->input_channel_count = ALI_SUPPORTED_INPUT_CHANNELS; + data->output_bus_channel_count = ALI_SUPPORTED_OUTPUT_CHANNELS; + data->input_bus_channel_count = ALI_SUPPORTED_INPUT_CHANNELS; + data->aux_bus_channel_count = 0; + + if ((int32) (sizeof(channel_descriptions)/sizeof(channel_descriptions[0])) + <= data->request_channel_count) { + memcpy(data->channels, &channel_descriptions, + sizeof(channel_descriptions)); + } + + data->output_formats = ALI_VALID_OUTPUT_FORMATS; + data->output_rates = ALI_VALID_OUTPUT_SAMPLE_RATES; + + data->input_formats = ALI_VALID_INPUT_FORMATS; + data->input_rates = ALI_VALID_INPUT_SAMPLE_RATES; + + 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 = 30000; + + strcpy(data->control_panel,""); + + return B_OK; +} + + +static status_t +get_enabled_channels(ali_dev *card, 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; + + return B_OK; +} + + +static status_t +get_global_format(ali_dev *card, multi_format_info *data) +{ + data->output_latency = 0; + data->input_latency = 0; + data->timecode_kind = 0; + + data->output.format = card->global_output_format.format; + data->output.rate = card->global_output_format.rate; + + data->input.format = card->global_input_format.format; + data->input.rate = card->global_input_format.rate; + + return B_OK; +} + + +static status_t +set_global_format(ali_dev *card, multi_format_info *data) +{ + if ((ALI_VALID_OUTPUT_FORMATS & data->output.format) != 0) + card->global_output_format.format = data->output.format; + if ((ALI_VALID_OUTPUT_SAMPLE_RATES & data->output.rate) != 0) + card->global_output_format.rate = data->output.rate; + + if ((ALI_VALID_INPUT_FORMATS & data->input.format) != 0) + card->global_input_format.format = data->input.format; + if ((ALI_VALID_INPUT_SAMPLE_RATES & data->input.rate) != 0) + card->global_input_format.rate = data->input.rate; + + if (!ali_stream_prepare(card->playback_stream, 2, + card->global_output_format.format, + util_sample_rate_in_bits(card->global_output_format.rate))) + return B_ERROR; + if (!ali_stream_prepare(card->record_stream, 2, + card->global_input_format.format, + util_sample_rate_in_bits(card->global_input_format.rate))) + return B_ERROR; + + return B_OK; +} + + +static void +get_mix_mixer(ali_dev *card, multi_mix_value *mmv, int32 id) +{ + uint8 mixer_id = (uint8) id >> 4, gadget_id = (uint8) id & 0xf; + const ali_ac97_mixer_info *mixer_info; + + if (ALI_ALL_MIXERS <= mixer_id) + return; + mixer_info = &ali_ac97_mixers[mixer_id]; + + switch (gadget_id) { + case ALI_MIX_MUTE_ID: + mmv->u.enable = ((ac97_read(card, mixer_info->reg) & 0x8000) + == 0x8000); + break; + + case ALI_MIX_LEFT_ID: + case ALI_MIX_RIGHT_ID: + { + float val = (ALI_MIXER_GRANULARITY + * ((ac97_read(card, mixer_info->reg) >> + ((gadget_id==ALI_MIX_LEFT_ID)?8:0)) & 0x1f)); + mmv->u.gain = ((mixer_info->features&ALI_MIX_FT_GAIN) + ? mixer_info->min_gain+val:mixer_info->max_gain-val); + } + break; + + case ALI_MIX_MIC_BOOST_ID: + mmv->u.enable = ((ac97_read(card, mixer_info->reg) & 0x40) == 0x40); + break; + + case ALI_MIX_INPUT_SELECTOR_ID: + mmv->u.mux = (ac97_read(card, AC97_MIX_RECORDSELECT) & 0x7); + if (ALI_MUX_INPUTS <= mmv->u.mux) { + mmv->u.mux = 0x2; + ac97_write(card, AC97_MIX_RECORDSELECT, 0x202); + } + break; + } +} + + +static status_t +get_mix(ali_dev *card, multi_mix_value_info *mmvi) +{ + int32 i; + + for (i = 0; i < mmvi->item_count; i++) + get_mix_mixer(card, &(mmvi->values[i]), + mmvi->values[i].id-MULTI_AUDIO_BASE_ID); + + return B_OK; +} + + +static void +set_mix_mixer(ali_dev *card, multi_mix_value *mmv, int32 id) +{ + uint8 mixer_id = (uint8) id >> 4, gadget_id = (uint8) id & 0xf; + const ali_ac97_mixer_info *mixer_info; + uint16 val; + + if (ALI_ALL_MIXERS <= mixer_id) + return; + mixer_info = &ali_ac97_mixers[mixer_id]; + + switch (gadget_id) { + case ALI_MIX_MUTE_ID: + val = ac97_read(card, mixer_info->reg); + val = mmv->u.enable ? val | 0x8000 : val & 0x7fff; + ac97_write(card, mixer_info->reg, val); + break; + + case ALI_MIX_LEFT_ID: + case ALI_MIX_RIGHT_ID: + val = ac97_read(card, mixer_info->reg); + val &= (gadget_id == ALI_MIX_LEFT_ID) ? 0x805f : 0x9f00; + val |= (uint16) (((mixer_info->features&ALI_MIX_FT_GAIN) + ? mixer_info->min_gain+mmv->u.gain + : mixer_info->max_gain-mmv->u.gain) + / ALI_MIXER_GRANULARITY) << + ((gadget_id == ALI_MIX_LEFT_ID) ? 8 : 0); + ac97_write(card, mixer_info->reg, val); + break; + + case ALI_MIX_MIC_BOOST_ID: + val = ac97_read(card, mixer_info->reg); + val = mmv->u.enable ? val | 0x40 : val & 0xffbf; + ac97_write(card, mixer_info->reg, val); + break; + + case ALI_MIX_INPUT_SELECTOR_ID: + val = (mmv->u.mux << 8) | mmv->u.mux; + ac97_write(card, AC97_MIX_RECORDSELECT, val); + break; + } +} + + +static status_t +set_mix(ali_dev *card, multi_mix_value_info *mmvi) +{ + int32 i; + + for (i = 0; i < mmvi->item_count; i++) + set_mix_mixer(card, &(mmvi->values[i]), + mmvi->values[i].id-MULTI_AUDIO_BASE_ID); + + return B_OK; +} + + +static int32 +create_group_control(multi_mix_control *multi, int32 *idx, int32 id, + int32 parent, int32 string_id, const char *name) +{ + multi_mix_control *control = multi + *idx; + + control->id = MULTI_AUDIO_BASE_ID + id; + control->parent = parent; + control->flags = B_MULTI_MIX_GROUP; + control->master = MULTI_AUDIO_MASTER_ID; + control->string = string_id; + if (string_id == S_null && name) + strcpy(control->name, name); + + (*idx)++; + + return control->id; +} + + +static void +create_vol_control(multi_mix_control *multi, int32 *idx, int32 id, int32 parent) +{ + const ali_ac97_mixer_info *mixer_info; + multi_mix_control *control; + + id &= 0xf; + + if (ALI_ALL_MIXERS <= id) + return; + mixer_info = &ali_ac97_mixers[id]; + + id <<= 4; + + parent = create_group_control(multi, idx, (parent & 0x100) | id + | ALI_MIX_GROUP_ID, parent, mixer_info->string_id, mixer_info->name); + + control = multi + *idx; + + if (mixer_info->features & ALI_MIX_FT_MUTE) { + control->id = MULTI_AUDIO_BASE_ID | (parent & 0x100) | id + | ALI_MIX_MUTE_ID; + control->parent = parent; + control->flags = B_MULTI_MIX_ENABLE; + control->master = MULTI_AUDIO_MASTER_ID; + control->string = S_MUTE; + + control++; + (*idx)++; + } + + control->id = MULTI_AUDIO_BASE_ID | (parent & 0x100) | id + | ((mixer_info->features&ALI_MIX_FT_STEREO) + ?ALI_MIX_LEFT_ID:ALI_MIX_MONO_ID); + control->parent = parent; + control->flags = B_MULTI_MIX_GAIN; + control->master = MULTI_AUDIO_MASTER_ID; + control->string = S_GAIN; + // S_null; ? FIXME + + control->u.gain.min_gain = mixer_info->min_gain; + control->u.gain.max_gain = mixer_info->max_gain; + control->u.gain.granularity = ALI_MIXER_GRANULARITY; + + if (mixer_info->features & ALI_MIX_FT_STEREO) { + control[1] = control[0]; + control++; + (*idx)++; + + control->master = control->id; + control->id = MULTI_AUDIO_BASE_ID | (parent & 0x100) | id + | ALI_MIX_RIGHT_ID; + } + + control++; + (*idx)++; + + if (mixer_info->features & ALI_MIX_FT_MICBST) { + control->id = MULTI_AUDIO_BASE_ID | (parent & 0x100) | id + | ALI_MIX_MIC_BOOST_ID; + control->parent = parent; + control->flags = B_MULTI_MIX_ENABLE; + control->master = MULTI_AUDIO_MASTER_ID; + control->string = S_null; + strcpy(control->name, "+20dB"); + + control++; + (*idx)++; + } + + if (mixer_info->features & ALI_MIX_FT_INSEL) { + int32 is_parent; + const ali_ac97_mux_input_info *mux_input; + + is_parent = control->id = MULTI_AUDIO_BASE_ID | (parent & 0x100) + | id | ALI_MIX_INPUT_SELECTOR_ID; + control->parent = parent; + control->flags = B_MULTI_MIX_MUX; + control->master = MULTI_AUDIO_MASTER_ID; + control->string = S_INPUT; + + control++; + (*idx)++; + + for (id = 0; id < ALI_MUX_INPUTS; id++) { + mux_input = &ali_ac97_mux_inputs[id]; + + control->id = MULTI_AUDIO_BASE_ID | (parent & 0x100) | (id<<4) + | ALI_MIX_RECORD_INPUT; + control->parent = is_parent; + control->flags = B_MULTI_MIX_MUX_VALUE; + control->master = MULTI_AUDIO_MASTER_ID; + control->string = mux_input->string_id; + if (control->string == S_null && mux_input->name) + strcpy(control->name, mux_input->name); + + control++; + (*idx)++; + } + } +} + + +static status_t +list_mix_controls(ali_dev *card, multi_mix_control_info *mmci) +{ + int32 index = 0, parent, i; + + parent = create_group_control(mmci->controls, &index, ALI_MIX_OUTPUT_ID, 0, + S_OUTPUT, NULL); + for (i = 0; i < ALI_OUTPUT_MIXERS; i++) + create_vol_control(mmci->controls, &index, i, parent); + + parent = create_group_control(mmci->controls, &index, ALI_MIX_INPUT_ID, 0, + S_INPUT, NULL); + for (i = ALI_OUTPUT_MIXERS; i < ALI_OUTPUT_MIXERS + ALI_INPUT_MIXERS; i++) + create_vol_control(mmci->controls, &index, i, parent); + + mmci->control_count = index; + + return B_OK; +} + + +static status_t +get_buffers(ali_dev *card, multi_buffer_list *data) +{ + uint8 buf_ndx, ch_ndx; + + data->return_playback_buffers = card->playback_stream->buf_count; + data->return_playback_channels = card->playback_stream->channels; + data->return_playback_buffer_size = card->playback_stream->buf_frames; + + for (buf_ndx=0; buf_ndxplayback_stream->buf_count; buf_ndx++) + for (ch_ndx = 0; ch_ndx < card->playback_stream->channels; ch_ndx++) + ali_stream_get_buffer_part(card->playback_stream, ch_ndx, buf_ndx, + &data->playback_buffers[buf_ndx][ch_ndx].base, + &data->playback_buffers[buf_ndx][ch_ndx].stride); + + data->return_record_buffers = card->record_stream->buf_count; + data->return_record_channels = card->record_stream->channels; + data->return_record_buffer_size = card->record_stream->buf_frames; + + for (buf_ndx = 0; buf_ndx < card->record_stream->buf_count; buf_ndx++) + for (ch_ndx = 0; ch_ndx < card->record_stream->channels; ch_ndx++) + ali_stream_get_buffer_part(card->record_stream, ch_ndx, buf_ndx, + &data->record_buffers[buf_ndx][ch_ndx].base, + &data->record_buffers[buf_ndx][ch_ndx].stride); + + return B_OK; +} + + +static status_t +buffer_exchange(ali_dev *card, multi_buffer_info *buffer_info) +{ + status_t res; + ali_stream *play_s, *rec_s; + + play_s = card->playback_stream; + rec_s = card->record_stream; + + if (!play_s || !rec_s) + return B_ERROR; + + if (!play_s->is_playing) + if (!ali_stream_start(play_s)) + return B_ERROR; + if (!rec_s->is_playing) + if (!ali_stream_start(rec_s)) + return B_ERROR; + + res = acquire_sem_etc(card->sem_buf_ready, 1, B_CAN_INTERRUPT, 0); + if (res != B_OK) { + TRACE("buffer_exchange: error acquiring semaphore\n"); + return res; + } + + LOCK(card->lock_sts); + + buffer_info->played_frames_count = play_s->frames_count; + buffer_info->played_real_time = play_s->real_time; + buffer_info->playback_buffer_cycle = play_s->buffer_cycle; + + buffer_info->recorded_frames_count = rec_s->frames_count; + buffer_info->recorded_real_time = rec_s->real_time; + buffer_info->record_buffer_cycle = rec_s->buffer_cycle; + + UNLOCK(card->lock_sts); + + return B_OK; +} + + +static status_t +force_stop(ali_dev *card) +{ + ali_stream *stream; + + TRACE("force_stop\n"); + + LIST_FOREACH(stream, &card->streams, next) { + ali_stream_stop(stream); + } + + return B_OK; +} + + +status_t +multi_audio_control(void *cookie, uint32 op, void *arg, size_t len) +{ + switch (op) { + case B_MULTI_GET_DESCRIPTION: return get_description(cookie, arg); + + case B_MULTI_GET_EVENT_INFO: return B_ERROR; + case B_MULTI_SET_EVENT_INFO: return B_ERROR; + case B_MULTI_GET_EVENT: return B_ERROR; + + case B_MULTI_GET_ENABLED_CHANNELS: return get_enabled_channels(cookie, arg); + case B_MULTI_SET_ENABLED_CHANNELS: return B_OK; + case B_MULTI_GET_GLOBAL_FORMAT: return get_global_format(cookie, arg); + case B_MULTI_SET_GLOBAL_FORMAT: return set_global_format(cookie, arg); + + case B_MULTI_GET_CHANNEL_FORMATS: return B_ERROR; + case B_MULTI_SET_CHANNEL_FORMATS: return B_ERROR; + case B_MULTI_GET_MIX: return get_mix(cookie, arg); + case B_MULTI_SET_MIX: return set_mix(cookie, arg); + + case B_MULTI_LIST_MIX_CHANNELS: return B_ERROR; + case B_MULTI_LIST_MIX_CONTROLS: return list_mix_controls(cookie, arg); + case B_MULTI_LIST_MIX_CONNECTIONS: return B_ERROR; + + case B_MULTI_GET_BUFFERS: return get_buffers(cookie, arg); + case B_MULTI_SET_BUFFERS: return B_ERROR; + // do not support in-software buffers + + case B_MULTI_SET_START_TIME: return B_ERROR; + case B_MULTI_BUFFER_EXCHANGE: return buffer_exchange(cookie, arg); + case B_MULTI_BUFFER_FORCE_STOP: return force_stop(cookie); + } + + return B_BAD_VALUE; +} diff --git a/src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_multi.h b/src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_multi.h new file mode 100644 index 0000000000..a1db88b570 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ali5451/ali_multi.h @@ -0,0 +1,14 @@ +/* + * Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com). + * + * All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + + +#ifndef _ALI_MULTI_H +#define _ALI_MULTI_H + +status_t multi_audio_control(void *cookie, uint32 op, void *arg, size_t len); + +#endif // _ALI_MULTI_H diff --git a/src/add-ons/kernel/drivers/audio/ac97/ali5451/debug.h b/src/add-ons/kernel/drivers/audio/ac97/ali5451/debug.h new file mode 100644 index 0000000000..6ebd37f2a2 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ali5451/debug.h @@ -0,0 +1,24 @@ +/* + * Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com). + * + * All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + + +#ifndef _ALI_DEBUG_H +#define _ALI_DEBUG_H + +#ifdef TRACE +#undef TRACE +#endif + +#define TRACE_ALI + +#ifdef TRACE_ALI +#define TRACE(a...) dprintf("\33[34mali5451:\33[0m " a) +#else +#define TRACE(a...) +#endif + +#endif // _ALI_DEBUG_H diff --git a/src/add-ons/kernel/drivers/audio/ac97/ali5451/driver.c b/src/add-ons/kernel/drivers/audio/ac97/ali5451/driver.c new file mode 100644 index 0000000000..db30a3ed88 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ali5451/driver.c @@ -0,0 +1,543 @@ +/* + * Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com). + * + * All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + + +#include +#include +#include +#include +#include +#include + +#include "queue.h" +#include "ali_multi.h" +#include "driver.h" +#include "util.h" +#include "ali_hardware.h" +#include "debug.h" + + +int32 api_version = B_CUR_DRIVER_API_VERSION; + +int32 gCardsCount; +ali_dev gCards[MAX_CARDS]; +pci_module_info *gPCI; + + +/* streams handling */ + + +static ali_stream * +ali_stream_new(ali_dev *card, bool rec, uint8 buf_count) +{ + ali_stream *stream; + + if (!buf_count) + return NULL; + + stream = malloc(sizeof(ali_stream)); + if (!stream) + return NULL; + + stream->card = card; + + stream->buffer = NULL; + + stream->buf_count = buf_count; + stream->buf_frames = 0; + + stream->rec = rec; + stream->channels = 0; + stream->format.format = 0; + stream->format.rate = 0; + + stream->is_playing = false; + + stream->used_voice = -1; + + stream->frames_count = 0; + stream->real_time = 0; + stream->buffer_cycle = (rec?buf_count-1:0); + + LOCK(card->lock_sts); + LIST_INSERT_HEAD(&(card->streams), stream, next); + UNLOCK(card->lock_sts); + + return stream; +} + + +static void +ali_stream_unprepare(ali_stream *stream) +{ + ali_stream_stop(stream); + + if (stream->buffer) { + ali_mem_free(stream->card, stream->buffer->log_base); + stream->buffer = NULL; + } + + stream->channels = 0; + stream->format.format = 0; + stream->format.rate = 0; + + if (-1 < stream->used_voice) { + LOCK(stream->card->lock_voices); + stream->card->used_voices &= ~((uint32) 1 << stream->used_voice); + UNLOCK(stream->card->lock_voices); + } +} + + +static void +ali_stream_delete(ali_stream *stream) +{ + ali_stream_unprepare(stream); + + LOCK(stream->card->lock_sts); + LIST_REMOVE(stream, next); + UNLOCK(stream->card->lock_sts); + + free(stream); +} + + +bool +ali_stream_prepare(ali_stream *stream, uint8 channels, uint32 format, + uint32 sample_rate) +{ + uint8 frame_size = util_format_to_sample_size(format) * channels, ch; + + TRACE("stream_prepare: frame_size: %d\n", (int) frame_size); + + if (!frame_size) + return false; + + if (stream->channels == channels && stream->format.format == format && + stream->format.rate == sample_rate) + return true; + + ali_stream_unprepare(stream); + + stream->channels = channels; + stream->format.format = format; + stream->format.rate = sample_rate; + + // find and set voice + LOCK(stream->card->lock_voices); + if (stream->rec) { + if ((stream->card->used_voices & (uint32) 1 << ALI_RECORD_VOICE) == 0) { + stream->used_voice = ALI_RECORD_VOICE; + stream->card->used_voices |= (uint32) 1 << ALI_RECORD_VOICE; + } + } else { + for (ch = 0; ch < ALI_VOICES; ch++) { + if ((stream->card->used_voices & (uint32) 1 << ch) == 0) { + stream->used_voice = ch; + stream->card->used_voices |= (uint32) 1 << ch; + break; + } + } + } + UNLOCK(stream->card->lock_voices); + + if (stream->used_voice == -1) { + ali_stream_unprepare(stream); + TRACE("stream_prepare: no free voices\n"); + return false; + } + + stream->buf_frames = util_get_buffer_length_for_rate(stream->format.rate); + + stream->buffer = ali_mem_alloc(stream->card, stream->buf_frames + * frame_size * stream->buf_count); + if (!stream->buffer) { + ali_stream_unprepare(stream); + TRACE("stream_prepare: failed to create buffer\n"); + return false; + } + + return true; +} + + +bool +ali_stream_start(ali_stream *stream) +{ + if (stream->is_playing) + return true; + + if (!ali_voice_start(stream)) { + TRACE("stream_start: trouble starting voice\n"); + return false; + } + + stream->is_playing = true; + + TRACE("stream_start: %s using voice: %d\n", + stream->rec?"recording":"playing", stream->used_voice); + + return true; +} + + +void +ali_stream_stop(ali_stream *stream) +{ + if (!stream->is_playing) + return; + + ali_voice_stop(stream); + + stream->is_playing = false; +} + + +void +ali_stream_get_buffer_part(ali_stream *stream, uint8 ch_ndx, uint8 buf_ndx, + char **buffer, size_t *stride) +{ + uint8 sample_size = util_format_to_sample_size(stream->format.format), + frame_size = sample_size * stream->channels; + + *buffer = stream->buffer->log_base + frame_size * stream->buf_frames + * buf_ndx + sample_size * ch_ndx; + *stride = frame_size; +} + + +static int32 +ali_interrupt_handler(void *cookie) +{ + ali_dev *card = (ali_dev *) cookie; + bool handled = false; + uint32 aina; + ali_stream *stream; + + aina = ali_read_int(card); + if (!aina) + goto exit_; + + LIST_FOREACH(stream, &card->streams, next) { + if (!stream->is_playing || stream->used_voice == -1) + continue; + + if ((aina & (uint32) 1 << stream->used_voice) == 0) + continue; + + ali_voice_clr_int(stream); + // clear interrupt flag + + // interrupts are disabled already when entering handler, lock only + acquire_spinlock(&card->lock_sts); + stream->frames_count += stream->buf_frames; + stream->real_time = system_time(); + stream->buffer_cycle = (stream->buffer_cycle + 1) % stream->buf_count; + release_spinlock(&card->lock_sts); + + release_sem_etc(card->sem_buf_ready, 1, B_DO_NOT_RESCHEDULE); + + handled = true; + } + +exit_: + if (!handled) + TRACE("unhandled interrupt\n"); + + return handled ? B_INVOKE_SCHEDULER : B_UNHANDLED_INTERRUPT; +} + + +static void +make_device_names(ali_dev *card) +{ + sprintf(card->name, DEVFS_PATH_FORMAT, card-gCards+1); +} + + +static void +ali_terminate(ali_dev *card) +{ + TRACE("terminate\n"); + + remove_io_interrupt_handler(card->irq, ali_interrupt_handler, card); + + if (card->sem_buf_ready != -1) + delete_sem(card->sem_buf_ready); + + hardware_terminate(card); +} + + +static status_t +ali_setup(ali_dev *card) +{ + uint32 cmd_reg; + status_t res; + + make_device_names(card); + + cmd_reg = (*gPCI->read_pci_config)(card->info.bus, card->info.device, + card->info.function, PCI_command, 2); + cmd_reg |= PCI_command_io | PCI_command_memory | PCI_command_master; + (*gPCI->write_pci_config)(card->info.bus, card->info.device, + card->info.function, PCI_command, 2, cmd_reg); + + card->io_base = card->info.u.h0.base_registers[0]; + TRACE("setup: base io is: %08x\n", (unsigned int) card->io_base); + + card->irq = card->info.u.h0.interrupt_line; + TRACE("setup: irq is: %02x\n", card->irq); + + card->lock_hw = 0; + card->lock_sts = 0; + card->lock_voices = 0; + + card->sem_buf_ready = create_sem(0, "ali5451_buf_ready"); + if (card->sem_buf_ready < B_OK) { + TRACE("setup: failed to create semaphore\n"); + return -1; + } + + TRACE("setup: revision: %02x\n", card->info.revision); + + card->playback_stream = NULL; + card->record_stream = NULL; + + card->used_voices = 0; + + card->global_output_format.format = B_FMT_16BIT; + card->global_output_format.rate = B_SR_48000; + card->global_input_format = card->global_output_format; + + res = install_io_interrupt_handler(card->irq, ali_interrupt_handler, card, + 0); + if (res != B_OK) { + TRACE("setup: cannot install interrupt handler\n"); + return res; + } + + res = hardware_init(card); + if (res != B_OK) + ali_terminate(card); + + return res; +} + + +/* driver's public functions */ + + +status_t +init_hardware(void) +{ + status_t res = ENODEV; + pci_info info; + int32 i; + + if (get_module(B_PCI_MODULE_NAME, (module_info**) &gPCI) != B_OK) + return res; + + for (i=0; (*gPCI->get_nth_pci_info)(i, &info) == B_OK; i++) { + if (info.class_base == PCI_multimedia && + info.vendor_id == ALI_VENDOR_ID && + info.device_id == ALI_DEVICE_ID) { + TRACE("hardware found\n"); + res = B_OK; + break; + } + } + + put_module(B_PCI_MODULE_NAME); + + return res; +} + + +status_t +init_driver(void) +{ + pci_info info; + int32 i; + + if (get_module(B_PCI_MODULE_NAME, (module_info**) &gPCI) != B_OK) + return ENODEV; + + gCardsCount = 0; + + for (i = 0; (*gPCI->get_nth_pci_info)(i, &info) == B_OK; i++) { + if (info.class_base == PCI_multimedia && + info.vendor_id == ALI_VENDOR_ID && + info.device_id == ALI_DEVICE_ID) { + if (gCardsCount == MAX_CARDS) { + TRACE("init_driver: too many cards\n"); + break; + } + + memset(&gCards[gCardsCount], 0, sizeof(ali_dev)); + gCards[gCardsCount].info = info; + if (ali_setup(&gCards[gCardsCount])) + TRACE("init_driver: setup of ali %ld failed\n", gCardsCount+1); + else + gCardsCount++; + } + } + + if (!gCardsCount) { + put_module(B_PCI_MODULE_NAME); + TRACE("init_driver: no cards found\n"); + return ENODEV; + } + + return B_OK; +} + + +void +uninit_driver(void) +{ + int32 i = gCardsCount; + + while (i--) + ali_terminate(&gCards[i]); + memset(&gCards, 0, sizeof(gCards)); + + put_module(B_PCI_MODULE_NAME); + + gCardsCount = 0; +} + + +const char ** +publish_devices(void) +{ + static const char *names[MAX_CARDS+1]; + int32 i; + + for (i = 0; i < gCardsCount; i++) + names[i] = gCards[i].name; + names[gCardsCount] = NULL; + + return (const char **) names; +} + + +/* driver's hooks */ + + +static status_t +ali_audio_open(const char *name, uint32 flags, void **cookie) +{ + ali_dev *card = NULL; + int32 i; + + for (i = 0; i < gCardsCount; i++) { + if (!strcmp(gCards[i].name, name)) { + card = &gCards[i]; + } + } + + if (!card) { + TRACE("audio_open: card not found\n"); + return B_ERROR; + } + + if (card->playback_stream || card->record_stream) + return B_ERROR; + + *cookie = card; + + card->playback_stream = ali_stream_new(card, false, ALI_BUF_CNT); + card->record_stream = ali_stream_new(card, true, ALI_BUF_CNT); + + if (!ali_stream_prepare(card->playback_stream, 2, + card->global_output_format.format, + util_sample_rate_in_bits(card->global_output_format.rate))) + return B_ERROR; + if (!ali_stream_prepare(card->record_stream, 2, + card->global_input_format.format, + util_sample_rate_in_bits(card->global_input_format.rate))) + return B_ERROR; + + return B_OK; +} + + +static status_t +ali_audio_read(void *cookie, off_t a, void *b, size_t *num_bytes) +{ + TRACE("audio_read attempt\n"); + + *num_bytes = 0; + return B_IO_ERROR; +} + + +static status_t +ali_audio_write(void *cookie, off_t a, const void *b, size_t *num_bytes) +{ + TRACE("audio_write attempt\n"); + + *num_bytes = 0; + return B_IO_ERROR; +} + + +static status_t +ali_audio_control(void *cookie, uint32 op, void *arg, size_t len) +{ + if (!cookie) + TRACE("audio_control: no cookie\n"); + + return cookie?multi_audio_control(cookie, op, arg, len):B_BAD_VALUE; +} + + +static status_t +ali_audio_close(void *cookie) +{ + ali_dev *card = (ali_dev *) cookie; + ali_stream *stream; + + LIST_FOREACH(stream, &card->streams, next) { + ali_stream_stop(stream); + } + + return B_OK; +} + + +static status_t +ali_audio_free(void *cookie) +{ + ali_dev *card = (ali_dev *) cookie; + + while (!LIST_EMPTY(&card->streams)) + ali_stream_delete(LIST_FIRST(&card->streams)); + + card->playback_stream = NULL; + card->record_stream = NULL; + + return B_OK; +} + + +device_hooks driver_hooks = { + ali_audio_open, + ali_audio_close, + ali_audio_free, + ali_audio_control, + ali_audio_read, + ali_audio_write +}; + + +device_hooks * +find_device(const char *name) +{ + return &driver_hooks; +} diff --git a/src/add-ons/kernel/drivers/audio/ac97/ali5451/driver.h b/src/add-ons/kernel/drivers/audio/ac97/ali5451/driver.h new file mode 100644 index 0000000000..b29424dcbc --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ali5451/driver.h @@ -0,0 +1,110 @@ +/* + * Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com). + * + * All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + + +#ifndef _ALI_AUDIO_DRIVER_H +#define _ALI_AUDIO_DRIVER_H + +#define MULTI_AUDIO_BASE_ID 1024 +#define MULTI_AUDIO_DEV_PATH "audio/hmulti" +#define MULTI_AUDIO_MASTER_ID 0 + +#define DEVFS_PATH_FORMAT MULTI_AUDIO_DEV_PATH "/ali5451/%lu" + +#define MAX_CARDS 3 +#define DEVNAME 32 + +#define ALI_VENDOR_ID 0x10b9 +#define ALI_DEVICE_ID 0x5451 + +#define ALI_BUF_CNT 2 +#define ALI_MAX_CHN 2 +#define ALI_VOICES 32 +#define ALI_RECORD_VOICE 31 + +typedef struct _ali_mem +{ + LIST_ENTRY(_ali_mem) next; + + void *log_base; + void *phy_base; + + area_id area; + size_t size; +} ali_mem; + +typedef struct { + uint32 format; + uint32 rate; +} ali_format; + +typedef struct _ali_stream +{ + LIST_ENTRY(_ali_stream) next; + + struct _ali_dev *card; + + ali_mem *buffer; + + uint8 buf_count; + uint32 buf_frames; + + bool rec; + uint8 channels; + ali_format format; + + bool is_playing; + + int8 used_voice; + + // multi_audio + volatile int64 frames_count; + volatile bigtime_t real_time; + volatile int32 buffer_cycle; +} ali_stream; + +typedef struct _ali_dev +{ + char name[DEVNAME]; + pci_info info; + ulong io_base; + uint8 irq; + + spinlock lock_hw; + spinlock lock_sts; + spinlock lock_voices; + sem_id sem_buf_ready; + + ali_stream *playback_stream; + ali_stream *record_stream; + + uint32 used_voices; + + ali_format global_output_format; + ali_format global_input_format; + + LIST_HEAD(, _ali_stream) streams; + LIST_HEAD(, _ali_mem) mems; +} ali_dev; + +#define LOCK(_lock) \ + { \ + cpu_status cp = disable_interrupts(); \ + acquire_spinlock(&_lock) +#define UNLOCK(_lock) \ + release_spinlock(&_lock); \ + restore_interrupts(cp); \ + } + +bool ali_stream_prepare(ali_stream *stream, uint8 channels, uint32 format, + uint32 sample_rate); +bool ali_stream_start(ali_stream *stream); +void ali_stream_stop(ali_stream *stream); +void ali_stream_get_buffer_part(ali_stream *stream, uint8 ch_ndx, uint8 buf_ndx, + char ** buffer, size_t *stride); + +#endif // _ALI_AUDIO_DRIVER_H diff --git a/src/add-ons/kernel/drivers/audio/ac97/ali5451/queue.h b/src/add-ons/kernel/drivers/audio/ac97/ali5451/queue.h new file mode 100644 index 0000000000..396c70fb2e --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ali5451/queue.h @@ -0,0 +1,529 @@ +/* $NetBSD: queue.h,v 1.31 2002/06/01 23:51:05 lukem Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so only elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ + if ((head)->lh_first && \ + (head)->lh_first->field.le_prev != &(head)->lh_first) \ + panic("LIST_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_OP(elm, field) \ + if ((elm)->field.le_next && \ + (elm)->field.le_next->field.le_prev != \ + &(elm)->field.le_next) \ + panic("LIST_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ + if (*(elm)->field.le_prev != (elm)) \ + panic("LIST_* back %p %s:%d", (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ + (elm)->field.le_next = (void *)1L; \ + (elm)->field.le_prev = (void *)1L; +#else +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_LIST_OP(elm, field) +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) +#endif + +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + QUEUEDEBUG_LIST_OP((elm), field) \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var); \ + (var) = ((var)->field.le_next)) + +/* + * List access methods. + */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) + +#define SLIST_INIT(head) do { \ + (head)->slh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var); \ + (var) = ((var)->field.sqe_next)) + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue functions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ + if ((head)->tqh_first && \ + (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ + panic("TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ + if (*(head)->tqh_last != NULL) \ + panic("TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_OP(elm, field) \ + if ((elm)->field.tqe_next && \ + (elm)->field.tqe_next->field.tqe_prev != \ + &(elm)->field.tqe_next) \ + panic("TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ + (elm)->field.tqe_next = (void *)1L; \ + (elm)->field.tqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) +#define QUEUEDEBUG_TAILQ_OP(elm, field) +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) +#endif + +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((elm), field) \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +/* + * Tail queue access methods. + */ +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ + (var); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { (void *)&head, (void *)&head } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != (void *)(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != (void *)(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#endif /* !_SYS_QUEUE_H_ */ diff --git a/src/add-ons/kernel/drivers/audio/ac97/ali5451/util.c b/src/add-ons/kernel/drivers/audio/ac97/ali5451/util.c new file mode 100644 index 0000000000..58fa041cf6 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ali5451/util.c @@ -0,0 +1,193 @@ +/* + * Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com). + * + * Original code for memory management is + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr). + * + * All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + + +#include +#include +#include +#include +#include + +#include "queue.h" +#include "driver.h" +#include "util.h" +#include "debug.h" + + +/* memory management */ + + +static uint32 +round_to_pagesize(uint32 size) +{ + return (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); +} + + +static 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; + + size = round_to_pagesize(size); + areaid = create_area(name, &logadr, B_ANY_KERNEL_ADDRESS, size, + B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA); + if (areaid < B_OK) { + TRACE("alloc_mem: 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("alloc_mem: couldn't map %s\n",name); + return B_ERROR; + } + memset(logadr, 0, size); + if (log) + *log = logadr; + if (phy) + *phy = pe.address; + + return areaid; +} + + +static ali_mem +*ali_mem_new(ali_dev *card, size_t size) +{ + ali_mem *mem; + + if (!(mem = malloc(sizeof(*mem)))) + return NULL; + + mem->area = alloc_mem(&mem->phy_base, &mem->log_base, size, "ali buffer"); + mem->size = size; + + if (mem->area < B_OK) { + free(mem); + return NULL; + } + + return mem; +} + + +static void +ali_mem_delete(ali_mem *mem) +{ + if (B_OK < mem->area) + delete_area(mem->area); + + free(mem); +} + + +void * +ali_mem_alloc(ali_dev *card, size_t size) +{ + ali_mem *mem; + + mem = ali_mem_new(card, size); + if (!mem) + return NULL; + + LIST_INSERT_HEAD(&(card->mems), mem, next); + + return mem; +} + + +void +ali_mem_free(ali_dev *card, void *ptr) +{ + ali_mem *mem; + + LIST_FOREACH(mem, &card->mems, next) { + if (mem->log_base != ptr) + continue; + + LIST_REMOVE(mem, next); + + ali_mem_delete(mem); + break; + } +} + + +/* misc utils */ + + +uint8 +util_format_to_sample_size(uint32 format) +{ + switch (format) { + case B_FMT_8BIT_U: + case B_FMT_8BIT_S: return 1; + + case B_FMT_16BIT: return 2; + + case B_FMT_18BIT: + case B_FMT_24BIT: + case B_FMT_32BIT: + case B_FMT_FLOAT: return 4; + + default: return 0; + } +} + + +bool +util_format_is_signed(uint32 format) +{ + return (format == B_FMT_8BIT_S || format == B_FMT_16BIT); +} + + +uint32 +util_get_buffer_length_for_rate(uint32 rate) +{ + // values are based on Haiku's hda driver + + switch (rate) { + case 8000: + case 11025: return 512; + + case 16000: + case 22050: return 1024; + + case 32000: + case 44100: + case 48000: return 2048; + } + + return 2048; +} + + +uint32 +util_sample_rate_in_bits(uint32 rate) +{ + switch (rate) { + case B_SR_8000: return 8000; + case B_SR_11025: return 11025; + case B_SR_12000: return 12000; + case B_SR_16000: return 16000; + case B_SR_22050: return 22050; + case B_SR_24000: return 24000; + case B_SR_32000: return 32000; + case B_SR_44100: return 44100; + case B_SR_48000: return 48000; + } + + return 0; +} diff --git a/src/add-ons/kernel/drivers/audio/ac97/ali5451/util.h b/src/add-ons/kernel/drivers/audio/ac97/ali5451/util.h new file mode 100644 index 0000000000..67d8755974 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/ali5451/util.h @@ -0,0 +1,23 @@ +/* + * Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com). + * + * Original code for memory management is + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr). + * + * All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + + +#ifndef _UTIL_H +#define _UTIL_H + +void *ali_mem_alloc(ali_dev *card, size_t size); +void ali_mem_free(ali_dev *card, void *ptr); + +uint8 util_format_to_sample_size(uint32 format); +bool util_format_is_signed(uint32 format); +uint32 util_get_buffer_length_for_rate(uint32 rate); +uint32 util_sample_rate_in_bits(uint32 rate); + +#endif // _UTIL_H