Audio driver for SiS 7018, Trident DX/NX and Acer Labs M5451 hardware

was rewritten to Haiku MultiAudio API.

This changeset replaces both old version of the same driver written for
BeOS R3 Sound API and ali5451 driver that handles the same audio hardware.
The driver was tested with SiS 7018 and ALi M5451 cards. Trident DX/NX
support still should be tested.

Some parts, related to ALi M5451 support were inspired by original ali5451
code so the copyright (c) 2009 by Krzysztof Ćwiertnia was added in the source.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@40273 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Siarzhuk Zharski
2011-01-23 18:47:57 +00:00
parent a5237f5fc6
commit 07910542fd
47 changed files with 2584 additions and 5648 deletions
+2 -2
View File
@@ -156,8 +156,8 @@ SYSTEM_ADD_ONS_PRINT_TRANSPORT = HP\ JetDirect IPP LPR Parallel\ Port
;
SYSTEM_ADD_ONS_SCREENSAVERS = Butterfly DebugNow Flurry
$(HAIKU_INCLUDE_TRADEMARKS)Haiku Icons IFS Leaves Message Spider ;
SYSTEM_ADD_ONS_DRIVERS_AUDIO = auich auvia echo3g emuxki hda ;
SYSTEM_ADD_ONS_DRIVERS_AUDIO_OLD = ; #cmedia sis7018 usb_audio ;
SYSTEM_ADD_ONS_DRIVERS_AUDIO = auich auvia echo3g emuxki hda sis7018 ;
SYSTEM_ADD_ONS_DRIVERS_AUDIO_OLD = ; #cmedia usb_audio ;
SYSTEM_ADD_ONS_DRIVERS_GRAPHICS = $(X86_ONLY)radeon $(X86_ONLY)nvidia
$(X86_ONLY)neomagic $(X86_ONLY)matrox $(X86_ONLY)intel_extreme
$(X86_ONLY)s3 $(X86_ONLY)vesa #$(X86_ONLY)via #$(X86_ONLY)vmware
-1
View File
@@ -9,6 +9,5 @@ SubInclude HAIKU_TOP src add-ons kernel drivers audio ice1712 ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio module_driver ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio null ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio sb16 ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio sis7018 ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio usb_audio ;
@@ -1,9 +1,9 @@
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 ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 geode ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 ich ;
# SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 ichaudio ;
SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 sis7018 ;
@@ -1,16 +0,0 @@
SubDir HAIKU_TOP src add-ons kernel drivers audio ac97 ali5451 ;
SetSubDirSupportedPlatformsBeOSCompatible ;
UsePrivateHeaders media ;
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
SEARCH_SOURCE += [ FDirName $(SUBDIR) $(DOTDOT) ] ;
KernelAddon ali5451 :
driver.c
ali_multi.c
ali_hardware.c
util.c
ac97.c
;
@@ -1,359 +0,0 @@
/*
* 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 "ac97.h"
#include "queue.h"
#include "driver.h"
#include "util.h"
#include "ali_hardware.h"
#include "debug.h"
#define ALI_5451_V02 0x02
// 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;
}
static uint16
ac97_read(void *cookie, uint8 reg)
{
ali_dev *card = (ali_dev *) cookie;
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);
}
static void
ac97_write(void *cookie, uint8 reg, uint16 val)
{
ali_dev *card = (ali_dev *) cookie;
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
hardware_reset(ali_dev *card)
{
uint32 cmd_reg;
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);
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);
ac97_attach(&card->codec, ac97_read, ac97_write, card,
card->info.u.h0.subsystem_vendor_id, card->info.u.h0.subsystem_id);
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);
ac97_detach(card->codec);
}
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);
}
@@ -1,20 +0,0 @@
/*
* 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
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
@@ -1,594 +0,0 @@
/*
* 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 "ac97.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_MASTER_VOLUME, -46.5f, 0.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE},
{S_WAVE, NULL, AC97_PCM_OUT_VOLUME, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE},
{S_HEADPHONE, NULL, AC97_AUX_OUT_VOLUME, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE},
{S_CD, NULL, AC97_CD_VOLUME, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE},
{S_LINE, NULL, AC97_LINE_IN_VOLUME, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE},
{S_MIC, NULL, AC97_MIC_VOLUME, -34.5f, 12.0f, ALI_MIX_FT_MUTE | ALI_MIX_FT_MICBST},
{S_AUX, NULL, AC97_AUX_IN_VOLUME, -34.5f, 12.0f, ALI_MIX_FT_STEREO | ALI_MIX_FT_MUTE},
{S_null, "Record", AC97_RECORD_GAIN, 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)
{
multi_description description;
if (user_memcpy(&description, data, sizeof(multi_description)) != B_OK)
return B_BAD_ADDRESS;
description.interface_version = B_CURRENT_INTERFACE_VERSION;
description.interface_minimum = B_CURRENT_INTERFACE_VERSION;
strcpy(description.friendly_name, "ALI M5451");
strcpy(description.vendor_info, "Krzysztof Ćwiertnia");
description.output_channel_count = ALI_SUPPORTED_OUTPUT_CHANNELS;
description.input_channel_count = ALI_SUPPORTED_INPUT_CHANNELS;
description.output_bus_channel_count = ALI_SUPPORTED_OUTPUT_CHANNELS;
description.input_bus_channel_count = ALI_SUPPORTED_INPUT_CHANNELS;
description.aux_bus_channel_count = 0;
description.output_formats = ALI_VALID_OUTPUT_FORMATS;
description.output_rates = ALI_VALID_OUTPUT_SAMPLE_RATES;
description.input_formats = ALI_VALID_INPUT_FORMATS;
description.input_rates = ALI_VALID_INPUT_SAMPLE_RATES;
description.lock_sources = B_MULTI_LOCK_INTERNAL;
description.timecode_sources = 0;
description.interface_flags = B_MULTI_INTERFACE_PLAYBACK|B_MULTI_INTERFACE_RECORD;
description.start_latency = 30000;
strcpy(description.control_panel,"");
if (user_memcpy(data, &description, sizeof(multi_description)) != B_OK)
return B_BAD_ADDRESS;
if (description.request_channel_count
>= sizeof(channel_descriptions) / sizeof(channel_descriptions[0])) {
if (user_memcpy(data->channels,
&channel_descriptions, sizeof(channel_descriptions)) != B_OK)
return B_BAD_ADDRESS;
}
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_reg_cached_read(card->codec, mixer_info->reg)
& 0x8000) == 0x8000);
break;
case ALI_MIX_LEFT_ID:
case ALI_MIX_RIGHT_ID:
{
float val = (ALI_MIXER_GRANULARITY
* ((ac97_reg_cached_read(card->codec, 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_reg_cached_read(card->codec, mixer_info->reg)
& 0x40) == 0x40);
break;
case ALI_MIX_INPUT_SELECTOR_ID:
mmv->u.mux = (ac97_reg_cached_read(card->codec,
AC97_RECORD_SELECT) & 0x7);
if (ALI_MUX_INPUTS <= mmv->u.mux) {
mmv->u.mux = 0x2;
ac97_reg_cached_write(card->codec, AC97_RECORD_SELECT, 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_reg_cached_read(card->codec, mixer_info->reg);
val = mmv->u.enable ? val | 0x8000 : val & 0x7fff;
ac97_reg_cached_write(card->codec, mixer_info->reg, val);
break;
case ALI_MIX_LEFT_ID:
case ALI_MIX_RIGHT_ID:
val = ac97_reg_cached_read(card->codec, 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_reg_cached_write(card->codec, mixer_info->reg, val);
break;
case ALI_MIX_MIC_BOOST_ID:
val = ac97_reg_cached_read(card->codec, mixer_info->reg);
val = mmv->u.enable ? val | 0x40 : val & 0xffbf;
ac97_reg_cached_write(card->codec, mixer_info->reg, val);
break;
case ALI_MIX_INPUT_SELECTOR_ID:
val = (mmv->u.mux << 8) | mmv->u.mux;
ac97_reg_cached_write(card->codec, AC97_RECORD_SELECT, 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 *info)
{
status_t res;
ali_stream *play_s, *rec_s;
multi_buffer_info buffer_info;
if (user_memcpy(&buffer_info, info, sizeof(multi_buffer_info)) != B_OK)
return B_BAD_ADDRESS;
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);
if (user_memcpy(info, &buffer_info, sizeof(multi_buffer_info)) != B_OK)
return B_BAD_ADDRESS;
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;
}
@@ -1,13 +0,0 @@
/*
* 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
@@ -1,23 +0,0 @@
/*
* 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
@@ -1,559 +0,0 @@
/*
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
*
* All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#include <Drivers.h>
#include <KernelExport.h>
#include <PCI.h>
#include <hmulti_audio.h>
#include <string.h>
#include <unistd.h>
#include "ac97.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->codec = NULL;
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;
status_t err;
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 ((err = (*gPCI->reserve_device)(info.bus, info.device, info.function,
DRIVER_NAME, &gCards[gCardsCount])) < B_OK) {
dprintf("%s: failed to reserve_device(%d, %d, %d,): %s\n",
DRIVER_NAME, info.bus, info.device, info.function,
strerror(err));
continue;
}
if (ali_setup(&gCards[gCardsCount])) {
TRACE("init_driver: setup of ali %ld failed\n", gCardsCount + 1);
(*gPCI->unreserve_device)(info.bus, info.device, info.function,
DRIVER_NAME, &gCards[gCardsCount]);
}
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]);
(*gPCI->unreserve_device)(gCards[i].info.bus, gCards[i].info.device,
gCards[i].info.function, DRIVER_NAME, &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;
}
@@ -1,113 +0,0 @@
/*
* 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
#define DRIVER_NAME "ali5451"
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;
ac97_dev *codec;
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
@@ -1,529 +0,0 @@
/* $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_ */
@@ -1,197 +0,0 @@
/*
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
*
* Original code for memory management is
* Copyright (c) 2003, Jerome Duval ([email protected]).
*
* 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 "ac97.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)
{
// TODO: phy should be phys_addr_t*!
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_32_BIT_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
// TODO: The rest of the code doesn't deal correctly with physical
// addresses > 4 GB, so we have to force 32 bit addresses here.
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 = (void*)(addr_t)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;
}
@@ -1,22 +0,0 @@
/*
* Copyright 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
*
* Original code for memory management is
* Copyright (c) 2003, Jerome Duval ([email protected]).
*
* 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
@@ -0,0 +1,625 @@
/*
* SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
* Copyright (c) 2002, 2008-2011 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
* Copyright for ali5451 support:
* (c) 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
*/
#include "Device.h"
#include <string.h>
#include "Settings.h"
#include "Registers.h"
Device::Device(Device::Info &DeviceInfo, pci_info &PCIInfo)
:
fStatus(B_ERROR),
fPCIInfo(PCIInfo),
fInfo(DeviceInfo),
fIOBase(0),
fHWSpinlock(0),
fInterruptsNest(0),
fBuffersReadySem(-1),
fMixer(this),
fPlaybackStream(this, false),
fRecordStream(this, true)
{
fStatus = _ReserveDeviceOnBus(true);
if (fStatus != B_OK)
return; // InitCheck will handle the rest
uint32 cmdRegister = gPCI->read_pci_config(PCIInfo.bus,
PCIInfo.device, PCIInfo.function, PCI_command, 2);
TRACE("cmdRegister:%#010x\n", cmdRegister);
cmdRegister |= PCI_command_io | PCI_command_memory | PCI_command_master;
gPCI->write_pci_config(PCIInfo.bus, PCIInfo.device,
PCIInfo.function, PCI_command, 2, cmdRegister);
fIOBase = PCIInfo.u.h0.base_registers[0];
TRACE("fIOBase:%#010x\n", fIOBase);
fStatus = B_OK;
}
Device::~Device()
{
fMixer.Free();
_ReserveDeviceOnBus(false);
if (fBuffersReadySem > B_OK) {
delete_sem(fBuffersReadySem);
}
}
void
Device::_ResetCard(uint32 resetMask, uint32 releaseMask)
{
// disable Legacy Control
gPCI->write_pci_config(fPCIInfo.bus, fPCIInfo.device,
fPCIInfo.function, 0x40, 4, 0);
uint32 cmdReg = gPCI->read_pci_config(fPCIInfo.bus, fPCIInfo.device,
fPCIInfo.function, 0x44, 4);
gPCI->write_pci_config(fPCIInfo.bus, fPCIInfo.device,
fPCIInfo.function, 0x44, 4, cmdReg & 0xffff0000);
snooze(100);
// audio engine reset
cmdReg = gPCI->read_pci_config(fPCIInfo.bus, fPCIInfo.device,
fPCIInfo.function, 0x44, 4);
gPCI->write_pci_config(fPCIInfo.bus, fPCIInfo.device,
fPCIInfo.function, 0x44, 4, cmdReg | resetMask);
snooze(100);
// release reset
cmdReg = gPCI->read_pci_config(fPCIInfo.bus, fPCIInfo.device,
fPCIInfo.function, 0x44, 4);
gPCI->write_pci_config(fPCIInfo.bus, fPCIInfo.device,
fPCIInfo.function, 0x44, 4, cmdReg & ~releaseMask);
snooze(100);
}
status_t
Device::Setup()
{
cpu_status cp = 0;
uint32 channelsIndex = ChIndexMidEna | ChIndexEndEna;
switch (HardwareId()) {
case SiS7018:
_ResetCard(0x000c0000, 0x00040000);
cp = Lock();
WritePCI32(RegSiSCodecGPIO, 0x00000000);
WritePCI32(RegSiSCodecStatus, SiSCodecResetOff);
channelsIndex |= ChIndexSiSEnaB;
Unlock(cp);
break;
case ALi5451:
_ResetCard(0x000c0000, 0x00040000);
cp = Lock();
WritePCI32(RegALiDigiMixer, ALiDigiMixerPCMIn);
WritePCI32(RegALiVolumeA, 0);
Unlock(cp);
break;
case TridentNX:
_ResetCard(0x00010000, 0x00010000);
cp = Lock();
WritePCI32(RegNXCodecStatus, NXCodecStatusDAC1ON);
Unlock(cp);
break;
case TridentDX:
_ResetCard(0x00040000, 0x00040000);
cp = Lock();
WritePCI32(RegCodecStatus, CodecStatusDACON);
Unlock(cp);
break;
}
// clear channels status
WritePCI32(RegStopA, 0xffffffff);
WritePCI32(RegStopB, 0xffffffff);
// disable channels interrupt
WritePCI32(RegEnaINTA, 0x00000000);
WritePCI32(RegEnaINTB, 0x00000000);
// enable loop interrupts
WritePCI32(RegChIndex, channelsIndex);
fRecordStream.Init();
fPlaybackStream.Init();
fBuffersReadySem = create_sem(0, DRIVER_NAME "_buffers_ready");
fMixer.Init();
return B_OK;
}
status_t
Device::Open(uint32 flags)
{
TRACE("flags:%x\n", flags);
if (atomic_add(&fInterruptsNest, 1) == 0) {
install_io_interrupt_handler(fPCIInfo.u.h0.interrupt_line,
InterruptHandler, this, 0);
TRACE("Interrupt handler installed at line %d.\n",
fPCIInfo.u.h0.interrupt_line);
}
status_t status = fRecordStream.Start();
if (status != B_OK) {
ERROR("Error of starting record stream:%#010x\n", status);
}
status = fPlaybackStream.Start();
if (status != B_OK) {
ERROR("Error of starting playback stream:%#010x\n", status);
}
return B_OK;
}
status_t
Device::Close()
{
TRACE("closed!\n");
status_t status = fPlaybackStream.Stop();
if (status != B_OK) {
ERROR("Error of stopping playback stream:%#010x\n", status);
}
status = fRecordStream.Stop();
if (status != B_OK) {
ERROR("Error of stopping record stream:%#010x\n", status);
}
if (atomic_add(&fInterruptsNest, -1) == 1) {
remove_io_interrupt_handler(fPCIInfo.u.h0.interrupt_line,
InterruptHandler, this);
TRACE("Interrupt handler at line %d uninstalled.\n",
fPCIInfo.u.h0.interrupt_line);
}
return B_OK;
}
status_t
Device::Free()
{
TRACE("freed\n");
return B_OK;
}
status_t
Device::Read(uint8 *buffer, size_t *numBytes)
{
*numBytes = 0;
return B_IO_ERROR;
}
status_t
Device::Write(const uint8 *buffer, size_t *numBytes)
{
*numBytes = 0;
return B_IO_ERROR;
}
status_t
Device::Control(uint32 op, void *buffer, size_t length)
{
switch (op) {
case B_MULTI_GET_DESCRIPTION:
return _MultiGetDescription((multi_description*)buffer);
case B_MULTI_GET_EVENT_INFO:
TRACE(("B_MULTI_GET_EVENT_INFO\n"));
return B_ERROR;
case B_MULTI_SET_EVENT_INFO:
TRACE(("B_MULTI_SET_EVENT_INFO\n"));
return B_ERROR;
case B_MULTI_GET_EVENT:
TRACE(("B_MULTI_GET_EVENT\n"));
return B_ERROR;
case B_MULTI_GET_ENABLED_CHANNELS:
return _MultiGetEnabledChannels((multi_channel_enable*)buffer);
case B_MULTI_SET_ENABLED_CHANNELS:
return _MultiSetEnabledChannels((multi_channel_enable*)buffer);
case B_MULTI_GET_GLOBAL_FORMAT:
return _MultiGetGlobalFormat((multi_format_info*)buffer);
case B_MULTI_SET_GLOBAL_FORMAT:
return _MultiSetGlobalFormat((multi_format_info*)buffer);
case B_MULTI_GET_CHANNEL_FORMATS:
TRACE(("B_MULTI_GET_CHANNEL_FORMATS\n"));
return B_ERROR;
case B_MULTI_SET_CHANNEL_FORMATS:
TRACE(("B_MULTI_SET_CHANNEL_FORMATS\n"));
return B_ERROR;
case B_MULTI_GET_MIX:
return _MultiGetMix((multi_mix_value_info *)buffer);
case B_MULTI_SET_MIX:
return _MultiSetMix((multi_mix_value_info *)buffer);
case B_MULTI_LIST_MIX_CHANNELS:
TRACE(("B_MULTI_LIST_MIX_CHANNELS\n"));
return B_ERROR;
case B_MULTI_LIST_MIX_CONTROLS:
return _MultiListMixControls((multi_mix_control_info*)buffer);
case B_MULTI_LIST_MIX_CONNECTIONS:
TRACE(("B_MULTI_LIST_MIX_CONNECTIONS\n"));
return B_ERROR;
case B_MULTI_GET_BUFFERS:
return _MultiGetBuffers((multi_buffer_list*)buffer);
case B_MULTI_SET_BUFFERS:
TRACE(("B_MULTI_SET_BUFFERS\n"));
return B_ERROR;
case B_MULTI_SET_START_TIME:
TRACE(("B_MULTI_SET_START_TIME\n"));
return B_ERROR;
case B_MULTI_BUFFER_EXCHANGE:
return _MultiBufferExchange((multi_buffer_info*)buffer);
case B_MULTI_BUFFER_FORCE_STOP:
TRACE(("B_MULTI_BUFFER_FORCE_STOP\n"));
return B_ERROR;
default:
ERROR("Unhandled IOCTL catched: %#010x\n", op);
}
return B_DEV_INVALID_IOCTL;
}
status_t
Device::_MultiGetDescription(multi_description *multiDescription)
{
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 },
};
multi_description Description;
if (user_memcpy(&Description,
multiDescription, sizeof(multi_description)) != B_OK)
return B_BAD_ADDRESS;
Description.interface_version = B_CURRENT_INTERFACE_VERSION;
Description.interface_minimum = B_CURRENT_INTERFACE_VERSION;
strncpy(Description.friendly_name, fInfo.Name(),
sizeof(Description.friendly_name));
strncpy(Description.vendor_info, "Haiku.Inc.",
sizeof(Description.vendor_info));
Description.output_channel_count = 2;
Description.input_channel_count = 2;
Description.output_bus_channel_count = 2;
Description.input_bus_channel_count = 2;
Description.aux_bus_channel_count = 0;
Description.output_rates = fMixer.OutputRates();
Description.input_rates = fMixer.InputRates();
Description.output_formats = fMixer.OutputFormats();
Description.input_formats = fMixer.InputFormats();
Description.min_cvsr_rate = 0;
Description.max_cvsr_rate = 0;
Description.lock_sources = B_MULTI_LOCK_INTERNAL;
Description.timecode_sources = 0;
Description.interface_flags
= B_MULTI_INTERFACE_PLAYBACK | B_MULTI_INTERFACE_RECORD;
Description.start_latency = 3000;
Description.control_panel[0] = '\0';
if (user_memcpy(multiDescription,
&Description, sizeof(multi_description)) != B_OK)
return B_BAD_ADDRESS;
if (Description.request_channel_count
>= (int)(_countof(channel_descriptions))) {
if (user_memcpy(multiDescription->channels,
&channel_descriptions, sizeof(channel_descriptions)) != B_OK)
return B_BAD_ADDRESS;
}
return B_OK;
}
status_t
Device::_MultiGetEnabledChannels(multi_channel_enable *Enable)
{
B_SET_CHANNEL(Enable->enable_bits, 0, true);
B_SET_CHANNEL(Enable->enable_bits, 1, true);
B_SET_CHANNEL(Enable->enable_bits, 2, true);
B_SET_CHANNEL(Enable->enable_bits, 3, true);
Enable->lock_source = B_MULTI_LOCK_INTERNAL;
return B_OK;
}
status_t
Device::_MultiSetEnabledChannels(multi_channel_enable *Enable)
{
TRACE("0:%s\n", B_TEST_CHANNEL(Enable->enable_bits, 0) ? "en" : "dis");
TRACE("1:%s\n", B_TEST_CHANNEL(Enable->enable_bits, 1) ? "en" : "dis");
TRACE("2:%s\n", B_TEST_CHANNEL(Enable->enable_bits, 2) ? "en" : "dis");
TRACE("3:%s\n", B_TEST_CHANNEL(Enable->enable_bits, 3) ? "en" : "dis");
return B_OK;
}
status_t
Device::_MultiGetGlobalFormat(multi_format_info *Format)
{
fPlaybackStream.GetFormat(Format);
fRecordStream.GetFormat(Format);
return B_OK;
}
status_t
Device::_MultiSetGlobalFormat(multi_format_info *Format)
{
status_t status = fPlaybackStream.SetFormat(Format->output,
fMixer.OutputFormats(), fMixer.OutputRates());
if (status != B_OK)
return status;
return fRecordStream.SetFormat(Format->input,
fMixer.InputFormats(), fMixer.InputRates());
}
status_t
Device::_MultiListMixControls(multi_mix_control_info* Info)
{
return fMixer.ListMixControls(Info);
}
status_t
Device::_MultiGetMix(multi_mix_value_info *Info)
{
return fMixer.GetMix(Info);
}
status_t
Device::_MultiSetMix(multi_mix_value_info *Info)
{
return fMixer.SetMix(Info);
}
status_t
Device::_MultiGetBuffers(multi_buffer_list* List)
{
fPlaybackStream.GetBuffers(List->flags, List->return_playback_buffers,
List->return_playback_channels, List->return_playback_buffer_size,
List->playback_buffers);
fRecordStream.GetBuffers(List->flags, List->return_record_buffers,
List->return_record_channels, List->return_record_buffer_size,
List->record_buffers);
return B_OK;
}
status_t
Device::_MultiBufferExchange(multi_buffer_info* bufferInfo)
{
multi_buffer_info BufferInfo;
if (user_memcpy(&BufferInfo, bufferInfo, sizeof(multi_buffer_info)) != B_OK) {
return B_BAD_ADDRESS;
}
status_t status = B_NO_INIT;
if (!fRecordStream.IsActive()) {
status = fRecordStream.Start();
if (status != B_OK) {
ERROR("Error of starting record stream:%#010x\n", status);
return status;
}
}
if (!fPlaybackStream.IsActive()) {
status = fPlaybackStream.Start();
if (status != B_OK) {
ERROR("Error of starting playback stream:%#010x\n", status);
return status;
}
}
status = acquire_sem_etc(fBuffersReadySem, 1,
B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, 50000);
if (status == B_TIMED_OUT) {
ERROR("Timeout during buffers exchange.\n");
}
cpu_status cst = Lock();
fRecordStream.ExchangeBuffers(BufferInfo.recorded_real_time,
BufferInfo.recorded_frames_count, BufferInfo.record_buffer_cycle);
fPlaybackStream.ExchangeBuffers(BufferInfo.played_real_time,
BufferInfo.played_frames_count, BufferInfo.playback_buffer_cycle);
Unlock(cst);
if (user_memcpy(bufferInfo, &BufferInfo, sizeof(multi_buffer_info)) != B_OK) {
return B_BAD_ADDRESS;
}
return B_OK;
}
int32
Device::InterruptHandler(void *interruptParam)
{
Device *device = reinterpret_cast<Device*>(interruptParam);
if (device == 0) {
ERROR("Invalid parameter in the interrupt handler.\n");
return B_HANDLED_INTERRUPT;
}
bool wasHandled = false;
acquire_spinlock(&device->fHWSpinlock);
uint32 mask = device->ReadPCI32(RegMiscINT);
if (mask & 0x00000020) {
wasHandled = device->fRecordStream.InterruptHandler();
wasHandled = device->fPlaybackStream.InterruptHandler() || wasHandled;
}
release_spinlock(&device->fHWSpinlock);
return wasHandled ? B_INVOKE_SCHEDULER : B_UNHANDLED_INTERRUPT;
}
void
Device::SignalReadyBuffers()
{
release_sem_etc(fBuffersReadySem, 1, B_DO_NOT_RESCHEDULE);
}
status_t
Device::_ReserveDeviceOnBus(bool reserve)
{
status_t result = B_NO_INIT;
if (reserve) {
result = gPCI->reserve_device(fPCIInfo.bus, fPCIInfo.device,
fPCIInfo.function, DRIVER_NAME, this);
if (result != B_OK)
ERROR("Unable to reserve PCI device %d:%d:%d on bus:%#010x\n",
fPCIInfo.bus, fPCIInfo.device, fPCIInfo.function, result);
} else {
result = gPCI->unreserve_device(fPCIInfo.bus, fPCIInfo.device,
fPCIInfo.function, DRIVER_NAME, this);
}
return result;
}
uint8
Device::ReadPCI8(int offset)
{
return gPCI->read_io_8(fIOBase + offset);
}
uint16
Device::ReadPCI16(int offset)
{
return gPCI->read_io_16(fIOBase + offset);
}
uint32
Device::ReadPCI32(int offset)
{
return gPCI->read_io_32(fIOBase + offset);
}
void
Device::WritePCI8(int offset, uint8 value)
{
gPCI->write_io_8(fIOBase + offset, value);
}
void
Device::WritePCI16(int offset, uint16 value)
{
gPCI->write_io_16(fIOBase + offset, value);
}
void
Device::WritePCI32(int offset, uint32 value)
{
gPCI->write_io_32(fIOBase + offset, value);
}
cpu_status
Device::Lock()
{
cpu_status st = disable_interrupts();
acquire_spinlock(&fHWSpinlock);
return st;
}
void
Device::Unlock(cpu_status st)
{
release_spinlock(&fHWSpinlock);
restore_interrupts(st);
}
@@ -0,0 +1,99 @@
/*
* SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
* Copyright (c) 2002, 2008-2011 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _SiS7018_DEVICE_H_
#define _SiS7018_DEVICE_H_
#include <KernelExport.h>
#include "Driver.h"
#include "Mixer.h"
#include "Registers.h"
#include "Settings.h"
#include "Stream.h"
// card ids for supported audio hardware
const uint32 SiS7018 = 0x70181039;
const uint32 ALi5451 = 0x545110b9;
const uint32 TridentDX = 0x20001023;
const uint32 TridentNX = 0x20011023;
class Device {
public:
class Info {
public:
const uint32 fId;
const char* fName;
inline const char* Name() { return fName; }
inline uint16 DeviceId() { return (fId >> 16) & 0xffff; }
inline uint16 VendorId() { return (fId) & 0xffff; }
inline uint32 Id() { return fId; }
};
uint32 HardwareId() { return fInfo.Id(); }
pci_info& PCIInfo() { return fPCIInfo; }
Device(Info &DeviceInfo, pci_info &PCIInfo);
~Device();
status_t Setup();
status_t InitCheck() { return fStatus; };
status_t Open(uint32 flags);
status_t Read(uint8 *buffer, size_t *numBytes);
status_t Write(const uint8 *buffer, size_t *numBytes);
status_t Control(uint32 op, void *buffer, size_t length);
status_t Close();
status_t Free();
static int32 InterruptHandler(void *interruptParam);
void SignalReadyBuffers();
cpu_status Lock();
void Unlock(cpu_status st);
uint8 ReadPCI8(int offset);
uint16 ReadPCI16(int offset);
uint32 ReadPCI32(int offset);
void WritePCI8(int offset, uint8 value);
void WritePCI16(int offset, uint16 value);
void WritePCI32(int offset, uint32 value);
private:
status_t _ReserveDeviceOnBus(bool reserve);
void _ResetCard(uint32 resetMask, uint32 releaseMask);
status_t _MultiGetDescription(multi_description *Description);
status_t _MultiGetEnabledChannels(multi_channel_enable *Enable);
status_t _MultiSetEnabledChannels(multi_channel_enable *Enable);
status_t _MultiGetBuffers(multi_buffer_list* List);
status_t _MultiGetGlobalFormat(multi_format_info *Format);
status_t _MultiSetGlobalFormat(multi_format_info *Format);
status_t _MultiGetMix(multi_mix_value_info *Info);
status_t _MultiSetMix(multi_mix_value_info *Info);
status_t _MultiListMixControls(multi_mix_control_info* Info);
status_t _MultiBufferExchange(multi_buffer_info* Info);
status_t fStatus;
pci_info fPCIInfo;
Info& fInfo;
int fIOBase;
int32 fHWSpinlock;
int32 fInterruptsNest;
sem_id fBuffersReadySem;
Mixer fMixer;
Stream fPlaybackStream;
Stream fRecordStream;
};
#endif // _SiS7018_DEVICE_H_
@@ -0,0 +1,227 @@
/*
* SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
* Copyright (c) 2002, 2008-2011 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#include "Driver.h"
#include <kernel_cpp.h>
#include <string.h>
#include "Device.h"
#include "Settings.h"
int32 api_version = B_CUR_DRIVER_API_VERSION;
size_t gNumCards = 0;
Device *gDevices[MAX_DEVICES] = { 0 };
char *gDeviceNames[MAX_DEVICES + 1] = { 0 };
pci_module_info *gPCI = NULL;
static Device::Info cardInfos[] = {
{ SiS7018, "SiS 7018" },
{ ALi5451, "ALi M5451" },
{ TridentDX, "Trident DX" },
{ TridentNX, "Trident NX" }
};
status_t
init_hardware()
{
dprintf("sis7018:init_hardware:ver:%s\n", kVersion);
status_t result = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCI);
if (result < B_OK) {
return ENOSYS;
}
pci_info info = {0};
for (long i = 0; B_OK == (*gPCI->get_nth_pci_info)(i, &info); i++) {
for (size_t idx = 0; idx < _countof(cardInfos); idx++) {
if (info.vendor_id == cardInfos[idx].VendorId() &&
info.device_id == cardInfos[idx].DeviceId())
{
put_module(B_PCI_MODULE_NAME);
return B_OK;
}
}
}
put_module(B_PCI_MODULE_NAME);
return ENODEV;
}
status_t
init_driver()
{
status_t status = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCI);
if (status < B_OK) {
return ENOSYS;
}
load_settings();
pci_info info = { 0 };
for (long i = 0; B_OK == (*gPCI->get_nth_pci_info)(i, &info); i++) {
for (size_t idx = 0; idx < _countof(cardInfos); idx++) {
if (info.vendor_id == cardInfos[idx].VendorId() &&
info.device_id == cardInfos[idx].DeviceId())
{
if (gNumCards == MAX_DEVICES) {
ERROR("Skipped:%s [%#06x:%#06x]\n", cardInfos[idx].Name(),
cardInfos[idx].VendorId(), cardInfos[idx].DeviceId());
break;
}
Device* device = new Device(cardInfos[idx], info);
if (device == 0) {
return ENODEV;
}
status_t status = device->InitCheck();
if (status < B_OK) {
delete device;
break;
}
status = device->Setup();
if (status < B_OK) {
delete device;
break;
}
char name[32] = {0};
sprintf(name, "audio/hmulti/%s/%ld",
cardInfos[idx].Name(), gNumCards);
gDeviceNames[gNumCards] = strdup(name);
gDevices[gNumCards++] = device;
TRACE("Found:%s [%#06x:%#06x]\n", cardInfos[idx].Name(),
cardInfos[idx].VendorId(), cardInfos[idx].DeviceId());
}
}
}
if (gNumCards == 0) {
put_module(B_PCI_MODULE_NAME);
return ENODEV;
}
return B_OK;
}
void
uninit_driver()
{
for (size_t i = 0; i < MAX_DEVICES; i++) {
if (gDevices[i]) {
delete gDevices[i];
gDevices[i] = NULL;
}
free(gDeviceNames[i]);
gDeviceNames[i] = NULL;
}
put_module(B_PCI_MODULE_NAME);
release_settings();
}
static status_t
SiS7018_open(const char *name, uint32 flags, void **cookie)
{
status_t status = ENODEV;
*cookie = NULL;
for (size_t i = 0; i < MAX_DEVICES; i++) {
if (gDeviceNames[i] && !strcmp(gDeviceNames[i], name)) {
status = gDevices[i]->Open(flags);
*cookie = gDevices[i];
}
}
return status;
}
static status_t
SiS7018_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
{
Device *device = (Device *)cookie;
return device->Read((uint8 *)buffer, numBytes);
}
static status_t
SiS7018_write(void *cookie, off_t position,
const void *buffer, size_t *numBytes)
{
Device *device = (Device *)cookie;
return device->Write((const uint8 *)buffer, numBytes);
}
static status_t
SiS7018_control(void *cookie, uint32 op, void *buffer, size_t length)
{
Device *device = (Device *)cookie;
return device->Control(op, buffer, length);
}
static status_t
SiS7018_close(void *cookie)
{
Device *device = (Device *)cookie;
return device->Close();
}
static status_t
SiS7018_free(void *cookie)
{
Device *device = (Device *)cookie;
return device->Free();
}
const char **
publish_devices()
{
for (size_t i = 0; i < MAX_DEVICES; i++) {
if (gDevices[i] == NULL)
continue;
if (gDeviceNames[i])
TRACE("%s\n", gDeviceNames[i]);
}
return (const char **)&gDeviceNames[0];
}
device_hooks *
find_device(const char *name)
{
static device_hooks deviceHooks = {
SiS7018_open,
SiS7018_close,
SiS7018_free,
SiS7018_control,
SiS7018_read,
SiS7018_write,
NULL, // select
NULL // deselect
};
return &deviceHooks;
}
@@ -0,0 +1,35 @@
/*
* SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
* Copyright (c) 2002, 2008-2011 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _SiS7018_DRIVER_H_
#define _SiS7018_DRIVER_H_
#include <Drivers.h>
#include <PCI.h>
#define DRIVER_NAME "sis7018"
#define MAX_DEVICES 3
const char* const kVersion = "ver.2.0.0";
extern pci_module_info *gPCI;
extern "C" {
status_t init_hardware();
status_t init_driver();
void uninit_driver();
const char** publish_devices();
device_hooks* find_device(const char* name);
}
#endif // _SiS7018_DRIVER_H_
@@ -0,0 +1,18 @@
SubDir HAIKU_TOP src add-ons kernel drivers audio ac97 sis7018 ;
UsePrivateHeaders kernel media ;
UsePrivateHeaders [ FDirName kernel util ] ;
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
SEARCH_SOURCE += [ FDirName $(SUBDIR) $(DOTDOT) ] ;
KernelAddon sis7018 :
Driver.cpp
Device.cpp
Mixer.cpp
Settings.cpp
Stream.cpp
ac97.c
;
@@ -0,0 +1,685 @@
/*
* SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
* Copyright (c) 2002, 2008-2011 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
* Copyright for ali5451 support:
* (c) 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
*/
#include "Mixer.h"
#include <string.h>
#include "Device.h"
#include "Registers.h"
#include "Settings.h"
Mixer::Mixer(Device *device)
:
fDevice(device),
fAC97Dev(NULL),
fReadPort(RegCodecRead),
fWritePort(RegCodecWrite),
fMaskRW(1 << 15),
fMaskRD(1 << 15),
fMaskWD(1 << 15),
fInputRates(0),
fOutputRates(0),
fInputFormats(0),
fOutputFormats(0)
{
switch (device->HardwareId()) {
case ALi5451:
if (fDevice->PCIInfo().revision > 0x01) {
fReadPort = RegCodecWrite;
fMaskWD = 1 << 8;
}
case SiS7018:
break;
case TridentDX:
break;
case TridentNX:
fReadPort = RegNXCodecRead;
fWritePort = RegNXCodecWrite;
fMaskRW = 1 << 11;
fMaskRD = 1 << 10;
fMaskWD = 1 << 11;
break;
}
TRACE("Regs:R:%#x;W:%#x;MRW:%#x;MRD:%#x;MWD:%#x\n",
fReadPort, fWritePort, fMaskRW, fMaskRD, fMaskWD);
}
void
Mixer::Init()
{
ac97_attach(&fAC97Dev, _ReadAC97, _WriteAC97, this,
fDevice->PCIInfo().u.h0.subsystem_vendor_id,
fDevice->PCIInfo().u.h0.subsystem_id);
_ReadSupportedFormats();
}
void
Mixer::_ReadSupportedFormats()
{
struct _Cap {
ac97_capability fCap;
uint32 fRate;
} caps[] = {
{ CAP_PCM_RATE_8000, B_SR_8000 },
{ CAP_PCM_RATE_11025, B_SR_11025 },
{ CAP_PCM_RATE_12000, B_SR_12000 },
{ CAP_PCM_RATE_16000, B_SR_16000 },
{ CAP_PCM_RATE_22050, B_SR_22050 },
{ CAP_PCM_RATE_24000, B_SR_24000 },
{ CAP_PCM_RATE_32000, B_SR_32000 },
{ CAP_PCM_RATE_44100, B_SR_44100 },
{ CAP_PCM_RATE_48000, B_SR_48000 }
};
for (size_t i = 0; i < _countof(caps); i++) {
if (ac97_has_capability(fAC97Dev, caps[i].fCap))
fOutputRates |= caps[i].fRate;
}
fInputRates = B_SR_48000;
fInputFormats = B_FMT_16BIT;
fOutputFormats = B_FMT_16BIT;
}
void
Mixer::Free()
{
ac97_detach(fAC97Dev);
}
uint16
Mixer::_ReadAC97(void* cookie, uint8 reg)
{
return reinterpret_cast<Mixer*>(cookie)->_ReadAC97(reg);
}
void
Mixer::_WriteAC97(void* cookie, uint8 reg, uint16 data)
{
return reinterpret_cast<Mixer*>(cookie)->_WriteAC97(reg, data);
}
bool
Mixer::_WaitPortReady(uint8 reg, uint32 mask, uint32* result)
{
int count = 200;
uint32 data = 0;
while (count--) {
data = fDevice->ReadPCI32(reg);
if ((data & mask) == 0) {
if (result != NULL)
*result = data;
return true;
}
spin(1);
}
ERROR("AC97 register %#04x is not ready.\n", reg);
return false;
}
bool
Mixer::_WaitSTimerReady()
{
if (fDevice->HardwareId() != ALi5451)
return true;
int count = 200;
uint32 time1 = fDevice->ReadPCI32(RegALiSTimer);
while (count--) {
uint32 time2 = fDevice->ReadPCI32(RegALiSTimer);
if (time2 != time1)
return true;
spin(1);
}
ERROR("AC97 STimer is not ready.\n");
return false;
}
uint16
Mixer::_ReadAC97(uint8 reg)
{
uint32 result = 0;
cpu_status cp = fDevice->Lock();
if (_WaitPortReady(fWritePort, fMaskRD)
&& _WaitPortReady(fReadPort, fMaskRD) && _WaitSTimerReady()) {
fDevice->WritePCI32(fReadPort, (reg & 0x7f) | fMaskRW);
if (_WaitSTimerReady() && _WaitPortReady(fReadPort, fMaskRD, &result))
result = (result >> 16) & 0xffff;
}
fDevice->Unlock(cp);
return result;
}
void
Mixer::_WriteAC97(uint8 reg, uint16 data)
{
cpu_status cp = fDevice->Lock();
if (_WaitPortReady(fWritePort, fMaskRW) && _WaitSTimerReady()) {
fDevice->WritePCI32(fWritePort,
(data << 16) | (reg & 0x7f) | fMaskRW | fMaskWD);
}
fDevice->Unlock(cp);
}
// Control ids are encoded in the following way:
// GGBBRRTT --
// GG - in gain controls: 10th of granularity. Signed!
// - in MUX controls: value of selector.
// BB - in gain controls: base level - correspond to 0 db.
// - mute, boost, enable controls: offset of "on/off" bit
// RR - AC97 Register handled by this control
// TT - MIXControlTypes bits
const int stepShift = 24; // offset to GG
const int baseShift = 16; // offset to BB
const int regShift = 8; // offset to RR
enum MIXControlTypes {
MIX_RGain = 0x01,
MIX_LGain = 0x10,
MIX_Mono = MIX_RGain,
MIX_Stereo = MIX_LGain | MIX_RGain,
MIX_Mute = 0x04,
MIX_Boost = 0x08,
MIX_MUX = 0x20,
MIX_Enable = 0x40,
MIX_Check = MIX_Mute | MIX_Boost | MIX_Enable
};
struct GainInfo {
uint8 fOff; // offset of mask in register word
uint8 fBase; // base - default value of register
uint8 fMask; // mask - maximal value of register
float fMult; // multiplier - bits to dB recalculate
uint8 fEnaOff; // offset of "on/off" bit in register word
};
GainInfo MixGain = { 0x00, 0x00, 0x3f, -1.5, 0x0f };
GainInfo InGain = { 0x00, 0x08, 0x1f, -1.5, 0x0f };
GainInfo RecGain = { 0x00, 0x00, 0x0f, 1.5, 0x0f };
GainInfo BeepGain = { 0x01, 0x00, 0x1e, 3.0, 0x0f };
GainInfo ToneGain = { 0x00, 0x07, 0x0f, -1.5, 0x10 }; // 0x10 - mean no "mute"
GainInfo D3DGain = { 0x00, 0x00, 0x0f, 1.0, 0x10 };
struct MIXControlInfo {
uint8 fAC97Reg;
GainInfo* fInfo;
strind_id fNameId;
uint16 fType;
const char* fName;
uint8 fExAC97Reg;
strind_id fExNameId;
const char* fExName;
uint8 fExOff;
};
MIXControlInfo OutputControls[] = {
{ AC97_MASTER_VOLUME, &MixGain, S_MASTER,
MIX_Stereo | MIX_Mute, NULL },
{ AC97_AUX_OUT_VOLUME, &MixGain, S_AUX,
MIX_Stereo | MIX_Mute, NULL },
{ AC97_MASTER_TONE, &ToneGain, S_OUTPUT_BASS,
MIX_LGain, NULL },
{ AC97_MASTER_TONE, &ToneGain, S_OUTPUT_TREBLE,
MIX_RGain, NULL },
{ AC97_3D_CONTROL, &D3DGain, S_OUTPUT_3D_DEPTH,
MIX_RGain | MIX_Enable, NULL,
AC97_GENERAL_PURPOSE, S_ENABLE, NULL, 0x0d },
{ AC97_3D_CONTROL, &D3DGain, S_OUTPUT_3D_CENTER,
MIX_LGain, NULL },
{ AC97_MONO_VOLUME, &MixGain, S_MONO_MIX,
MIX_Mono | MIX_Mute, NULL },
{ AC97_PC_BEEP_VOLUME, &BeepGain, S_BEEP,
MIX_Mono | MIX_Mute, NULL }
};
MIXControlInfo InputControls[] = {
{ AC97_PCM_OUT_VOLUME, &InGain, S_WAVE,
MIX_Stereo | MIX_Mute, NULL },
{ AC97_MIC_VOLUME, &InGain, S_MIC,
MIX_Mono | MIX_Mute | MIX_Boost, NULL,
AC97_MIC_VOLUME, S_null, "+ 20 dB", 0x06 },
{ AC97_LINE_IN_VOLUME, &InGain, S_LINE,
MIX_Stereo | MIX_Mute, NULL },
{ AC97_CD_VOLUME, &InGain, S_CD,
MIX_Stereo | MIX_Mute, NULL },
{ AC97_VIDEO_VOLUME, &InGain, S_VIDEO,
MIX_Stereo | MIX_Mute, NULL },
{ AC97_AUX_IN_VOLUME, &InGain, S_AUX,
MIX_Stereo | MIX_Mute, NULL },
{ AC97_PHONE_VOLUME, &InGain, S_PHONE,
MIX_Mono | MIX_Mute, NULL }
};
strind_id RecordSources[] = {
S_MIC,
S_CD,
S_VIDEO,
S_AUX,
S_LINE,
S_STEREO_MIX,
S_MONO_MIX,
S_PHONE
};
MIXControlInfo RecordControls[] = {
{ AC97_RECORD_GAIN, &RecGain, S_null,
MIX_Stereo | MIX_Mute | MIX_MUX, "Record Gain",
AC97_RECORD_SELECT, S_null, "Source" },
{ AC97_RECORD_GAIN_MIC, &RecGain, S_MIC,
MIX_Mono | MIX_Mute, NULL }
};
void
Mixer::_InitGainLimits(multi_mix_control& Control, GainInfo& Info)
{
float base = Info.fBase >> Info.fOff;
float max = Info.fMask >> Info.fOff;
float min_gain = Info.fMult * (0.0 - base);
float max_gain = Info.fMult * (max - base);
Control.gain.min_gain = (Info.fMult > 0) ? min_gain : max_gain;
Control.gain.max_gain = (Info.fMult > 0) ? max_gain : min_gain;
Control.gain.granularity = Info.fMult;
// encode gain granularity in the MSB of the control id.
uint8 gran = (uint8)(Info.fMult * 10.);
Control.id |= (gran << stepShift);
Control.id |= (Info.fBase << baseShift);
TRACE("base:%#04x; mask:%#04x; mult:%f\n",
Info.fBase, Info.fMask, Info.fMult);
TRACE(" min:%f; max:%f; gran:%f -> %#010x\n",
Control.gain.min_gain, Control.gain.max_gain,
Control.gain.granularity, Control.id);
}
bool
Mixer::_CheckRegFeatures(uint8 AC97Reg, uint16& mask, uint16& result)
{
uint16 backup = ac97_reg_cached_read(fAC97Dev, AC97Reg);
ac97_reg_cached_write(fAC97Dev, AC97Reg, mask);
result = ac97_reg_cached_read(fAC97Dev, AC97Reg);
TRACE("Check for register %#02x: %#x -> %#x.\n", AC97Reg, mask, result);
ac97_reg_cached_write(fAC97Dev, AC97Reg, backup);
return mask != result;
}
bool
Mixer::_CorrectMIXControlInfo(MIXControlInfo& Info, GainInfo& gainInfo)
{
uint16 newMask = gainInfo.fMask;
uint16 testMask = 0;
uint16 testResult = 0;
switch (Info.fAC97Reg) {
case AC97_AUX_OUT_VOLUME:
if (ac97_has_capability(fAC97Dev, CAP_HEADPHONE_OUT)) {
Info.fNameId = S_HEADPHONE;
Info.fName = NULL;
}
case AC97_MASTER_VOLUME:
testMask = 0x2020;
newMask = 0x1f;
break;
case AC97_MASTER_TONE:
if (!ac97_has_capability(fAC97Dev, CAP_BASS_TREBLE_CTRL))
return false;
testMask = 0x0f0f;
break;
case AC97_3D_CONTROL:
if (!ac97_has_capability(fAC97Dev, CAP_3D_ENHANCEMENT))
return false;
testMask = 0x0f0f;
break;
case AC97_RECORD_GAIN_MIC:
if (!ac97_has_capability(fAC97Dev, CAP_PCM_MIC))
return false;
testMask = 0x0f;
break;
case AC97_MONO_VOLUME:
testMask = 0x20;
newMask = 0x1f;
break;
case AC97_PC_BEEP_VOLUME:
testMask = 0x1e;
break;
case AC97_VIDEO_VOLUME:
case AC97_AUX_IN_VOLUME:
case AC97_PHONE_VOLUME:
testMask = 0x1f;
break;
default:
return true;
}
if (_CheckRegFeatures(Info.fAC97Reg, testMask, testResult)) {
if (testResult == 0)
return false;
gainInfo.fMask = newMask;
}
return true;
}
int32
Mixer::_CreateMIXControlGroup(multi_mix_control_info* MultiInfo, int32& index,
int32 parentIndex, MIXControlInfo& Info)
{
// check the optional registers and features,
// correct the range if required and do not add if not supported
GainInfo gainInfo = *Info.fInfo;
if (!_CorrectMIXControlInfo(Info, gainInfo))
return 0;
int32 IdReg = Info.fAC97Reg << regShift;
multi_mix_control* Controls = MultiInfo->controls;
int32 groupIndex
= Controls[index].id = IdReg | Info.fType;
Controls[index].flags = B_MULTI_MIX_GROUP;
Controls[index].parent = parentIndex;
Controls[index].string = Info.fNameId;
if (Info.fName != NULL)
strncpy(Controls[index].name, Info.fName, sizeof(Controls[index].name));
index++;
if (Info.fType & MIX_Mute) {
Controls[index].id = IdReg | MIX_Check;
Controls[index].id |= Info.fInfo->fEnaOff << baseShift;
Controls[index].flags = B_MULTI_MIX_ENABLE;
Controls[index].parent = groupIndex;
Controls[index].string = S_MUTE;
TRACE("Mute:%#010x\n", Controls[index].id);
index++;
}
if (Info.fType & MIX_Enable) {
int32 IdExReg = Info.fExAC97Reg << regShift;
Controls[index].id = IdExReg | MIX_Check;
Controls[index].id |= (Info.fExOff << baseShift);
Controls[index].flags = B_MULTI_MIX_ENABLE;
Controls[index].parent = groupIndex;
Controls[index].string = Info.fExNameId;
if (Info.fExName != NULL)
strncpy(Controls[index].name, Info.fExName,
sizeof(Controls[index].name));
TRACE("Enable:%#010x\n", Controls[index].id);
index++;
}
int32 gainIndex = 0;
if (Info.fType & MIX_LGain) {
Controls[index].id = IdReg | MIX_LGain;
Controls[index].flags = B_MULTI_MIX_GAIN;
Controls[index].parent = groupIndex;
Controls[index].string = S_GAIN;
_InitGainLimits(Controls[index], gainInfo);
gainIndex = Controls[index].id;
index++;
}
if (Info.fType & MIX_RGain) {
Controls[index].id = IdReg | MIX_RGain;
Controls[index].flags = B_MULTI_MIX_GAIN;
Controls[index].parent = groupIndex;
Controls[index].master = gainIndex;
Controls[index].string = S_GAIN;
_InitGainLimits(Controls[index], gainInfo);
index++;
}
if (Info.fType & MIX_Boost) {
Controls[index].id = IdReg | MIX_Check;
Controls[index].id |= (Info.fExOff << baseShift);
Controls[index].flags = B_MULTI_MIX_ENABLE;
Controls[index].parent = groupIndex;
Controls[index].string = Info.fExNameId;
if (Info.fExName != NULL)
strncpy(Controls[index].name, Info.fExName,
sizeof(Controls[index].name));
TRACE("Boost:%#010x\n", Controls[index].id);
index++;
}
if (Info.fType & MIX_MUX) {
int32 IdMUXReg = Info.fExAC97Reg << regShift;
int32 recordMUX
= Controls[index].id = IdMUXReg | MIX_MUX;
Controls[index].flags = B_MULTI_MIX_MUX;
Controls[index].parent = groupIndex;
Controls[index].string = S_null;
strncpy(Controls[index].name, Info.fExName, sizeof(Controls[index].name));
TRACE("MUX:%#010x\n", Controls[index].id);
index++;
for (size_t i = 0; i < _countof(RecordSources); i++) {
Controls[index].id = IdMUXReg | (i << stepShift) | MIX_MUX;
Controls[index].flags = B_MULTI_MIX_MUX_VALUE;
Controls[index].master = 0;
Controls[index].string = RecordSources[i];
Controls[index].parent = recordMUX;
TRACE("MUX Item:%#010x\n", Controls[index].id);
index++;
}
}
return groupIndex;
}
status_t
Mixer::ListMixControls(multi_mix_control_info* Info)
{
int32 index = 0;
multi_mix_control* Controls = Info->controls;
int32 mixerGroup
= Controls[index].id = 0x8000; // 0x80 - is not a valid AC97 register
Controls[index].flags = B_MULTI_MIX_GROUP;
Controls[index].parent = 0;
Controls[index].string = S_OUTPUT;
index++;
for (size_t i = 0; i < _countof(OutputControls); i++) {
_CreateMIXControlGroup(Info, index, mixerGroup, OutputControls[i]);
}
int32 inputGroup
= Controls[index].id = 0x8100;
Controls[index].flags = B_MULTI_MIX_GROUP;
Controls[index].parent = 0;
Controls[index].string = S_INPUT;
index++;
for (size_t i = 0; i < _countof(InputControls); i++) {
_CreateMIXControlGroup(Info, index, inputGroup, InputControls[i]);
}
int32 recordGroup
= Controls[index].id = 0x8200;
Controls[index].flags = B_MULTI_MIX_GROUP;
Controls[index].parent = 0;
Controls[index].string = S_null;
strncpy(Controls[index].name, "Record", sizeof(Controls[index].name));
index++;
for (size_t i = 0; i < _countof(RecordControls); i++) {
_CreateMIXControlGroup(Info, index, recordGroup, RecordControls[i]);
}
return B_OK;
}
status_t
Mixer::GetMix(multi_mix_value_info *Info)
{
for (int32 i = 0; i < Info->item_count; i++) {
int32 Id= Info->values[i].id;
uint8 Reg = (Id >> regShift) & 0xFF;
if ((Reg & 0x01) || Reg > 0x7e) {
ERROR("Invalid AC97 register:%#04x.Bypass it.\n", Reg);
continue;
}
uint16 RegValue = ac97_reg_cached_read(fAC97Dev, Reg);
if ((Id & MIX_Check) == MIX_Check) {
uint16 mask = 1 << ((Id >> baseShift) & 0xff);
Info->values[i].enable = (RegValue & mask) == mask;
TRACE("%#04x Mute|Enable|Boost:%d [data:%#04x]\n",
Reg, Info->values[i].enable, RegValue);
continue;
}
if ((Id & MIX_MUX) == MIX_MUX) {
Info->values[i].mux = (RegValue | (RegValue >> 8)) & 0x7;
TRACE("%#04x MUX:%d [data:%#04x]\n",
Reg, Info->values[i].mux, RegValue);
continue;
}
float mult = 0.1 * (int8)(Id >> stepShift);
float base = mult * ((Id >> baseShift) & 0xff);
if ((Id & MIX_RGain) == MIX_RGain) {
uint8 gain = RegValue & 0x3f;
Info->values[i].gain = mult * gain - base;
TRACE("%#04x for RGain:%f [mult:%f base:%f] <- %#04x\n",
Reg, Info->values[i].gain, mult, base, gain);
continue;
}
if ((Id & MIX_LGain) == MIX_LGain) {
uint8 gain = (RegValue >> 8) & 0x3f;
Info->values[i].gain = mult * gain - base;
TRACE("%#04x for LGain:%f [mult:%f base:%f] <- %#04x\n",
Reg, Info->values[i].gain, mult, base, gain);
}
}
return B_OK;
}
status_t
Mixer::SetMix(multi_mix_value_info *Info)
{
for (int32 i = 0; i < Info->item_count; i++) {
int32 Id = Info->values[i].id;
uint8 Reg = (Id >> regShift) & 0xFF;
if ((Reg & 0x01) || Reg > 0x7e) {
ERROR("Invalid AC97 register:%#04x.Bypass it.\n", Reg);
continue;
}
uint16 RegValue = ac97_reg_cached_read(fAC97Dev, Reg);
if ((Id & MIX_Check) == MIX_Check) {
uint16 mask = 1 << ((Id >> baseShift) & 0xff);
if (Info->values[i].enable)
RegValue |= mask;
else
RegValue &= ~mask;
TRACE("%#04x Mute/Enable:%d -> data:%#04x\n",
Reg, Info->values[i].enable, RegValue);
}
if ((Id & MIX_MUX) == MIX_MUX) {
uint8 mux = Info->values[i].mux & 0x7;
RegValue = mux | (mux << 8);
TRACE("%#04x MUX:%d -> data:%#04x\n",
Reg, Info->values[i].mux, RegValue);
}
float mult = 0.1 * (int8)(Id >> stepShift);
float base = mult * ((Id >> baseShift) & 0xff);
float gain = (Info->values[i].gain + base) / mult;
gain += (gain > 0.) ? 0.5 : -0.5;
uint8 gainValue = (uint8)gain;
if ((Id & MIX_RGain) == MIX_RGain) {
RegValue &= 0xffc0;
RegValue |= gainValue;
TRACE("%#04x for RGain:%f [mult:%f base:%f] -> %#04x\n",
Reg, Info->values[i].gain, mult, base, gainValue);
}
if ((Id & MIX_LGain) == MIX_LGain) {
RegValue &= 0xc0ff;
RegValue |= (gainValue << 8);
TRACE("%#04x for LGain:%f [mult:%f base:%f] -> %#04x\n",
Reg, Info->values[i].gain, mult, base, gainValue);
}
TRACE("%#04x Write:%#06x\n", Reg, RegValue);
ac97_reg_cached_write(fAC97Dev, Reg, RegValue);
}
return B_OK;
}
@@ -0,0 +1,68 @@
/*
* SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
* Copyright (c) 2002, 2008-2011 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _SiS7018_MIXER_H_
#define _SiS7018_MIXER_H_
#include <OS.h>
#include "ac97.h"
#include "hmulti_audio.h"
class Device;
struct GainInfo;
struct MIXControlInfo;
class Mixer {
public:
Mixer(Device *cdc);
void Init();
void Free();
status_t GetMix(multi_mix_value_info *Info);
status_t SetMix(multi_mix_value_info *Info);
status_t ListMixControls(multi_mix_control_info* Info);
uint32 InputRates() { return fInputRates; }
uint32 OutputRates() { return fOutputRates; }
uint32 InputFormats() { return fInputFormats; }
uint32 OutputFormats() { return fOutputFormats; }
private:
void _ReadSupportedFormats();
bool _WaitPortReady(uint8 reg, uint32 mask, uint32* result = NULL);
bool _WaitSTimerReady();
uint16 _ReadAC97(uint8 reg);
void _WriteAC97(uint8 reg, uint16 date);
static uint16 _ReadAC97(void* cookie, uint8 reg);
static void _WriteAC97(void* cookie, uint8 reg, uint16 data);
bool _CheckRegFeatures(uint8 AC97Reg, uint16& mask, uint16& result);
bool _CorrectMIXControlInfo(MIXControlInfo& info, GainInfo& gainInfo);
void _InitGainLimits(multi_mix_control& Control, GainInfo& Info);
int32 _CreateMIXControlGroup(multi_mix_control_info* MultiInfo,
int32& index, int32 parentIndex, MIXControlInfo& Info);
Device* fDevice;
ac97_dev* fAC97Dev;
uint8 fReadPort;
uint8 fWritePort;
uint32 fMaskRW;
uint32 fMaskRD;
uint32 fMaskWD;
uint32 fInputRates;
uint32 fOutputRates;
uint32 fInputFormats;
uint32 fOutputFormats;
};
#endif // _SiS7018_MIXER_H_
@@ -0,0 +1,82 @@
/*
* SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
* Copyright (c) 2002, 2008-2011 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
* Copyright for ali5451 support:
* (c) 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
*/
#ifndef _SiS7018_REGISTERS_H_
#define _SiS7018_REGISTERS_H_
enum Registers {
// shared registers
RegCodecWrite = 0x40,
RegCodecRead = 0x44,
RegCodecStatus = 0x48,
CodecStatusSBCtrl = 0x40,
CodecStatusActive = 0x20,
CodecStatusReady = 0x10,
CodecStatusADCON = 0x08,
CodecStatusDACON = 0x02,
CodecStatusReset = 0x01,
CodecTimeout = 0xffff,
RegRecChannel = 0x70,
RegStartA = 0x80,
RegStopA = 0x84,
RegCurSPFA = 0x90,
RegAddrINTA = 0x98,
RegChIndex = 0xa0,
ChIndexMask = 0x0000003f,
ChIndexEndEna = 0x00001000,
ChIndexMidEna = 0x00002000,
RegEnaINTA = 0xa4,
RegMiscINT = 0xb0,
RegStartB = 0xb4,
RegStopB = 0xb8,
RegCurSPFB = 0xbc,
RegAddrINTB = 0xd8,
RegEnaINTB = 0xdc,
RegCSOAlphaFMS = 0xe0,
RegLBA_PPTR = 0xe4,
RegDeltaESO = 0xe8,
RegRVolCVolFMC = 0xec,
RegGVSelVolCtrl = 0xf0,
RegEBuf1 = 0xf4,
RegEBuf2 = 0xf8,
// SiS7018 specific registers
RegSiSCodecWrite = RegCodecWrite,
RegSiSCodecRead = RegCodecRead,
RegSiSCodecStatus = RegCodecStatus,
SiSCodecResetOff = 0x000f0000,
RegSiSCodecGPIO = 0x4c,
ChIndexSiSEnaB = 0x00010000,
// ALi5451 registers
RegALiVolumeA = 0xa8,
RegALiMPUR2 = 0x22,
RegALiSTimer = 0xc8,
RegALiDigiMixer = 0xd4,
ALiDigiMixerPCMIn = 0x80000000,
// Trident NX specific registers
RegNXCodecStatus = 0x40,
NXCodecStatusReady2 = 0x40,
NXCodecStatusADC2ON = 0x20,
NXCodecStatusDAC2ON = 0x10,
NXCodecStatusReady1 = 0x08,
NXCodecStatusADC1ON = 0x04,
NXCodecStatusDAC1ON = 0x02,
NXCodecStatusReset = 0x01,
RegNXCodecWrite = 0x44,
RegNXCodecRead = 0x48,
RegNXCodecRead2 = 0x4c
};
#endif // _SIS7018_REGISTERS_H_
@@ -0,0 +1,113 @@
/*
* SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
* Copyright (c) 2002, 2008-2011 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#include "Settings.h"
#include <lock.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
bool gTraceOn = false;
bool gTruncateLogFile = false;
bool gAddTimeStamp = true;
static char *gLogFilePath = NULL;
mutex gLogLock;
static
void create_log()
{
if (gLogFilePath == NULL)
return;
int flags = O_WRONLY | O_CREAT | ((gTruncateLogFile) ? O_TRUNC : 0);
close(open(gLogFilePath, flags, 0666));
mutex_init(&gLogLock, DRIVER_NAME"-logging");
}
void load_settings()
{
void *handle = load_driver_settings(DRIVER_NAME);
if (handle == 0)
return;
gTraceOn = get_driver_boolean_parameter(handle, "trace", gTraceOn, true);
gTruncateLogFile = get_driver_boolean_parameter(handle, "truncate_logfile",
gTruncateLogFile, true);
gAddTimeStamp = get_driver_boolean_parameter(handle, "add_timestamp",
gAddTimeStamp, true);
const char * logFilePath = get_driver_parameter(handle, "logfile",
NULL, "/var/log/"DRIVER_NAME".log");
if (logFilePath != NULL) {
gLogFilePath = strdup(logFilePath);
}
unload_driver_settings(handle);
create_log();
}
void release_settings()
{
if (gLogFilePath != NULL) {
mutex_destroy(&gLogLock);
free(gLogFilePath);
}
}
void SiS7018_trace(bool force, const char* func, const char *fmt, ...)
{
if (!(force || gTraceOn)) {
return;
}
va_list arg_list;
static const char *prefix = DRIVER_NAME":";
static char buffer[1024];
char *buf_ptr = buffer;
if (gLogFilePath == NULL) {
strcpy(buffer, prefix);
buf_ptr += strlen(prefix);
}
if (gAddTimeStamp) {
bigtime_t time = system_time();
uint32 msec = time / 1000;
uint32 sec = msec / 1000;
sprintf(buf_ptr, "%02ld.%02ld.%03ld:",
sec / 60, sec % 60, msec % 1000);
buf_ptr += strlen(buf_ptr);
}
if (func != NULL) {
sprintf(buf_ptr, "%s::", func);
buf_ptr += strlen(buf_ptr);
}
va_start(arg_list, fmt);
vsprintf(buf_ptr, fmt, arg_list);
va_end(arg_list);
if (gLogFilePath == NULL) {
dprintf(buffer);
return;
}
mutex_lock(&gLogLock);
int fd = open(gLogFilePath, O_WRONLY | O_APPEND);
write(fd, buffer, strlen(buffer));
close(fd);
mutex_unlock(&gLogLock);
}
@@ -0,0 +1,40 @@
/*
* SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
* Copyright (c) 2002, 2008-2011 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _SiS7018_SETTINGS_H_
#define _SiS7018_SETTINGS_H_
#include <driver_settings.h>
#include "Driver.h"
#ifdef _countof
#warning "_countof(...) WAS ALREADY DEFINED!!! Remove local definition!"
#undef _countof
#endif
#define _countof(array)(sizeof(array) / sizeof(array[0]))
void load_settings();
void release_settings();
void SiS7018_trace(bool force, const char *func, const char *fmt, ...);
#ifdef TRACE
#undef TRACE
#endif
#define TRACE(x...) SiS7018_trace(false, __func__, x)
#ifdef ERROR
#undef ERROR
#endif
#define ERROR(x...) SiS7018_trace(true, __func__, x)
#endif //_SiS7018_SETTINGS_H_
@@ -0,0 +1,362 @@
/*
* SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
* Copyright (c) 2002, 2008-2011 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
* Copyright for ali5451 support:
* (c) 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
*/
#include "Stream.h"
#include "Device.h"
#include "Registers.h"
#include "Settings.h"
Stream::Stream(Device *device, bool isInput)
:
fDevice(device),
fStatus(B_NO_INIT),
fIsInput(isInput),
fIsActive(false),
fHWChannel(isInput ? 0 : 1),
fBuffersArea(-1),
fBuffersAreaSize(0),
fBufferSamplesCount(0),
fBuffersAddress(0),
fBuffersPhysAddress(0),
fRealTime(0),
fFramesCount(0),
fBufferCycle(fIsInput ? 1 :0)
{
fFormat.format = B_FMT_16BIT;
fFormat.rate = B_SR_48000;
fFormat.cvsr = _DecodeRate(fFormat.rate);
}
Stream::~Stream()
{
Free();
}
uint32
Stream::_HWId()
{
return fDevice->HardwareId();
}
status_t
Stream::Init()
{
if (fStatus == B_OK)
Free();
fHWChannel = fIsInput ? 0 : 1;
if (_HWId() == SiS7018)
fHWChannel += 0x20; // bank B optimized for PCM
else if (_HWId() == ALi5451 && fIsInput)
fHWChannel = 31;
// assume maximal possible buffers size
fBuffersAreaSize = 1024; // samples
fBuffersAreaSize *= 2 * 2 * 2; // stereo + 16-bit samples + 2 buffers
fBuffersAreaSize = (fBuffersAreaSize + (B_PAGE_SIZE - 1)) &~ (B_PAGE_SIZE - 1);
fBuffersArea = create_area(
(fIsInput) ? DRIVER_NAME "_record_area" : DRIVER_NAME "_playback_area",
&fBuffersAddress, B_ANY_KERNEL_ADDRESS, fBuffersAreaSize,
B_32_BIT_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
if (fBuffersArea < 0) {
ERROR("Error of creating %#lx-bytes size buffer area:%#010x\n",
fBuffersAreaSize, fBuffersArea);
fStatus = fBuffersArea;
return fStatus;
}
physical_entry PhysEntry;
get_memory_map(fBuffersAddress, fBuffersAreaSize, &PhysEntry, 1);
fBuffersPhysAddress = PhysEntry.address;
TRACE("Created area id %d with size: %#x at address %#x[phys:%#lx]\n",
fBuffersArea, fBuffersAreaSize, fBuffersAddress, fBuffersPhysAddress);
// back to samples - half of buffer for 16-bit stereo data
fBufferSamplesCount = fBuffersAreaSize / (2 * 2 * 2);
fStatus = B_OK;
return fStatus;
}
void
Stream::Free()
{
delete_area(fBuffersArea);
fStatus = B_NO_INIT;
}
uint32
Stream::_DecodeRate(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;
}
ERROR("Rate:%x is not supported. Fall to default 48000\n", rate);
return 48000;
}
void
Stream::GetFormat(multi_format_info *Format)
{
if (fIsInput) {
Format->input_latency = 0;
Format->input = fFormat;
} else {
Format->output_latency = 0;
Format->output = fFormat;
}
}
status_t
Stream::SetFormat(_multi_format& format, uint32 formats, uint32 rates)
{
if (fFormat.rate == format.rate && fFormat.format == format.format)
return B_OK;
if ((format.rate & rates) == 0 || (format.format & formats) == 0) {
ERROR("Unsupported data format:%x or rate:%x. Ignore.\n",
format.format, format.rate);
return B_ERROR;
}
fFormat = format;
fFormat.cvsr = _DecodeRate(fFormat.rate);
fBufferSamplesCount = fBuffersAreaSize / (2 * 2);
switch (fFormat.format) {
default:
ERROR("Unsupported data format:%x. 16 bit assumed.\n", fFormat.format);
case B_FMT_16BIT:
fBufferSamplesCount /= 2;
break;
case B_FMT_8BIT_S:
case B_FMT_8BIT_U:
break;
}
TRACE("Format:%#x;Rate:%#x;cvsr:%.2f\n",
fFormat.format, fFormat.rate, fFormat.cvsr);
// stop the stream - it will be rewaked during next exchnage buffers call
Stop();
return B_OK;
}
void
Stream::GetBuffers(uint32& Flags, int32& BuffersCount, int32& ChannelsCount,
uint32& BufferSize, buffer_desc** Buffers)
{
Flags |= fIsInput ? B_MULTI_BUFFER_RECORD : B_MULTI_BUFFER_PLAYBACK;
BuffersCount = 2;
ChannelsCount = 2;
BufferSize = fBufferSamplesCount;
uint32 stride = 4;
if (fFormat.format == B_FMT_8BIT_S || fFormat.format == B_FMT_8BIT_U) {
stride = 2;
}
// [b][c] init buffers
Buffers[0][0].base
= Buffers[1][0].base
= Buffers[0][1].base
= Buffers[1][1].base = (char*)fBuffersAddress;
Buffers[0][0].stride
= Buffers[1][0].stride
= Buffers[0][1].stride
= Buffers[1][1].stride = stride;
// shift pair of second part of buffers
Buffers[1][0].base += BufferSize * stride;
Buffers[1][1].base += BufferSize * stride;
// shift right channel buffers
Buffers[0][1].base += stride / 2;
Buffers[1][1].base += stride / 2;
TRACE("%s buffers:\n", fIsInput ? "input" : "output");
TRACE("1: %#010x %#010x\n", Buffers[0][0].base, Buffers[0][1].base);
TRACE("2: %#010x %#010x\n", Buffers[1][0].base, Buffers[1][1].base);
}
status_t
Stream::Start()
{
uint32 CSO = 0;
uint32 LBA = uint32(fBuffersPhysAddress) & 0x3fffffff;
uint32 ESO = ((fBufferSamplesCount * 2) - 1) & 0xffff;
uint32 Delta = fIsInput ? ((48000 << 12) / uint32(fFormat.cvsr)) & 0xffff
: ((uint32(fFormat.cvsr) << 12) / 48000) & 0xffff;
uint32 DeltaESO = (ESO << 16) | Delta;
uint32 FMControlEtc = fIsInput ? 0 : (0x03 << 14);
uint32 ControlEtc = 1 << 14 | 1 << 12; // stereo data + loop enabled
switch (fFormat.format) {
case B_FMT_16BIT:
ControlEtc |= (1 << 15); // 16 bit
case B_FMT_8BIT_S:
ControlEtc |= (1 << 13); // signed
break;
}
switch (_HWId()) {
case TridentDX:
FMControlEtc |= (0x7f << 7) < 0x7f;
break;
case TridentNX:
CSO = Delta << 24;
DeltaESO = ((Delta << 16) & 0xff000000) | (ESO & 0x00ffffff);
FMControlEtc |= (0x7f << 7) < 0x7f;
break;
case SiS7018:
FMControlEtc = fIsInput ? (0x8a80 << 16) : FMControlEtc;
break;
}
cpu_status cst = fDevice->Lock();
// select used channel
uint32 ChIntReg = fDevice->ReadPCI32(RegChIndex) & ~0x3f;
ChIntReg |= (fHWChannel & 0x3f);
fDevice->WritePCI32(RegChIndex, ChIntReg);
fDevice->WritePCI32(RegCSOAlphaFMS, CSO);
fDevice->WritePCI32(RegLBA_PPTR, LBA);
fDevice->WritePCI32(RegDeltaESO, DeltaESO);
fDevice->WritePCI32(RegRVolCVolFMC, FMControlEtc);
fDevice->WritePCI32(RegGVSelVolCtrl, ControlEtc);
fDevice->WritePCI32(RegEBuf1, 0);
fDevice->WritePCI32(RegEBuf2, 0);
if (fIsInput) {
uint32 reg = 0;
switch (_HWId()) {
case ALi5451:
reg = fDevice->ReadPCI32(RegALiDigiMixer);
fDevice->WritePCI32(RegALiDigiMixer, reg | (1 << _HWVoice()));
break;
case TridentDX:
reg = fDevice->ReadPCI8(RegCodecStatus);
fDevice->WritePCI8(RegCodecStatus,
reg | CodecStatusADCON | CodecStatusSBCtrl);
// enable and set record channel
fDevice->WritePCI8(RegRecChannel, 0x80 | _HWVoice());
break;
case TridentNX:
reg = fDevice->ReadPCI16(RegMiscINT);
fDevice->WritePCI8(RegMiscINT, reg | 0x1000);
// enable and set record channel
fDevice->WritePCI8(RegRecChannel, 0x80 | _HWVoice());
break;
}
}
// enable INT for current channel
uint32 ChIntMask = fDevice->ReadPCI32(_UseBankB() ? RegEnaINTB : RegEnaINTA);
ChIntMask |= 1 << _HWVoice();
fDevice->WritePCI32(_UseBankB() ? RegAddrINTB : RegAddrINTA, 1 << _HWVoice());
fDevice->WritePCI32(_UseBankB() ? RegEnaINTB : RegEnaINTA, ChIntMask);
// start current channel
fDevice->WritePCI32(_UseBankB() ? RegStartB : RegStartA, 1 << _HWVoice());
fDevice->Unlock(cst);
TRACE("%s:CSO:%#x;LBA:%#x;ESO:%#x;Delta:%#x;FM:%#x:Ctrl:%#x;CIR:%#x\n",
fIsInput ? "Rec" : "Play", CSO, LBA, ESO, Delta, FMControlEtc,
ControlEtc, ChIntReg);
fIsActive = true;
return B_OK;
}
status_t
Stream::Stop()
{
cpu_status cst = fDevice->Lock();
// stop current channel
fDevice->WritePCI32(_UseBankB() ? RegStopB : RegStopA, 1 << _HWVoice());
if (_HWId() == ALi5451 && fIsInput) {
uint32 reg = fDevice->ReadPCI32(RegALiDigiMixer);
fDevice->WritePCI32(RegALiDigiMixer, reg & ~(1 << _HWVoice()));
}
fDevice->Unlock(cst);
TRACE("%s:OK\n", fIsInput ? "Rec" : "Play");
fIsActive = false;
fBufferCycle = fIsInput ? 1 : 0;
return B_OK;
}
bool
Stream::InterruptHandler()
{
uint32 SignaledMask = fDevice->ReadPCI32(
_UseBankB() ? RegAddrINTB : RegAddrINTA);
uint32 ChannelMask = 1 << _HWVoice();
if ((SignaledMask & ChannelMask) == 0) {
return false;
}
// first clear signalled channel bit
fDevice->WritePCI32(_UseBankB() ? RegAddrINTB : RegAddrINTA, ChannelMask);
fRealTime = system_time();
fFramesCount += fBufferSamplesCount;
fBufferCycle = (fBufferCycle + 1) % 2;
fDevice->SignalReadyBuffers();
return true;
}
void
Stream::ExchangeBuffers(bigtime_t& RealTime,
bigtime_t& FramesCount, int32& BufferCycle)
{
RealTime = fRealTime;
FramesCount = fFramesCount;
BufferCycle = fBufferCycle;
}
@@ -0,0 +1,71 @@
/*
* SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
* Copyright (c) 2002, 2008-2011 S.Zharski <[email protected]>
* Distributed under the terms of the MIT license.
*
*/
#ifndef _SiS7018_STREAM_H_
#define _SiS7018_STREAM_H_
#include "hmulti_audio.h"
class Device;
class Stream {
public:
Stream(Device* device, bool isInput);
~Stream();
status_t Init();
void Free();
status_t InitCheck() { return fStatus; }
status_t Start();
status_t Stop();
bool IsActive() { return fIsActive; }
void GetBuffers(uint32& Flags,
int32& BuffersCount,
int32& ChannelsCount,
uint32& BufferSize,
buffer_desc** Buffers);
void ExchangeBuffers(bigtime_t& RealTime,
bigtime_t& FramesCount,
int32& BufferCycle);
void GetFormat(multi_format_info *Format);
status_t SetFormat(_multi_format& format,
uint32 formats, uint32 rates);
bool InterruptHandler();
private:
uint32 _DecodeRate(uint32 rate);
inline bool _UseBankB() { return (fHWChannel & 0x20) == 0x20; }
inline uint32 _HWVoice() { return fHWChannel & 0x1f; }
inline uint32 _HWId();
Device* fDevice;
status_t fStatus;
bool fIsInput;
bool fIsActive;
uint32 fHWChannel;
area_id fBuffersArea;
size_t fBuffersAreaSize;
size_t fBufferSamplesCount;
void* fBuffersAddress;
phys_addr_t fBuffersPhysAddress;
_multi_format fFormat;
bigtime_t fRealTime;
bigtime_t fFramesCount;
int32 fBufferCycle;
};
#endif // _SiS7018_STREAM_H_
@@ -0,0 +1,127 @@
## BeOS Generic Makefile v2.2 ##
## Fill in this file to specify the project being created, and the referenced
## makefile-engine will do all of the hard work for you. This handles both
## Intel and PowerPC builds of the BeOS.
## Application Specific Settings ---------------------------------------------
# specify the name of the binary
NAME=sis7018
# specify the type of binary
# APP: Application
# SHARED: Shared library or add-on
# STATIC: Static library archive
# DRIVER: Kernel Driver
TYPE=DRIVER
# add support for new Pe and Eddie features
# to fill in generic makefile
#%{
# @src->@
# specify the source files to use
# full paths or paths relative to the makefile can be included
# all files, regardless of directory, will have their object
# files created in the common object directory.
# Note that this means this makefile will not work correctly
# if two source files with the same name (source.c or source.cpp)
# are included from different directories. Also note that spaces
# in folder names do not work well with this makefile.
SRCS=Driver.cpp\
Device.cpp\
Mixer.cpp\
Stream.cpp\
Settings.cpp\
../ac97.c
# specify the resource files to use
# full path or a relative path to the resource file can be used.
RSRCS=
# @<-src@
#%}
# end support for Pe and Eddie
# specify additional libraries to link against
# there are two acceptable forms of library specifications
# - if your library follows the naming pattern of:
# libXXX.so or libXXX.a you can simply specify XXX
# library: libbe.so entry: be
#
# - if your library does not follow the standard library
# naming scheme you need to specify the path to the library
# and it's name
# library: my_lib.a entry: my_lib.a or path/my_lib.a
LIBS=
# specify additional paths to directories following the standard
# libXXX.so or libXXX.a naming scheme. You can specify full paths
# or paths relative to the makefile. The paths included may not
# be recursive, so include all of the paths where libraries can
# be found. Directories where source files are found are
# automatically included.
LIBPATHS=
# additional paths to look for system headers
# thes use the form: #include <header>
# source file directories are NOT auto-included here
SYSTEM_INCLUDE_PATHS=../../../../trunk/headers/private/kernel \
../../../../trunk/headers/private/kernel/util
# additional paths to look for local headers
# thes use the form: #include "header"
# source file directories are automatically included
LOCAL_INCLUDE_PATHS=../../../../trunk/build/config_headers \
../../../../trunk/headers/private/media
# specify the level of optimization that you desire
# NONE, SOME, FULL
OPTIMIZE=
# specify any preprocessor symbols to be defined. The symbols will not
# have their values set automatically; you must supply the value (if any)
# to use. For example, setting DEFINES to "DEBUG=1" will cause the
# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG"
# would pass "-DDEBUG" on the compiler's command line.
DEFINES=
# specify special warning levels
# if unspecified default warnings will be used
# NONE = supress all warnings
# ALL = enable all warnings
WARNINGS=ALL
# specify whether image symbols will be created
# so that stack crawls in the debugger are meaningful
# if TRUE symbols will be created
SYMBOLS=
# specify debug settings
# if TRUE will allow application to be run from a source-level
# debugger. Note that this will disable all optimzation.
DEBUGGER=
# specify additional compiler flags for all files
COMPILER_FLAGS=
# specify additional linker flags
LINKER_FLAGS=
# specify the version of this particular item
# (for example, -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL")
# This may also be specified in a resource.
APP_VERSION=
# (for TYPE == DRIVER only) Specify desired location of driver in the /dev
# hierarchy. Used by the driverinstall rule. E.g., DRIVER_PATH = video/usb will
# instruct the driverinstall rule to place a symlink to your driver's binary in
# ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will appear at
# /dev/video/usb when loaded. Default is "misc".
DRIVER_PATH=audio/hmulti
## include the makefile-engine
include $(BUILDHOME)/etc/makefile-engine
@@ -0,0 +1,29 @@
#
# The sample sis7018 driver settings file
#
# Rename it to sis7018 and put into
# /boot/home/config/settings/kernel/drivers/
# After restarting Media Services the new settings will works.
## trace [on|off] - activate additional tracing.
## default value: off
trace on
## logfile [full path to private log file]
## default path value: /var/log/sis7018.log
## if disabled - all output goes to syslog
logfile /var/log/sis7018.log
## reset_logfile [on|off] - truncate private log file on driver/system restart
## default value: off
##
# reset_logfile off
## add_timestamp [on|off] - add time of writing the string in private log file.
## default value: on
##
# add_timestamp off
@@ -1,25 +0,0 @@
SubDir HAIKU_TOP src add-ons kernel drivers audio sis7018 ;
SetSubDirSupportedPlatformsBeOSCompatible ;
UsePrivateHeaders audio ;
KernelAddon sis7018 :
ac97.c
midi.c
pcm.c
sis7018.c
sis7018hw.c
;
AddResources sis7018 : vsis7018.rdef ;
Package haiku-sis7018-cvs
:
sis7018
:
boot home config add-ons kernel drivers bin ;
PackageDriverSymLink haiku-sis7018-cvs : audio sis7018 ;
Package haiku-sis7018-cvs : <sis7018!driver>sis7018.settings.sample
: boot home config settings kernel drivers sample ;
@@ -1,84 +0,0 @@
BeOS sound driver for SiS 7018 PCI Audio Accelerator
This driver is designed to supports following chipsets:
SiS 7018 (vendor:1039 card:7018)
Trident 4D Wave DX (vendor:1023 card:2000)
Trident 4D Wave NX (vendor:1023 card:2001) - not tested
Acer Lab's M5451 (vendor:10b9 card:5451)
What's new:
-----------------------------------------------------------------------------------------------
version 0.1.0-dev-8
* The AC'97 codec id checking restored.
- to be continued!
version 0.1.0-dev-7
* Added use_io_registers to settings file. Just for testers.
* The AC'97 codec id checking bypassed - for testing purposes only
version 0.1.0-dev-6
* Fixed problem with "Real-time Audio". Currently works in this mode too.
version 0.1.0-dev-5
* Fixed AC'97 codec handling - 6bit master gain codecs schould be supported correctly
+ For testing purposes added Trident DX with vendorID : 0x03 - such card found
by one tester.
version 0.1.0-dev-4
+ Finished some codec manipulations - mio volumes, mio mutes etc.
version 0.1.0-dev-3
* Fixed "last thrashed" sample problem
+ makefile and install script are fixed.
version 0.1.0-dev-2
* changed AC'97 initialization
+ Added settings file
+ Added possibility to write into private log file
+ More complicated AC'97 tracing possible ...
version 0.1.0-dev-1
* changed AC'97 initialization
version 0.1.0-dev-0
initial build.
-----------------------------------------------------------------------------------------------
!!! WANTED !!!
Wanted testers with Trident NX sound hardware. Current driver is not tested on such hardware. It'll be good to asure that it work with that cards too!
Installation
Run install.sh
Uninstallation
Run uninstall.sh
Problems solving
If this driver crash your system - reboot and press "Space" during load. In boot menu select "Disable user add-ons" and continue boot. Uninstall driver and reboot again into normal mode. Write me a e-mail about this problem.
Professional testing:
If you found a problem with this driver and want to help me in development of this software make the following things (Assume that driver is already installed):
1) Open the file /boot/home/config/settings/kernel/drivers/sis7018 and set following parameters:
debug_output true
debug_output_in_file true
if you have a problems with mixer - it's not work, or settings is not saved on reboot - activate this one too:
debug_trace_ac97 true
2) Reboot your system and try to play something.
3) After that get the file /boot/home/sis7018.log and send it to me.
4) It'll be good on your first bug report to me send also hardware configuration of your system. To get it go into Terminal and run following commands
listdev
Get all that it say to you and send me too, please ...
Thank you !!!
Siarzuk Zharski <[email protected]>
http://www.zharik.host.sk/
@@ -1,605 +0,0 @@
/*
* This file is a part of SiS 7018 BeOS driver project.
* Copyright (c) 2002-2003 by Siarzuk Zharski <imker@gmx.li>
* Portional Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
*
* This file may be used under the terms of the BSD License
* See the file "License" for details.
*/
#include "sis7018.h"
#include "sis7018hw.h"
#include "t4dwave.h"
#include "ac97.h"
//#include "pcm.h"
#define TR_TIMEOUT_CDC 0xffff
//typedef bool (*ac97init_proc)(sis7018_dev *dev, bool bOn);
/* init functions*/
status_t ac97init_CS4299(sis7018_dev *dev, bool bOn);
status_t ac97init_default(sis7018_dev *dev, bool bOn);
static struct ac97_codecid
{
uint32 id;
int noext;
char *name;
status_t (*ac97init)(sis7018_dev *dev, bool bOn);
} ac97codecid[] = {
{ 0x41445303, 0, "Analog Devices AD1819", NULL },
{ 0x41445340, 0, "Analog Devices AD1881", NULL },
{ 0x41445348, 0, "Analog Devices AD1881A", NULL },
{ 0x41445360, 0, "Analog Devices AD1885", NULL },
{ 0x414b4d00, 1, "Asahi Kasei AK4540", NULL },
{ 0x414b4d01, 1, "Asahi Kasei AK4542", NULL },
{ 0x414b4d02, 1, "Asahi Kasei AK4543", NULL },
{ 0x414c4710, 0, "Avance Logic ALC200/200P", NULL },
{ 0x43525900, 0, "Cirrus Logic CS4297", NULL },
{ 0x43525903, 0, "Cirrus Logic CS4297", NULL },
{ 0x43525913, 0, "Cirrus Logic CS4297A", NULL },
{ 0x43525914, 0, "Cirrus Logic CS4297B", NULL },
{ 0x43525923, 0, "Cirrus Logic CS4294C", NULL },
{ 0x4352592b, 0, "Cirrus Logic CS4298C", NULL },
{ 0x43525931, 0, "Cirrus Logic CS4299A", ac97init_CS4299 },
{ 0x43525933, 0, "Cirrus Logic CS4299C", ac97init_CS4299 },
{ 0x43525934, 0, "Cirrus Logic CS4299D", ac97init_CS4299 },
{ 0x43525941, 0, "Cirrus Logic CS4201A", NULL },
{ 0x43525951, 0, "Cirrus Logic CS4205A", NULL },
{ 0x43525961, 0, "Cirrus Logic CS4291A", NULL },
{ 0x45838308, 0, "ESS Technology ES1921", NULL },
{ 0x49434511, 0, "ICEnsemble ICE1232", NULL },
{ 0x4e534331, 0, "National Semiconductor LM4549", NULL },
{ 0x83847600, 0, "SigmaTel STAC9700/9783/9784", NULL },
{ 0x83847604, 0, "SigmaTel STAC9701/9703/9704/9705", NULL },
{ 0x83847605, 0, "SigmaTel STAC9704", NULL },
{ 0x83847608, 0, "SigmaTel STAC9708/9711", NULL },
{ 0x83847609, 0, "SigmaTel STAC9721/9723", NULL },
{ 0x83847644, 0, "SigmaTel STAC9744", NULL },
{ 0x83847656, 0, "SigmaTel STAC9756/9757", NULL },
{ 0x53494c22, 0, "Silicon Laboratory Si3036", NULL },
{ 0x53494c23, 0, "Silicon Laboratory Si3038", NULL },
{ 0x54524103, 0, "TriTech TR?????", NULL },
{ 0x54524106, 0, "TriTech TR28026", NULL },
{ 0x54524108, 0, "TriTech TR28028", NULL },
{ 0x54524123, 0, "TriTech TR28602", NULL },
{ 0x574d4c00, 0, "Wolfson WM9701A", NULL },
{ 0x574d4c03, 0, "Wolfson WM9703/9704", NULL },
{ 0x574d4c04, 0, "Wolfson WM9704 (quad)", NULL },
{ 0, 0, NULL }
};
static char *ac97enhancement[] = {
"No 3D Stereo Enhancement",
"Analog Devices Phat Stereo",
"Creative Stereo Enhancement",
"National Semi 3D Stereo Enhancement",
"Yamaha Ymersion",
"BBE 3D Stereo Enhancement",
"Crystal Semi 3D Stereo Enhancement",
"Qsound QXpander",
"Spatializer 3D Stereo Enhancement",
"SRS 3D Stereo Enhancement",
"Platform Tech 3D Stereo Enhancement",
"AKM 3D Audio",
"Aureal Stereo Enhancement",
"Aztech 3D Enhancement",
"Binaura 3D Audio Enhancement",
"ESS Technology Stereo Enhancement",
"Harman International VMAx",
"Nvidea 3D Stereo Enhancement",
"Philips Incredible Sound",
"Texas Instruments 3D Stereo Enhancement",
"VLSI Technology 3D Stereo Enhancement",
"TriTech 3D Stereo Enhancement",
"Realtek 3D Stereo Enhancement",
"Samsung 3D Stereo Enhancement",
"Wolfson Microelectronics 3D Enhancement",
"Delta Integration 3D Enhancement",
"SigmaTel 3D Enhancement",
"KS Waves",
"Rockwell 3D Stereo Enhancement",
"Reserved 29",
"Reserved 30",
"Reserved 31"
};
static char *ac97feature[] = {
"mic channel",
"reserved",
"tone",
"simulated stereo",
"headphone",
"bass boost",
"18 bit DAC",
"20 bit DAC",
"18 bit ADC",
"20 bit ADC"
};
static char *ac97extfeature[] = {
"variable rate PCM",
"double rate PCM",
"reserved 1",
"variable rate mic",
"reserved 2",
"reserved 3",
"center DAC",
"surround DAC",
"LFE DAC",
"AMAP",
"reserved 4",
"reserved 5",
"reserved 6",
"reserved 7",
};
int ac97read(sis7018_dev *dev, int regno)
{
cpu_status cp;
int i, j, reg, rw;
if(b_trace_ac97)
TRACE(" ac97read: regno=%x\n", regno);
switch (dev->type->ids.chip_id){
case SPA_PCI_ID:
reg=SPA_REG_CODECRD;
rw=SPA_CDC_RWSTAT;
break;
case ALI_PCI_ID:
if(dev->info.revision > 0x01)
reg=TDX_REG_CODECWR;
else
reg=TDX_REG_CODECRD;
rw=TDX_CDC_RWSTAT;
break;
case TDX_PCI_ID:
case xDX_PCI_ID:
reg=TDX_REG_CODECRD;
rw=TDX_CDC_RWSTAT;
break;
case TNX_PCI_ID:
reg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD;
rw=TNX_CDC_RWSTAT;
break;
default:
TRACE("ac97read defaulted !!!\n");
return ENODEV;
}
cp = disable_interrupts();
acquire_spinlock(&dev->hardware);
regno &= 0x7f;
if(dev->type->ids.chip_id == ALI_PCI_ID){
j = rw;
for (i=TR_TIMEOUT_CDC; (i > 0) && (j & rw); i--){
j = hw_read(dev, reg, 4);
}
if(i > 0){
uint32 c1 = hw_read(dev, 0xc8, 4);
uint32 c2 = hw_read(dev, 0xc8, 4);
for(i=TR_TIMEOUT_CDC; (i > 0) && (c1 == c2); i--){
c2 = hw_read(dev, 0xc8, 4);
}
}
}
if(dev->type->ids.chip_id != ALI_PCI_ID || i > 0){
hw_write(dev, reg, regno | rw, 4);
j=rw;
for (i=TR_TIMEOUT_CDC; (i > 0) && (j & rw); i--){
j=hw_read(dev, reg, 4);
}
}
release_spinlock(&dev->hardware);
restore_interrupts(cp);
if (i == 0)
TRACE_ALWAYS("ac97 codec timeout during read of register %x\n", regno);
if(b_trace_ac97)
TRACE(" ac97read: regno=%x ret = %x\n", regno, j);
return (j >> TR_CDC_DATA) & 0xffff;
}
status_t ac97write(sis7018_dev *dev, int regno, uint32 data)
{
cpu_status cp;
int i, j, reg, rw;
if(b_trace_ac97)
TRACE("\nac97write: regno=%x, data=%lx\n", regno, data);
switch (dev->type->ids.chip_id){
case SPA_PCI_ID:
reg=SPA_REG_CODECWR;
rw=SPA_CDC_RWSTAT;
break;
case ALI_PCI_ID:
case TDX_PCI_ID:
case xDX_PCI_ID:
reg=TDX_REG_CODECWR;
rw=TDX_CDC_RWSTAT;
break;
case TNX_PCI_ID:
reg=TNX_REG_CODECWR;
rw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0);
break;
default:
TRACE("ac97write defaulted !!!");
return ENODEV;
}
regno &= 0x7f;
if(b_trace_ac97)
TRACE("ac97write: reg %x was %x\n", regno, ac97read(dev, regno));
cp = disable_interrupts();
acquire_spinlock(&dev->hardware);
j=rw;
if(dev->type->ids.chip_id == ALI_PCI_ID){
for(i = TR_TIMEOUT_CDC; (i > 0) && (j & rw); i--){
j = hw_read(dev, reg, 4);
}
if(i > 0) {
uint32 c1 = hw_read(dev, 0xc8, 4);
uint32 c2 = hw_read(dev, 0xc8, 4);
for (i = TR_TIMEOUT_CDC; (i > 0) && (c1 == c2); i--)
c2 = hw_read(dev, 0xc8, 4);
}
}
if(dev->type->ids.chip_id != ALI_PCI_ID || i > 0){
for(i=TR_TIMEOUT_CDC; (i>0) && (j & rw); i--){
j=hw_read(dev, reg, 4);
}
if(dev->type->ids.chip_id == ALI_PCI_ID && dev->info.revision > 0x01)
rw |= 0x0100;
hw_write(dev, reg, (data << TR_CDC_DATA) | regno | rw, 4);
}
release_spinlock(&dev->hardware);
restore_interrupts(cp);
if(b_trace_ac97)
TRACE("ac97write: wrote %x, now %x\n", data, ac97read(dev, regno));
if (i==0)
TRACE_ALWAYS("ac97 codec timeout writing %x, data %lx\n", regno, data);
if(b_trace_ac97)
TRACE("ac97write: ret = %x\n\n", i);
return (i > 0)? B_OK : B_TIMED_OUT;
}
status_t ac97init(sis7018_dev *card)
{
// cpu_status cp;
status_t err=B_OK;
int i, /*j,*/ caps, se, noext, extcaps, extid, extstat, reg=0;
int32 id;
/* old manner
err |= ac97write(card, AC97_REG_POWER, 0);
err |= ac97write(card, AC97_REG_RESET, 0);
err |= ac97write(card, AC97_MIX_PCM, 0x707);
err |= ac97write(card, AC97_MIX_BEEP, 0x06);
err |= ac97write(card, AC97_MIX_MIC, 0x8000);
err |= ac97write(card, AC97_MIX_PHONE, 0x707);
err |= ac97write(card, AC97_MIX_PHONES, 0x707);
*/
//BSD init manner
err |= ac97write(card, AC97_REG_POWER, 0x0000);
err |= ac97write(card, AC97_REG_RESET, 0);
snooze(100000);
//snooze(50000);
err |= ac97write(card, AC97_REG_POWER, 0x0000);
err |= ac97write(card, AC97_REG_GEN, 0x0000);
/* Marcus's init manner*/
// err |= ac97write(card, AC97_REG_RESET, 0);
// snooze(50000); // 50 ms
if (err != B_OK){
TRACE("ac97 codec resetting failed: %lx\n", err);
return ENODEV;
}
// cp = disable_interrupts();
// acquire_spinlock(&card->hardware);
i = ac97read(card, AC97_REG_RESET);
caps = i & 0x03ff;
se = (i & 0x7c00) >> 10;
id = (ac97read(card, AC97_REG_ID1) << 16) | ac97read(card, AC97_REG_ID2);
// codec->rev = id & 0x000000ff;
// release_spinlock(&card->hardware);
// restore_interrupts(cp);
#if 1
if (id == 0 || id == 0xffffffff){
TRACE("ac97 codec invalid or not present (id == %lx)\n", id);
return ENODEV;
}
#endif
noext = 0;
for (i = 0; ac97codecid[i].id; i++){
if (ac97codecid[i].id == id){
status_t err = B_OK;
noext = ac97codecid[i].noext;
if (ac97codecid[i].name)
TRACE("\nac97 codec id 0x%08lx (%s)\n", id, ac97codecid[i].name);
if(ac97codecid[i].ac97init)
err |= (*ac97codecid[i].ac97init)(card, true);
else
err |= ac97init_default(card, true);
if(err != B_OK)
TRACE("ac97 codec initializatin error\n");
}
}
TRACE("ac97 codec features:\n");
for (i = 0; i < 10; i++)
if (caps & (1 << i))
TRACE("> %s\n", ac97feature[i]);
TRACE("> %s\n", ac97enhancement[se]);
TRACE("\n");
extcaps = 0;
extid = 0;
extstat = 0;
if (!noext){
i = ac97read(card, AC97_REGEXT_ID);
if (i != 0xffff){
extcaps = i & 0x3fff;
extid = (i & 0xc000) >> 14;
extstat = ac97read(card, AC97_REGEXT_STAT) & AC97_EXTCAPS;
}
}
if (extcaps != 0 || extid){
TRACE("\nac97 %s codec %s:\n", extid? "secondary" : "primary",
extcaps? "extended features":"");
for (i = 0; i < 14; i++)
if (extcaps & (1 << i))
TRACE("> %s\n", ac97extfeature[i]);
TRACE("\n");
}
if ((ac97read(card, AC97_REG_POWER) & 2) == 0)
TRACE("ac97 codec reports dac not ready\n");
// some addition
reg = ac97read(card, AC97_MIX_MASTER);
err |= ac97write(card, AC97_MIX_MASTER, 0x3f3f);
if(0x3f3f == ac97read(card, AC97_MIX_MASTER)){
TRACE("AC97 master gain is wide!\n");
card->ac97.wide_main_gain = true;
}
err |= ac97write(card, AC97_MIX_MASTER, reg);
// BSD manner
err |= ac97write(card, AC97_MIX_PCM, 0x707);
// err |= ac97write(card, AC97_MIX_BEEP, 0x06);
// err |= ac97write(card, AC97_MIX_MIC, 0x8000);
// err |= ac97write(card, AC97_MIX_PHONE, 0x707);
// err |= ac97write(card, AC97_MIX_PHONES, 0x707);
/*Marcus's manner*/
// err |= ac97write(card, AC97_MIX_MASTER, 0x00);
// err |= ac97write(card, AC97_MIX_PCM, 0x404);
return err;
}
status_t ac97get_params(sis7018_dev *dev, sound_setup *sound)
{
uint16 reg = 0;
int main_gain_multiplier = dev->ac97.wide_main_gain ? 1 : 2;
sound->sample_rate = kHz_48_0;
sound->dither_enable = false;
sound->output_boost = 0;
sound->highpass_enable = 0;
reg=ac97read(dev, AC97_MIX_MONO);
sound->mono_gain = (char)reg;
sound->mono_mute = (char)(reg & AC97_MUTE) ? 1 : 0;
reg=ac97read(dev, AC97_REG_RECSEL);
switch (reg&0x07){
case 4: sound->right.adc_source = line; break;
case 3: sound->right.adc_source = aux1; break;
case 0: sound->right.adc_source = mic; break;
default: sound->right.adc_source = loopback; break;
}
switch ((reg >> 8) & 0x07){
case 4: sound->left.adc_source = line; break;
case 3: sound->left.adc_source = aux1; break;
case 0: sound->left.adc_source = mic; break;
default: sound->left.adc_source = loopback; break;
}
reg = ac97read(dev, AC97_MIX_MIC);
sound->loop_attn = (reg & 0x1f)*2;
sound->loop_enable = (reg & AC97_MUTE) ? 0 : 1;
sound->left.mic_gain_enable =
sound->right.mic_gain_enable = (reg >> 6) & 0x01;
reg = ac97read(dev, AC97_MIX_RGAIN);
sound->left.adc_gain = (reg >> 0x8) & 0xf;
sound->right.adc_gain = reg & 0xf;
reg = ac97read(dev, AC97_MIX_CD);
sound->left.aux1_mix_gain = ((reg >> 8) & 0x1f);
sound->right.aux1_mix_gain = (reg & 0x1f);
sound->left.aux1_mix_mute =
sound->right.aux1_mix_mute = (reg & AC97_MUTE) ? 1 : 0;
reg = ac97read(dev, AC97_MIX_AUX);
sound->left.aux2_mix_gain = ((reg >> 8) & 0x1f);
sound->right.aux2_mix_gain = (reg & 0x1f);
sound->left.aux2_mix_mute =
sound->right.aux2_mix_mute = (reg & AC97_MUTE) ? 1 : 0;
reg = ac97read(dev, AC97_MIX_LINE);
sound->left.line_mix_gain = ((reg >> 8) & 0x1f);
sound->right.line_mix_gain = (reg & 0x1f);
sound->left.line_mix_mute =
sound->right.line_mix_mute = (reg & AC97_MUTE) ? 1 : 0;
reg = ac97read(dev, AC97_MIX_MASTER);
sound->left.dac_attn = ((reg >> 8) & 0x1f) * main_gain_multiplier;
sound->right.dac_attn = (reg & 0x1f) * main_gain_multiplier;
sound->left.dac_mute =
sound->right.dac_mute = (reg & AC97_MUTE) ? 1 : 0;
return B_OK;
}
status_t ac97set_params(sis7018_dev *dev, sound_setup * sound)
{
status_t err = B_OK;
uint16 reg = 0;
int main_gain_divider = dev->ac97.wide_main_gain ? 1 : 2;
#if 0 //DEBUG
TRACE("\nsound setup (left/right)\n");
TRACE("adc_source %x/%x\n", sound->left.adc_source, sound->right.adc_source);
TRACE("adc_gain %x/%x\n", sound->left.adc_gain, sound->right.adc_gain);
TRACE("mic_gain_enable %x/%x\n", sound->left.mic_gain_enable, sound->right.mic_gain_enable);
TRACE("aux1_mix_gain %x/%x\n", sound->left.aux1_mix_gain, sound->right.aux1_mix_gain);
TRACE("aux1_mix_mute %x/%x\n", sound->left.aux1_mix_mute, sound->right.aux1_mix_mute);
TRACE("aux2_mix_gain %x/%x\n", sound->left.aux2_mix_gain, sound->right.aux2_mix_gain);
TRACE("aux2_mix_mute %x/%x\n", sound->left.aux2_mix_mute, sound->right.aux2_mix_mute);
TRACE("line_mix_gain %x/%x\n", sound->left.line_mix_gain, sound->right.line_mix_gain);
TRACE("line_mix_mute %x/%x\n", sound->left.line_mix_mute, sound->right.line_mix_mute);
TRACE("dac_attn %x/%x\n", sound->left.dac_attn, sound->right.dac_attn);
TRACE("dac_mute %x/%x\n", sound->left.dac_mute, sound->right.dac_mute);
TRACE("sound setup\n");
TRACE("sample_rate %x\n", sound->sample_rate);
TRACE("playback_format %x\n", sound->playback_format);
TRACE("capture_format %x\n", sound->capture_format);
TRACE("dither_enable %x\n", sound->dither_enable);
TRACE("loop_attn %x\n", sound->loop_attn);
TRACE("loop_enable %x\n", sound->loop_enable);
TRACE("output_boost %x\n", sound->output_boost);
TRACE("highpass_enable %x\n", sound->highpass_enable);
TRACE("mono_gain %x\n", sound->mono_gain);
TRACE("mono_mute %x\n", sound->mono_mute);
#endif
//TRACE("sample-rate:%d\n", sound->sample_rate);
sound->sample_rate = kHz_48_0;
sound->dither_enable = false;
sound->output_boost = 0;
sound->highpass_enable = 0;
reg = sound->mono_gain | ((sound->mono_mute) ? AC97_MUTE : 0);
err |= ac97write(dev, AC97_MIX_MONO, reg);
reg = sound->loop_attn / 2;
reg |= ((sound->left.mic_gain_enable || sound->right.mic_gain_enable) ? 0x01 : 0x00) << 6;
reg |= (sound->loop_enable) ? 0 : AC97_MUTE;
err |= ac97write(dev, AC97_MIX_MIC, reg);
reg = 0;
switch(sound->left.adc_source){
case line: reg = 4; break;
case aux1: reg = 3; break;
case mic: reg = 0; break;
default: reg = 0; break;
}
reg <<= 8;
switch(sound->right.adc_source){
case line: reg |= 4; break;
case aux1: reg |= 3; break;
case mic: reg |= 0; break;
default: reg |= 0; break;
}
err |= ac97write(dev, AC97_REG_RECSEL, reg);
reg = (sound->left.adc_gain) << 8;
reg |= sound->right.adc_gain;
err |= ac97write(dev, AC97_MIX_RGAIN, reg);
reg = (sound->left.aux1_mix_gain) << 0x08;
reg |= sound->right.aux1_mix_gain;
reg |= (sound->left.aux1_mix_mute || sound->right.aux1_mix_mute) ? AC97_MUTE : 0 ;
err |= ac97write(dev, AC97_MIX_CD, reg);
reg = (sound->left.aux2_mix_gain) << 0x08;
reg |= sound->right.aux2_mix_gain;
reg |= (sound->left.aux2_mix_mute || sound->right.aux2_mix_mute) ? AC97_MUTE : 0 ;
err |= ac97write(dev, AC97_MIX_AUX, reg);
reg = (sound->left.line_mix_gain) << 0x08;
reg |= sound->right.line_mix_gain;
reg |= (sound->left.line_mix_mute || sound->right.line_mix_mute) ? AC97_MUTE : 0 ;
err |= ac97write(dev, AC97_MIX_LINE, reg);
reg = ((sound->left.dac_attn) / main_gain_divider) << 0x08;
reg |= (sound->right.dac_attn) / main_gain_divider;
reg |= (sound->left.dac_mute || sound->right.dac_mute) ? AC97_MUTE : 0 ;
err |= ac97write(dev, AC97_MIX_MASTER, reg);
return err;
}
status_t ac97init_default(sis7018_dev *dev, bool bOn)
{
status_t err = B_OK;
//TRACE("powerdown register was = %#04x\n",ich_codec_read(AC97_POWERDOWN)));
// if (bOn)
// ac97write(dev, AC97_REG_POWER, ac97read(dev, AC97_REG_POWER) & ~0x8000); /* switch on (low active) */
// else
// ac97write(dev, AC97_REG_POWER, ac97read(dev, AC97_REG_POWER) | 0x8000); /* switch off */
//LOG(("powerdown register is = %#04x\n",ich_codec_read(AC97_POWERDOWN)));
return err;
}
status_t ac97init_CS4299(sis7018_dev *dev, bool bOn)
{
status_t err = B_OK;
TRACE("Use CS4299 init\n");
// if (bOn)
// err |= ac97write(dev, 0x68, 0x8004);
// else
// err |= ac97write(dev, 0x68, 0);
return err;
}
#if DEBUG
static struct
{
int16 reg;
char name[DEVNAME];
} ac97_regs[]=
{
{AC97_REG_RESET, "Reset"}, {AC97_MIX_MASTER, "Master volume"},
{AC97_MIX_PHONES, "AUX out volume"}, {AC97_MIX_MONO, "Mono volume"},
{AC97_MIX_TONE, "Master tone"}, {AC97_MIX_BEEP, "PC beep"},
{AC97_MIX_PHONE, "Phone volume"}, {AC97_MIX_MIC, "Mic volume"},
{AC97_MIX_LINE, "Line in volume"}, {AC97_MIX_CD, "CD volume"},
{AC97_MIX_VIDEO, "Video volume"}, {AC97_MIX_AUX, "AUX in volume"},
{AC97_MIX_PCM, "PCM out volume"}, {AC97_REG_RECSEL, "Record select"},
{AC97_MIX_RGAIN, "Record gain"}, {AC97_MIX_MGAIN, "Record gain Mic"},
{AC97_REG_GEN, "General purpose"}, {AC97_REG_3D, "3D control"},
{0x24, "Audio int & paging"}, {AC97_REG_POWER, "Powerdown ctrl/stat"},
{AC97_REG_ID1, "VendorID 1"}, {AC97_REG_ID2, "VendorID 2"},
};
void ac97dump(int card_id)
{
int i;
for(i=0; i < sizeof(ac97_regs)/sizeof(ac97_regs[0]); i++)
kprintf("%s : %x\n", ac97_regs[i].name, ac97read(&cards[card_id], ac97_regs[i].reg));
}
#endif //DEBUG
@@ -1,112 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
* $FreeBSD: src/sys/dev/sound/pcm/ac97.h,v 1.3.2.5 2001/08/01 03:41:03 cg Exp $
*/
/*
* This file is a part of SiS 7018 BeOS driver project.
* Copyright permissions see above.
*
* Portion copyright (c) 2002-2003 by Siarzuk Zharski <imker@gmx.li>
*
* $Source: /cvsroot/sis4be/sis7018/ac97.h,v $
* $Author: zharik $
* $Revision: 1.4 $
* $Date: 2003/04/08 19:18:37 $
*/
#define AC97_MUTE 0x8000
#define AC97_REG_RESET 0x00
#define AC97_MIX_MASTER 0x02
#define AC97_MIX_PHONES 0x04
#define AC97_MIX_MONO 0x06
#define AC97_MIX_TONE 0x08
#define AC97_MIX_BEEP 0x0a
#define AC97_MIX_PHONE 0x0c
#define AC97_MIX_MIC 0x0e
#define AC97_MIX_LINE 0x10
#define AC97_MIX_CD 0x12
#define AC97_MIX_VIDEO 0x14
#define AC97_MIX_AUX 0x16
#define AC97_MIX_PCM 0x18
#define AC97_REG_RECSEL 0x1a
#define AC97_MIX_RGAIN 0x1c
#define AC97_MIX_MGAIN 0x1e
#define AC97_REG_GEN 0x20
#define AC97_REG_3D 0x22
#define AC97_REG_POWER 0x26
#define AC97_REGEXT_ID 0x28
#define AC97_EXTCAP_VRA (1 << 0)
#define AC97_EXTCAP_DRA (1 << 1)
#define AC97_EXTCAP_VRM (1 << 3)
#define AC97_EXTCAPS (AC97_EXTCAP_VRA | AC97_EXTCAP_DRA | AC97_EXTCAP_VRM)
#define AC97_REGEXT_STAT 0x2a
#define AC97_REGEXT_FDACRATE 0x2c
#define AC97_REGEXT_SDACRATE 0x2e
#define AC97_REGEXT_LDACRATE 0x30
#define AC97_REGEXT_LADCRATE 0x32
#define AC97_REGEXT_MADCRATE 0x34
#define AC97_MIXEXT_CLFE 0x36
#define AC97_MIXEXT_SURROUND 0x38
#define AC97_REG_ID1 0x7c
#define AC97_REG_ID2 0x7e
/* Added by S.Zharski*/
status_t ac97init(sis7018_dev *dev);
int ac97read(sis7018_dev *dev, int regno);
status_t ac97write(sis7018_dev *dev, int regno, uint32 data);
status_t ac97get_params(sis7018_dev *dev, sound_setup *sound);
status_t ac97set_params(sis7018_dev *dev, sound_setup *sound);
void ac97dump(int card_id);
/*
Siarzhuk Zharski: Following not needed in BeOS build. =-)
*/
/*#define AC97_F_EAPD_INV 0x00000001
#define AC97_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, sizeof(struct kobj))
#define AC97_CREATE(dev, devinfo, cls) ac97_create(dev, devinfo, &cls ## _class)
struct ac97_info;
#include "ac97_if.h"
extern kobj_class_t ac97_getmixerclass(void);
struct ac97_info *ac97_create(device_t dev, void *devinfo, kobj_class_t cls);
void ac97_destroy(struct ac97_info *codec);
void ac97_setflags(struct ac97_info *codec, u_int32_t val);
u_int32_t ac97_getflags(struct ac97_info *codec);
int ac97_setrate(struct ac97_info *codec, int which, int rate);
int ac97_setextmode(struct ac97_info *codec, u_int16_t mode);
u_int16_t ac97_getextmode(struct ac97_info *codec);
u_int16_t ac97_getextcaps(struct ac97_info *codec);
*/
@@ -1,21 +0,0 @@
#!/bin/sh
answer=`alert "Do you really want to install the SiS 7018 driver?" "No" "Yes"`
if [ $answer == "No" ]; then
exit
fi
/boot/beos/bin/install -d ~/config/add-ons/kernel/drivers/bin/
/boot/beos/bin/install -d ~/config/settings/kernel/drivers/
/boot/beos/bin/install -d ~/config/add-ons/kernel/drivers/dev/audio/old/
/boot/beos/bin/install `dirname $0`/sis7018 ~/config/add-ons/kernel/drivers/bin/
/boot/beos/bin/cp `dirname $0`/sis7018.settings.sample ~/config/settings/kernel/drivers/sis7018
mimeset ~/config/add-ons/kernel/drivers/bin/sis7018
mimeset ~/config/settings/kernel/drivers/sis7018
ln -sf ../../../bin/sis7018 ~/config/add-ons/kernel/drivers/dev/audio/old/sis7018
alert "The SiS 7018 driver is installed in your system.
Go to Preferences->Media and restart media services to activate driver."
@@ -1,176 +0,0 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
/*
* This file is a part of SiS 7018 BeOS driver project.
* Copyright permissions see above.
*
* Portion copyright (c) 2002-2003 by Siarzuk Zharski <imker@gmx.li>
*/
//#include <string.h>
//#include <stdlib.h>
//#include <signal.h>
#include "sis7018.h"
#include "sis7018hw.h"
#include "midi_driver.h"
#include "midi.h"
generic_mpu401_module *mpu401;
//extern void dump_card(sonic_vibes_dev * card);
#if !defined(_KERNEL_EXPORT_H)
#include <KernelExport.h>
#endif /* _KERNEL_EXPORT_H */
//#define MIDI_ACTIVE_SENSE 0xfe
//#define SNOOZE_GRANULARITY 500
//extern generic_mpu401_module * mpu401;
void midi_interrupt_op(int32 op, void * data)
{
midi_dev * port = (midi_dev *)data;
TRACE("port = %p\n", port);
if (op == B_MPU_401_ENABLE_CARD_INT)
{
cpu_status cp;
TRACE("B_MPU_401_ENABLE_CARD_INT\n");
cp = disable_interrupts();
acquire_spinlock(&port->card->hardware);
hw_increment_interrupt_handler(port->card);
//set_direct(port->card, 0x01, 0x00, 0x80);
//set_indirect(port->card, 0x2A, 0x04, 0xff);
release_spinlock(&port->card->hardware);
restore_interrupts(cp);
}
else if (op == B_MPU_401_DISABLE_CARD_INT)
{
/* turn off MPU interrupts */
cpu_status cp;
TRACE("B_MPU_401_DISABLE_CARD_INT\n");
cp = disable_interrupts();
acquire_spinlock(&port->card->hardware);
//set_direct(port->card, 0x01, 0x80, 0x80);
/* remove interrupt handler if necessary */
hw_decrement_interrupt_handler(port->card);
release_spinlock(&port->card->hardware);
restore_interrupts(cp);
}
TRACE("midi_interrupt_op() done\n");
}
static status_t midi_open(const char *name, uint32 flags, void **cookie);
static status_t midi_close(void *cookie);
static status_t midi_free(void *cookie);
static status_t midi_control(void *cookie, uint32 op, void *data, size_t len);
static status_t midi_read(void *cookie, off_t pos, void *data, size_t *len);
static status_t midi_write(void *cookie, off_t pos, const void *data, size_t *len);
device_hooks midi_hooks = {
&midi_open,
&midi_close,
&midi_free,
&midi_control,
&midi_read,
&midi_write,
NULL, /* select */
NULL, /* deselect */
NULL, /* readv */
NULL /* writev */
};
static status_t midi_open(const char * name, uint32 flags, void ** cookie)
{
int ix;
int ret;
TRACE("midi_open()\n");
*cookie = NULL;
for (ix=0; ix<num_cards; ix++)
{
if (!strcmp(name, cards[ix].midi.name))
{
break;
}
}
if (ix >= num_cards)
{
TRACE("bad device\n");
return ENODEV;
}
TRACE("mpu401: %p open(): %p driver: %p\n", mpu401, mpu401->open_hook, cards[ix].midi.driver);
ret = (*mpu401->open_hook)(cards[ix].midi.driver, flags, cookie);
if (ret >= B_OK)
{
cards[ix].midi.cookie = *cookie;
atomic_add(&cards[ix].midi.count, 1);
}
TRACE("mpu401: open returns %x / %p\n", ret, *cookie);
return ret;
}
static status_t midi_close(void * cookie)
{
TRACE("midi_close()\n");
return (*mpu401->close_hook)(cookie);
}
static status_t midi_free(void * cookie)
{
int ix;
status_t f;
TRACE("midi_free()\n");
f = (*mpu401->free_hook)(cookie);
for (ix=0; ix<num_cards; ix++)
{
if (cards[ix].midi.cookie == cookie)
{
if (atomic_add(&cards[ix].midi.count, -1) == 1)
{
cards[ix].midi.cookie = NULL;
TRACE("cleared %p card %d\n", cookie, ix);
}
break;
}
}
TRACE("midi_free() done\n");
return f;
}
static status_t midi_control(void * cookie, uint32 iop, void * data, size_t len)
{
TRACE("midi_control\n");
return (*mpu401->control_hook)(cookie, iop, data, len);
}
static status_t midi_read( void * cookie, off_t pos, void * ptr, size_t * nread)
{
TRACE("midi_read\n");
return (*mpu401->read_hook)(cookie, pos, ptr, nread);
}
static status_t midi_write(void * cookie, off_t pos, const void * ptr, size_t * nwritten)
{
TRACE("midi_write\n");
return (*mpu401->write_hook)(cookie, pos, ptr, nwritten);
}
bool midi_interrupt( sis7018_dev * dev)
{
if (!dev->midi.driver)
{
// kprintf("aiigh\n");
return false;
}
kprintf("midi_interrupt\n");
return (*mpu401->interrupt_hook)(dev->midi.driver);
}
@@ -1,19 +0,0 @@
/*
* This file is a part of SiS 7018 BeOS driver project.
* Copyright (c) 2002-2003 by Siarzuk Zharski <imker@gmx.li>
*
* This file may be used under the terms of the BSD License
* See the file "License" for details.
*/
#ifndef _MIDI_H_
#define _MIDI_H_
#ifndef _DRIVERS_H
#include <Drivers.h>
#endif //_DRIVERS_H
extern device_hooks midi_hooks;
void midi_interrupt_op(int32 op, void * data);
extern generic_mpu401_module *mpu401;
#endif //_MIDI_H_
@@ -1,117 +0,0 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
/*
* This file is a part of SiS 7018 BeOS driver project.
* Copyright permissions see above.
*
* Portion copyright (c) 2002-2003 by Siarzuk Zharski <imker@gmx.li>
*/
#ifndef _MIDI_DRIVER_H
#define _MIDI_DRIVER_H
#include <Drivers.h>
#include <module.h>
/* -----
ioctl codes
----- */
/* the old opcodes are deprecated, and may or may not work
in newer drivers */
enum {
B_MIDI_GET_READ_TIMEOUT = B_MIDI_DRIVER_BASE,
B_MIDI_SET_READ_TIMEOUT,
B_MIDI_TIMED_READ,
B_MIDI_TIMED_WRITE,
B_MIDI_WRITE_SYNC,
B_MIDI_WRITE_CLEAR,
B_MIDI_GET_READ_TIMEOUT_OLD = B_DEVICE_OP_CODES_END + 1,
B_MIDI_SET_READ_TIMEOUT_OLD
};
/* the default timeout when you open a midi driver */
#define B_MIDI_DEFAULT_TIMEOUT 1000000000000000LL
/* Usage:
To read, set "data" to a pointer to your buffer, and "size" to the
maximum size of this buffer. On return, "when" will contain the time
at which the data was received (first byte) and "size" will contain
the actual amount of data read.
Call ioctl(fd, B_MIDI_TIMED_READ, &midi_timed_data, sizeof(midi_timed_data));
To write, set "when" to when you want the first byte to go out the
wire, set "data" to point to your data, and set "size" to the size
of your data.
Call ioctl(fd, B_MIDI_TIMED_WRITE, &midi_timed_data, sizeof(midi_timed_data));
*/
typedef struct {
bigtime_t when;
size_t size;
unsigned char * data;
} midi_timed_data;
/* The MIDI parser returns the number of bytes that a message contains, given the */
/* initial byte. For some messages, this is not known until the second byte is seen. */
/* For such messages, a state > 0 is returned as well as some count > 0. When state */
/* is > 0, you should call (*parse) for the next byte as well, which might modify */
/* the returned message size. Message size will always be returned with the current */
/* byte being counted as byte 1. A return of 0 means that the byte initiates a new */
/* message. SysX is handled by returning max_size until the end or next initial message */
/* is seen. So your loop looks something like: */
/*
uint32 state = 0; // Only set this to 0 the first time you call the parser.
// preserve the 'state' between invocations of your read() hook.
int todo = 0;
unsigned char * end = in_buf+buf_size
unsigned char * out_buf = in_buf;
while (true) {
uchar byte = read_midi();
if (!todo || state) {
todo = (*parser->parse)(&state, byte, end-out_buf);
}
if (todo < 1) {
unput_midi(byte);
} else {
*(out_buf++) = byte;
todo--;
}
if (todo < 1 || out_buf >= end) {
received_midi_message(in_buf, out_buf-in_buf);
todo = 0;
}
}
*/
#define B_MIDI_PARSER_MODULE_NAME "media/midiparser/v1"
typedef struct _midi_parser_module_info {
module_info minfo;
int (*parse)(uint32 * state, uchar byte, size_t max_size);
int _reserved_;
} midi_parser_module_info;
#define B_MPU_401_MODULE_NAME "generic/mpu401/v1"
enum {
B_MPU_401_ENABLE_CARD_INT = 1,
B_MPU_401_DISABLE_CARD_INT
};
typedef struct _generic_mpu401_module {
module_info minfo;
status_t (*create_device)(int port, void ** out_storage, uint32 workarounds, void (*interrupt_op)(int32 op, void * card), void * card);
status_t (*delete_device)(void * storage);
status_t (*open_hook)(void * storage, uint32 flags, void ** out_cookie);
status_t (*close_hook)(void * cookie);
status_t (*free_hook)(void * cookie);
status_t (*control_hook)(void * cookie, uint32 op, void * data, size_t len);
status_t (*read_hook)(void * cookie, off_t pos, void * data, size_t * len);
status_t (*write_hook)(void * cookie, off_t pos, const void * data, size_t * len);
bool (*interrupt_hook)(void * cookie);
int _reserved_;
} generic_mpu401_module;
#endif
@@ -1,479 +0,0 @@
/*
* This file is a part of SiS 7018 BeOS driver project.
* Copyright (c) 2002-2003 by Siarzuk Zharski <imker@gmx.li>
*
* This file may be used under the terms of the BSD License
* See the file "License" for details.
*/
#include "pcm.h"
#include "sis7018.h"
#include "sis7018hw.h" // ioctl definitions
#include "sound.h"
#include "ac97.h"
#include "R3MediaDefs.h" // sound_buffer definition
#include "t4dwave.h"
#if DEBUG
int32 int_cnt;
int32 put_cnt;
bigtime_t the_time;
#endif
/* pcm_open - handle open() calls */
static status_t pcm_open (const char *name, uint32 flags, void** cookie)
{
int ix;
pcm_dev *port = NULL;
TRACE("pcm_open(%s, %lx)\n", name, flags);
*cookie = NULL;
for (ix=0; ix<num_cards; ix++)
if (!strcmp(name, cards[ix].pcm.name))
goto gotit;
for (ix=0; ix<num_cards; ix++)
if (!strcmp(name, cards[ix].pcm.oldname))
goto gotit;
TRACE("%s not found\n", name);
return ENODEV;
gotit:
*cookie = port = &cards[ix].pcm;
acquire_sem(port->init_sem);
if (atomic_add(&port->open_count, 1) == 0)
{
TRACE("pcm_open: first open\n");
port->card = &cards[ix];
port->open_mode = (flags & O_RWMASK);
if (port->open_mode != O_RDONLY)
;// TODO start playback "dma"
if (port->open_mode != O_WRONLY)
;//TODO start record "dma"
port->old_cap_sem = -1;
port->old_play_sem = -1;
hw_pchannel_init(port);
hw_increment_interrupt_handler(port->card);
}
else
{
if ((flags & O_RWMASK) == O_RDWR)
{
if (port->open_mode == O_RDONLY)
; //TODO start playback "dma"
else
if (port->open_mode == O_WRONLY)
; //TODO start record "dma"
port->open_mode = O_RDWR;
}
TRACE("pcm_open: next opens\n");
}
hw_start(port);
release_sem(port->init_sem);
return B_OK;
}
/* pcm_read - handle read() calls */
static status_t pcm_read (void* cookie, off_t position, void *buf, size_t* num_bytes)
{
pcm_dev * port = (pcm_dev *)cookie;
if (port->open_mode == O_WRONLY)
return EPERM;
// TRACE("pcm_read(%p, %d, %p, %d)\n", cookie, position, buf, *num_bytes);
*num_bytes = 0; /* tell caller nothing was read */
return B_IO_ERROR;
}
extern int intrs;
static status_t pcm_write(void * cookie, off_t pos, const void * data, size_t * nwritten)
{
pcm_dev * port = (pcm_dev *)cookie;
sis7018_ch *ch = &((pcm_dev *)cookie)->play_channel;
status_t err;
cpu_status cp;
int written = 0;
int to_write = *nwritten; /* in play bytes, not input bytes! */
int block;
int bytes_xferred;
if (port->open_mode == O_RDONLY)
return EPERM;
*nwritten = 0;
if ((err = acquire_sem_etc(ch->wr_entry, 1, B_CAN_INTERRUPT, 0)) < B_OK)
{
TRACE("wr_entry acquisition failed\n");
return err;
}
atomic_add(&ch->write_waiting, 1);
while (to_write > 0)
{
/* wait to write */
if ((err = acquire_sem_etc(ch->write_sem, 1, B_CAN_INTERRUPT, 0)) < B_OK)
{
TRACE("write_sem acquisition failed\n");
release_sem(ch->wr_entry);
return err;
}
#if DEBUG
put_cnt++;
{
bigtime_t delta = system_time() - the_time;
if (delta < 1) {
TRACE("delta %Ld (low!) #%ld\n", delta, put_cnt);
}
else if (delta > 2000) {
TRACE("delta %Ld (high!) #%ld\n", delta, put_cnt);
}
}
if (put_cnt != int_cnt) {
static int last;
if (last != int_cnt-put_cnt)
TRACE("%ld mismatch\n", int_cnt-put_cnt);
last = int_cnt-put_cnt;
}
#endif /* DEBUG */
// KTRACE();
cp = disable_interrupts();
acquire_spinlock(&ch->wr_lock);
block = ch->wr_size-ch->was_written;
if (block > to_write)
{
/* must let next guy in */
if (atomic_add(&ch->write_waiting, -1) > 0)
release_sem_etc(ch->write_sem, 1, B_DO_NOT_RESCHEDULE);
else
atomic_add(&ch->write_waiting, 1); /* undo damage */
block = to_write;
}
else
if (block < to_write)
atomic_add(&ch->write_waiting, 1); /* we will loop back */
/* switch (port->config.format)
{*/
// case 0x24: /* floats */
/* copy_float_to_short((short *)(port->wr_cur+port->was_written), (const float *)data,
block, !B_HOST_IS_LENDIAN == !port->config.big_endian);
bytes_xferred = block * 2;
break; */
// case 0x02: /* shorts */
// if (!B_HOST_IS_LENDIAN == !port->config.big_endian) {
/* we need to swap */
/* swap_copy((short *)(port->wr_cur+port->was_written), (const short *)data, block);
bytes_xferred = block;
break;
}
*/ /* else fall through to default case */
// case 0x11: /* bytes */
// default:
// my_copy((short *)(ch->wr_cur+ch->was_written), (const short *)data, block);
memcpy((void *)(ch->wr_cur+ch->was_written), data, block);
bytes_xferred = block;
/* break;
}*/
ch->was_written += block;
ch->wr_silence = 0;
release_spinlock(&ch->wr_lock);
restore_interrupts(cp);
#if DEBUG
if(ch->wr_cur == ch->wr_cur_prv)
{
int tt = (system_time() - ch->time_prv)/1000;
TRACE("%d ms block - %x, to_write: %x ,size: %x,was_written: %x\n", tt, block, to_write, ch->wr_size, ch->was_written);
ch->time_prv = system_time();
}
ch->wr_cur_prv = ch->wr_cur;
#endif
data = ((char *)data)+bytes_xferred;
written += bytes_xferred;
to_write -= block;
}
*nwritten = written;
release_sem(ch->wr_entry);
return B_OK;
}
bool playback_interrupt(sis7018_ch * ch, bool bHalf)
{
bool ret = false;
// pcm_dev * port = &dev->pcm;
// sis7018_ch *ch = &dev->pcm.play_channel;
volatile uchar * ptr;
// uint32 addr;
uint32 offs;
bigtime_t st = system_time();
// int32 ww;
// sis7018_ch ch2 = *ch;
#if 0
ddprintf(("sonic_vibes: dma_a 0x%x+0x%x\n", PCI_IO_RD_32((int)port->dma_c), PCI_IO_RD_32((int)port->dma_c+4)));
#endif
// KTRACE(); /* */
acquire_spinlock(&ch->wr_lock);
if (ch->write_sem < 0)
{
dprintf("spurious play interrupt!\n");
release_spinlock(&ch->wr_lock);
return false;
}
/* copy possible silence into playback buffer */
if (ch->was_written > 0 && ch->was_written < ch->wr_size)
// {
/* if (port->config.format == 0x11)
{
memset((void *)(port->wr_cur+port->was_written), 0x80, port->wr_size-port->was_written);
}
else {*/
memset((void *)(ch->wr_cur+ch->was_written), 0, ch->wr_size-ch->was_written);
// }
// }
/* because the system may be lacking and not hand us the */
/* interrupt in time, we check which half is currently being */
/* played, and set the pointer to the other half */
// addr = PCI_IO_RD_32((uint32)port->dma_a);
// if ((offs = addr-(uint32)port->card->low_phys) < port->wr_size)
// hw_rdch(&ch2);
// select channel ...
offs = hw_read(ch->card, TR_REG_CIR, 4);
offs &= ~TR_CIR_MASK;
offs |= ch->index & 0x3f;
hw_write(ch->card, TR_REG_CIR, offs, 4);
// read current cso
switch(ch->card->type->ids.chip_id)
{
case SPA_PCI_ID:
case ALI_PCI_ID:
case TDX_PCI_ID:
case xDX_PCI_ID:
offs=((hw_read(ch->card, TR_REG_CHNBASE, 4) & 0xffff0000) >> 16) * 4;
break;
case TNX_PCI_ID:
offs=(hw_read(ch->card, TR_REG_CHNBASE, 4) & 0x00ffffff) * 4;
break;
}
if (offs < ch->wr_size)
ptr = ch->wr_2;
else
ptr = ch->wr_1;
ch->wr_total += ch->mem_size/2;
/* compensate for interrupt latency */
/* assuming 4 byte frames */
ch->wr_time = st-(offs&(ch->mem_size/2-1))*250000LL/(int64)48000;
/* if ((ww = atomic_add(&ch->wr_time_wait, -1)) > 0)
{
release_sem_etc(ch->wr_time_sem, 1, B_DO_NOT_RESCHEDULE);
ret = true;
}
else
{
*/// atomic_add(&ch->wr_time_wait, 1); /* re-set to 0 */
// }
if (ch->wr_cur == ptr)
{
ch->wr_skipped++;
dprintf("write skipped %ld\n", ch->wr_skipped);
}
ch->wr_cur = ptr;
ch->was_written = 0;
/* check for client there to write into buffer */
if (atomic_add(&ch->write_waiting, -1) > 0)
{
#if DEBUG
int_cnt++;
the_time = st;
#endif
release_sem_etc(ch->write_sem, 1, B_DO_NOT_RESCHEDULE);
ret = true;
}
else
{
atomic_add(&ch->write_waiting, 1);
/* if none there, fill with silence */
if (ch->wr_silence < ch->mem_size)
{
/* if (port->config.format == 0x11) {
memset((void *)ptr, 0x80, port->wr_size);
}
else {*/
memset((void *)ptr, 0, ch->wr_size);
//}
ch->wr_silence += ch->wr_size;
}
}
/* copying will be done in user thread */
release_spinlock(&ch->wr_lock);
return ret;
}
/* pcm_control - handle ioctl calls */
static status_t pcm_control (void* cookie, uint32 op, void* arg, size_t len)
{
status_t err = B_BAD_VALUE;
pcm_dev * port = (pcm_dev *)cookie;
// TRACE("pcm_control(%p, %lx)\n", cookie, op);
switch (op)
{
case SOUND_GET_PARAMS:
// TRACE("SOUND_GET_PARAMS\n");
err = ac97get_params(port->card, (sound_setup *)arg);
break;
case SOUND_SET_PARAMS:
// TRACE("SOUND_SET_PARAMS\n");
err = ac97set_params(port->card, (sound_setup *)arg);
break;
case SOUND_SET_PLAYBACK_COMPLETION_SEM:
// TRACE("SOUND_SET_PLAYBACK_COMPLETION_SEM\n");
port->old_play_sem = *(sem_id *)arg;
err = B_OK;
break;
case SOUND_SET_CAPTURE_COMPLETION_SEM:
// TRACE("SOUND_SET_CAPTURE_COMPLETION_SEM\n");
port->old_cap_sem = *(sem_id *)arg;
err = B_OK;
break;
case SOUND_UNSAFE_WRITE:
{
sis7018_ch *ch = &((sis7018_dev *)cookie)->pcm.play_channel;
audio_buffer_header * buf = (audio_buffer_header *)arg;
size_t n = buf->reserved_1-sizeof(*buf);
//TRACE("%ld %ld %ld %ld\n", buf->reserved_1, buf->reserved_2, buf->sample_clock, buf->time );
//TRACE("SOUND_UNSAFE_WRITE %d\n", n);
err = pcm_write(cookie, 0, buf+1, &n);
buf->time = ch->wr_time;
buf->sample_clock = ch->wr_total/4 * 1000 / 48;
err |= release_sem(port->old_play_sem);
// err = B_OK;
}
break;
case SOUND_UNSAFE_READ:
TRACE("SOUND_UNSAFE_READ\n");
// ASSERT(0);
err = release_sem(port->old_cap_sem);
err = B_IO_ERROR;
break;
case SOUND_LOCK_FOR_DMA:
TRACE("SOUND_LOCK_FOR_DMA\n");
err = B_OK;
break;
case SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE:
{
int32 new_size = (int32)arg;
TRACE("SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE: 0x%x\n", new_size);
hw_pchannel_setblocksize(port, new_size);
err = B_OK;
}
break;
case SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE:
// TRACE("SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE\n");
// config.rec_buf_size = (int32)data;
// configure = true;
err = B_OK;
break;
case SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE:
TRACE("SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE\n");
*(int32*)arg = port->play_channel.mem_size/2;
err = B_OK;
break;
case SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE:
// TRACE("SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE\n");
// *(int32*)data = config.rec_buf_size;
err = B_OK;
break;
default:
TRACE("pcm_control : unknown code %ld\n", op);
err = B_BAD_VALUE;
break;
}
return err;
}
/* pcm_close - handle close() calls */
static status_t pcm_close (void* cookie)
{
pcm_dev * port = (pcm_dev *)cookie;
TRACE("pcm_close(%p)\n", cookie);
acquire_sem(port->init_sem);
if (atomic_add(&port->open_count, -1) == 1)
{
TRACE("pcm_close: last close\n");
hw_stop(port);
hw_pchannel_free(port);
}
else
{
TRACE("pcm_close: previous closes\n");
}
release_sem(port->init_sem);
return B_OK;
}
/* pcm_free - called after the last device is closed, and after all i/o is complete. */
static status_t pcm_free (void* cookie)
{
pcm_dev *port = (pcm_dev *)cookie;
TRACE("pcm_free(%p)\n", cookie);
acquire_sem(port->init_sem);
if (port->open_count == 0)
{
hw_decrement_interrupt_handler(port->card);
}
release_sem(port->init_sem);
return B_OK;
}
/* function pointers for the device hooks entry points */
device_hooks pcm_hooks =
{
pcm_open, /* -> open entry point */
pcm_close, /* -> close entry point */
pcm_free, /* -> free cookie */
pcm_control, /* -> control entry point */
pcm_read, /* -> read entry point */
pcm_write /* -> write entry point */
};
@@ -1,43 +0,0 @@
/*
* This file is a part of SiS 7018 BeOS driver project.
* Copyright (c) 2002-2003 by Siarzuk Zharski <imker@gmx.li>
*
* This file may be used under the terms of the BSD License
* See the file "License" for details.
*/
#ifndef _PCM_H_
#define _PCM_H_
#ifndef _DRIVERS_H
#include <Drivers.h>
#endif //_DRIVERS_H
#ifndef _SIS7018_H_
#include "sis7018.h"
#endif //_SIS7018_H_
extern device_hooks pcm_hooks;
bool playback_interrupt(sis7018_ch *ch, bool bHalf);
/* secret handshake ioctl()s */
/*#define SV_SECRET_HANDSHAKE 10100
typedef struct {
bigtime_t wr_time;
bigtime_t rd_time;
uint32 wr_skipped;
uint32 rd_skipped;
uint64 wr_total;
uint64 rd_total;
uint32 _reserved_[6];
} sv_handshake;
#define SV_RD_TIME_WAIT 10101
#define SV_WR_TIME_WAIT 10102
typedef struct {
bigtime_t time;
bigtime_t bytes;
uint32 skipped;
uint32 _reserved_[3];
} sv_timing;
*/
#endif //_PCM_H_
@@ -1,441 +0,0 @@
/*
* This file is a part of SiS 7018 BeOS driver project.
* Copyright (c) 2002-2003 by Siarzuk Zharski <imker@gmx.li>
*
* This file may be used under the terms of the BSD License
* See the file "License" for details.
*/
#include <stdio.h> //sprintf
#include <string.h>
#include <unistd.h> //posix file i/o - create, write, close
#include <driver_settings.h>
#include "sis7018.h"
#include "sis7018hw.h"
#include "ac97.h"
#if DO_PCM
#include "pcm.h"
#endif
#if DO_MIDI
#include "midi_driver.h"
#include "midi.h"
#endif
#define NUM_CARDS 3
int32 num_cards;
sis7018_dev cards[NUM_CARDS];
int num_names;
char * names[NUM_CARDS*7+1];
#if DEBUG
bool b_log = true;
#else
bool b_log = false;
#endif
bool b_log_file = false;
bool b_log_append = false;
bool b_trace_ac97 = false;
bool use_io_regs = false;
static const char *private_log_path="/boot/home/sis7018.log";
static sem_id loglock;
static void reload_sis7018_setting()
{
void *settingshandle;
settingshandle = load_driver_settings(CHIPNAME);
#if !DEBUG
b_log = get_driver_boolean_parameter(settingshandle, "debug_output", b_log, true);
#endif
b_log_file = get_driver_boolean_parameter(settingshandle, "debug_output_in_file", b_log_file, true);
b_log_append = ! get_driver_boolean_parameter(settingshandle, "debug_output_file_rewrite", !b_log_append, true);
b_trace_ac97 = get_driver_boolean_parameter(settingshandle, "debug_trace_ac97", b_trace_ac97, true);
use_io_regs = get_driver_boolean_parameter(settingshandle, "use_io_registers", use_io_regs, true);
unload_driver_settings(settingshandle);
}
void create_log()
{
int flags = O_WRONLY | O_CREAT | ((!b_log_append) ? O_TRUNC : 0);
if(!b_log_file)
return;
close(open(private_log_path, flags, 0666));
loglock = create_sem(1,"sis7018 logfile");
}
void sis7018_trace(bool b_force, char *fmt, ...)
{
if(!(b_force || b_log))
return;
{
va_list arg_list;
static char *prefix = "\33[32m"CHIPNAME":\33[0m";
static char buf[1024];
char *buf_ptr = buf;
if(!b_log_file)
{
strcpy(buf, prefix);
buf_ptr += strlen(prefix);
}
va_start(arg_list, fmt);
vsprintf(buf_ptr, fmt, arg_list);
va_end(arg_list);
if(b_log_file)
{
int fd;
if(!b_trace_ac97)
acquire_sem(loglock);
fd = open(private_log_path, O_WRONLY | O_APPEND);
write(fd, buf, strlen(buf));
close(fd);
if(!b_trace_ac97)
release_sem(loglock);
}
else
dprintf(buf);
}
}
card_type card_types[]=
{
{{TDX_PCI_ID}, "Trident DX"},
{{xDX_PCI_ID}, "Trident XX"},
{{TNX_PCI_ID}, "Trident NX"},
{{ALI_PCI_ID}, "ALi 5451"},
{{SPA_PCI_ID}, "SiS 7018"},
};
#ifdef DEBUG
int _assert_(char *a, int b, char *c)
{
char sz[1024];
sprintf(sz, CHIPNAME":tripped assertion in %s/%d (%s)", a, b, c);
TRACE("tripped assertion in %s/%d (%s)\n", a, b, c);
kernel_debugger(sz);
return 0;
}
#endif
static void make_device_names(sis7018_dev * card)
{
char * name = card->name;
sprintf(name, "%s/%ld", card->type->chip_name, card-cards+1);
#if DO_MIDI
sprintf(card->midi.name, "midi/%s", name);
names[num_names++] = card->midi.name;
#endif //DO_MIDI
#if DO_PCM
// sprintf(card->pcm.name, "audio/raw/%s", name);
// names[num_names++] = card->pcm.name;
sprintf(card->pcm.oldname, "audio/old/%s", name);
names[num_names++] = card->pcm.oldname;
#endif //DO_PCM
names[num_names] = NULL;
}
static status_t setup_hardware( sis7018_dev * card)
{
status_t err = B_OK;
int32 command_reg;
TRACE("setup_hardware(%p)\n", card);
if ((card->pcm.init_sem = create_sem(1, "sis7018 pcm init")) < B_OK)
goto bail;
#if DO_MIDI //TODO: below?
if ((*mpu401->create_device)(card->info.u.h0.base_registers[3], &card->midi.driver,
0, midi_interrupt_op, &card->midi) < B_OK)
goto bail3;
TRACE("midi %p\n", card->midi.driver);
card->midi.card = card;
#endif //DO_MIDI
make_device_names(card);
command_reg = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function,
PCI_command, 2);
// TRACE("PCI command config was: %lx\n", command_reg);
command_reg |= PCI_command_io | PCI_command_memory | PCI_command_master;
(*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function,
PCI_command, 2, command_reg);
// TRACE("PCI command config set to: %lx\n", command_reg);
if(use_io_regs)
{
card->io_base = card->info.u.h0.base_registers[0];
TRACE("%s base at %x\n", card->name, card->io_base);
}
else // USE_IO_RANGE
{
TRACE("%s memory %lx length %lx\n", card->name, card->info.u.h0.base_registers[1], card->info.u.h0.base_register_sizes[1]);
card->regs_area = map_physical_memory("Regs sis7018",
card->info.u.h0.base_registers[1],
card->info.u.h0.base_register_sizes[1],
B_ANY_KERNEL_ADDRESS,
B_READ_AREA + B_WRITE_AREA,
&card->regs_mem_base);
TRACE("%s registers memory base at 0x%p\n", card->name, card->regs_mem_base);
}// USE_IO_RANGE
err = hw_initialize(card);
return err;
#if DO_MIDI
bail3:
#endif //DO_MIDI
delete_sem(card->pcm.init_sem);
bail:
return err < B_OK ? err : B_ERROR;
}
#if DEBUG
static int debug_sis7018(int argc, char * argv[])
{
int err = (argc < 2 || argc > 3);
int card_id = 0;
if(!err)
{
if(argc == 3)
card_id = parse_expression(argv[2]);
if(card_id < 0 || card_id > num_cards-1)
{
kprintf("ERROR: Wrong card_id: %d\n", card_id);
kprintf("\n");
err = 1;
}
else
{
switch((char)*(argv[1]))
{
case 'd': hw_dump_device(card_id); break;
case 'i': hw_dump_pci_info(card_id); break;
case 'p': hw_dump_pcm_device(card_id); break;
case 'm': hw_dump_midi_device(card_id); break;
case 'c': hw_dump_play_channel(card_id); break;
case 'a': ac97dump(card_id); break;
default:
kprintf("ERROR: Wrong command: %s\n",argv[1]);
kprintf("\n");
err=1;
break;
}
}
}
if (err)
{
kprintf("Syntax: "CHIPNAME" < command > [card id]\n");
kprintf(" - where commands is:\n");
kprintf(" d - dump device info\n");
kprintf(" i - dump pci_info\n");
kprintf(" p - dump pcm info\n");
kprintf(" m - dump midi info\n");
kprintf(" c - dump playback channel info\n");
kprintf(" a - dump AC'97 registers info\n");
kprintf(" - and card id is zero-based id of card to be investigated\n");
kprintf(" default card id is 0\n");
kprintf(" current cards count is %ld\n", num_cards);
kprintf("\n");
}
return 0;
}
#endif /* DEBUG */
/************** SYSTEM CALLBACKS ************************************/
int32 api_version = B_CUR_DRIVER_API_VERSION;
/* init_hardware - called once the first time the driver is loaded */
status_t init_hardware (void)
{
int ix = 0, jx = 0;
pci_info info;
status_t err = ENODEV;
TRACE("init_hardware()\n");
if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci))
return ENOSYS;
while ((*pci->get_nth_pci_info)(ix, &info) == B_OK)
{
for(jx = 0; jx < sizeof(card_types)/sizeof(card_types[0]); jx++)
if (info.vendor_id == card_types[jx].ids.ids.vendor_id
&& info.device_id == card_types[jx].ids.ids.card_id)
{
err = B_OK;
}
ix++;
}
put_module(B_PCI_MODULE_NAME);
return err;
}
status_t
init_driver(void)
{
int ix = 0, jx = 0;
pci_info info;
num_cards = 0;
reload_sis7018_setting();
create_log();
TRACE("\n>>> init_driver()\n");
if (get_module(B_PCI_MODULE_NAME, (module_info **) &pci))
return ENOSYS;
#if DO_MIDI
TRACE("MPU\n");
if (get_module(B_MPU_401_MODULE_NAME, (module_info **) &mpu401))
{
put_module(B_PCI_MODULE_NAME);
return ENOSYS;
}
TRACE("MPU: %p\n", mpu401);
#endif //DO_MIDI
while ((*pci->get_nth_pci_info)(ix, &info) == B_OK)
{
for(jx = 0; jx < sizeof(card_types)/sizeof(card_types[0]); jx++)
if (info.vendor_id == card_types[jx].ids.ids.vendor_id
&& info.device_id == card_types[jx].ids.ids.card_id)
{
if (num_cards == NUM_CARDS)
{
TRACE_ALWAYS("Too many cards installed! Only %d will be used.\n", NUM_CARDS);
break;
}
memset(&cards[num_cards], 0, sizeof(sis7018_dev));
cards[num_cards].info = info;
cards[num_cards].type = &card_types[jx];
if (setup_hardware(&cards[num_cards]))
{
TRACE_ALWAYS("Setup of %s driver for %ld-th card failed\n", CHIPNAME, num_cards+1);
}
else
num_cards++;
TRACE_ALWAYS("%s <vendor:%x, card:%x> found.\n",
card_types[jx].chip_name,
card_types[jx].ids.ids.vendor_id,
card_types[jx].ids.ids.card_id);
}
ix++;
}
if (!num_cards)
{
#if DO_MIDI
put_module(B_MPU_401_MODULE_NAME);
#endif //DO_MIDI
put_module(B_PCI_MODULE_NAME);
TRACE("No suitable cards found\n");
return ENODEV;
}
#if DEBUG
add_debugger_command(CHIPNAME, debug_sis7018, CHIPNAME" driver info.");
#endif
return B_OK;
}
static void teardown_card(sis7018_dev *card)
{
/* remove created devices */
#if DO_MIDI
(*mpu401->delete_device)(card->midi.driver);
#endif //DO_MIDI
if(!use_io_regs)
delete_area(card->regs_area);
delete_sem(card->pcm.init_sem);
}
/* uninit_driver - optional function - called every time the driver is unloaded */
void uninit_driver (void)
{
int ix, cnt = num_cards;
num_cards = 0;
TRACE("<<< uninit_driver()\n\n");
#if DEBUG
remove_debugger_command(CHIPNAME, debug_sis7018);
#endif
for (ix=0; ix<cnt; ix++)
{
teardown_card(&cards[ix]);
}
memset(&cards, 0, sizeof(cards));
#if DO_MIDI
put_module(B_MPU_401_MODULE_NAME);
#endif // DO_MIDI
put_module(B_PCI_MODULE_NAME);
}
/* publish_devices - return a null-terminated array of devices
supported by this driver. */
const char** publish_devices()
{
int ix = 0;
TRACE("publish_devices()\n");
for (ix=0; names[ix]; ix++)
TRACE("publish %s\n", names[ix]);
return (const char **)names;
}
/* find_device - return ptr to device hooks structure for a
given device name */
device_hooks* find_device(const char* name)
{
int ix;
TRACE("find_device(%s)\n", name);
for (ix=0; ix<num_cards; ix++)
{
#if DO_MIDI
if (!strcmp(cards[ix].midi.name, name))
{
return &midi_hooks;
}
#endif /* DO_MIDI */
#if DO_PCM
if (!strcmp(cards[ix].pcm.name, name))
{
return &pcm_hooks;
}
if (!strcmp(cards[ix].pcm.oldname, name))
{
return &pcm_hooks;
}
#endif //DO_PCM
}
TRACE("find_device(%s) failed\n", name);
return NULL;
}
@@ -1,166 +0,0 @@
/*
* This file is a part of SiS 7018 BeOS driver project.
* Copyright (c) 2002-2003 by Siarzuk Zharski <imker@gmx.li>
*
* This file may be used under the terms of the BSD License
* See the file "License" for details.
*/
#ifndef _SIS7018_H_
#define _SIS7018_H_
#include <KernelExport.h>
#include <Drivers.h>
#include <Errors.h>
#include <PCI.h>
#define CHIPNAME "sis7018"
void sis7018_trace(bool b_force, char *fmt, ...);
//#define TRACE_ALWAYS(x...) dprintf("\33[32m"CHIPNAME":\33[0m"x);
#define TRACE_ALWAYS(x...) sis7018_trace(true, x);
#define TRACE(x...) sis7018_trace(false, x);
extern bool b_trace_ac97;
#if DEBUG
//#define TRACE(x...) TRACE_ALWAYS(x)
int _assert_(char *,int,char *);
#define ASSERT(c) (!(c) ? _assert_(__FILE__, __LINE__, #c) : 0)
#else
//#define TRACE(x...)
#define ASSERT(c)
#endif
#define DEVNAME 32
#define TDX_PCI_ID 0x20001023 // Trident 4D Wave/DX
#define xDX_PCI_ID 0x20000003 // Hypotese?
#define TNX_PCI_ID 0x20011023 // Trident 4D Wave/NX
#define ALI_PCI_ID 0x545110b9 // Acer Labs 5451 Audio
#define SPA_PCI_ID 0x70181039 // SiS PCI Audio
typedef struct _card_type
{
union _ids
{
uint32 chip_id;
struct {
uint16 vendor_id;
uint16 card_id;
} ids;
} ids;
char chip_name[DEVNAME];
} card_type;
#define DO_PCM 1
#define DO_MIDI 0
//#define USE_IO_REGS 0
extern bool use_io_regs;
typedef struct _midi_dev {
struct _sis7018_dev *card;
void * driver;
void * cookie;
int32 count;
char name[64];
} midi_dev;
/* channel registers */
typedef struct _sis7018_ch {
int32 cso, alpha, fms, fmc, ec;
int32 lba;
int32 eso, delta;
int32 rvol, cvol;
int32 gvsel, pan, vol, ctrl;
int32 active/*:1, was_active:1*/;
int index/*, bufhalf*/;
struct _sis7018_dev *card;
size_t mem_size; /* size of low memory */
vuchar * mem_ptr;
vuchar * mem_phys; /* physical address */
area_id areaid; /* area pointing to low memory */
vuchar * wr_1;
vuchar * wr_2;
vuchar * wr_cur;
#if DEBUG
vuchar * wr_cur_prv;
bigtime_t time_prv;
bool prevInt;
#endif
size_t wr_size;
int32 wr_lock;
int wr_silence;
int32 write_waiting;
sem_id write_sem;
size_t was_written;
uint32 wr_skipped;
sem_id wr_entry;
bigtime_t wr_time;
uint64 wr_total;
// sem_id wr_time_sem;
// int32 wr_time_wait;
} sis7018_ch;
typedef struct {
struct _sis7018_dev * card;
char name[DEVNAME];
int32 open_count;
int32 open_mode;
sem_id init_sem;
sem_id old_cap_sem;
sem_id old_play_sem;
char oldname[DEVNAME];
struct _sis7018_ch play_channel;
} pcm_dev;
typedef struct _ac97_codec
{
bool wide_main_gain;
} ac97_codec;
typedef struct _sis7018_dev
{
char name[DEVNAME]; /* used for resources */
int32 hardware; /* spinlock */
//#if(USE_IO_REGS)
int io_base; /* offset to port */
//#else
void *regs_mem_base;
//#endif
int32 inth_count;
/* int dma_base;
size_t low_size;*/ /* size of low memory */
/* vuchar * low_mem;
vuchar * low_phys;*/ /* physical address */
// area_id map_low; /* area pointing to low memory */
//#if(!USE_IO_REGS)
area_id regs_area;
//#endif
pci_info info;
midi_dev midi;
/* joy_dev joy;*/
pcm_dev pcm;
/* mux_dev mux;
mixer_dev mixer;*/
card_type *type;
ac97_codec ac97;
} sis7018_dev;
extern int32 num_cards;
extern sis7018_dev cards[];
/*uint32 io_rd(sis7018_dev *dev, int offset, int size);
void io_wr(sis7018_dev *dev, int offset, uint32 value, int size);
int rd_cd(sis7018_dev *dev, int regno);
int wr_cd(sis7018_dev *dev, int regno, uint32 data);
void increment_interrupt_handler(sis7018_dev * card);
void decrement_interrupt_handler(sis7018_dev * card);
*/
#endif //_SIS7018_H_
@@ -1,54 +0,0 @@
resource(101, "BEOS:L:STD_ICON") #'ICON' array {
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFF3F7E3DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFFFFFF3F3F7E3E28FFFFFFFFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFFFF3F3F7E3E5F3E28FFFFFFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFF3F3F7E3E5F3E5F3E27FFFFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFF3F3F7E3E5F3E5F3E937E3DFFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFF3F3F7E3E5F3E5F3E937E3E5FDFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFF3F3F7E3E5F3E5F3E937E3E5F3E9301FFFFFFFFFFFFFFFFFF"
$"FFFFFF3F3F3F3F3F3F7E3E5F3E5F3F3F3F3E937E3E5F403F3F3F3F3FFFFFFFFF"
$"FFFF3FFAFAFAFAFAFA3F7E3E5F1FFAFAFA3F3E5F3E3FFCFAFAFAFAFA1FFFFFFF"
$"FF3FFAFAFAFAFAFAFAFA3F7E3E3FFBFAFA3F257E3FFAFAFAFAFAFAFAFA3FFFFF"
$"3FFAFAFA3F3F3F3FFAFAFA3F7E1FFAFAFA1FDD3FFAFAFA3F3F3F3FFAFAFA3FFF"
$"3FFAFA3F252525253F3F3F3F24253F3F3F25253FFAFA3F252525253F3F3F3F25"
$"3FFAFA3F257E3E5FDD242525257E252525253E3FFCFA1F257F3E5EDD25242525"
$"3FFAFAFA3F3F7E3E5F3E5F3E93403F3F3F3F7E1FFAFAFA3F3F3E937E3E28FFFF"
$"253FFAFAFAFA3F3F7E3E5F3E5F3FFAFAFA3FDC263FFAFAFAFA3F3F7E3E5F3DFF"
$"FF253F3FFAFAFAFA3F3F7E3E5F3FFAFAFA3FDD7E253F3FFAFAFAFA3F3F3E5F3D"
$"FFFF25253F3FFAFAFAFA3F7E3E3FFBFAFA3F247F3E21253F3FFAFAFAFA3F01FF"
$"FFFFFF3F25253F3FFAFAFA3F7E1FFAFAFA3FDD7E3E5F3E21253F3FFAFAFA3FFF"
$"FFFFFFFF3F7E26253FFAFA3F253FFAFAFA3F243E5F3E5F3E93242540FAFA3F25"
$"3F3F3F3FFF3F7E3E3FFBFA3F253FFAFAFA3F241F3F3F3F3E5F3E5F3FFAFA3FDC"
$"3FFAFAFA3F3F3F3FFAFAFA3F253FFAFAFA3F253FFAFAFA3F3F3F3FFAFAFA3F25"
$"253FFAFAFAFAFAFAFAFA3F25253FFAFAFA3F25241FFAFAFAFAFAFAFAFA3F2525"
$"FF253FFAFAFAFAFA3F3F25257E1FFAFAFA3FDC7F253FFAFAFAFAFA3F3F2524FF"
$"FFFF253F3F3F3F3F2525257E3E3F3F3F3F3F227E3E221F3F3F3F3F252525FFFF"
$"FFFFFF252525252425FF3F3F7F2425252525243E5F3E222524252525FFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFF3F3F7E3E5F3E5F3E937E3DE0FFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFFFF3F3F7E3E5F3E5F3E27FFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFFFFFF3F3F7E3E5F3E28FFFFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFF3F3F7E3E28FFFFFFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F7E3DFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
};
resource(101, "BEOS:M:STD_ICON") #'MICN' array {
$"FFFFFFFFFFFFFFB500FFFFFFFFFFFFFF"
$"FFFFFFFFFFFF19947F01FFFFFFFFFFFF"
$"FFFFFFFFFF19943E5F7F01FFFFFFFFFF"
$"FFFFFFFF18947E3E5F3E7E3DFFFFFFFF"
$"FF63FCFD687E6EFC687E74FCFDFC07FF"
$"63FAFDFCFA6867FAFCB4E5FBFDFBFB07"
$"FCFDDDDCAD12248DD487FA10DCB310D3"
$"17FAFC687E3E6CFDFED4FBFB42947F00"
$"29ADFCFAFD9468FAFDDDB418FBFB4229"
$"B807B410E5FD8DFAFC7E7F7EAE15FA10"
$"FEFC186DFBFCA7FAFD8EFD626E19FA11"
$"D4FBFAFAFCB487FAFC2468FAFAFB6226"
$"FFB5A78D278DB4A78EDC7E11ADCE27FF"
$"FFFFFFFFFF07417E7F3EDCFFFFFFFFFF"
$"FFFFFFFFFFFF07617E3BFFFFFFFFFFFF"
$"FFFFFFFFFFFFFFD6DEFFFFFFFFFFFFFF"
};
@@ -1,37 +0,0 @@
#
# The sample sis7018 driver settings file
#
# Rename it to sis7018 and put into
# /boot/home/config/settings/kernel/drivers/
# After restarting Media Services the new settings will works.
# Debug output - this is a possibility to view some kind of development related
# information - error and warning messages etc.
# debug_output - activates debug output. The debug builds has debug output
# activated by default and this key has no effect on it. The release builds requires
# to activate this if you want to see debug messages in syslog or in private log file.
#
# debug_output true
# debug_output_in_file - activates writing of debug messages into private log file.
# The name of this file is /boot/home/sis7018.log. If this key is set to true - no messages
# will be written into syslog.
#
# debug_output_in_file true
# debug_output_file_rewrite - activates rewriting of private log file on
# calling driver_init() syscall. By default this set in false.
#
# debug_output_file_rewrite false
# debug_trace_ac97 - activates more detailed logging of AC'97 operations.
# Do not use it with syslog output - you'll get only <TRUNCATED> messages
# because BeOS's syslog daemon don't like a lot of messages in short time
#
# debug_trace_ac97 true
# use_io_registers - switches driver from "memory" scheme to "io registers"
# scheme. This key is usefull for testers. Don't use it if you don't need it.
#
# use_io_registers true
@@ -1,563 +0,0 @@
/*
* This file is a part of SiS 7018 BeOS driver project.
* Copyright (c) 2002-2003 by Siarzuk Zharski <imker@gmx.li>
*
* This file may be used under the terms of the BSD License
* See the file "License" for details.
*/
#include <stdio.h> //sprintf
#include <string.h>
#include "sis7018.h"
#include "sis7018hw.h"
#include "t4dwave.h"
#include "ac97.h"
#include "pcm.h"
pci_module_info* pci;
#define MIN_MEMORY_SIZE 0x2000
#define PCHANNEL_ID 0x00
int intrs;
uint32 hw_read(sis7018_dev *dev, int offset, int size)
{
uint32 ret = 0xffffffff;
if(use_io_regs){
switch(size){
case 1: ret = (*pci->read_io_8)(dev->io_base + offset); break;
case 2: ret = (*pci->read_io_16)(dev->io_base + offset); break;
case 4: ret = (*pci->read_io_32)(dev->io_base + offset); break;
default: break;
}
}
else{
switch(size){
case 1: ret = *(int8 *)((char *)dev->regs_mem_base + offset); break;
case 2: ret = *(int16 *)((char *)dev->regs_mem_base + offset); break;
case 4: ret = *(int32 *)((char *)dev->regs_mem_base + offset); break;
default: break;
}
}
// TRACE("tr_rd off=%x ret=%lx\n", offset, ret);
return ret;
}
void hw_write(sis7018_dev *dev, int offset, uint32 value, int size)
{
//TRACE("tr_wr off=%x value = %lx\n", offset, value);
if(use_io_regs){
switch(size){
case 1: (*pci->write_io_8)(dev->io_base + offset, (uint8) value); break;
case 2: (*pci->write_io_16)(dev->io_base + offset, (uint16) value); break;
case 4: (*pci->write_io_32)(dev->io_base + offset, value); break;
default: break;
}
}
else{
switch(size){
case 1: *(int8 *)((char *)dev->regs_mem_base + offset) = (int8)value; break;
case 2: *(int16 *)((char *)dev->regs_mem_base + offset) = (int16)value; break;
case 4: *(int32 *)((char *)dev->regs_mem_base + offset) = (int32)value; break;
default: break;
}
}
}
static void hw_clrint(sis7018_ch *ch)
{
sis7018_dev *dev = ch->card;
int bank, chan;
bank = (ch->index & 0x20) ? 1 : 0;
chan = ch->index & 0x1f;
hw_write(dev, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 1 << chan, 4);
}
static void hw_enaint(sis7018_ch *ch, int enable)
{
sis7018_dev *dev = ch->card;
int32 i, reg;
int bank, chan;
cpu_status cp;
cp = disable_interrupts();
acquire_spinlock(&dev->hardware);
bank = (ch->index & 0x20) ? 1 : 0;
chan = ch->index & 0x1f;
reg = bank? TR_REG_INTENB : TR_REG_INTENA;
i = hw_read(dev, reg, 4);
i &= ~(1 << chan);
i |= (enable? 1 : 0) << chan;
hw_clrint(ch);
hw_write(dev, reg, i, 4);
release_spinlock(&dev->hardware);
restore_interrupts(cp);
}
static void hw_selch(sis7018_ch *ch)
{
sis7018_dev *dev = ch->card;
int32 i;
i = hw_read(dev, TR_REG_CIR, 4);
i &= ~TR_CIR_MASK;
i |= ch->index & 0x3f;
hw_write(dev, TR_REG_CIR, i, 4);
}
static void hw_startch(sis7018_ch *ch)
{
sis7018_dev *dev = ch->card;
int bank, chan;
bank = (ch->index & 0x20) ? 1 : 0;
chan = ch->index & 0x1f;
hw_write(dev, bank? TR_REG_STARTB : TR_REG_STARTA, 1 << chan, 4);
}
static void hw_stopch(sis7018_ch *ch)
{
sis7018_dev *dev = ch->card;
int bank, chan;
bank = (ch->index & 0x20) ? 1 : 0;
chan = ch->index & 0x1f;
hw_write(dev, bank? TR_REG_STOPB : TR_REG_STOPA, 1 << chan, 4);
}
static void hw_wrch(sis7018_ch *ch)
{
sis7018_dev *dev = ch->card;
int32 cr[TR_CHN_REGS], i;
cpu_status cp;
ch->gvsel &= 0x00000001;
ch->fmc &= 0x00000003;
ch->fms &= 0x0000000f;
ch->ctrl &= 0x0000000f;
ch->pan &= 0x0000007f;
ch->rvol &= 0x0000007f;
ch->cvol &= 0x0000007f;
ch->vol &= 0x000000ff;
ch->ec &= 0x00000fff;
ch->alpha &= 0x00000fff;
ch->delta &= 0x0000ffff;
ch->lba &= 0x3fffffff;
cr[1]=ch->lba;
cr[3]=(ch->fmc<<14) | (ch->rvol<<7) | (ch->cvol);
cr[4]=(ch->gvsel<<31) | (ch->pan<<24) | (ch->vol<<16) | (ch->ctrl<<12) | (ch->ec);
switch (ch->card->type->ids.chip_id)
{
case SPA_PCI_ID:
case ALI_PCI_ID:
case TDX_PCI_ID:
case xDX_PCI_ID:
ch->cso &= 0x0000ffff;
ch->eso &= 0x0000ffff;
cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms);
cr[2]=(ch->eso<<16) | (ch->delta);
break;
case TNX_PCI_ID:
ch->cso &= 0x00ffffff;
ch->eso &= 0x00ffffff;
cr[0]=((ch->delta & 0xff)<<24) | (ch->cso);
cr[2]=((ch->delta>>8)<<24) | (ch->eso);
cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14);
break;
}
cp = disable_interrupts();
acquire_spinlock(&dev->hardware);
hw_selch(ch);
for (i=0; i<TR_CHN_REGS; i++)
hw_write(dev, TR_REG_CHNBASE+(i<<2), cr[i], 4);
release_spinlock(&dev->hardware);
restore_interrupts(cp);
}
void hw_rdch(sis7018_ch *ch)
{
sis7018_dev *dev = ch->card;
int32 cr[5], i;
cpu_status cp;
cp = disable_interrupts();
acquire_spinlock(&dev->hardware);
hw_selch(ch);
for (i=0; i<5; i++)
cr[i]=hw_read(dev, TR_REG_CHNBASE+(i<<2), 4);
release_spinlock(&dev->hardware);
restore_interrupts(cp);
ch->lba= (cr[1] & 0x3fffffff);
ch->fmc= (cr[3] & 0x0000c000) >> 14;
ch->rvol= (cr[3] & 0x00003f80) >> 7;
ch->cvol= (cr[3] & 0x0000007f);
ch->gvsel= (cr[4] & 0x80000000) >> 31;
ch->pan= (cr[4] & 0x7f000000) >> 24;
ch->vol= (cr[4] & 0x00ff0000) >> 16;
ch->ctrl= (cr[4] & 0x0000f000) >> 12;
ch->ec= (cr[4] & 0x00000fff);
switch(ch->card->type->ids.chip_id)
{
case SPA_PCI_ID:
case ALI_PCI_ID:
case TDX_PCI_ID:
case xDX_PCI_ID:
ch->cso= (cr[0] & 0xffff0000) >> 16;
ch->alpha= (cr[0] & 0x0000fff0) >> 4;
ch->fms= (cr[0] & 0x0000000f);
ch->eso= (cr[2] & 0xffff0000) >> 16;
ch->delta= (cr[2] & 0x0000ffff);
break;
case TNX_PCI_ID:
ch->cso= (cr[0] & 0x00ffffff);
ch->eso= (cr[2] & 0x00ffffff);
ch->delta= ((cr[2] & 0xff000000) >> 16) | ((cr[0] & 0xff000000) >> 24);
ch->alpha= (cr[3] & 0xfff00000) >> 20;
ch->fms= (cr[3] & 0x000f0000) >> 16;
break;
}
}
status_t hw_pchannel_init(pcm_dev *dev)
{
char name_buf[256];
sis7018_ch *ch = &dev->play_channel;
ch->index = PCHANNEL_ID;
hw_pchannel_setblocksize(dev, 8192);
ch->ctrl = 0x0f; // 16-bit stereo signed + loop mode enabled
ch->delta = (48000 << 12 ) / 48000;
ch->card = dev->card;
ch->areaid=-1;
ch->wr_lock = 0;
ch->write_waiting = 0;
sprintf(name_buf, "WS:%s", dev->name);
name_buf[B_OS_NAME_LENGTH-1] = 0;
ch->write_sem = create_sem(0, name_buf);
if (ch->write_sem < B_OK)
return ch->write_sem;
set_sem_owner(ch->write_sem, B_SYSTEM_TEAM);
name_buf[0] = 'W'; name_buf[1] = 'E';
ch->wr_entry = create_sem(1, name_buf);
if (ch->wr_entry < B_OK){
delete_sem(ch->write_sem);
return ch->wr_entry;
}
set_sem_owner(ch->wr_entry, B_SYSTEM_TEAM);
ch->wr_time = 0;
return B_OK;
}
void hw_pchannel_free(pcm_dev *dev)
{
sis7018_ch *ch = &dev->play_channel;
if(ch->areaid >= 0){
delete_area(ch->areaid);
ch->areaid = -1;
delete_sem(ch->write_sem);
delete_sem(ch->wr_entry);
ch->write_sem = -1;
ch->wr_entry = -1;
}
}
void hw_start(pcm_dev *dev)
{
sis7018_ch *ch = &dev->play_channel;
if(ch->active)
return;
/* start out with a clean slate */
TRACE("hw_start()\n");
/* if (port->config.format == 0x11)
{
memset((void*)port->card->low_mem, 0x80, port->config.play_buf_size +
port->config.rec_buf_size);
}
else {*/
// TODO?
// memset((void *)port->card->low_mem, 0, port->config.play_buf_size +
// port->config.rec_buf_size);
//}
ch->wr_cur = ch->wr_2;
ch->wr_silence = ch->mem_size;
ch->was_written = 0;
ch->wr_total = 0;
ch->fmc = 3;
ch->fms = 0;
ch->ec = 0;
ch->alpha = 0;
ch->lba = (int32)ch->mem_phys;
ch->cso = 0;
ch->eso = ch->mem_size/(sizeof(int16)*2)-1;
ch->rvol = ch->cvol = 0x7f;
ch->gvsel = 0;
ch->pan = 0;
ch->vol = 0;
hw_wrch(ch);
hw_enaint(ch, 1);
hw_startch(ch);
ch->active = 1;
}
void hw_stop(pcm_dev *dev)
{
sis7018_ch *ch = &dev->play_channel;
if(!ch->active)
return;
hw_stopch(ch);
ch->active = 0;
}
static int32 hw_interrupt_handler(void * data)
{
int32 active, mask, bufhalf, chnum, intsrc;
sis7018_dev *card = (sis7018_dev *)data;
sis7018_ch *ch;
int32 handled = B_UNHANDLED_INTERRUPT;
intrs++;
acquire_spinlock(&card->hardware);
intsrc = hw_read(card, TR_REG_MISCINT, 4);
#if DO_PCM
if (intsrc & TR_INT_ADDR){
chnum = PCHANNEL_ID;
mask = 0x00000001 << PCHANNEL_ID;
active = hw_read(card, (chnum < 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, 4);
bufhalf = hw_read(card, (chnum < 32)? TR_REG_CSPF_A : TR_REG_CSPF_B, 4);
if (active & mask){
ch = &card->pcm.play_channel;
if(playback_interrupt(ch, (bufhalf & mask)))
handled = B_INVOKE_SCHEDULER;
else
handled = B_HANDLED_INTERRUPT;
}
hw_write(card, (chnum <= 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, active, 4);
}
#endif
#if DO_MIDI
ssssssss
/*if (status & 0x80) {
if (midi_interrupt(card)) {
handled = B_INVOKE_SCHEDULER;
} else {
handled = B_HANDLED_INTERRUPT;
}
}*/
handled = B_HANDLED_INTERRUPT;
#endif
release_spinlock(&card->hardware);
return handled;
}
void hw_increment_interrupt_handler(sis7018_dev * card)
{
TRACE("hw_increment_interrupt_handler()\n");
if (atomic_add(&card->inth_count, 1) == 0){
TRACE("intline %d int %p\n", card->info.u.h0.interrupt_line, hw_interrupt_handler);
install_io_interrupt_handler(card->info.u.h0.interrupt_line, hw_interrupt_handler, card, 0);
}
}
void hw_decrement_interrupt_handler(sis7018_dev * card)
{
TRACE("hw_decrement_interrupt_handler()\n");
if (atomic_add(&card->inth_count, -1) == 1){
TRACE("remove_io_interrupt_handler()\n");
remove_io_interrupt_handler(card->info.u.h0.interrupt_line, hw_interrupt_handler, card);
}
}
status_t hw_initialize(sis7018_dev *card)
{
status_t err = B_OK;
cpu_status cp = disable_interrupts();
acquire_spinlock(&card->hardware);
switch (card->type->ids.chip_id){
case SPA_PCI_ID:
hw_write(card, SPA_REG_GPIO, 0, 4);
hw_write(card, SPA_REG_CODECST, SPA_RST_OFF, 4);
break;
case TDX_PCI_ID:
case xDX_PCI_ID:
hw_write(card, TDX_REG_CODECST, TDX_CDC_ON, 4);
break;
case TNX_PCI_ID:
hw_write(card, TNX_REG_CODECST, TNX_CDC_ON, 4);
break;
}
hw_write(card, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4);
release_spinlock(&card->hardware);
restore_interrupts(cp);
err |= ac97init(card);
return err;
}
void hw_dump_device(int card_id)
{
kprintf("interrupt handler called: %d\n", intrs);
}
void hw_dump_pci_info(int card_id)
{
kprintf("dump_pci_info is not yet implemented.\n");
}
void hw_dump_pcm_device(int card_id)
{
kprintf("dump_pcm_device is not yet implemented.\n");
}
void hw_dump_midi_device(int card_id)
{
kprintf("dump_midi_device is not yet implemented.\n");
}
static void hw_dump_channel(sis7018_ch *ch)
{
kprintf("Pointer : %p\n", ch);
kprintf(" cso:%8lx, alpha:%8lx, fms:%8lx, fmc:%8lx, ec:%lx\n",
ch->cso, ch->alpha, ch->fms, ch->fmc, ch->ec);
kprintf(" lba:%8lx, eso:%8lx, delta:%8lx, rvol:%8lx, cvol:%lx\n",
ch->lba, ch->eso, ch->delta, ch->rvol, ch->cvol);
kprintf("gvsel:%8lx, pan:%8lx, vol:%8lx, ctrl:%8lx, active:%x\n",
ch->gvsel, ch->pan, ch->vol, ch->ctrl, (int)ch->active);
kprintf("index:%8x, card:0x%8p, mem_ptr:0x%8p, mem_size:%8lx, mem_phys:0x%8p\n",
ch->index, ch->card, ch->mem_ptr, ch->mem_size, ch->mem_phys);
kprintf("\n");
}
void hw_dump_play_channel(int card_id)
{
sis7018_ch ch;
memcpy(&ch, &cards[card_id].pcm.play_channel, sizeof(sis7018_ch));
kprintf("Dumping play channel information:\n");
kprintf(" - cached:\n");
hw_dump_channel(&ch);
kprintf(" - readed from hardware:\n");
hw_rdch(&ch);
hw_dump_channel(&ch);
}
static status_t hw_find_memory(sis7018_ch *ch, size_t blocksize)
{
size_t low_size = (blocksize * 2+(B_PAGE_SIZE-1))&~(B_PAGE_SIZE-1);
physical_entry where;
size_t trysize;
area_id curarea;
void * addr;
char name[DEVNAME];
sprintf(name, "%s_area", ch->card->name);
if (low_size < MIN_MEMORY_SIZE)
low_size = MIN_MEMORY_SIZE;
trysize = low_size;
curarea = find_area(name);
if (curarea != B_NAME_NOT_FOUND){
TRACE("delete previously allocated area\n");
delete_area(curarea);
curarea = -1;
}
TRACE("allocating new area\n");
curarea = create_area(name, &addr, B_ANY_KERNEL_ADDRESS,
trysize, B_LOMEM, B_READ_AREA | B_WRITE_AREA);
TRACE("create_area(%lx) returned %lx logical %p\n", trysize, curarea, addr);
if (curarea < 0)
goto oops;
if (get_memory_map(addr, low_size, &where, 1) < 0){
delete_area(curarea);
curarea = B_ERROR;
goto oops;
}
TRACE("physical %#" B_PRIxPHYSADDR "\n", where.address);
if ((where.address & ~(phys_addr_t)0xffffff) != 0){
delete_area(curarea);
curarea = B_ERROR;
goto oops;
}
if (((where.address + low_size) & ~(phys_addr_t)0xffffff) != 0){
delete_area(curarea);
curarea = B_ERROR;
goto oops;
}
/* hey, it worked! */
if (trysize > low_size) /* don't waste */
resize_area(curarea, low_size);
oops:
if (curarea < 0){
TRACE("failed to create area\n");
return curarea;
}
//a_o_k:
TRACE("successfully created area!\n");
ch->mem_size = low_size;
ch->mem_ptr = addr;
ch->mem_phys = (vuchar *)(addr_t)where.address;
ch->wr_1 = ch->mem_ptr;
ch->wr_2 = ch->wr_1+ch->mem_size/2;
ch->wr_size = ch->mem_size/2;
ch->areaid = curarea;
if(ch->active){
hw_stop(&ch->card->pcm);
hw_start(&ch->card->pcm);
}
return B_OK;
}
size_t hw_pchannel_setblocksize(pcm_dev *dev, size_t blocksize)
{
sis7018_ch *ch = &dev->play_channel;
if(ch->mem_size/2 != blocksize){
if(ch->areaid >= 0){
TRACE("delete area on resize\n");
delete_area(ch->areaid);
ch->areaid = -1;
}
//TODO : error cheking.
hw_find_memory(ch, blocksize);
}
return blocksize;
}
@@ -1,35 +0,0 @@
/*
* This file is a part of SiS 7018 BeOS driver project.
* Copyright (c) 2002-2003 by Siarzuk Zharski <imker@gmx.li>
*
* This file may be used under the terms of the BSD License
* See the file "License" for details.
*/
#ifndef _SIS7018HW_H_
#define _SIS7018HW_H_
#ifndef _SOUND_H
#include "sound.h"
#endif
extern pci_module_info* pci;
uint32 hw_read(sis7018_dev *dev, int offset, int size);
void hw_write(sis7018_dev *dev, int offset, uint32 value, int size);
status_t hw_pchannel_init(pcm_dev *dev);
void hw_pchannel_free(pcm_dev *dev);
size_t hw_pchannel_setblocksize(pcm_dev *dev, size_t blocksize);
void hw_start(pcm_dev *dev);
void hw_stop(pcm_dev *dev);
void hw_increment_interrupt_handler(sis7018_dev * card);
void hw_decrement_interrupt_handler(sis7018_dev * card);
status_t hw_initialize(sis7018_dev *card);
void hw_rdch(sis7018_ch *ch);
void hw_dump_device(int card_id);
void hw_dump_pci_info(int card_id);
void hw_dump_pcm_device(int card_id);
void hw_dump_midi_device(int card_id);
void hw_dump_play_channel(int card_id);
#endif //_SIS7018HW_H_
@@ -1,94 +0,0 @@
/* ++++++++++
Data structures and control calls for using the sound driver
+++++ */
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
/*
* This file is a part of SiS 7018 BeOS driver project.
* Copyright permissions see above.
*
* Portion copyright (c) 2002-2003 by Siarzuk Zharski <imker@gmx.li>
*/
#ifndef _SOUND_H
#define _SOUND_H
#ifndef _DRIVERS_H
#include <Drivers.h>
#endif
enum adc_source {
line=0, aux1, mic, loopback
};
enum sample_rate {
kHz_8_0 = 0, kHz_5_51, kHz_16_0, kHz_11_025, kHz_27_42, kHz_18_9,
kHz_32_0, kHz_22_05, kHz_37_8 = 9, kHz_44_1 = 11, kHz_48_0, kHz_33_075,
kHz_9_6, kHz_6_62
};
enum sample_format {
linear_8bit_unsigned_mono = 0, linear_8bit_unsigned_stereo,
ulaw_8bit_companded_mono, ulaw_8bit_companded_stereo,
linear_16bit_little_endian_mono, linear_16bit_little_endian_stereo,
alaw_8bit_companded_mono, alaw_8bit_companded_stereo,
sample_format_reserved_1, sample_format_reserved_2,
adpcm_4bit_mono, adpcm_4bit_stereo,
linear_16bit_big_endian_mono, linear_16bit_big_endian_stereo,
sample_format_reserved_3, sample_format_reserved_4
};
struct channel {
enum adc_source adc_source; /* adc input source */
char adc_gain; /* 0..15 adc gain, in 1.5 dB steps */
char mic_gain_enable; /* non-zero enables 20 dB MIC input gain */
char aux1_mix_gain; /* 0..31 aux1 mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */
char aux1_mix_mute; /* non-zero mutes aux1 mix */
char aux2_mix_gain; /* 0..31 aux2 mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */
char aux2_mix_mute; /* non-zero mutes aux2 mix */
char line_mix_gain; /* 0..31 line mix to output gain. 12.0 to -34.5 dB in 1.5dB steps */
char line_mix_mute; /* non-zero mutes line mix */
char dac_attn; /* 0..61 dac attenuation, in -1.5 dB steps */
char dac_mute; /* non-zero mutes dac output */
};
typedef struct sound_setup {
struct channel left; /* left channel setup */
struct channel right; /* right channel setup */
enum sample_rate sample_rate; /* sample rate */
enum sample_format playback_format; /* sample format for playback */
enum sample_format capture_format; /* sample format for capture */
char dither_enable; /* non-zero enables dither on 16 => 8 bit */
char loop_attn; /* 0..64 adc to dac loopback attenuation, in -1.5 dB steps */
char loop_enable; /* non-zero enables loopback */
char output_boost; /* zero (2.0 Vpp) non-zero (2.8 Vpp) output level boost */
char highpass_enable;/* non-zero enables highpass filter in adc */
char mono_gain; /* 0..64 mono speaker gain */
char mono_mute; /* non-zero mutes speaker */
} sound_setup;
/* -----
control opcodes for sound driver
----- */
enum {
SOUND_GET_PARAMS = B_DEVICE_OP_CODES_END,
SOUND_SET_PARAMS, /* 10000 */
SOUND_SET_PLAYBACK_COMPLETION_SEM,
SOUND_SET_CAPTURE_COMPLETION_SEM,
SOUND_GET_PLAYBACK_TIMESTAMP, /* 10003 */
SOUND_GET_CAPTURE_TIMESTAMP,
SOUND_DEBUG_ON,
SOUND_DEBUG_OFF, /* 10006 */
SOUND_UNSAFE_WRITE,
SOUND_UNSAFE_READ,
SOUND_LOCK_FOR_DMA, /* 10009 */
SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE,
SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE,
SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE, /* 10012 */
SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE
};
#endif
@@ -1,114 +0,0 @@
/*
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
* $FreeBSD: src/sys/dev/sound/pci/t4dwave.h,v 1.3.2.3 2001/08/30 23:10:50 greid Exp $
*/
/*
* This file is a part of SiS 7018 BeOS driver project.
* Copyright permissions see above.
*
* Portion copyright (c) 2002 by Siarzuk Zharski <imker@gmx.li>
*
* $Source: /cvsroot/sis4be/sis7018/t4dwave.h,v $
* $Author: zharik $
* $Revision: 1.4 $
* $Date: 2004/06/24 15:47:40 $
*/
#ifndef _T4DWAVE_REG_H
#define _T4DWAVE_REG_H
#define TR_REG_CIR 0xa0
#define TR_CIR_MASK 0x0000003f
#define TR_CIR_ADDRENA 0x00001000
#define TR_CIR_MIDENA 0x00002000
#define TR_REG_MISCINT 0xb0
#define TR_INT_ADDR 0x00000020
#define TR_INT_SB 0x00000004
#define TR_REG_DMAR0 0x00
#define TR_REG_DMAR4 0x04
#define TR_REG_DMAR11 0x0b
#define TR_REG_DMAR15 0x0f
#define TR_REG_SBR4 0x14
#define TR_REG_SBR5 0x15
#define TR_SB_INTSTATUS 0x82
#define TR_REG_SBR9 0x1e
#define TR_REG_SBR10 0x1f
#define TR_REG_SBBL 0xc0
#define TR_REG_SBCTRL 0xc4
#define TR_REG_SBDELTA 0xac
#define TR_CDC_DATA 16
#define TDX_REG_CODECWR 0x40
#define TDX_REG_CODECRD 0x44
#define TDX_CDC_RWSTAT 0x00008000
#define TDX_REG_CODECST 0x48
#define TDX_CDC_SBCTRL 0x40
#define TDX_CDC_ACTIVE 0x20
#define TDX_CDC_READY 0x10
#define TDX_CDC_ADCON 0x08
#define TDX_CDC_DACON 0x02
#define TDX_CDC_RESET 0x01
#define TDX_CDC_ON (TDX_CDC_ADCON|TDX_CDC_DACON)
#define SPA_REG_CODECRD 0x44
#define SPA_REG_CODECWR 0x40
#define SPA_REG_CODECST 0x48
#define SPA_RST_OFF 0x0f0000
#define SPA_REG_GPIO 0x48
#define SPA_CDC_RWSTAT 0x00008000
#define TNX_REG_CODECWR 0x44
#define TNX_REG_CODEC1RD 0x48
#define TNX_REG_CODEC2RD 0x4c
#define TNX_CDC_RWSTAT 0x00000c00
#define TNX_CDC_SEC 0x00000100
#define TNX_REG_CODECST 0x40
#define TNX_CDC_READY2 0x40
#define TNX_CDC_ADC2ON 0x20
#define TNX_CDC_DAC2ON 0x10
#define TNX_CDC_READY1 0x08
#define TNX_CDC_ADC1ON 0x04
#define TNX_CDC_DAC1ON 0x02
#define TNX_CDC_RESET 0x01
#define TNX_CDC_ON (TNX_CDC_ADC1ON|TNX_CDC_DAC1ON)
#define TR_REG_STARTA 0x80
#define TR_REG_STOPA 0x84
#define TR_REG_CSPF_A 0x90
#define TR_REG_ADDRINTA 0x98
#define TR_REG_INTENA 0xa4
#define TR_REG_STARTB 0xb4
#define TR_REG_STOPB 0xb8
#define TR_REG_CSPF_B 0xbc
#define TR_REG_ADDRINTB 0xd8
#define TR_REG_INTENB 0xdc
#define TR_REG_CHNBASE 0xe0
#define TR_CHN_REGS 5
#endif
@@ -1,14 +0,0 @@
#!/bin/sh
answer=`alert "Do you really want to uninstall the Sis 7018 driver?" "No" "Yes"`
if [ $answer == "No" ]; then
exit
fi
rm ~/config/add-ons/kernel/drivers/bin/sis7018
rm ~/config/settings/kernel/drivers/sis7018
rm ~/config/add-ons/kernel/drivers/dev/audio/old/sis7018
alert "The SiS7018 driver has been removed.
Restart your system now."