ALI 5451 audio driver for Haiku multi audio by Krzysiek Cwiertnia

* playback, tested with 16bit format, with sample rate range from 8 to 48kHz
 * recording, fixed at 48kHz 16 bit (read below)
 * controlling some mixers, input selector, etc.

I placed the driver in the ac97 directory as it fits better.
Also a few coding style fixes by me.
This driver collides at least with one pci id of the sis7018 driver.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34452 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Jérôme Duval 2009-12-02 20:49:37 +00:00
parent def9898c9b
commit 88e5c1ac3b
12 changed files with 2476 additions and 0 deletions

View File

@ -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 ;

View File

@ -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
;

View File

@ -0,0 +1,404 @@
/*
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
*
* All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#include <drivers/Drivers.h>
#include <drivers/KernelExport.h>
#include <drivers/PCI.h>
#include <hmulti_audio.h>
#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);
}

View File

@ -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

View File

@ -0,0 +1,576 @@
/*
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
*
* All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#include <drivers/Drivers.h>
#include <drivers/KernelExport.h>
#include <drivers/PCI.h>
#include <hmulti_audio.h>
#include <string.h>
#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_ndx<card->playback_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;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,543 @@
/*
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
*
* All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#include <drivers/Drivers.h>
#include <drivers/KernelExport.h>
#include <drivers/PCI.h>
#include <hmulti_audio.h>
#include <string.h>
#include <unistd.h>
#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;
}

View File

@ -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

View File

@ -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_ */

View File

@ -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 <drivers/Drivers.h>
#include <drivers/KernelExport.h>
#include <drivers/PCI.h>
#include <hmulti_audio.h>
#include <string.h>
#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;
}

View File

@ -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