From 822fd939383c3feaf7cb74cc933fbab95535bd2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Sat, 12 Apr 2003 00:27:14 +0000 Subject: [PATCH] Auvia driver for Via audio chipsets VT82C686 and VT8233/35 git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3063 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/drivers/audio/ac97/auvia/Jamfile | 18 + .../kernel/drivers/audio/ac97/auvia/ac97.c | 292 +++++ .../kernel/drivers/audio/ac97/auvia/ac97.h | 107 ++ .../kernel/drivers/audio/ac97/auvia/auvia.c | 670 ++++++++++ .../kernel/drivers/audio/ac97/auvia/auvia.h | 167 +++ .../drivers/audio/ac97/auvia/auviareg.h | 128 ++ .../kernel/drivers/audio/ac97/auvia/config.h | 49 + .../kernel/drivers/audio/ac97/auvia/debug.c | 95 ++ .../kernel/drivers/audio/ac97/auvia/debug.h | 70 ++ .../kernel/drivers/audio/ac97/auvia/io.c | 193 +++ .../kernel/drivers/audio/ac97/auvia/io.h | 47 + .../kernel/drivers/audio/ac97/auvia/multi.c | 1115 +++++++++++++++++ .../kernel/drivers/audio/ac97/auvia/multi.h | 64 + .../drivers/audio/ac97/auvia/multi_audio.h | 699 +++++++++++ .../kernel/drivers/audio/ac97/auvia/queue.h | 529 ++++++++ .../kernel/drivers/audio/ac97/auvia/util.c | 88 ++ .../kernel/drivers/audio/ac97/auvia/util.h | 40 + 17 files changed, 4371 insertions(+) create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/Jamfile create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/ac97.c create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/ac97.h create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/auvia.c create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/auvia.h create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/auviareg.h create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/config.h create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/debug.c create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/debug.h create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/io.c create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/io.h create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/multi.c create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/multi.h create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/multi_audio.h create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/queue.h create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/util.c create mode 100644 src/add-ons/kernel/drivers/audio/ac97/auvia/util.h diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/Jamfile b/src/add-ons/kernel/drivers/audio/ac97/auvia/Jamfile new file mode 100644 index 0000000000..92d81414b3 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/Jamfile @@ -0,0 +1,18 @@ +SubDir OBOS_TOP src add-ons kernel drivers audio ac97 auvia ; + +R5KernelAddon auvia : kernel drivers bin : + ac97.c + debug.c + auvia.c + io.c + multi.c + util.c +; + +# Link to kernel/drivers/dev/audio/multi +{ + local dir = [ FDirName $(OBOS_ADDON_DIR) kernel drivers dev audio multi ] ; + local instDriver = auvia ; + MakeLocate $(instDriver) : $(dir) ; + RelSymLink $(instDriver) : auvia ; +} diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/ac97.c b/src/add-ons/kernel/drivers/audio/ac97/auvia/ac97.c new file mode 100644 index 0000000000..2275bdedc8 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/ac97.c @@ -0,0 +1,292 @@ +/* + * Auvia BeOS Driver for Via VT82xx Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include "ac97.h" + +#define REVERSE_EAMP_POLARITY 0 + +#include "debug.h" +#include "io.h" + +#define B_UTF8_REGISTERED "\xC2\xAE" + +const char * stereo_enhancement_technique[] = +{ + "No 3D Stereo Enhancement", + "Analog Devices", + "Creative Technology", + "National Semiconductor", + "Yamaha", + "BBE Sound", + "Crystal Semiconductor", + "Qsound Labs", + "Spatializer Audio Laboratories", + "SRS Labs", + "Platform Tech", + "AKM Semiconductor", + "Aureal", + "Aztech Labs", + "Binaura", + "ESS Technology", + "Harman International", + "Nvidea", + "Philips", + "Texas Instruments", + "VLSI Technology", + "TriTech", + "Realtek", + "Samsung", + "Wolfson Microelectronics", + "Delta Integration", + "SigmaTel", + "KS Waves", + "Rockwell", + "Unknown (29)", + "Unknown (30)", + "Unknown (31)" +}; + +typedef void (* codec_init)(device_config *); +typedef void (* codec_amp_enable)(device_config *, bool); + +typedef struct codec_ops_tag +{ + codec_init init; + codec_amp_enable amp_enable; +} codec_ops; + +typedef struct codec_table_tag +{ + uint32 id; + uint32 mask; + codec_ops *ops; + const char *info; +} codec_table; + +void default_init(device_config *); +void ad1886_init(device_config *); + +void default_amp_enable(device_config *, bool); +void cs4299_amp_enable(device_config *, bool); + +codec_ops default_ops = { default_init, default_amp_enable }; +codec_ops ad1886_ops = { ad1886_init, default_amp_enable }; +codec_ops cs4299_ops = { default_init, cs4299_amp_enable }; + +codec_table codecs[] = +{ + /* Vendor ID and description imported from FreeBSD src/sys/dev/sound/pcm/ac97.c */ + { 0x414b4d00, 0xffffffff, &default_ops, "Asahi Kasei AK4540" }, + { 0x414b4d01, 0xffffffff, &default_ops, "Asahi Kasei AK4542" }, + { 0x414b4d02, 0xffffffff, &default_ops, "Asahi Kasei AK4543" }, + { 0x43525900, 0xffffffff, &default_ops, "Cirrus Logic CS4297" }, + { 0x43525903, 0xffffffff, &default_ops, "Cirrus Logic CS4297" }, + { 0x43525913, 0xffffffff, &default_ops, "Cirrus Logic CS4297A" }, + { 0x43525914, 0xffffffff, &default_ops, "Cirrus Logic CS4297B" }, + { 0x43525923, 0xffffffff, &default_ops, "Cirrus Logic CS4294C" }, + { 0x4352592b, 0xffffffff, &default_ops, "Cirrus Logic CS4298C" }, + { 0x43525931, 0xffffffff, &cs4299_ops, "Cirrus Logic CS4299A" }, + { 0x43525933, 0xffffffff, &cs4299_ops, "Cirrus Logic CS4299C" }, + { 0x43525934, 0xffffffff, &cs4299_ops, "Cirrus Logic CS4299D" }, + { 0x43525941, 0xffffffff, &default_ops, "Cirrus Logic CS4201A" }, + { 0x43525951, 0xffffffff, &default_ops, "Cirrus Logic CS4205A" }, + { 0x43525961, 0xffffffff, &default_ops, "Cirrus Logic CS4291A" }, + { 0x45838308, 0xffffffff, &default_ops, "ESS Technology ES1921" }, + { 0x49434511, 0xffffffff, &default_ops, "ICEnsemble ICE1232" }, + { 0x4e534331, 0xffffffff, &default_ops, "National Semiconductor LM4549" }, + { 0x83847600, 0xffffffff, &default_ops, "SigmaTel STAC9700/9783/9784" }, + { 0x83847604, 0xffffffff, &default_ops, "SigmaTel STAC9701/9703/9704/9705" }, + { 0x83847605, 0xffffffff, &default_ops, "SigmaTel STAC9704" }, + { 0x83847608, 0xffffffff, &default_ops, "SigmaTel STAC9708/9711" }, + { 0x83847609, 0xffffffff, &default_ops, "SigmaTel STAC9721/9723" }, + { 0x83847644, 0xffffffff, &default_ops, "SigmaTel STAC9744" }, + { 0x83847656, 0xffffffff, &default_ops, "SigmaTel STAC9756/9757" }, + { 0x53494c22, 0xffffffff, &default_ops, "Silicon Laboratory Si3036" }, + { 0x53494c23, 0xffffffff, &default_ops, "Silicon Laboratory Si3038" }, + { 0x54524103, 0xffffffff, &default_ops, "TriTech TR?????" }, + { 0x54524106, 0xffffffff, &default_ops, "TriTech TR28026" }, + { 0x54524108, 0xffffffff, &default_ops, "TriTech TR28028" }, + { 0x54524123, 0xffffffff, &default_ops, "TriTech TR28602" }, + { 0x574d4c00, 0xffffffff, &default_ops, "Wolfson WM9701A" }, + { 0x574d4c03, 0xffffffff, &default_ops, "Wolfson WM9703/9704" }, + { 0x574d4c04, 0xffffffff, &default_ops, "Wolfson WM9704 (quad)" }, + /* Assembled from datasheets: */ + { 0x41445303, 0xffffffff, &default_ops, "Analog Devices AD1819B SoundPort"B_UTF8_REGISTERED }, + { 0x41445340, 0xffffffff, &default_ops, "Analog Devices AD1881 SoundMAX"B_UTF8_REGISTERED }, + { 0x41445348, 0xffffffff, &default_ops, "Analog Devices AD1881A SoundMAX"B_UTF8_REGISTERED }, + { 0x41445360, 0xffffffff, &default_ops, "Analog Devices AD1885 SoundMAX"B_UTF8_REGISTERED }, + { 0x41445361, 0xffffffff, &ad1886_ops, "Analog Devices AD1886 SoundMAX"B_UTF8_REGISTERED }, + { 0x41445362, 0xffffffff, &default_ops, "Analog Devices AD1887 SoundMAX"B_UTF8_REGISTERED }, + { 0x41445363, 0xffffffff, &default_ops, "Analog Devices AD1886A SoundMAX"B_UTF8_REGISTERED }, + { 0x41445371, 0xffffffff, &default_ops, "Analog Devices AD1981A SoundMAX"B_UTF8_REGISTERED }, + { 0x41445372, 0xffffffff, &default_ops, "Analog Devices AD1981A SoundMAX"B_UTF8_REGISTERED }, + { 0x414c4320, 0xfffffff0, &default_ops, "Avance Logic (Realtek) ALC100/ALC100P, RL5383/RL5522" }, + { 0x414c4730, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC101" }, +#if 0 + { 0x414c4710, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC200/ALC200A" }, /* datasheet says id2 = 4710 */ + { 0x414c4710, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC201/ALC201A" }, /* 4710 or 4720 */ + { 0x414c4720, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC650" }, /* datasheet says id2 = 4720 */ +#else + { 0x414c4710, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC200/ALC200A or ALC201/ALC201A" }, + { 0x414c4720, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC650 or ALC201/ALC201A" }, +#endif + { 0x414c4740, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC202/ALC202A" }, + /* Vendors only: */ + { 0x41445300, 0xffffff00, &default_ops, "Analog Devices" }, + { 0x414b4d00, 0xffffff00, &default_ops, "Asahi Kasei" }, + { 0x414c4700, 0xffffff00, &default_ops, "Avance Logic (Realtek)" }, + { 0x43525900, 0xffffff00, &default_ops, "Cirrus Logic" }, + { 0x45838300, 0xffffff00, &default_ops, "ESS Technology" }, + { 0x49434500, 0xffffff00, &default_ops, "ICEnsemble" }, + { 0x4e534300, 0xffffff00, &default_ops, "National Semiconductor" }, + { 0x83847600, 0xffffff00, &default_ops, "SigmaTel" }, + { 0x53494c00, 0xffffff00, &default_ops, "Silicon Laboratory" }, + { 0x54524100, 0xffffff00, &default_ops, "TriTech" }, + { 0x574d4c00, 0xffffff00, &default_ops, "Wolfson" }, + { 0x00000000, 0x00000000, &default_ops, "Unknown" } /* must be last one, matches every codec */ +}; + +codec_table * +find_codec_table(uint32 codecid) +{ + codec_table *codec; + for (codec = codecs; codec->id; codec++) + if ((codec->id & codec->mask) == (codecid & codec->mask)) + break; + return codec; +} + +const char * +ac97_get_3d_stereo_enhancement(device_config *config) +{ + uint16 data; + data = auvia_codec_read(config, AC97_RESET); + data = (data >> 10) & 31; + return stereo_enhancement_technique[data]; +} + +const char * +ac97_get_vendor_id_description() +{ + uint32 id = ac97_get_vendor_id(); + codec_table *codec = find_codec_table(id); + char f = (id >> 24) & 0xff; + char s = (id >> 16) & 0xff; + char t = (id >> 8) & 0xff; + if (f == 0) f = '?'; + if (s == 0) s = '?'; + if (t == 0) t = '?'; + LOG(("codec %c%c%c %u\n",f,s,t,id & 0xff)); + LOG(("info: %s\n",codec->info)); + return codec->info; +} + +uint32 +ac97_get_vendor_id(device_config *config) +{ + uint16 data1; + uint16 data2; + data1 = auvia_codec_read(config, AC97_VENDOR_ID1); + data2 = auvia_codec_read(config, AC97_VENDOR_ID2); + return (((uint32)data1) << 16) | data2; +} + +void +ac97_amp_enable(device_config *config, bool yesno) +{ + codec_table *codec; + LOG(("ac97_amp_enable\n")); + codec = find_codec_table(ac97_get_vendor_id(config)); + codec->ops->amp_enable(config, yesno); +} + +void +ac97_init(device_config *config) +{ + codec_table *codec; + LOG(("ac97_init\n")); + codec = find_codec_table(ac97_get_vendor_id(config)); + codec->ops->init(config); +} + +void default_init(device_config *config) +{ + LOG(("default_init\n")); +} + +void ad1886_init(device_config *config) +{ + LOG(("ad1886_init\n")); + auvia_codec_write(config, 0x72, 0x0010); +} + +void default_amp_enable(device_config *config, bool yesno) +{ + LOG(("default_amp_enable\n")); + LOG(("powerdown register was = %#04x\n",auvia_codec_read(config, AC97_POWERDOWN))); + #if REVERSE_EAMP_POLARITY + yesno = !yesno; + LOG(("using reverse eamp polarity\n")); + #endif + if (yesno) + auvia_codec_write(config, AC97_POWERDOWN, auvia_codec_read(config, AC97_POWERDOWN) & ~0x8000); /* switch on (low active) */ + else + auvia_codec_write(config, AC97_POWERDOWN, auvia_codec_read(config, AC97_POWERDOWN) | 0x8000); /* switch off */ + LOG(("powerdown register is = %#04x\n", auvia_codec_read(config, AC97_POWERDOWN))); +} + +void cs4299_amp_enable(device_config *config, bool yesno) +{ + LOG(("cs4299_amp_enable\n")); + if (yesno) + auvia_codec_write(config, 0x68, 0x8004); + else + auvia_codec_write(config, 0x68, 0); +} + +const ac97_source_info source_info[] = { + { "Record", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO|B_MIX_RECORDMUX, 100, AC97_RECORD_GAIN, 0x8000, 4, 0, 1, 0, 0.0, 22.5, 1.5 }, + { "Master", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 101, AC97_MASTER_VOLUME, 0x8000, 5, 0, 1, 1,-46.5, 0.0, 1.5 }, + //{ "Bass/Trebble", B_MIX_GAIN|B_MIX_STEREO, 102, AC97_MASTER_TONE, 0x0f0f, 4, 0, 1, 1,-12.0, 10.5, 1.5 }, + //{ "Aux Out", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 103, AC97_AUX_OUT_VOLUME, 0x8000, 5, 0, 1, 1,-46.5, 0.0, 1.5 }, + { "PCM Out", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 104, AC97_PCM_OUT_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 }, + { "CD", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 105, AC97_CD_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 }, + { "Aux In", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 106, AC97_AUX_IN_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 }, + { "TAD", B_MIX_GAIN|B_MIX_MUTE|B_MIX_MONO, 107, AC97_PHONE_VOLUME, 0x8008, 5, 0, 1, 1,-34.5, 12.0, 1.5 }, + { "Mic", B_MIX_GAIN|B_MIX_MUTE|B_MIX_MONO|B_MIX_MICBOOST, 108, AC97_MIC_VOLUME, 0x8008, 5, 0, 1, 1,-34.5, 12.0, 1.5 }, + { "Line In", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 109, AC97_LINE_IN_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 }, + //{ "Center/Lfe", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 111, AC97_CENTER_LFE_VOLUME, 0x8080, 5, 0, 1, 1,-46.5, 0.0, 1.5 }, + { "Center/Lfe" /* should be "Surround" but no */, B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 110, AC97_SURROUND_VOLUME, 0x8080, 5, 0, 1, 1,-46.5, 0.0, 1.5 } +}; + +const int32 source_info_size = (sizeof(source_info)/sizeof(source_info[0])); diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/ac97.h b/src/add-ons/kernel/drivers/audio/ac97/auvia/ac97.h new file mode 100644 index 0000000000..59a08155a7 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/ac97.h @@ -0,0 +1,107 @@ +/* + * Auvia BeOS Driver for Via VT82xx Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _AC97_H_ +#define _AC97_H_ + +#include "config.h" + +enum AC97_REGISTER { + AC97_RESET = 0x00, + AC97_MASTER_VOLUME = 0x02, + AC97_AUX_OUT_VOLUME = 0x04, + AC97_MONO_VOLUME = 0x06, + AC97_MASTER_TONE = 0x08, + AC97_PC_BEEP_VOLUME = 0x0A, + AC97_PHONE_VOLUME = 0x0C, + AC97_MIC_VOLUME = 0x0E, + AC97_LINE_IN_VOLUME = 0x10, + AC97_CD_VOLUME = 0x12, + AC97_VIDEO_VOLUME = 0x14, + AC97_AUX_IN_VOLUME = 0x16, + AC97_PCM_OUT_VOLUME = 0x18, + AC97_RECORD_SELECT = 0x1A, + AC97_RECORD_GAIN = 0x1C, + AC97_RECORD_GAIN_MIC = 0x1E, + AC97_GENERAL_PURPOSE = 0x20, + AC97_3D_CONTROL = 0x22, + AC97_PAGING = 0x24, + AC97_POWERDOWN = 0x26, + AC97_EXTENDED_AUDIO_ID = 0x28, + AC97_EXTENDED_AUDIO_STATUS = 0x2A, + AC97_PCM_FRONT_DAC_RATE = 0x2C, + AC97_PCM_SURR_DAC_RATE = 0x2E, + AC97_PCM_LFE_DAC_RATE = 0x30, + AC97_PCM_LR_ADC_RATE = 0x32, + AC97_MIC_ADC_RATE = 0x34, + AC97_CENTER_LFE_VOLUME = 0x36, + AC97_SURROUND_VOLUME = 0x38, + AC97_SPDIF_CONTROL = 0x3A, + AC97_VENDOR_ID1 = 0x7C, + AC97_VENDOR_ID2 = 0x7E +}; + +const char * ac97_get_3d_stereo_enhancement(); +const char * ac97_get_vendor_id_description(); +uint32 ac97_get_vendor_id(); +void ac97_init(); + +void ac97_amp_enable(device_config *config, bool yesno); + +typedef enum { + B_MIX_GAIN = 1 << 0, + B_MIX_MUTE = 1 << 1, + B_MIX_MONO = 1 << 2, + B_MIX_STEREO = 1 << 3, + B_MIX_MUX = 1 << 4, + B_MIX_MICBOOST = 1 << 5, + B_MIX_RECORDMUX = 1 << 6 +} ac97_mixer_type; + +typedef struct _ac97_source_info { + const char *name; + ac97_mixer_type type; + + int32 id; + uint8 reg; + uint16 default_value; + uint8 bits:3; + uint8 ofs:4; + uint8 mute:1; + uint8 polarity:1; // max_gain -> 0 + float min_gain; + float max_gain; + float granularity; +} ac97_source_info; + +extern const ac97_source_info source_info[]; +extern const int32 source_info_size; + +#endif diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/auvia.c b/src/add-ons/kernel/drivers/audio/ac97/auvia/auvia.c new file mode 100644 index 0000000000..f5892d3124 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/auvia.c @@ -0,0 +1,670 @@ +/* + * Auvia BeOS Driver for Via VT82xx Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + + * This code is derived from software contributed to The NetBSD Foundation + * by Tyler C. Sarna + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include "auvia.h" +#include "debug.h" +#include "config.h" +#include "util.h" +#include "io.h" +#include +#include +#include +#include "ac97.h" + +status_t init_hardware(void); +status_t init_driver(void); +void uninit_driver(void); +const char ** publish_devices(void); +device_hooks * find_device(const char *); + +static char pci_name[] = B_PCI_MODULE_NAME; +static pci_module_info *pci; + +int32 num_cards; +auvia_dev cards[NUM_CARDS]; +int32 num_names; +char * names[NUM_CARDS*20+1]; + +extern device_hooks multi_hooks; + +/* Auvia Memory management */ + +static auvia_mem * +auvia_mem_new(auvia_dev *card, size_t size) +{ + auvia_mem *mem; + + if ((mem = malloc(sizeof(*mem))) == NULL) + return (NULL); + + mem->area = alloc_mem(&mem->phy_base, &mem->log_base, size, "auvia buffer"); + mem->size = size; + if (mem->area < B_OK) { + free(mem); + return NULL; + } + return mem; +} + +static void +auvia_mem_delete(auvia_mem *mem) +{ + if(mem->area > B_OK) + delete_area(mem->area); + free(mem); +} + +void * +auvia_mem_alloc(auvia_dev *card, size_t size) +{ + auvia_mem *mem; + + mem = auvia_mem_new(card, size); + if (mem == NULL) + return (NULL); + + LIST_INSERT_HEAD(&(card->mems), mem, next); + + return mem; +} + +void +auvia_mem_free(auvia_dev *card, void *ptr) +{ + auvia_mem *mem; + + LIST_FOREACH(mem, &card->mems, next) { + if (mem->log_base != ptr) + continue; + LIST_REMOVE(mem, next); + + auvia_mem_delete(mem); + break; + } +} + +/* Auvia stream functions */ + +status_t +auvia_stream_set_audioparms(auvia_stream *stream, uint8 channels, + uint8 b16, uint32 sample_rate) +{ + uint8 sample_size, frame_size; + LOG(("auvia_stream_set_audioparms\n")); + + if ((stream->channels == channels) && + (stream->b16 == b16) && + (stream->sample_rate == sample_rate)) + return B_OK; + + if(stream->buffer) + auvia_mem_free(stream->card, stream->buffer->log_base); + + stream->b16 = b16; + stream->sample_rate = sample_rate; + stream->channels = channels; + + sample_size = stream->b16 + 1; + frame_size = sample_size * stream->channels; + + stream->buffer = auvia_mem_alloc(stream->card, stream->bufframes * frame_size * stream->bufcount); + + stream->trigblk = 0; /* This shouldn't be needed */ + stream->blkmod = stream->bufcount; + stream->blksize = stream->bufframes * frame_size; + + return B_OK; +} + +status_t +auvia_stream_commit_parms(auvia_stream *stream) +{ + int i; + uint32 *page; + uint32 value; + LOG(("auvia_stream_commit_parms\n")); + + page = stream->dmaops_log_base; + + for(i=0; ibufcount; i++) { + page[2*i] = ((uint32)stream->buffer->phy_base) + + i * stream->blksize; + page[2*i + 1] = AUVIA_DMAOP_FLAG | stream->blksize; + } + + page[2*stream->bufcount - 1] &= ~AUVIA_DMAOP_FLAG; + page[2*stream->bufcount - 1] |= AUVIA_DMAOP_EOL; + + auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE, + (uint32)stream->dmaops_phy_base); + + if(stream->use & AUVIA_USE_RECORD) + auvia_codec_write(&stream->card->config, AC97_PCM_LR_ADC_RATE, (uint16)stream->sample_rate); + else + auvia_codec_write(&stream->card->config, AC97_PCM_FRONT_DAC_RATE, (uint16)stream->sample_rate); + + if(IS_8233(&stream->card->config)) { + if(stream->base != AUVIA_8233_MP_BASE) { + value = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_8233_RP_RATEFMT); + value &= ~(AUVIA_8233_RATEFMT_48K | AUVIA_8233_RATEFMT_STEREO | AUVIA_8233_RATEFMT_16BIT); + if(stream->use & AUVIA_USE_PLAY) + value |= AUVIA_8233_RATEFMT_48K * (stream->sample_rate / 20) / (48000 / 20); + value |= (stream->channels == 2 ? AUVIA_8233_RATEFMT_STEREO : 0) + | (stream->b16 ? AUVIA_8233_RATEFMT_16BIT : 0); + auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_8233_RP_RATEFMT, value); + } else { + static const uint32 slottab[7] = {0, 0xff000011, 0xff000021, 0xff000521, + 0xff004321, 0xff054321, 0xff654321}; + value = (stream->b16 ? AUVIA_8233_MP_FORMAT_16BIT : AUVIA_8233_MP_FORMAT_8BIT) + | ((stream->channels << 4) & AUVIA_8233_MP_FORMAT_CHANNEL_MASK) ; + auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_OFF_MP_FORMAT, value); + auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_8233_OFF_MP_STOP, + slottab[stream->channels]); + } + } + //auvia_codec_write(&stream->card->config, AC97_SPDIF_CONTROL, (uint16)stream->sample_rate); + + return B_OK; +} + +status_t +auvia_stream_get_nth_buffer(auvia_stream *stream, uint8 chan, uint8 buf, + char** buffer, size_t *stride) +{ + uint8 sample_size, frame_size; + LOG(("auvia_stream_get_nth_buffer\n")); + + sample_size = stream->b16 + 1; + frame_size = sample_size * stream->channels; + + *buffer = stream->buffer->log_base + (buf * stream->bufframes * frame_size) + + chan * sample_size; + *stride = frame_size; + + return B_OK; +} + +static uint32 +auvia_stream_curaddr(auvia_stream *stream) +{ + uint32 addr; + if(IS_8233(&stream->card->config)) { + addr = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE); + //TRACE(("auvia_stream_curaddr %p\n", addr)); + //TRACE(("auvia_stream_curaddr %p\n", (uint32)stream->dmaops_phy_base)); + return (addr - (uint32)stream->dmaops_phy_base - 4) / 8; + } else { + addr = auvia_reg_read_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE); + return (addr - (uint32)stream->dmaops_phy_base - 8) / 8; + } +} + +void +auvia_stream_start(auvia_stream *stream, void (*inth) (void *), void *inthparam) +{ + LOG(("auvia_stream_start\n")); + + stream->inth = inth; + stream->inthparam = inthparam; + + stream->state |= AUVIA_STATE_STARTED; + + if(IS_8233(&stream->card->config)) { + if(stream->base != AUVIA_8233_MP_BASE) { + auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_RP_DXS_LVOL, 0); + auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_8233_RP_DXS_RVOL, 0); + } + auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL, + AUVIA_RPCTRL_START | AUVIA_RPCTRL_AUTOSTART | AUVIA_RPCTRL_STOP + | AUVIA_RPCTRL_EOL | AUVIA_RPCTRL_FLAG); + } else { + uint8 regvalue = (stream->channels > 1 ? AUVIA_RPMODE_STEREO : 0) + | (stream->b16 == 1 ? AUVIA_RPMODE_16BIT : 0) + | AUVIA_RPMODE_INTR_FLAG | AUVIA_RPMODE_INTR_EOL | AUVIA_RPMODE_AUTOSTART; + auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_MODE, regvalue); + auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL, AUVIA_RPCTRL_START); + } +} + +void +auvia_stream_halt(auvia_stream *stream) +{ + LOG(("auvia_stream_halt\n")); + + stream->state &= ~AUVIA_STATE_STARTED; + + auvia_reg_write_8(&stream->card->config, stream->base + AUVIA_RP_CONTROL, AUVIA_RPCTRL_TERMINATE); +} + +auvia_stream * +auvia_stream_new(auvia_dev *card, uint8 use, uint32 bufframes, uint8 bufcount) +{ + auvia_stream *stream; + cpu_status status; + LOG(("auvia_stream_new\n")); + + stream = malloc(sizeof(auvia_stream)); + if (stream == NULL) + return (NULL); + stream->card = card; + stream->use = use; + stream->state = !AUVIA_STATE_STARTED; + stream->b16 = 0; + stream->sample_rate = 0; + stream->channels = 0; + stream->bufframes = bufframes; + stream->bufcount = bufcount; + stream->inth = NULL; + stream->inthparam = NULL; + stream->buffer = NULL; + stream->blksize = 0; + stream->trigblk = 0; + stream->blkmod = 0; + + if(use & AUVIA_USE_PLAY) { + if(IS_8233(&card->config)) + stream->base = AUVIA_8233_MP_BASE; + //stream->base = AUVIA_PLAY_BASE; + else + stream->base = AUVIA_PLAY_BASE; + } else { + if(IS_8233(&card->config)) + stream->base = AUVIA_8233_RECORD_BASE; + else + stream->base = AUVIA_RECORD_BASE; + } + + stream->frames_count = 0; + stream->real_time = 0; + stream->update_needed = false; + + /* allocate memory for our dma ops */ + stream->dmaops_area = alloc_mem(&stream->dmaops_phy_base, &stream->dmaops_log_base, + VIA_TABLE_SIZE, "auvia dmaops"); + + if (stream->dmaops_area < B_OK) { + PRINT(("couldn't allocate memory\n")); + free(stream); + return NULL; + } + + status = lock(); + LIST_INSERT_HEAD((&card->streams), stream, next); + unlock(status); + + return stream; +} + +void +auvia_stream_delete(auvia_stream *stream) +{ + cpu_status status; + LOG(("auvia_stream_delete\n")); + + auvia_stream_halt(stream); + + auvia_reg_write_32(&stream->card->config, stream->base + AUVIA_RP_DMAOPS_BASE, 0); + + if (stream->dmaops_area > B_OK) + delete_area(stream->dmaops_area); + + if(stream->buffer) + auvia_mem_free(stream->card, stream->buffer->log_base); + + status = lock(); + LIST_REMOVE(stream, next); + unlock(status); + + free(stream); +} + +/* Auvia interrupt */ + +int32 auvia_int(void *arg) +{ + auvia_dev *card = arg; + bool gotone = false; + uint32 curblk; + auvia_stream *stream; + + if(auvia_reg_read_32(&card->config, AUVIA_SGD_SHADOW) + & card->interrupt_mask) { + + LIST_FOREACH(stream, &card->streams, next) + if(auvia_reg_read_8(&card->config, stream->base + AUVIA_RP_STAT) & AUVIA_RPSTAT_INTR) { + gotone = true; + //TRACE(("interrupt\n")); + + curblk = auvia_stream_curaddr(stream); + //TRACE(("AUVIA_RPSTAT_INTR at trigblk %lu\n", curblk)); + //TRACE(("AUVIA_RPSTAT_INTR at stream->trigblk %lu\n", stream->trigblk)); + if (curblk == stream->trigblk) { + //TRACE(("AUVIA_RPSTAT_INTR at trigblk %lu\n", curblk)); + + if(stream->inth) + stream->inth(stream->inthparam); + + stream->trigblk++; + stream->trigblk %= stream->blkmod; + } + + auvia_reg_write_8(&card->config, stream->base + AUVIA_RP_STAT, AUVIA_RPSTAT_INTR); + } + } else { + TRACE(("AUVIA_SGD_SHADOW %x %x\n", card->interrupt_mask, auvia_reg_read_32(&card->config, AUVIA_SGD_SHADOW))); + } + + if(gotone) + return B_HANDLED_INTERRUPT; + + TRACE(("Got unhandled interrupt\n")); + return B_UNHANDLED_INTERRUPT; +} + +/* Auvia driver functions */ + +/* detect presence of our hardware */ +status_t +init_hardware(void) +{ + int ix=0; + pci_info info; + status_t err = ENODEV; + + LOG_CREATE(); + + PRINT(("init_hardware()\n")); + + if (get_module(pci_name, (module_info **)&pci)) + return ENOSYS; + + while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) { + if (info.vendor_id == VIATECH_VENDOR_ID && + (info.device_id == VIATECH_82C686_AC97_DEVICE_ID + || info.device_id == VIATECH_8233_AC97_DEVICE_ID + )) { + err = B_OK; + } + ix++; + } + + put_module(pci_name); + + return err; +} + +static void +make_device_names( + auvia_dev * card) +{ + sprintf(card->name, "audio/multi/auvia/%ld", card-cards+1); + names[num_names++] = card->name; + + names[num_names] = NULL; +} + + +status_t +auvia_init(auvia_dev * card) +{ + uint32 pr; + + pr = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, AUVIA_PCICONF_JUNK, 4); + PRINT(("AUVIA_PCICONF_JUNK before: %x\n", pr)); + pr &= ~AUVIA_PCICONF_ENABLES; + pr |= AUVIA_PCICONF_ACLINKENAB | AUVIA_PCICONF_ACNOTRST | AUVIA_PCICONF_ACVSR | AUVIA_PCICONF_ACSGD; + pr &= ~(AUVIA_PCICONF_ACFM | AUVIA_PCICONF_ACSB); + (*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function, AUVIA_PCICONF_JUNK, 4, pr ); + snooze(100); + pr = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, AUVIA_PCICONF_JUNK, 4); + PRINT(("AUVIA_PCICONF_JUNK after: %x\n", pr)); + + if(IS_8233(&card->config)) { + card->interrupt_mask = + AUVIA_8233_SGD_STAT_FLAG_EOL | + AUVIA_8233_SGD_STAT_FLAG_EOL << 4 | + AUVIA_8233_SGD_STAT_FLAG_EOL << 8 | + AUVIA_8233_SGD_STAT_FLAG_EOL << 12 | + AUVIA_8233_SGD_STAT_FLAG_EOL << 16 | + AUVIA_8233_SGD_STAT_FLAG_EOL << 24 | + AUVIA_8233_SGD_STAT_FLAG_EOL << 28; + } else { + card->interrupt_mask = AUVIA_SGD_STAT_ALL | (AUVIA_SGD_STAT_ALL << 4); + } + + + /* Init streams list */ + LIST_INIT(&(card->streams)); + + /* Init mems list */ + LIST_INIT(&(card->mems)); + + return B_OK; +} + +static status_t +auvia_setup(auvia_dev * card) +{ + status_t err = B_OK; + unsigned char cmd; + + PRINT(("auvia_setup(%p)\n", card)); + + make_device_names(card); + + card->config.nabmbar = card->info.u.h0.base_registers[0]; + card->config.irq = card->info.u.h0.interrupt_line; + card->config.type = 0; + if(card->info.device_id == VIATECH_82C686_AC97_DEVICE_ID) + card->config.type |= TYPE_686; + if(card->info.device_id == VIATECH_8233_AC97_DEVICE_ID) + card->config.type |= TYPE_8233; + + PRINT(("%s deviceid = %#04x chiprev = %x model = %x enhanced at %x\n", card->name, card->info.device_id, + card->info.revision, card->info.u.h0.subsystem_id, card->config.nabmbar)); + + cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2); + PRINT(("PCI command before: %x\n", cmd)); + (*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2, cmd | PCI_command_io); + cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2); + PRINT(("PCI command after: %x\n", cmd)); + + /* reset the codec */ + PRINT(("codec reset\n")); + auvia_codec_write(&card->config, 0x00, 0x0000); + snooze(50000); // 50 ms + + ac97_init(&card->config); + ac97_amp_enable(&card->config, true); + + PRINT(("codec vendor id = %#08x\n",ac97_get_vendor_id())); + PRINT(("codec description = %s\n",ac97_get_vendor_id_description())); + PRINT(("codec 3d enhancement = %s\n",ac97_get_3d_stereo_enhancement())); + + PRINT(("installing interrupt : %x\n", card->config.irq)); + install_io_interrupt_handler(card->config.irq, auvia_int, card, 0); + + /*PRINT(("codec master output = %#04x\n",auvia_codec_read(&card->config, 0x02))); + PRINT(("codec aux output = %#04x\n",auvia_codec_read(&card->config, 0x04))); + PRINT(("codec mono output = %#04x\n",auvia_codec_read(&card->config, 0x06))); + PRINT(("codec pcm output = %#04x\n",auvia_codec_read(&card->config, 0x18))); + PRINT(("codec line in = %#04x\n",auvia_codec_read(&card->config, 0x10))); + PRINT(("codec record line in= %#04x\n",auvia_codec_read(&card->config, 0x1a))); + PRINT(("codec record gain = %#04x\n",auvia_codec_read(&card->config, 0x1c)));*/ + + PRINT(("writing codec registers\n")); + // TODO : to move with AC97 + /* enable master output */ + auvia_codec_write(&card->config, AC97_MASTER_VOLUME, 0x0000); + /* enable aux output */ + auvia_codec_write(&card->config, AC97_AUX_OUT_VOLUME, 0x0000); + /* enable mono output */ + //auvia_codec_write(&card->config, AC97_MONO_VOLUME, 0x0004); + /* enable pcm output */ + auvia_codec_write(&card->config, AC97_PCM_OUT_VOLUME, 0x0808); + /* enable line in */ + //auvia_codec_write(&card->config, AC97_LINE_IN_VOLUME, 0x8808); + /* set record line in */ + auvia_codec_write(&card->config, AC97_RECORD_SELECT, 0x0404); + /* set record gain */ + //auvia_codec_write(&card->config, AC97_RECORD_GAIN, 0x0000); + + PRINT(("codec master output = %#04x\n",auvia_codec_read(&card->config, AC97_MASTER_VOLUME))); + PRINT(("codec aux output = %#04x\n",auvia_codec_read(&card->config, AC97_AUX_OUT_VOLUME))); + PRINT(("codec mono output = %#04x\n",auvia_codec_read(&card->config, AC97_MONO_VOLUME))); + PRINT(("codec pcm output = %#04x\n",auvia_codec_read(&card->config, AC97_PCM_OUT_VOLUME))); + PRINT(("codec line in = %#04x\n",auvia_codec_read(&card->config, AC97_LINE_IN_VOLUME))); + PRINT(("codec record line in= %#04x\n",auvia_codec_read(&card->config, AC97_RECORD_SELECT))); + PRINT(("codec record gain = %#04x\n",auvia_codec_read(&card->config, AC97_RECORD_GAIN))); + + if ((err = auvia_init(card))) + return (err); + + PRINT(("init_driver done\n")); + + return err; +} + + +status_t +init_driver(void) +{ + int ix=0; + + pci_info info; + num_cards = 0; + + PRINT(("init_driver()\n")); + load_driver_symbols("auvia"); + + if (get_module(pci_name, (module_info **) &pci)) + return ENOSYS; + + while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) { + if (info.vendor_id == VIATECH_VENDOR_ID && + (info.device_id == VIATECH_82C686_AC97_DEVICE_ID + || info.device_id == VIATECH_8233_AC97_DEVICE_ID + )) { + if (num_cards == NUM_CARDS) { + PRINT(("Too many auvia cards installed!\n")); + break; + } + memset(&cards[num_cards], 0, sizeof(auvia_dev)); + cards[num_cards].info = info; + if (auvia_setup(&cards[num_cards])) { + PRINT(("Setup of auvia %ld failed\n", num_cards+1)); + } + else { + num_cards++; + } + } + ix++; + } + if (!num_cards) { + PRINT(("no cards\n")); + put_module(pci_name); + PRINT(("no suitable cards found\n")); + return ENODEV; + } + + +#if DEBUG + //add_debugger_command("auvia", auvia_debug, "auvia [card# (1-n)]"); +#endif + return B_OK; +} + + +static void +auvia_shutdown(auvia_dev *card) +{ + PRINT(("shutdown(%p)\n", card)); + remove_io_interrupt_handler(card->config.irq, auvia_int, card); +} + + +void +uninit_driver(void) +{ + int ix, cnt = num_cards; + num_cards = 0; + + PRINT(("uninit_driver()\n")); + //remove_debugger_command("auvia", auvia_debug); + + for (ix=0; ix +#include +#include +#include +#include "auviareg.h" +#include "config.h" +#include "queue.h" +#include "multi_audio.h" +#include "multi.h" + +#define VIATECH_VENDOR_ID 0x1106 /* Via Technologies */ +#define VIATECH_82C686_AC97_DEVICE_ID 0x3058 /* Via Technologies VT82C686 AC97 */ +#define VIATECH_8233_AC97_DEVICE_ID 0x3059 /* Via Technologies VT8233 AC97 */ + +#define VIATECH_8233_AC97_REV_8233C 0x20 +#define VIATECH_8233_AC97_REV_8233 0x30 +#define VIATECH_8233_AC97_REV_8233A 0x40 +#define VIATECH_8233_AC97_REV_8235 0x50 + +#define VERSION "Version alpha 2, Copyright (c) 2003 Jérôme Duval, compiled on " ## __DATE__ ## " " ## __TIME__ +#define DRIVER_NAME "auvia" +#define FRIENDLY_NAME "Auvia" +#define FRIENDLY_NAME_686 FRIENDLY_NAME" 82C686" +#define FRIENDLY_NAME_8233C FRIENDLY_NAME" 8233C" +#define FRIENDLY_NAME_8233 FRIENDLY_NAME" 8233" +#define FRIENDLY_NAME_8233A FRIENDLY_NAME" 8233A" +#define FRIENDLY_NAME_8235 FRIENDLY_NAME" 8235" +#define AUTHOR "Jérôme Duval" + +#define VIA_TABLE_SIZE 255 + +#define AUVIA_USE_PLAY (1 << 0) +#define AUVIA_USE_RECORD (1 << 1) +#define AUVIA_STATE_STARTED (1 << 0) + +/* + * Auvia memory managment + */ + +typedef struct _auvia_mem { + LIST_ENTRY(_auvia_mem) next; + void *log_base; + void *phy_base; + area_id area; + size_t size; +} auvia_mem; + +/* + * Streams + */ + +typedef struct _auvia_stream { + struct _auvia_dev *card; + uint8 use; + uint8 state; + uint8 b16; + uint32 sample_rate; + uint8 channels; + uint32 bufframes; + uint8 bufcount; + + uint32 base; + + LIST_ENTRY(_auvia_stream) next; + + void (*inth) (void *); + void *inthparam; + + void *dmaops_log_base; + void *dmaops_phy_base; + area_id dmaops_area; + + auvia_mem *buffer; + uint16 blksize; /* in samples */ + uint16 trigblk; /* blk on which to trigger inth */ + uint16 blkmod; /* Modulo value to wrap trigblk */ + + /* multi_audio */ + volatile int64 frames_count; // for play or record + volatile bigtime_t real_time; // for play or record + volatile int32 buffer_cycle; // for play or record + int32 first_channel; + bool update_needed; +} auvia_stream; + +/* + * Devices + */ + +typedef struct _auvia_dev { + char name[DEVNAME]; /* used for resources */ + pci_info info; + device_config config; + + void *ptb_log_base; + void *ptb_phy_base; + area_id ptb_area; + + sem_id buffer_ready_sem; + + uint32 interrupt_mask; + + LIST_HEAD(, _auvia_stream) streams; + + LIST_HEAD(, _auvia_mem) mems; + + auvia_stream *pstream; + auvia_stream *rstream; + + /* multi_audio */ + multi_dev multi; +} auvia_dev; + +extern int32 num_cards; +extern auvia_dev cards[NUM_CARDS]; + +status_t auvia_stream_set_audioparms(auvia_stream *stream, uint8 channels, + uint8 b16, uint32 sample_rate); +status_t auvia_stream_commit_parms(auvia_stream *stream); +status_t auvia_stream_get_nth_buffer(auvia_stream *stream, uint8 chan, uint8 buf, + char** buffer, size_t *stride); +void auvia_stream_start(auvia_stream *stream, void (*inth) (void *), void *inthparam); +void auvia_stream_halt(auvia_stream *stream); +auvia_stream *auvia_stream_new(auvia_dev *card, uint8 use, uint32 bufframes, uint8 bufcount); +void auvia_stream_delete(auvia_stream *stream); + +#endif /* _DEV_PCI_AUVIA_H_ */ diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/auviareg.h b/src/add-ons/kernel/drivers/audio/ac97/auvia/auviareg.h new file mode 100644 index 0000000000..83a8851726 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/auviareg.h @@ -0,0 +1,128 @@ +/* + * Auvia BeOS Driver for Via VT82xx Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + + * This code is derived from software contributed to The NetBSD Foundation + * by Tyler C. Sarna + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DEV_PCI_AUVIAREG_H_ +#define _DEV_PCI_AUVIAREG_H_ + +// VT8233 specific registers contain a '8233_' + +#define AUVIA_PCICONF_JUNK 0x40 +#define AUVIA_PCICONF_ENABLES 0x00ff0000 /* reg 42 mask */ +#define AUVIA_PCICONF_ACLINKENAB 0x00008000 /* ac link enab */ +#define AUVIA_PCICONF_ACNOTRST 0x00004000 /* ~(ac reset) */ +#define AUVIA_PCICONF_ACSYNC 0x00002000 /* ac sync */ +#define AUVIA_PCICONF_ACVSR 0x00000800 /* var. samp. rate */ +#define AUVIA_PCICONF_ACSGD 0x00000400 /* SGD enab */ +#define AUVIA_PCICONF_ACFM 0x00000200 /* FM enab */ +#define AUVIA_PCICONF_ACSB 0x00000100 /* SB enab */ + +#define AUVIA_PLAY_BASE 0x00 +#define AUVIA_RECORD_BASE 0x10 +#define AUVIA_8233_RECORD_BASE 0x60 + +/* *_RP_* are offsets from AUVIA_PLAY_BASE or AUVIA_RECORD_BASE or AUVIA_8233_RECORD_BASE*/ +#define AUVIA_RP_STAT 0x00 +#define AUVIA_RPSTAT_INTR 0x03 +#define AUVIA_RP_CONTROL 0x01 +#define AUVIA_RPCTRL_START 0x80 +#define AUVIA_RPCTRL_TERMINATE 0x40 +#define AUVIA_RPCTRL_AUTOSTART 0x20 +/* The following are 8233 specific */ +#define AUVIA_RPCTRL_STOP 0x04 +#define AUVIA_RPCTRL_EOL 0x02 +#define AUVIA_RPCTRL_FLAG 0x01 +#define AUVIA_RP_MODE 0x02 /* 82c686 specific */ +#define AUVIA_RPMODE_INTR_FLAG 0x01 +#define AUVIA_RPMODE_INTR_EOL 0x02 +#define AUVIA_RPMODE_STEREO 0x10 +#define AUVIA_RPMODE_16BIT 0x20 +#define AUVIA_RPMODE_AUTOSTART 0x80 +#define AUVIA_RP_DMAOPS_BASE 0x04 + +#define AUVIA_8233_RP_DXS_LVOL 0x02 +#define AUVIA_8233_RP_DXS_RVOL 0x03 +#define AUVIA_8233_RP_RATEFMT 0x08 +#define AUVIA_8233_RATEFMT_48K 0xfffff +#define AUVIA_8233_RATEFMT_STEREO 0x00100000 +#define AUVIA_8233_RATEFMT_16BIT 0x00200000 + +#define VIA_RP_DMAOPS_COUNT 0x0c + +#define AUVIA_8233_MP_BASE 0x40 + /* STAT, CONTROL, DMAOPS_BASE, DMAOPS_COUNT are valid */ +#define AUVIA_8233_OFF_MP_FORMAT 0x02 +#define AUVIA_8233_MP_FORMAT_8BIT 0x00 +#define AUVIA_8233_MP_FORMAT_16BIT 0x80 +#define AUVIA_8233_MP_FORMAT_CHANNEL_MASK 0x70 /* 1, 2, 4, 6 */ +#define AUVIA_8233_OFF_MP_SCRATCH 0x03 +#define AUVIA_8233_OFF_MP_STOP 0x08 + +#define AUVIA_CODEC_CTL 0x80 +#define AUVIA_CODEC_READ 0x00800000 +#define AUVIA_CODEC_BUSY 0x01000000 +#define AUVIA_CODEC_PRIVALID 0x02000000 +#define AUVIA_CODEC_INDEX(x) ((x)<<16) + +#define AUVIA_SGD_SHADOW 0x84 +#define AUVIA_SGD_STAT_FLAG_MASK 0x00000007 +#define AUVIA_SGD_STAT_EOL_MASK 0x00000070 +#define AUVIA_SGD_STAT_STOP_MASK 0x00000700 +#define AUVIA_SGD_STAT_ACTIVE_MASK 0x00007000 +#define AUVIA_SGD_STAT_PLAYBACK 0x00000001 +#define AUVIA_SGD_STAT_RECORD 0x00000002 +#define AUVIA_SGD_STAT_FM 0x00000004 +#define AUVIA_SGD_STAT_ALL 0x00000007 + +#define AUVIA_8233_SGD_STAT_SDX0_MASK 0x00000008 +#define AUVIA_8233_SGD_STAT_SDX1_MASK 0x00000080 +#define AUVIA_8233_SGD_STAT_SDX2_MASK 0x00000800 +#define AUVIA_8233_SGD_STAT_SDX3_MASK 0x00008000 +#define AUVIA_8233_SGD_STAT_MP_MASK 0x00080000 +#define AUVIA_8233_SGD_STAT_REC0_MASK 0x08000000 +#define AUVIA_8233_SGD_STAT_REC1_MASK 0x80000000 +#define AUVIA_8233_SGD_STAT_FLAG 0x00000001 +#define AUVIA_8233_SGD_STAT_EOL 0x00000002 +#define AUVIA_8233_SGD_STAT_STOP 0x00000004 +#define AUVIA_8233_SGD_STAT_ACTIVE 0x00000008 +#define AUVIA_8233_SGD_STAT_FLAG_EOL 0x00000003 + +#define AUVIA_DMAOP_EOL 0x80000000 +#define AUVIA_DMAOP_FLAG 0x40000000 +#define AUVIA_DMAOP_STOP 0x20000000 +#define AUVIA_DMAOP_COUNT(x) ((x)&0x00FFFFFF) + +#endif /* _DEV_PCI_AUVIAREG_H_ */ diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/config.h b/src/add-ons/kernel/drivers/audio/ac97/auvia/config.h new file mode 100644 index 0000000000..a0176f2041 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/config.h @@ -0,0 +1,49 @@ +/* + * Auvia BeOS Driver for Via VT82xx Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#define NUM_CARDS 3 +#define DEVNAME 32 + +typedef struct +{ + uint32 nabmbar; + uint32 irq; + uint32 type; +} device_config; + +#define TYPE_686 0x01 +#define TYPE_8233 0x02 + +#define IS_686(x) ((x)->type & TYPE_686) +#define IS_8233(x) ((x)->type & TYPE_8233) +#endif diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/debug.c b/src/add-ons/kernel/drivers/audio/ac97/auvia/debug.c new file mode 100644 index 0000000000..3b99b3d765 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/debug.c @@ -0,0 +1,95 @@ +/* + * Auvia BeOS Driver for Via VT82xx Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include +#include +#include "debug.h" +#include "auvia.h" + +#if DEBUG > 0 +static const char * logfile="/boot/home/auvia.log"; +static sem_id loglock; +#endif + +void debug_printf(const char *text,...); +void log_printf(const char *text,...); +void log_create(); + +void debug_printf(const char *text,...) +{ + char buf[1024]; + va_list ap; + + va_start(ap,text); + vsprintf(buf,text,ap); + va_end(ap); + + dprintf(DRIVER_NAME ": %s",buf); +} + +void log_create() +{ +#if DEBUG > 0 + int fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); + const char *text = DRIVER_NAME ", " VERSION "\n"; + loglock = create_sem(1,"logfile sem"); + write(fd,text,strlen(text)); + close(fd); +#endif +} + +void log_printf(const char *text,...) +{ +#if DEBUG > 0 + int fd; + char buf[1024]; + va_list ap; + + va_start(ap,text); + vsprintf(buf,text,ap); + va_end(ap); + + dprintf(DRIVER_NAME ": %s",buf); + + acquire_sem(loglock); + fd = open(logfile, O_WRONLY | O_APPEND); + write(fd,buf,strlen(buf)); + close(fd); + release_sem(loglock); + + #if DEBUG > 1 + snooze(150000); + #endif +#endif +} + diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/debug.h b/src/add-ons/kernel/drivers/audio/ac97/auvia/debug.h new file mode 100644 index 0000000000..11bfe596f7 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/debug.h @@ -0,0 +1,70 @@ +/* + * Auvia BeOS Driver for Via VT82xx Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +/* + * PRINT() executes dprintf if DEBUG = 0 (disabled), or expands to LOG() when DEBUG > 0 + * TRACE() executes dprintf if DEBUG > 0 + * LOG() executes dprintf and writes to the logfile if DEBUG > 0 + */ + +/* DEBUG == 0, no debugging, PRINT writes to syslog + * DEBUG == 1, TRACE & LOG, PRINT + * DEBUG == 2, TRACE & LOG, PRINT with snooze() + */ +#ifndef DEBUG + #define DEBUG 0 +#endif + +#undef PRINT +#undef TRACE +#undef ASSERT + +#if DEBUG > 0 + #define PRINT(a) log_printf a + #define TRACE(a) debug_printf a + #define LOG(a) log_printf a + #define LOG_CREATE() log_create() + #define ASSERT(a) if (a) {} else LOG(("ASSERT failed! file = %s, line = %d\n",__FILE__,__LINE__)) + void log_create(); + void log_printf(const char *text,...); + void debug_printf(const char *text,...); +#else + void debug_printf(const char *text,...); + #define PRINT(a) debug_printf a + #define TRACE(a) ((void)(0)) + #define ASSERT(a) ((void)(0)) + #define LOG(a) ((void)(0)) + #define LOG_CREATE() +#endif + +#endif diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/io.c b/src/add-ons/kernel/drivers/audio/ac97/auvia/io.c new file mode 100644 index 0000000000..b03fc98ab7 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/io.c @@ -0,0 +1,193 @@ +/* + * Auvia BeOS Driver for Via VT82xx Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include "io.h" +#include "auviareg.h" +#include "debug.h" + +/* + * from BeOS R3 KernelExport.h + * should be replaced by PCI bus manager functions + */ +uint8 read_io_8(int mapped_io_addr); +void write_io_8(int mapped_io_addr, uint8 value); +uint16 read_io_16(int mapped_io_addr); +void write_io_16(int mapped_io_addr, uint16 value); +uint32 read_io_32(int mapped_io_addr); +void write_io_32(int mapped_io_addr, uint32 value); + +uint8 +auvia_reg_read_8(device_config *config, int regno) +{ + return read_io_8(config->nabmbar + regno); +} + +uint16 +auvia_reg_read_16(device_config *config, int regno) +{ + return read_io_16(config->nabmbar + regno); +} + +uint32 +auvia_reg_read_32(device_config *config, int regno) +{ + return read_io_32(config->nabmbar + regno); +} + +void +auvia_reg_write_8(device_config *config, int regno, uint8 value) +{ + write_io_8(config->nabmbar + regno, value); +} + +void +auvia_reg_write_16(device_config *config, int regno, uint16 value) +{ + write_io_16(config->nabmbar + regno, value); +} + +void +auvia_reg_write_32(device_config *config, int regno, uint32 value) +{ + write_io_32(config->nabmbar + regno, value); +} + +/* Emu10k1 Low level */ + +/*uint32 +auvia_chan_read(device_config *config, uint16 chano, uint32 reg) +{ + uint32 ptr, mask = 0xFFFFFFFF; + uint8 size, offset = 0; + + ptr = ((((uint32) reg) << 16) & + (IS_AUDIGY(config) ? EMU_A_PTR_ADDR_MASK : EMU_PTR_ADDR_MASK)) | + (chano & EMU_PTR_CHNO_MASK); + if (reg & 0xff000000) { + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + } + write_io_32(config->nabmbar + EMU_PTR, ptr); + ptr = (read_io_32(config->nabmbar + EMU_DATA) & mask) >> offset; + return ptr; +} + +void +auvia_chan_write(device_config *config, uint16 chano, + uint32 reg, uint32 data) +{ + uint32 ptr, mask; + uint8 size, offset; + + ptr = ((((uint32) reg) << 16) & + (IS_AUDIGY(config) ? EMU_A_PTR_ADDR_MASK : EMU_PTR_ADDR_MASK)) | + (chano & EMU_PTR_CHNO_MASK); + if (reg & 0xff000000) { + size = (reg >> 24) & 0x3f; + offset = (reg >> 16) & 0x1f; + mask = ((1 << size) - 1) << offset; + data = ((data << offset) & mask) | + (auvia_chan_read(config, chano, reg & 0xFFFF) & ~mask); + } + write_io_32(config->nabmbar + EMU_PTR, ptr); + write_io_32(config->nabmbar + EMU_DATA, data); +}*/ + +/* codec */ + +#define AUVIA_TIMEOUT 200 + +int +auvia_codec_waitready(device_config *config) +{ + int i; + + /* poll until codec not busy */ + for(i=0; (inabmbar + + AUVIA_CODEC_CTL) & AUVIA_CODEC_BUSY) ; i++) + snooze(1); + if(i>=AUVIA_TIMEOUT) { + //PRINT(("codec busy\n")); + return B_ERROR; + } + return B_OK; +} + +int +auvia_codec_waitvalid(device_config *config) +{ + int i; + + /* poll until codec valid */ + for(i=0; (inabmbar + + AUVIA_CODEC_CTL) & AUVIA_CODEC_PRIVALID) ; i++) + snooze(1); + if(i>=AUVIA_TIMEOUT) { + //PRINT(("codec invalid\n")); + return B_ERROR; + } + return B_OK; +} + +uint16 +auvia_codec_read(device_config *config, int regno) +{ + if(auvia_codec_waitready(config)!=B_OK) { + PRINT(("codec busy (1)\n")); + return -1; + } + write_io_32(config->nabmbar + AUVIA_CODEC_CTL, + AUVIA_CODEC_PRIVALID | AUVIA_CODEC_READ | AUVIA_CODEC_INDEX(regno)); + + if(auvia_codec_waitready(config)!=B_OK) { + PRINT(("codec busy (2)\n")); + return -1; + } + if(auvia_codec_waitvalid(config)!=B_OK) { + PRINT(("codec invalid (3)\n")); + return -1; + } + + return read_io_16(config->nabmbar + AUVIA_CODEC_CTL); +} + +void +auvia_codec_write(device_config *config, int regno, uint16 value) +{ + if(auvia_codec_waitready(config)!=B_OK) { + PRINT(("codec busy (4)\n")); + return; + } + write_io_32(config->nabmbar + AUVIA_CODEC_CTL, + AUVIA_CODEC_PRIVALID | AUVIA_CODEC_INDEX(regno) | value); +} diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/io.h b/src/add-ons/kernel/drivers/audio/ac97/auvia/io.h new file mode 100644 index 0000000000..ffdc00b46b --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/io.h @@ -0,0 +1,47 @@ +/* + * Auvia BeOS Driver for Via VT82xx Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _IO_H_ +#define _IO_H_ + +#include "config.h" + +uint8 auvia_reg_read_8(device_config *config, int regno); +uint16 auvia_reg_read_16(device_config *config, int regno); +uint32 auvia_reg_read_32(device_config *config, int regno); + +void auvia_reg_write_8(device_config *config, int regno, uint8 value); +void auvia_reg_write_16(device_config *config, int regno, uint16 value); +void auvia_reg_write_32(device_config *config, int regno, uint32 value); + +uint16 auvia_codec_read(device_config *config, int regno); +void auvia_codec_write(device_config *config, int regno, uint16 value); + +#endif diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/multi.c b/src/add-ons/kernel/drivers/audio/ac97/auvia/multi.c new file mode 100644 index 0000000000..3129febc15 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/multi.c @@ -0,0 +1,1115 @@ +/* + * Auvia BeOS Driver for Via VT82xx Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include "multi_audio.h" +#include "multi.h" +#include "ac97.h" + +//#define DEBUG 1 + +#include "debug.h" +#include "auvia.h" +#include "util.h" +#include "io.h" + +static void +auvia_ac97_get_mix(void *card, const void *cookie, int32 type, float *values) { + auvia_dev *dev = (auvia_dev*)card; + ac97_source_info *info = (ac97_source_info *)cookie; + uint16 value, mask; + float gain; + + switch(type) { + case B_MIX_GAIN: + value = auvia_codec_read(&dev->config, info->reg); + //PRINT(("B_MIX_GAIN value : %u\n", value)); + if(info->type & B_MIX_STEREO) { + mask = ((1 << (info->bits + 1)) - 1) << 8; + gain = ((value & mask) >> 8) * info->granularity; + if(info->polarity == 1) + values[0] = info->max_gain - gain; + else + values[0] = gain - info->min_gain; + + mask = ((1 << (info->bits + 1)) - 1); + gain = (value & mask) * info->granularity; + if(info->polarity == 1) + values[1] = info->max_gain - gain; + else + values[1] = gain - info->min_gain; + } else { + mask = ((1 << (info->bits + 1)) - 1); + gain = (value & mask) * info->granularity; + if(info->polarity == 1) + values[0] = info->max_gain - gain; + else + values[0] = gain - info->min_gain; + } + break; + case B_MIX_MUTE: + mask = ((1 << 1) - 1) << 15; + value = auvia_codec_read(&dev->config, info->reg); + //PRINT(("B_MIX_MUTE value : %u\n", value)); + value &= mask; + values[0] = ((value >> 15) == 1) ? 1.0 : 0.0; + break; + case B_MIX_MICBOOST: + mask = ((1 << 1) - 1) << 6; + value = auvia_codec_read(&dev->config, info->reg); + //PRINT(("B_MIX_MICBOOST value : %u\n", value)); + value &= mask; + values[0] = ((value >> 6) == 1) ? 1.0 : 0.0; + break; + case B_MIX_MUX: + mask = ((1 << 3) - 1); + value = auvia_codec_read(&dev->config, AC97_RECORD_SELECT); + value &= mask; + //PRINT(("B_MIX_MUX value : %u\n", value)); + values[0] = (float)value; + break; + } +} + +static void +auvia_ac97_set_mix(void *card, const void *cookie, int32 type, float *values) { + auvia_dev *dev = (auvia_dev*)card; + ac97_source_info *info = (ac97_source_info *)cookie; + uint16 value, mask; + float gain; + + switch(type) { + case B_MIX_GAIN: + value = auvia_codec_read(&dev->config, info->reg); + if(info->type & B_MIX_STEREO) { + mask = ((1 << (info->bits + 1)) - 1) << 8; + value &= ~mask; + + if(info->polarity == 1) + gain = info->max_gain - values[0]; + else + gain = values[0] - info->min_gain; + value |= ((uint16)(gain / info->granularity) << 8) & mask; + + mask = ((1 << (info->bits + 1)) - 1); + value &= ~mask; + if(info->polarity == 1) + gain = info->max_gain - values[1]; + else + gain = values[1] - info->min_gain; + value |= ((uint16)(gain / info->granularity)) & mask; + } else { + mask = ((1 << (info->bits + 1)) - 1); + value &= ~mask; + if(info->polarity == 1) + gain = info->max_gain - values[0]; + else + gain = values[0] - info->min_gain; + value |= ((uint16)(gain / info->granularity)) & mask; + } + //PRINT(("B_MIX_GAIN value : %u\n", value)); + auvia_codec_write(&dev->config, info->reg, value); + break; + case B_MIX_MUTE: + mask = ((1 << 1) - 1) << 15; + value = auvia_codec_read(&dev->config, info->reg); + value &= ~mask; + value |= ((values[0] == 1.0 ? 1 : 0 ) << 15 & mask); + if(info->reg == AC97_SURROUND_VOLUME) { + // there is a independent mute for each channel + mask = ((1 << 1) - 1) << 7; + value &= ~mask; + value |= ((values[0] == 1.0 ? 1 : 0 ) << 7 & mask); + } + //PRINT(("B_MIX_MUTE value : %u\n", value)); + auvia_codec_write(&dev->config, info->reg, value); + break; + case B_MIX_MICBOOST: + mask = ((1 << 1) - 1) << 6; + value = auvia_codec_read(&dev->config, info->reg); + value &= ~mask; + value |= ((values[0] == 1.0 ? 1 : 0 ) << 6 & mask); + //PRINT(("B_MIX_MICBOOST value : %u\n", value)); + auvia_codec_write(&dev->config, info->reg, value); + break; + case B_MIX_MUX: + mask = ((1 << 3) - 1); + value = ((int32)values[0]) & mask; + value = value | (value << 8); + //PRINT(("B_MIX_MUX value : %u\n", value)); + auvia_codec_write(&dev->config, AC97_RECORD_SELECT, value); + break; + } + +} + +static int32 +auvia_create_group_control(multi_dev *multi, int32 *index, int32 parent, + int32 string, const char* name) { + int32 i = *index; + (*index)++; + multi->controls[i].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + i; + multi->controls[i].mix_control.parent = parent; + multi->controls[i].mix_control.flags = B_MULTI_MIX_GROUP; + multi->controls[i].mix_control.master = EMU_MULTI_CONTROL_MASTERID; + multi->controls[i].mix_control.string = string; + if(name) + strcpy(multi->controls[i].mix_control.name, name); + + return multi->controls[i].mix_control.id; +} + +static status_t +auvia_create_controls_list(multi_dev *multi) +{ + uint32 i = 0, index = 0, count, id, parent, parent2, parent3; + auvia_dev *card = (auvia_dev*)multi->card; + const ac97_source_info *info; + + parent = auvia_create_group_control(multi, &index, 0, 0, "Record"); + + /* AC97 Record */ + info = &source_info[0]; + PRINT(("name : %s\n", info->name)); + + parent2 = auvia_create_group_control(multi, &index, parent, 0, info->name); + + if(info->type & B_MIX_GAIN) { + if(info->type & B_MIX_MUTE) { + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_ENABLE; + multi->controls[index].mix_control.master = EMU_MULTI_CONTROL_MASTERID; + multi->controls[index].mix_control.parent = parent2; + multi->controls[index].mix_control.string = S_MUTE; + multi->controls[index].cookie = info; + multi->controls[index].type = B_MIX_MUTE; + multi->controls[index].get = &auvia_ac97_get_mix; + multi->controls[index].set = &auvia_ac97_set_mix; + index++; + } + + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_GAIN; + multi->controls[index].mix_control.master = EMU_MULTI_CONTROL_MASTERID; + multi->controls[index].mix_control.parent = parent2; + strcpy(multi->controls[index].mix_control.name, info->name); + multi->controls[index].mix_control.u.gain.min_gain = info->min_gain; + multi->controls[index].mix_control.u.gain.max_gain = info->max_gain; + multi->controls[index].mix_control.u.gain.granularity = info->granularity; + multi->controls[index].cookie = info; + multi->controls[index].type = B_MIX_GAIN; + multi->controls[index].get = &auvia_ac97_get_mix; + multi->controls[index].set = &auvia_ac97_set_mix; + id = multi->controls[index].mix_control.id; + index++; + + if(info->type & B_MIX_STEREO) { + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_GAIN; + multi->controls[index].mix_control.master = id; + multi->controls[index].mix_control.parent = parent2; + strcpy(multi->controls[index].mix_control.name, info->name); + multi->controls[index].mix_control.u.gain.min_gain = info->min_gain; + multi->controls[index].mix_control.u.gain.max_gain = info->max_gain; + multi->controls[index].mix_control.u.gain.granularity = info->granularity; + multi->controls[index].cookie = info; + multi->controls[index].type = B_MIX_GAIN; + multi->controls[index].get = &auvia_ac97_get_mix; + multi->controls[index].set = &auvia_ac97_set_mix; + index++; + } + + if(info->type & B_MIX_RECORDMUX) { + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX; + multi->controls[index].mix_control.parent = parent2; + strcpy(multi->controls[index].mix_control.name, "Record Mux"); + multi->controls[index].cookie = info; + multi->controls[index].type = B_MIX_MUX; + multi->controls[index].get = &auvia_ac97_get_mix; + multi->controls[index].set = &auvia_ac97_set_mix; + parent3 = multi->controls[index].mix_control.id; + index++; + + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; + multi->controls[index].mix_control.parent = parent3; + multi->controls[index].mix_control.string = S_MIC; + index++; + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; + multi->controls[index].mix_control.parent = parent3; + strcpy(multi->controls[index].mix_control.name, "CD In"); + index++; + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; + multi->controls[index].mix_control.parent = parent3; + strcpy(multi->controls[index].mix_control.name, "Video In"); + index++; + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; + multi->controls[index].mix_control.parent = parent3; + strcpy(multi->controls[index].mix_control.name, "Aux In"); + index++; + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; + multi->controls[index].mix_control.parent = parent3; + strcpy(multi->controls[index].mix_control.name, "Line In"); + index++; + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; + multi->controls[index].mix_control.parent = parent3; + multi->controls[index].mix_control.string = S_STEREO_MIX; + index++; + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; + multi->controls[index].mix_control.parent = parent3; + multi->controls[index].mix_control.string = S_MONO_MIX; + index++; + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_MUX_VALUE; + multi->controls[index].mix_control.parent = parent3; + strcpy(multi->controls[index].mix_control.name, "TAD"); + index++; + } + } + + parent = auvia_create_group_control(multi, &index, 0, 0, "AC97 Mixer"); + + count = source_info_size; + count--; + + for(i=1; i < count ; i++) { + info = &source_info[i]; + PRINT(("name : %s\n", info->name)); + + parent2 = auvia_create_group_control(multi, &index, parent, 0, info->name); + + if(info->type & B_MIX_GAIN) { + if(info->type & B_MIX_MUTE) { + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_ENABLE; + multi->controls[index].mix_control.master = EMU_MULTI_CONTROL_MASTERID; + multi->controls[index].mix_control.parent = parent2; + multi->controls[index].mix_control.string = S_MUTE; + multi->controls[index].cookie = info; + multi->controls[index].type = B_MIX_MUTE; + multi->controls[index].get = &auvia_ac97_get_mix; + multi->controls[index].set = &auvia_ac97_set_mix; + index++; + } + + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_GAIN; + multi->controls[index].mix_control.master = EMU_MULTI_CONTROL_MASTERID; + multi->controls[index].mix_control.parent = parent2; + strcpy(multi->controls[index].mix_control.name, info->name); + multi->controls[index].mix_control.u.gain.min_gain = info->min_gain; + multi->controls[index].mix_control.u.gain.max_gain = info->max_gain; + multi->controls[index].mix_control.u.gain.granularity = info->granularity; + multi->controls[index].cookie = info; + multi->controls[index].type = B_MIX_GAIN; + multi->controls[index].get = &auvia_ac97_get_mix; + multi->controls[index].set = &auvia_ac97_set_mix; + id = multi->controls[index].mix_control.id; + index++; + + if(info->type & B_MIX_STEREO) { + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_GAIN; + multi->controls[index].mix_control.master = id; + multi->controls[index].mix_control.parent = parent2; + strcpy(multi->controls[index].mix_control.name, info->name); + multi->controls[index].mix_control.u.gain.min_gain = info->min_gain; + multi->controls[index].mix_control.u.gain.max_gain = info->max_gain; + multi->controls[index].mix_control.u.gain.granularity = info->granularity; + multi->controls[index].cookie = info; + multi->controls[index].type = B_MIX_GAIN; + multi->controls[index].get = &auvia_ac97_get_mix; + multi->controls[index].set = &auvia_ac97_set_mix; + index++; + } + } + } + + parent = auvia_create_group_control(multi, &index, 0, S_SETUP, NULL); + + /* AC97 20db Boost Mic */ + info = &source_info[6]; + + if(info->type & B_MIX_GAIN && info->type & B_MIX_MICBOOST) { + multi->controls[index].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + index; + multi->controls[index].mix_control.flags = B_MULTI_MIX_ENABLE; + multi->controls[index].mix_control.master = EMU_MULTI_CONTROL_MASTERID; + multi->controls[index].mix_control.parent = parent; + strcpy(multi->controls[index].mix_control.name, "Mic +20dB"); + multi->controls[index].cookie = info; + multi->controls[index].type = B_MIX_MICBOOST; + multi->controls[index].get = &auvia_ac97_get_mix; + multi->controls[index].set = &auvia_ac97_set_mix; + index++; + } + + multi->control_count = index; + PRINT(("multi->control_count %u\n", multi->control_count)); + return B_OK; +} + +static status_t +auvia_get_mix(auvia_dev *card, multi_mix_value_info * MMVI) +{ + int32 i, id; + multi_mixer_control *control = NULL; + for(i=0; iitem_count; i++) { + id = MMVI->values[i].id - EMU_MULTI_CONTROL_FIRSTID; + if(id < 0 || id >= card->multi.control_count) { + PRINT(("auvia_get_mix : invalid control id requested : %i\n", id)); + continue; + } + control = &card->multi.controls[id]; + + if(control->mix_control.flags & B_MULTI_MIX_GAIN) { + if(control->get) { + float values[2]; + control->get(card, control->cookie, control->type, values); + if(control->mix_control.master == EMU_MULTI_CONTROL_MASTERID) + MMVI->values[i].u.gain = values[0]; + else + MMVI->values[i].u.gain = values[1]; + } + } + + if(control->mix_control.flags & B_MULTI_MIX_ENABLE && control->get) { + float values[1]; + control->get(card, control->cookie, control->type, values); + MMVI->values[i].u.enable = (values[0] == 1.0); + } + + if(control->mix_control.flags & B_MULTI_MIX_MUX && control->get) { + float values[1]; + control->get(card, control->cookie, control->type, values); + MMVI->values[i].u.mux = (int32)values[0]; + } + } + return B_OK; +} + +static status_t +auvia_set_mix(auvia_dev *card, multi_mix_value_info * MMVI) +{ + int32 i, id; + multi_mixer_control *control = NULL; + for(i=0; iitem_count; i++) { + id = MMVI->values[i].id - EMU_MULTI_CONTROL_FIRSTID; + if(id < 0 || id >= card->multi.control_count) { + PRINT(("auvia_set_mix : invalid control id requested : %i\n", id)); + continue; + } + control = &card->multi.controls[id]; + + if(control->mix_control.flags & B_MULTI_MIX_GAIN) { + multi_mixer_control *control2 = NULL; + if(i+1item_count) { + id = MMVI->values[i + 1].id - EMU_MULTI_CONTROL_FIRSTID; + if(id < 0 || id >= card->multi.control_count) { + PRINT(("auvia_set_mix : invalid control id requested : %i\n", id)); + } else { + control2 = &card->multi.controls[id]; + if(control2->mix_control.master != control->mix_control.id) + control2 = NULL; + } + } + + if(control->set) { + float values[2]; + values[0] = 0.0; + values[1] = 0.0; + + if(control->mix_control.master == EMU_MULTI_CONTROL_MASTERID) + values[0] = MMVI->values[i].u.gain; + else + values[1] = MMVI->values[i].u.gain; + + if(control2 && control2->mix_control.master != EMU_MULTI_CONTROL_MASTERID) + values[1] = MMVI->values[i+1].u.gain; + + control->set(card, control->cookie, control->type, values); + } + + if(control2) + i++; + } + + if(control->mix_control.flags & B_MULTI_MIX_ENABLE && control->set) { + float values[1]; + + values[0] = MMVI->values[i].u.enable ? 1.0 : 0.0; + control->set(card, control->cookie, control->type, values); + } + + if(control->mix_control.flags & B_MULTI_MIX_MUX && control->set) { + float values[1]; + + values[0] = (float)MMVI->values[i].u.mux; + control->set(card, control->cookie, control->type, values); + } + } + return B_OK; +} + +static status_t +auvia_list_mix_controls(auvia_dev *card, multi_mix_control_info * MMCI) +{ + multi_mix_control *MMC; + int32 i; + + MMC = MMCI->controls; + if(MMCI->control_count < 24) + return B_ERROR; + + if(auvia_create_controls_list(&card->multi) < B_OK) + return B_ERROR; + for(i=0; imulti.control_count; i++) { + MMC[i] = card->multi.controls[i].mix_control; + } + + MMCI->control_count = card->multi.control_count; + return B_OK; +} + +static status_t +auvia_list_mix_connections(auvia_dev *card, multi_mix_connection_info * data) +{ + return B_ERROR; +} + +static status_t +auvia_list_mix_channels(auvia_dev *card, multi_mix_channel_info *data) +{ + return B_ERROR; +} + +/*multi_channel_info chans[] = { +{ 0, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, +{ 1, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 }, +{ 2, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, +{ 3, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 }, +{ 4, B_MULTI_INPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, +{ 5, B_MULTI_INPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 }, +{ 6, B_MULTI_INPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, +{ 7, B_MULTI_INPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 }, +{ 8, B_MULTI_OUTPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, +{ 9, B_MULTI_OUTPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, +{ 10, B_MULTI_INPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, +{ 11, B_MULTI_INPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, +};*/ + +/*multi_channel_info chans[] = { +{ 0, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, +{ 1, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 }, +{ 2, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_SURROUND_BUS, 0 }, +{ 3, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_SURROUND_BUS, 0 }, +{ 4, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_REARLEFT | B_CHANNEL_SURROUND_BUS, 0 }, +{ 5, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_REARRIGHT | B_CHANNEL_SURROUND_BUS, 0 }, +{ 6, B_MULTI_INPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, +{ 7, B_MULTI_INPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 }, +{ 8, B_MULTI_INPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 }, +{ 9, B_MULTI_INPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 }, +{ 10, B_MULTI_OUTPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, +{ 11, B_MULTI_OUTPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, +{ 12, B_MULTI_INPUT_BUS, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, +{ 13, B_MULTI_INPUT_BUS, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO }, +};*/ + + +static void +auvia_create_channels_list(multi_dev *multi) +{ + auvia_stream *stream; + uint32 index, i, mode, designations; + multi_channel_info *chans; + uint32 chan_designations[] = { + B_CHANNEL_LEFT, + B_CHANNEL_RIGHT, + B_CHANNEL_REARLEFT, + B_CHANNEL_REARRIGHT, + B_CHANNEL_CENTER, + B_CHANNEL_SUB + }; + + chans = multi->chans; + index = 0; + + for(mode=AUVIA_USE_PLAY; mode!=-1; + mode = (mode == AUVIA_USE_PLAY) ? AUVIA_USE_RECORD : -1) { + LIST_FOREACH(stream, &((auvia_dev*)multi->card)->streams, next) { + if ((stream->use & mode) == 0) + continue; + + if(stream->channels == 2) + designations = B_CHANNEL_STEREO_BUS; + else + designations = B_CHANNEL_SURROUND_BUS; + + for(i=0; ichannels; i++) { + chans[index].channel_id = index; + chans[index].kind = (mode == AUVIA_USE_PLAY) ? B_MULTI_OUTPUT_CHANNEL : B_MULTI_INPUT_CHANNEL; + chans[index].designations = designations | chan_designations[i]; + chans[index].connectors = 0; + index++; + } + } + + if(mode==AUVIA_USE_PLAY) { + multi->output_channel_count = index; + } else { + multi->input_channel_count = index - multi->output_channel_count; + } + } + + chans[index].channel_id = index; + chans[index].kind = B_MULTI_OUTPUT_BUS; + chans[index].designations = B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS; + chans[index].connectors = B_CHANNEL_MINI_JACK_STEREO; + index++; + + chans[index].channel_id = index; + chans[index].kind = B_MULTI_OUTPUT_BUS; + chans[index].designations = B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS; + chans[index].connectors = B_CHANNEL_MINI_JACK_STEREO; + index++; + + multi->output_bus_channel_count = index - multi->output_channel_count + - multi->input_channel_count; + + chans[index].channel_id = index; + chans[index].kind = B_MULTI_INPUT_BUS; + chans[index].designations = B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS; + chans[index].connectors = B_CHANNEL_MINI_JACK_STEREO; + index++; + + chans[index].channel_id = index; + chans[index].kind = B_MULTI_INPUT_BUS; + chans[index].designations = B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS; + chans[index].connectors = B_CHANNEL_MINI_JACK_STEREO; + index++; + + multi->input_bus_channel_count = index - multi->output_channel_count + - multi->input_channel_count - multi->output_bus_channel_count; + + multi->aux_bus_channel_count = 0; +} + + +static status_t +auvia_get_description(auvia_dev *card, multi_description *data) +{ + uint32 size; + + data->interface_version = B_CURRENT_INTERFACE_VERSION; + data->interface_minimum = B_CURRENT_INTERFACE_VERSION; + + if(IS_686(&card->config)) + strncpy(data->friendly_name, FRIENDLY_NAME_686, 32); + else if(IS_8233(&card->config)) { + switch(card->info.revision) { + case VIATECH_8233_AC97_REV_8233C: + strncpy(data->friendly_name, FRIENDLY_NAME_8233C, 32); + break; + case VIATECH_8233_AC97_REV_8233: + strncpy(data->friendly_name, FRIENDLY_NAME_8233, 32); + break; + case VIATECH_8233_AC97_REV_8233A: + strncpy(data->friendly_name, FRIENDLY_NAME_8233A, 32); + break; + case VIATECH_8233_AC97_REV_8235: + strncpy(data->friendly_name, FRIENDLY_NAME_8235, 32); + break; + } + } + strcpy(data->vendor_info, AUTHOR); + + /*data->output_channel_count = 6; + data->input_channel_count = 4; + data->output_bus_channel_count = 2; + data->input_bus_channel_count = 2; + data->aux_bus_channel_count = 0;*/ + + data->output_channel_count = card->multi.output_channel_count; + data->input_channel_count = card->multi.input_channel_count; + data->output_bus_channel_count = card->multi.output_bus_channel_count; + data->input_bus_channel_count = card->multi.input_bus_channel_count; + data->aux_bus_channel_count = card->multi.aux_bus_channel_count; + + size = card->multi.output_channel_count + card->multi.input_channel_count + + card->multi.output_bus_channel_count + card->multi.input_bus_channel_count + + card->multi.aux_bus_channel_count; + + // for each channel, starting with the first output channel, + // then the second, third..., followed by the first input + // channel, second, third, ..., followed by output bus + // channels and input bus channels and finally auxillary channels, + + LOG(("request_channel_count = %d\n",data->request_channel_count)); + if (data->request_channel_count >= size) { + LOG(("copying data\n")); + memcpy(data->channels, card->multi.chans, size * sizeof(card->multi.chans[0])); + } + + data->output_rates = B_SR_48000;// | B_SR_44100 | B_SR_CVSR; + data->input_rates = B_SR_48000;// | B_SR_44100 | B_SR_CVSR; + //data->output_rates = B_SR_44100; + //data->input_rates = B_SR_44100; + data->min_cvsr_rate = 0; + data->max_cvsr_rate = 48000; + //data->max_cvsr_rate = 44100; + + data->output_formats = B_FMT_16BIT; + data->input_formats = B_FMT_16BIT; + data->lock_sources = B_MULTI_LOCK_INTERNAL; + data->timecode_sources = 0; + data->interface_flags = B_MULTI_INTERFACE_PLAYBACK | B_MULTI_INTERFACE_RECORD; + data->start_latency = 3000; + + strcpy(data->control_panel,""); + + return B_OK; +} + +static status_t +auvia_get_enabled_channels(auvia_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; +/* + uint32 lock_source; + int32 lock_data; + uint32 timecode_source; + uint32 * connectors; +*/ + return B_OK; +} + +static status_t +auvia_set_enabled_channels(auvia_dev *card, multi_channel_enable *data) +{ + PRINT(("set_enabled_channels 0 : %s\n", B_TEST_CHANNEL(data->enable_bits, 0) ? "enabled": "disabled")); + PRINT(("set_enabled_channels 1 : %s\n", B_TEST_CHANNEL(data->enable_bits, 1) ? "enabled": "disabled")); + PRINT(("set_enabled_channels 2 : %s\n", B_TEST_CHANNEL(data->enable_bits, 2) ? "enabled": "disabled")); + PRINT(("set_enabled_channels 3 : %s\n", B_TEST_CHANNEL(data->enable_bits, 3) ? "enabled": "disabled")); + return B_OK; +} + +static status_t +auvia_get_global_format(auvia_dev *card, multi_format_info *data) +{ + data->output_latency = 0; + data->input_latency = 0; + data->timecode_kind = 0; + data->input.rate = B_SR_48000; + data->input.cvsr = 48000; + data->input.format = B_FMT_16BIT; + data->output.rate = B_SR_48000; + data->output.cvsr = 48000; + data->output.format = B_FMT_16BIT; + /*data->input.rate = B_SR_44100; + data->input.cvsr = 44100; + data->input.format = B_FMT_16BIT; + data->output.rate = B_SR_44100; + data->output.cvsr = 44100; + data->output.format = B_FMT_16BIT;*/ + return B_OK; +} + +static status_t +auvia_get_buffers(auvia_dev *card, multi_buffer_list *data) +{ + uint32 i, j, pchannels, rchannels; + + LOG(("flags = %#x\n",data->flags)); + LOG(("request_playback_buffers = %#x\n",data->request_playback_buffers)); + LOG(("request_playback_channels = %#x\n",data->request_playback_channels)); + LOG(("request_playback_buffer_size = %#x\n",data->request_playback_buffer_size)); + LOG(("request_record_buffers = %#x\n",data->request_record_buffers)); + LOG(("request_record_channels = %#x\n",data->request_record_channels)); + LOG(("request_record_buffer_size = %#x\n",data->request_record_buffer_size)); + + pchannels = card->pstream->channels; + rchannels = card->rstream->channels; + + if (data->request_playback_buffers < BUFFER_COUNT || + data->request_playback_channels < (pchannels) || + data->request_record_buffers < BUFFER_COUNT || + data->request_record_channels < (rchannels)) { + LOG(("not enough channels/buffers\n")); + } + + ASSERT(BUFFER_COUNT == 2); + + data->flags = B_MULTI_BUFFER_PLAYBACK | B_MULTI_BUFFER_RECORD; // XXX ??? +// data->flags = 0; + + data->return_playback_buffers = BUFFER_COUNT; /* playback_buffers[b][] */ + data->return_playback_channels = pchannels; /* playback_buffers[][c] */ + data->return_playback_buffer_size = BUFFER_FRAMES; /* frames */ + + for(i=0; ipstream, j, i, + &data->playback_buffers[i][j].base, + &data->playback_buffers[i][j].stride); + + data->return_record_buffers = BUFFER_COUNT; + data->return_record_channels = rchannels; + data->return_record_buffer_size = BUFFER_FRAMES; /* frames */ + + for(i=0; irstream, j, i, + &data->record_buffers[i][j].base, + &data->record_buffers[i][j].stride); + + return B_OK; +} + + +void +auvia_play_inth(void* inthparams) +{ + auvia_stream *stream = (auvia_stream *)inthparams; + //int32 count; + + //TRACE(("auvia_play_inth\n")); + + acquire_spinlock(&slock); + stream->real_time = system_time(); + stream->frames_count += BUFFER_FRAMES; + stream->buffer_cycle = (stream->trigblk + + stream->blkmod -1) % stream->blkmod; + stream->update_needed = true; + release_spinlock(&slock); + + //get_sem_count(stream->card->buffer_ready_sem, &count); + //if (count <= 0) + release_sem_etc(stream->card->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE); +} + +void +auvia_record_inth(void* inthparams) +{ + auvia_stream *stream = (auvia_stream *)inthparams; + //int32 count; + + //TRACE(("auvia_record_inth\n")); + + acquire_spinlock(&slock); + stream->real_time = system_time(); + stream->frames_count += BUFFER_FRAMES; + stream->buffer_cycle = (stream->trigblk + + stream->blkmod -1) % stream->blkmod; + stream->update_needed = true; + release_spinlock(&slock); + + //get_sem_count(stream->card->buffer_ready_sem, &count); + //if (count <= 0) + release_sem_etc(stream->card->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE); +} + +static status_t +auvia_buffer_exchange(auvia_dev *card, multi_buffer_info *data) +{ + cpu_status status; + auvia_stream *pstream, *rstream; + + data->flags = B_MULTI_BUFFER_PLAYBACK | B_MULTI_BUFFER_RECORD; + + if (!(card->pstream->state & AUVIA_STATE_STARTED)) + auvia_stream_start(card->pstream, auvia_play_inth, card->pstream); + + if (!(card->rstream->state & AUVIA_STATE_STARTED)) + auvia_stream_start(card->rstream, auvia_record_inth, card->rstream); + + if(acquire_sem_etc(card->buffer_ready_sem, 1, B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, 50000) + == B_TIMED_OUT) { + LOG(("buffer_exchange timeout ff\n")); + } + + status = lock(); + + LIST_FOREACH(pstream, &card->streams, next) { + if ((pstream->use & AUVIA_USE_PLAY) == 0 || + (pstream->state & AUVIA_STATE_STARTED) == 0) + continue; + if(pstream->update_needed) + break; + } + + LIST_FOREACH(rstream, &card->streams, next) { + if ((rstream->use & AUVIA_USE_RECORD) == 0 || + (rstream->state & AUVIA_STATE_STARTED) == 0) + continue; + if(rstream->update_needed) + break; + } + + if(!pstream) + pstream = card->pstream; + if(!rstream) + rstream = card->rstream; + + /* do playback */ + data->playback_buffer_cycle = pstream->buffer_cycle; + data->played_real_time = pstream->real_time; + data->played_frames_count = pstream->frames_count; + data->_reserved_0 = pstream->first_channel; + pstream->update_needed = false; + + /* do record */ + data->record_buffer_cycle = rstream->buffer_cycle; + data->recorded_frames_count = rstream->frames_count; + data->recorded_real_time = rstream->real_time; + data->_reserved_1 = rstream->first_channel; + rstream->update_needed = false; + unlock(status); + + //TRACE(("buffer_exchange ended\n")); + return B_OK; +} + +static status_t +auvia_buffer_force_stop(auvia_dev *card) +{ + //auvia_voice_halt(card->pvoice); + return B_OK; +} + +static status_t +auvia_multi_control(void *cookie, uint32 op, void *data, size_t length) +{ + auvia_dev *card = (auvia_dev *)cookie; + + switch (op) { + case B_MULTI_GET_DESCRIPTION: + LOG(("B_MULTI_GET_DESCRIPTION\n")); + return auvia_get_description(card, (multi_description *)data); + case B_MULTI_GET_EVENT_INFO: + LOG(("B_MULTI_GET_EVENT_INFO\n")); + return B_ERROR; + case B_MULTI_SET_EVENT_INFO: + LOG(("B_MULTI_SET_EVENT_INFO\n")); + return B_ERROR; + case B_MULTI_GET_EVENT: + LOG(("B_MULTI_GET_EVENT\n")); + return B_ERROR; + case B_MULTI_GET_ENABLED_CHANNELS: + LOG(("B_MULTI_GET_ENABLED_CHANNELS\n")); + return auvia_get_enabled_channels(card, (multi_channel_enable *)data); + case B_MULTI_SET_ENABLED_CHANNELS: + LOG(("B_MULTI_SET_ENABLED_CHANNELS\n")); + return auvia_set_enabled_channels(card, (multi_channel_enable *)data); + case B_MULTI_GET_GLOBAL_FORMAT: + LOG(("B_MULTI_GET_GLOBAL_FORMAT\n")); + return auvia_get_global_format(card, (multi_format_info *)data); + case B_MULTI_SET_GLOBAL_FORMAT: + LOG(("B_MULTI_SET_GLOBAL_FORMAT\n")); + return B_OK; /* XXX BUG! we *MUST* return B_OK, returning B_ERROR will prevent + * BeOS to accept the format returned in B_MULTI_GET_GLOBAL_FORMAT + */ + case B_MULTI_GET_CHANNEL_FORMATS: + LOG(("B_MULTI_GET_CHANNEL_FORMATS\n")); + return B_ERROR; + case B_MULTI_SET_CHANNEL_FORMATS: /* only implemented if possible */ + LOG(("B_MULTI_SET_CHANNEL_FORMATS\n")); + return B_ERROR; + case B_MULTI_GET_MIX: + LOG(("B_MULTI_GET_MIX\n")); + return auvia_get_mix(card, (multi_mix_value_info *)data); + case B_MULTI_SET_MIX: + LOG(("B_MULTI_SET_MIX\n")); + return auvia_set_mix(card, (multi_mix_value_info *)data); + case B_MULTI_LIST_MIX_CHANNELS: + LOG(("B_MULTI_LIST_MIX_CHANNELS\n")); + return auvia_list_mix_channels(card, (multi_mix_channel_info *)data); + case B_MULTI_LIST_MIX_CONTROLS: + LOG(("B_MULTI_LIST_MIX_CONTROLS\n")); + return auvia_list_mix_controls(card, (multi_mix_control_info *)data); + case B_MULTI_LIST_MIX_CONNECTIONS: + LOG(("B_MULTI_LIST_MIX_CONNECTIONS\n")); + return auvia_list_mix_connections(card, (multi_mix_connection_info *)data); + case B_MULTI_GET_BUFFERS: /* Fill out the struct for the first time; doesn't start anything. */ + LOG(("B_MULTI_GET_BUFFERS\n")); + return auvia_get_buffers(card, data); + case B_MULTI_SET_BUFFERS: /* Set what buffers to use, if the driver supports soft buffers. */ + LOG(("B_MULTI_SET_BUFFERS\n")); + return B_ERROR; /* we do not support soft buffers */ + case B_MULTI_SET_START_TIME: /* When to actually start */ + LOG(("B_MULTI_SET_START_TIME\n")); + return B_ERROR; + case B_MULTI_BUFFER_EXCHANGE: /* stop and go are derived from this being called */ + //TRACE(("B_MULTI_BUFFER_EXCHANGE\n")); + return auvia_buffer_exchange(card, (multi_buffer_info *)data); + case B_MULTI_BUFFER_FORCE_STOP: /* force stop of playback, nothing in data */ + LOG(("B_MULTI_BUFFER_FORCE_STOP\n")); + return auvia_buffer_force_stop(card); + } + LOG(("ERROR: unknown multi_control %#x\n",op)); + return B_ERROR; +} + +static status_t auvia_open(const char *name, uint32 flags, void** cookie); +static status_t auvia_close(void* cookie); +static status_t auvia_free(void* cookie); +static status_t auvia_control(void* cookie, uint32 op, void* arg, size_t len); +static status_t auvia_read(void* cookie, off_t position, void *buf, size_t* num_bytes); +static status_t auvia_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes); + +device_hooks multi_hooks = { + auvia_open, /* -> open entry point */ + auvia_close, /* -> close entry point */ + auvia_free, /* -> free cookie */ + auvia_control, /* -> control entry point */ + auvia_read, /* -> read entry point */ + auvia_write, /* -> write entry point */ + NULL, /* start select */ + NULL, /* stop select */ + NULL, /* scatter-gather read from the device */ + NULL /* scatter-gather write to the device */ +}; + +static status_t +auvia_open(const char *name, uint32 flags, void** cookie) +{ + auvia_dev *card = NULL; + int ix; + + LOG(("open()\n")); + + for (ix=0; ixpstream !=NULL) + return B_ERROR; + if(card->rstream !=NULL) + return B_ERROR; + + *cookie = card; + card->multi.card = card; + + LOG(("stream_new\n")); + + card->rstream = auvia_stream_new(card, AUVIA_USE_RECORD, BUFFER_FRAMES, BUFFER_COUNT); + card->pstream = auvia_stream_new(card, AUVIA_USE_PLAY, BUFFER_FRAMES, BUFFER_COUNT); + + card->buffer_ready_sem = create_sem(0, "pbuffer ready"); + + LOG(("stream_setaudio\n")); + + auvia_stream_set_audioparms(card->pstream, 2, true, 48000); + auvia_stream_set_audioparms(card->rstream, 2, true, 48000); + + card->pstream->first_channel = 0; + card->rstream->first_channel = 2; + + auvia_stream_commit_parms(card->pstream); + auvia_stream_commit_parms(card->rstream); + + auvia_create_channels_list(&card->multi); + + return B_OK; +} + +static status_t +auvia_close(void* cookie) +{ + //auvia_dev *card = cookie; + LOG(("close()\n")); + + return B_OK; +} + +static status_t +auvia_free(void* cookie) +{ + auvia_dev *card = cookie; + auvia_stream *stream; + LOG(("free()\n")); + + if (card->buffer_ready_sem > B_OK) + delete_sem(card->buffer_ready_sem); + + LIST_FOREACH(stream, &card->streams, next) { + auvia_stream_halt(stream); + } + + while(!LIST_EMPTY(&card->streams)) { + auvia_stream_delete(LIST_FIRST(&card->streams)); + } + + return B_OK; +} + +static status_t +auvia_control(void* cookie, uint32 op, void* arg, size_t len) +{ + return auvia_multi_control(cookie, op, arg, len); +} + +static status_t +auvia_read(void* cookie, off_t position, void *buf, size_t* num_bytes) +{ + *num_bytes = 0; /* tell caller nothing was read */ + return B_IO_ERROR; +} + +static status_t +auvia_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes) +{ + *num_bytes = 0; /* tell caller nothing was written */ + return B_IO_ERROR; +} + diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/multi.h b/src/add-ons/kernel/drivers/audio/ac97/auvia/multi.h new file mode 100644 index 0000000000..eac0c662f7 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/multi.h @@ -0,0 +1,64 @@ +/* + * Auvia BeOS Driver for Via VT82xx Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _MULTI_H_ +#define _MULTI_H_ + +#define BUFFER_FRAMES 512 +#define BUFFER_COUNT 2 + +typedef struct _multi_mixer_control { + struct _multi_dev *multi; + void (*get) (void *card, const void *cookie, int32 type, float *values); + void (*set) (void *card, const void *cookie, int32 type, float *values); + const void *cookie; + int32 type; + multi_mix_control mix_control; +} multi_mixer_control; + +#define EMU_MULTI_CONTROL_FIRSTID 1024 +#define EMU_MULTI_CONTROL_MASTERID 0 + +typedef struct _multi_dev { + void *card; +#define EMU_MULTICONTROLSNUM 64 + multi_mixer_control controls[EMU_MULTICONTROLSNUM]; + uint32 control_count; + +#define EMU_MULTICHANNUM 64 + multi_channel_info chans[EMU_MULTICHANNUM]; + uint32 output_channel_count; + uint32 input_channel_count; + uint32 output_bus_channel_count; + uint32 input_bus_channel_count; + uint32 aux_bus_channel_count; +} multi_dev; + +#endif diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/multi_audio.h b/src/add-ons/kernel/drivers/audio/ac97/auvia/multi_audio.h new file mode 100644 index 0000000000..5964973d61 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/multi_audio.h @@ -0,0 +1,699 @@ +/* multi_audio.h */ + +/* Interface description for drivers implementing studio-level audio I/O with */ +/* possible auxillary functions (transport, time code, etc). */ +/* Copyright © 1998-1999 Be Incorporated. All rights reserved. */ + +/* This is the first release candidate for the API. Unless we hear feedback that */ +/* forces a change before the end of August, we will try to stay binary compatible */ +/* with this interface. */ +/* Send feedback to trinity@be.com [1999-07-02] */ + +#if !defined(_MULTI_AUDIO_H) +#define _MULTI_AUDIO_H + +#include +#include + +#define B_MULTI_DRIVER_BASE (B_AUDIO_DRIVER_BASE+20) + +enum { /* open() modes */ + /* O_RDONLY is 0, O_WRONLY is 1, O_RDWR is 2 */ + B_MULTI_CONTROL = 3 +}; + +/* ioctl codes */ +enum { + /* multi_description */ + B_MULTI_GET_DESCRIPTION = B_MULTI_DRIVER_BASE, + /* multi_event_handling */ + B_MULTI_GET_EVENT_INFO, + B_MULTI_SET_EVENT_INFO, + B_MULTI_GET_EVENT, + /* multi_channel_enable */ + B_MULTI_GET_ENABLED_CHANNELS, + B_MULTI_SET_ENABLED_CHANNELS, + /* multi_format_info */ + B_MULTI_GET_GLOBAL_FORMAT, + B_MULTI_SET_GLOBAL_FORMAT, /* always sets for all channels, always implemented */ + /* multi_channel_formats */ + B_MULTI_GET_CHANNEL_FORMATS, + B_MULTI_SET_CHANNEL_FORMATS, /* only implemented if possible */ + /* multi_mix_value_info */ + B_MULTI_GET_MIX, + B_MULTI_SET_MIX, + /* multi_mix_channel_info */ + B_MULTI_LIST_MIX_CHANNELS, + /* multi_mix_control_info */ + B_MULTI_LIST_MIX_CONTROLS, + /* multi_mix_connection_info */ + B_MULTI_LIST_MIX_CONNECTIONS, + /* multi_buffer_list */ + B_MULTI_GET_BUFFERS, /* Fill out the struct for the first time; doesn't start anything. */ + B_MULTI_SET_BUFFERS, /* Set what buffers to use, if the driver supports soft buffers. */ + /* bigtime_t */ + B_MULTI_SET_START_TIME, /* When to actually start */ + /* multi_buffer_info */ + B_MULTI_BUFFER_EXCHANGE, /* stop and go are derived from this being called */ + B_MULTI_BUFFER_FORCE_STOP, /* force stop of playback */ + /* extension protocol */ + B_MULTI_LIST_EXTENSIONS, /* get a list of supported extensions */ + B_MULTI_GET_EXTENSION, /* get the value of an extension (or return error if not supported) */ + B_MULTI_SET_EXTENSION, /* set the value of an extension */ + /* multi_mode_list */ + B_MULTI_LIST_MODES, /* get a list of possible modes (multi_mode_list * arg) */ + B_MULTI_GET_MODE, /* get the current mode (int32 * arg) */ + B_MULTI_SET_MODE /* set a new mode (int32 * arg) */ +}; + +/* sample rate values */ +/* various fixed sample rates we support (for hard-sync clocked values) */ +#define B_SR_8000 0x1 +#define B_SR_11025 0x2 +#define B_SR_12000 0x4 +#define B_SR_16000 0x8 +#define B_SR_22050 0x10 +#define B_SR_24000 0x20 +#define B_SR_32000 0x40 +#define B_SR_44100 0x80 +#define B_SR_48000 0x100 +#define B_SR_64000 0x200 +#define B_SR_88200 0x400 +#define B_SR_96000 0x800 +#define B_SR_176400 0x1000 +#define B_SR_192000 0x2000 +#define B_SR_384000 0x4000 +#define B_SR_1536000 0x10000 +/* continuously variable sample rate (typically board-generated) */ +#define B_SR_CVSR 0x10000000UL +/* sample rate parameter global to all channels (input and output rates respectively) */ +#define B_SR_IS_GLOBAL 0x80000000UL +/* output sample rate locked to input sample rate (output_rates only; the common case!) */ +#define B_SR_SAME_AS_INPUT 0x40000000UL + + +/* format values */ +/* signed char */ +#define B_FMT_8BIT_S 0x01 +/* unsigned char -- this is special case */ +#define B_FMT_8BIT_U 0x02 +/* traditional 16 bit signed format (host endian) */ +#define B_FMT_16BIT 0x10 +/* left-adjusted in 32 bit signed word */ +#define B_FMT_18BIT 0x20 +#define B_FMT_20BIT 0x40 +#define B_FMT_24BIT 0x100 +#define B_FMT_32BIT 0x1000 +/* 32-bit floating point, -1.0 to 1.0 */ +#define B_FMT_FLOAT 0x20000 +/* 64-bit floating point, -1.0 to 1.0 */ +#define B_FMT_DOUBLE 0x40000 +/* 80-bit floating point, -1.0 to 1.0 */ +#define B_FMT_EXTENDED 0x80000 +/* bit stream */ +#define B_FMT_BITSTREAM 0x1000000 +/* format parameter global to all channels (input and output formats respectively) */ +#define B_FMT_IS_GLOBAL 0x80000000UL +/* output format locked to input format (output_formats) */ +#define B_FMT_SAME_AS_INPUT 0x40000000UL + +/* possible sample lock sources */ +#define B_MULTI_LOCK_INPUT_CHANNEL 0x0 /* lock_source_data is channel id */ +#define B_MULTI_LOCK_INTERNAL 0x1 +#define B_MULTI_LOCK_WORDCLOCK 0x2 +#define B_MULTI_LOCK_SUPERCLOCK 0x4 +#define B_MULTI_LOCK_LIGHTPIPE 0x8 +#define B_MULTI_LOCK_VIDEO 0x10 /* or blackburst */ +#define B_MULTI_LOCK_FIRST_CARD 0x20 /* if you have more than one card */ +#define B_MULTI_LOCK_MTC 0x40 +#define B_MULTI_LOCK_SPDIF 0x80 + +/* possible timecode sources */ +#define B_MULTI_TIMECODE_MTC 0x1 +#define B_MULTI_TIMECODE_VTC 0x2 +#define B_MULTI_TIMECODE_SMPTE 0x4 +#define B_MULTI_TIMECODE_SUPERCLOCK 0x8 +#define B_MULTI_TIMECODE_FIREWIRE 0x10 + +/* interface_flags values */ +/* Available functions on this device. */ +#define B_MULTI_INTERFACE_PLAYBACK 0x1 +#define B_MULTI_INTERFACE_RECORD 0x2 +#define B_MULTI_INTERFACE_TRANSPORT 0x4 +#define B_MULTI_INTERFACE_TIMECODE 0x8 +/* "Soft" buffers means you can change the pointer values and the driver will still be happy. */ +#define B_MULTI_INTERFACE_SOFT_PLAY_BUFFERS 0x10000 +#define B_MULTI_INTERFACE_SOFT_REC_BUFFERS 0x20000 +/* Whether the data stream is interrupted when changing channel enables. */ +#define B_MULTI_INTERFACE_CLICKS_WHEN_ENABLING_OUTPUTS 0x40000 +#define B_MULTI_INTERFACE_CLICKS_WHEN_ENABLING_INPUTS 0x80000 + +#define B_CURRENT_INTERFACE_VERSION 0x4502 +#define B_MINIMUM_INTERFACE_VERSION 0x4502 + +typedef struct multi_description multi_description; +typedef struct multi_channel_info multi_channel_info; +struct multi_description { + + size_t info_size; /* sizeof(multi_description) */ + uint32 interface_version; /* current version of interface that's implemented */ + uint32 interface_minimum; /* minimum version required to understand driver */ + + char friendly_name[32]; /* name displayed to user (C string) */ + char vendor_info[32]; /* name used internally by vendor (C string) */ + + int32 output_channel_count; + int32 input_channel_count; + int32 output_bus_channel_count; + int32 input_bus_channel_count; + int32 aux_bus_channel_count; + + int32 request_channel_count; /* how many channel_infos are there */ + multi_channel_info * + channels; + + uint32 output_rates; + uint32 input_rates; + float min_cvsr_rate; + float max_cvsr_rate; + uint32 output_formats; + uint32 input_formats; + uint32 lock_sources; + uint32 timecode_sources; + + uint32 interface_flags; + bigtime_t start_latency; /* how much in advance driver needs SET_START_TIME */ + + uint32 _reserved_[11]; + + char control_panel[64]; /* MIME type of control panel application */ +}; + +#if !defined(_MEDIA_DEFS_H) /* enum in MediaDefs.h */ +/* designation values */ +/* mono channels have no designation */ +#define B_CHANNEL_LEFT 0x1 +#define B_CHANNEL_RIGHT 0x2 +#define B_CHANNEL_CENTER 0x4 /* 5.1+ or fake surround */ +#define B_CHANNEL_SUB 0x8 /* 5.1+ */ +#define B_CHANNEL_REARLEFT 0x10 /* quad surround or 5.1+ */ +#define B_CHANNEL_REARRIGHT 0x20 /* quad surround or 5.1+ */ +#define B_CHANNEL_FRONT_LEFT_CENTER 0x40 +#define B_CHANNEL_FRONT_RIGHT_CENTER 0x80 +#define B_CHANNEL_BACK_CENTER 0x100 /* 6.1 or fake surround */ +#define B_CHANNEL_SIDE_LEFT 0x200 +#define B_CHANNEL_SIDE_RIGHT 0x400 +#define B_CHANNEL_TOP_CENTER 0x800 +#define B_CHANNEL_TOP_FRONT_LEFT 0x1000 +#define B_CHANNEL_TOP_FRONT_CENTER 0x2000 +#define B_CHANNEL_TOP_FRONT_RIGHT 0x4000 +#define B_CHANNEL_TOP_BACK_LEFT 0x8000 +#define B_CHANNEL_TOP_BACK_CENTER 0x10000 +#define B_CHANNEL_TOP_BACK_RIGHT 0x20000 +#endif + +#define B_CHANNEL_MONO_BUS 0x4000000 +#define B_CHANNEL_STEREO_BUS 0x2000000 /* + left/right */ +#define B_CHANNEL_SURROUND_BUS 0x1000000 /* multichannel */ + +/* If you have interactions where some inputs can not be used when some */ +/* outputs are used, mark both inputs and outputs with this flag. */ +#define B_CHANNEL_INTERACTION 0x80000000UL +/* If input channel #n is simplexed with output channel #n, they should both */ +/* have this flag set (different from the previous flag, which is more vague). */ +#define B_CHANNEL_SIMPLEX 0x40000000UL + +/* connector values */ +/* analog connectors */ +#define B_CHANNEL_RCA 0x1 +#define B_CHANNEL_XLR 0x2 +#define B_CHANNEL_TRS 0x4 +#define B_CHANNEL_QUARTER_INCH_MONO 0x8 +#define B_CHANNEL_MINI_JACK_STEREO 0x10 +#define B_CHANNEL_QUARTER_INCH_STEREO 0x20 +#define B_CHANNEL_ANALOG_HEADER 0x100 /* internal on card */ +#define B_CHANNEL_SNAKE 0x200 /* or D-sub */ +/* digital connectors (stereo) */ +#define B_CHANNEL_OPTICAL_SPDIF 0x1000 +#define B_CHANNEL_COAX_SPDIF 0x2000 +#define B_CHANNEL_COAX_EBU 0x4000 +#define B_CHANNEL_XLR_EBU 0x8000 +#define B_CHANNEL_TRS_EBU 0x10000 +#define B_CHANNEL_SPDIF_HEADER 0x20000 /* internal on card */ +/* multi-channel digital connectors */ +#define B_CHANNEL_LIGHTPIPE 0x100000 +#define B_CHANNEL_TDIF 0x200000 +#define B_CHANNEL_FIREWIRE 0x400000 +#define B_CHANNEL_USB 0x800000 +/* If you have multiple output connectors, only one of which can */ +/* be active at a time. */ +#define B_CHANNEL_EXCLUSIVE_SELECTION 0x80000000UL + + +typedef enum { + B_MULTI_NO_CHANNEL_KIND, + B_MULTI_OUTPUT_CHANNEL = 0x1, + B_MULTI_INPUT_CHANNEL = 0x2, + B_MULTI_OUTPUT_BUS = 0x4, + B_MULTI_INPUT_BUS = 0x8, + B_MULTI_AUX_BUS = 0x10 +} channel_kind; + +struct multi_channel_info { + int32 channel_id; + channel_kind kind; + uint32 designations; + uint32 connectors; + uint32 _reserved_[4]; +}; + + +/* Constants */ +#define B_MULTI_EVENT_MINMAX 16 +/* Event flags/masks */ +#define B_MULTI_EVENT_TRANSPORT 0x40000000UL +#define B_MULTI_EVENT_HAS_TIMECODE 0x80000000UL + +/* possible transport events */ +#define B_MULTI_EVENT_NONE 0x00000000UL +#define B_MULTI_EVENT_START 0x40010000UL +#define B_MULTI_EVENT_LOCATION 0x40020000UL /* location when shuttling or locating */ +#define B_MULTI_EVENT_SHUTTLING 0x40040000UL +#define B_MULTI_EVENT_STOP 0x40080000UL +#define B_MULTI_EVENT_RECORD 0x40100000UL +#define B_MULTI_EVENT_PAUSE 0x40200000UL +#define B_MULTI_EVENT_RUNNING 0x40400000UL /* location when running */ +/* possible device events */ +enum { + B_MULTI_EVENT_STARTED = 0x1, + B_MULTI_EVENT_STOPPED = 0x2, + B_MULTI_EVENT_CHANNEL_FORMAT_CHANGED= 0x4, + B_MULTI_EVENT_BUFFER_OVERRUN = 0x8, + B_MULTI_EVENT_SIGNAL_LOST = 0x10, + B_MULTI_EVENT_SIGNAL_DETECTED = 0x20, + B_MULTI_EVENT_CLOCK_LOST = 0x40, + B_MULTI_EVENT_CLOCK_DETECTED = 0x80, + B_MULTI_EVENT_NEW_MODE = 0x100, + B_MULTI_EVENT_CONTROL_CHANGED = 0x200 +}; + +typedef struct multi_get_event_info multi_get_event_info; +struct multi_get_event_info { + size_t info_size; /* sizeof(multi_get_event_info) */ + uint32 supported_mask; /* what events h/w supports */ + uint32 current_mask; /* current driver value */ + uint32 queue_size; /* current queue size */ + uint32 event_count; /* number of events currently in queue*/ + uint32 _reserved[3]; +}; + +typedef struct multi_set_event_info multi_set_event_info; +struct multi_set_event_info { + size_t info_size; /* sizeof(multi_set_event_info) */ + uint32 in_mask; /* what events to wait for */ + int32 semaphore; /* semaphore app will wait on */ + uint32 queue_size; /* minimum number of events to save */ + uint32 _reserved[4]; +}; + +typedef struct multi_get_event multi_get_event; +struct multi_get_event { + size_t info_size; /* sizeof(multi_get_event) */ + uint32 event; + bigtime_t timestamp; /* real time at which event was received */ + int32 count; /* used for configuration events */ + union { + int32 channels[100]; + uint32 clocks; + int32 mode; + int32 controls[100]; + struct { /* transport event */ + float out_rate; /* what rate it's now playing at */ + int32 out_hours; /* location at the time given */ + int32 out_minutes; + int32 out_seconds; + int32 out_frames; + }transport; + char _reserved_[400]; + #if defined(__cplusplus) + }; + #else + } u; + #endif + uint32 _reserved_1[10]; +}; + +typedef struct multi_channel_enable multi_channel_enable; +struct multi_channel_enable { + size_t info_size; /* sizeof(multi_channel_enable) */ + /* this must have bytes for all channels (see multi_description) */ + /* channel 0 is lowest bit of first byte */ + uchar * enable_bits; + + uint32 lock_source; + int32 lock_data; + uint32 timecode_source; + uint32 * connectors; /* which connector(s) is/are active, per channel */ +}; + +#include + +#if defined(__cplusplus) + inline void B_SET_CHANNEL(void * bits, int channel, bool value) + { + ASSERT(channel>=0); + (((uchar *)(bits))[((channel)&0x7fff)>>3] = + (((uchar *)(bits))[((channel)&0x7fff)>>3] & ~(1<<((channel)&0x7))) | + ((value) ? (1<<((channel)&0x7)) : 0)); + } + inline bool B_TEST_CHANNEL(const void * bits, int channel) + { + return ((((uchar *)(bits))[((channel)&0x7fff)>>3] >> ((channel)&0x7)) & 1); + } +#else + #define B_SET_CHANNEL(bits, channel, value) \ + ASSERT(channel>=0); \ + (((uchar *)(bits))[((channel)&0x7fff)>>3] = \ + (((uchar *)(bits))[((channel)&0x7fff)>>3] & ~(1<<((channel)&0x7))) | \ + ((value) ? (1<<((channel)&0x7)) : 0)) + #define B_TEST_CHANNEL(bits, channel) \ + ((((uchar *)(bits))[((channel)&0x7fff)>>3] >> ((channel)&0x7)) & 1) +#endif + +typedef struct multi_channel_formats multi_channel_formats; +typedef struct multi_format_info multi_format_info; +typedef struct _multi_format _multi_format; + +struct _multi_format { + uint32 rate; + float cvsr; + uint32 format; + uint32 _reserved_[3]; +}; +enum { /* timecode kinds */ + B_MULTI_NO_TIMECODE, + B_MULTI_TIMECODE_30, /* MIDI */ + B_MULTI_TIMECODE_30_DROP_2, /* NTSC */ + B_MULTI_TIMECODE_30_DROP_4, /* Brazil */ + B_MULTI_TIMECODE_25, /* PAL */ + B_MULTI_TIMECODE_24 /* Film */ +}; +struct multi_format_info { + size_t info_size; /* sizeof(multi_format_info) */ + bigtime_t output_latency; + bigtime_t input_latency; + int32 timecode_kind; + uint32 _reserved_[7]; + _multi_format input; + _multi_format output; +}; +struct multi_channel_formats { + size_t info_size; /* sizeof(multi_channel_formats) */ + int32 request_channel_count; + int32 request_first_channel; + int32 returned_channel_count; + int32 timecode_kind; + int32 _reserved_[4]; + _multi_format * + channels; + bigtime_t * latencies; /* DMA/hardware latencies; client calculates for buffers */ +}; + + +typedef struct multi_mix_value multi_mix_value; +struct multi_mix_value { + int32 id; + union { + float gain; + uint32 mux; /* bitmask of mux points */ + bool enable; + uint32 _reserved_[2]; +#if defined(__cplusplus) + }; +#else + } u; +#endif + int32 ramp; + uint32 _reserved_2[2]; +}; + +typedef struct multi_mix_value_info multi_mix_value_info; +struct multi_mix_value_info { + size_t info_size; /* sizeof(multi_mix_value_info) */ + int32 item_count; + multi_mix_value * + values; + int32 at_frame; /* time at which to start the change */ +}; + +// only one of these should be set +#define B_MULTI_MIX_JUNCTION 0x1 +#define B_MULTI_MIX_GAIN 0x2 +#define B_MULTI_MIX_MUX 0x4 +#define B_MULTI_MIX_ENABLE 0x8 +#define B_MULTI_MIX_GROUP 0x10 +#define B_MULTI_MIX_KIND_MASK 0xffff + +#define B_MULTI_MIX_MUX_VALUE 0x0104 +// any combination of these can be set +#define B_MULTI_MIX_RAMP 0x10000 + +enum strind_id { + S_null = 0, S_OUTPUT, S_INPUT, S_SETUP, S_TONE_CONTROL, S_EXTENDED_SETUP, + S_ENHANDED_SETUP, S_MASTER, S_BEEP, S_PHONE, S_MIC, S_LINE, S_CD, S_VIDEO, + S_AUX, S_WAVE, S_GAIN, S_LEVEL, S_VOLUME, S_MUTE, S_ENABLE, S_STEREO_MIX, + S_MONO_MIX, S_OUTPUT_STEREO_MIX, S_OUTPUT_MONO_MIX, S_OUTPUT_BASS, + S_OUTPUT_TREBLE, S_OUTPUT_3D_CENTER, S_OUTPUT_3D_DEPTH, + S_USERID = 1000000 +}; + +typedef struct multi_mix_control multi_mix_control; +struct multi_mix_control { + int32 id; /* unique for device -- not same id as any channel/bus ! */ + uint32 flags; /* including kind */ + int32 master; /* or 0 if it's not slaved */ + union { + struct { + float min_gain; /* dB */ + float max_gain; /* dB */ + float granularity; /* dB */ + } gain; + struct { + uint32 _reserved; + } mux; + struct { + uint32 _reserved; + } enable; + uint32 _reserved[12]; +#if defined(__cplusplus) + }; +#else + } u; +#endif + enum strind_id string; /* string id (S_null : use name) */ + int32 parent; /* parent id */ + char name[48]; +}; + +typedef struct multi_mix_channel_info multi_mix_channel_info; +struct multi_mix_channel_info { + size_t info_size; /* sizeof(multi_mix_channel_info) */ + int32 channel_count; + int32 * channels; /* allocated by caller, lists requested channels */ + int32 max_count; /* in: control ids per channel */ + int32 actual_count; /* out: actual max # controls for any individual requested channel */ + int32 ** controls; +}; + +typedef struct multi_mix_control_info multi_mix_control_info; +struct multi_mix_control_info { + size_t info_size; /* sizeof(multi_mix_control_info) */ + int32 control_count; /* in: number of controls */ + multi_mix_control * + controls; /* allocated by caller, returns control description for each */ +}; + +typedef struct multi_mix_connection multi_mix_connection; +struct multi_mix_connection { + int32 from; + int32 to; + uint32 _reserved_[2]; +}; + +typedef struct multi_mix_connection_info multi_mix_connection_info; +struct multi_mix_connection_info { + size_t info_size; + int32 max_count; /* in: available space */ + int32 actual_count; /* out: actual count */ + multi_mix_connection * + connections; /* allocated by caller, returns connections */ +}; + + +/* possible flags values for what is available (in and out) */ +#define B_MULTI_BUFFER_PLAYBACK 0x1 +#define B_MULTI_BUFFER_RECORD 0x2 +#define B_MULTI_BUFFER_METERING 0x4 +#define B_MULTI_BUFFER_TIMECODE 0x40000 + +typedef struct multi_buffer_list multi_buffer_list; +typedef struct buffer_desc buffer_desc; +/* This struct is used to query the driver about what buffers it will use, */ +/* and to tell it what buffers to use if it supports soft buffers. */ +struct multi_buffer_list { + + size_t info_size; /* sizeof(multi_buffer_list) */ + uint32 flags; + + int32 request_playback_buffers; + int32 request_playback_channels; + uint32 request_playback_buffer_size; /* frames per buffer */ + int32 return_playback_buffers; /* playback_buffers[b][] */ + int32 return_playback_channels; /* playback_buffers[][c] */ + uint32 return_playback_buffer_size; /* frames */ + buffer_desc ** playback_buffers; + void * _reserved_1; + + int32 request_record_buffers; + int32 request_record_channels; + uint32 request_record_buffer_size; /* frames per buffer */ + int32 return_record_buffers; + int32 return_record_channels; + uint32 return_record_buffer_size; /* frames */ + buffer_desc ** record_buffers; + void * _reserved_2; + +}; + +struct buffer_desc { + char * base; /* pointer to first sample for channel for buffer */ + size_t stride; /* offset to next sample */ + uint32 _reserved_[2]; +}; + + +/* This struct is used when actually queuing data to be played, and/or */ +/* receiving data from a recorder. */ +typedef struct multi_buffer_info multi_buffer_info; +struct multi_buffer_info { + + size_t info_size; /* sizeof(multi_buffer_info) */ + uint32 flags; + + bigtime_t played_real_time; + bigtime_t played_frames_count; + int32 _reserved_0; + int32 playback_buffer_cycle; + + bigtime_t recorded_real_time; + bigtime_t recorded_frames_count; + int32 _reserved_1; + int32 record_buffer_cycle; + + int32 meter_channel_count; + char * meters_peak; /* in the same format as the data; allocated by caller */ + char * meters_average; /* in the same format as the data; allocated by caller */ + + /* timecode sent and received at buffer swap */ + int32 hours; + int32 minutes; + int32 seconds; + int32 tc_frames; /* for timecode frames as opposed to sample frames */ + int32 at_frame_delta; /* how far into buffer (or before buffer for negative) */ + +}; + + +typedef struct multi_mode_info multi_mode_info; +typedef struct multi_mode_list multi_mode_list; + +struct multi_mode_list { + size_t info_size; /* sizeof(multi_mode_list) */ + int32 in_request_count; + int32 out_actual_count; + int32 out_current_mode; + multi_mode_info * + io_modes; +}; + +struct multi_mode_info { + int32 mode_id; + uint32 flags; + char mode_name[64]; + int32 input_channel_count; + int32 output_channel_count; + float best_frame_rate_in; + float best_frame_rate_out; + uint32 sample_formats_in; + uint32 sample_formats_out; + char _reserved[160]; +}; + + +/* This extension protocol can grow however much you want. */ +/* Good extensions should be put into this header; really */ +/* good extensions should become part of the regular API. */ +/* For developer-developed extensions, use all lowercase */ +/* and digits (no upper case). If we then bless a third- */ +/* party extension, we can just upper-case the selector. */ + +typedef struct multi_extension_list multi_extension_list; +typedef struct multi_extension_info multi_extension_info; +struct multi_extension_info { + uint32 code; + uint32 flags; + char name[24]; +}; + +#define B_MULTI_MAX_EXTENSION_COUNT 31 +struct multi_extension_list { /* MULTI_LIST_EXTENSIONS */ + size_t info_size; /* sizeof(multi_extension_list) */ + uint32 max_count; + int32 actual_count; /* return # of actual extensions */ + multi_extension_info * + extensions; /* allocated by caller */ +}; + +typedef struct multi_extension_cmd multi_extension_cmd; +struct multi_extension_cmd { /* MULTI_GET_EXTENSION and MULTI_SET_EXTENSION */ + size_t info_size; /* sizeof(multi_extension_cmd) */ + uint32 code; + uint32 _reserved_1; + void * in_data; + size_t in_size; + void * out_data; + size_t out_size; +}; + +enum { + B_MULTI_EX_CLOCK_GENERATION = 'CLGE', + B_MULTI_EX_DIGITAL_FORMAT = 'DIFO', + B_MULTI_EX_OUTPUT_NOMINAL = 'OUNO', + B_MULTI_EX_INPUT_NOMINAL = 'INNO' +}; + +typedef struct multi_ex_clock_generation multi_ex_clock_generation; +struct multi_ex_clock_generation { + int32 channel; /* if specific, or -1 for all */ + uint32 clock; /* WORDCLOCK or SUPERCLOCK, typically */ +}; + +typedef struct multi_ex_digital_format multi_ex_digital_format; +struct multi_ex_digital_format { + int32 channel; /* if specific, or -1 for all */ + uint32 format; /* B_CHANNEL_*_SPDIF or B_CHANNEL_*_EBU */ +}; + +enum { + B_MULTI_NOMINAL_MINUS_10 = 1, + B_MULTI_NOMINAL_PLUS_4 +}; + +typedef struct multi_ex_nominal_level multi_ex_nominal_level; +struct multi_ex_nominal_level { + int32 channel; /* if specific, or -1 for all */ + int32 level; +}; + +#endif /* _MULTI_AUDIO_H */ + diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/queue.h b/src/add-ons/kernel/drivers/audio/ac97/auvia/queue.h new file mode 100644 index 0000000000..396c70fb2e --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/queue.h @@ -0,0 +1,529 @@ +/* $NetBSD: queue.h,v 1.31 2002/06/01 23:51:05 lukem Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so only elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ + if ((head)->lh_first && \ + (head)->lh_first->field.le_prev != &(head)->lh_first) \ + panic("LIST_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_OP(elm, field) \ + if ((elm)->field.le_next && \ + (elm)->field.le_next->field.le_prev != \ + &(elm)->field.le_next) \ + panic("LIST_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ + if (*(elm)->field.le_prev != (elm)) \ + panic("LIST_* back %p %s:%d", (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ + (elm)->field.le_next = (void *)1L; \ + (elm)->field.le_prev = (void *)1L; +#else +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_LIST_OP(elm, field) +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) +#endif + +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + QUEUEDEBUG_LIST_OP((elm), field) \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var); \ + (var) = ((var)->field.le_next)) + +/* + * List access methods. + */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) + +#define SLIST_INIT(head) do { \ + (head)->slh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var); \ + (var) = ((var)->field.sqe_next)) + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue functions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ + if ((head)->tqh_first && \ + (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ + panic("TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ + if (*(head)->tqh_last != NULL) \ + panic("TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_OP(elm, field) \ + if ((elm)->field.tqe_next && \ + (elm)->field.tqe_next->field.tqe_prev != \ + &(elm)->field.tqe_next) \ + panic("TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ + (elm)->field.tqe_next = (void *)1L; \ + (elm)->field.tqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) +#define QUEUEDEBUG_TAILQ_OP(elm, field) +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) +#endif + +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((elm), field) \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +/* + * Tail queue access methods. + */ +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ + (var); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { (void *)&head, (void *)&head } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != (void *)(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != (void *)(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#endif /* !_SYS_QUEUE_H_ */ diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/util.c b/src/add-ons/kernel/drivers/audio/ac97/auvia/util.c new file mode 100644 index 0000000000..d71521def3 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/util.c @@ -0,0 +1,88 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include + +//#define DEBUG 2 + +#include "debug.h" +#include "util.h" + +spinlock slock = 0; + +uint32 round_to_pagesize(uint32 size); + +cpu_status lock(void) +{ + cpu_status status = disable_interrupts(); + acquire_spinlock(&slock); + return status; +} + +void unlock(cpu_status status) +{ + release_spinlock(&slock); + restore_interrupts(status); +} + +uint32 round_to_pagesize(uint32 size) +{ + return (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); +} + +area_id alloc_mem(void **phy, void **log, size_t size, const char *name) +{ + physical_entry pe; + void * logadr; + area_id areaid; + status_t rv; + + LOG(("allocating %d bytes for %s\n",size,name)); + + size = round_to_pagesize(size); + areaid = create_area(name, &logadr, B_ANY_KERNEL_ADDRESS,size,B_FULL_LOCK | B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA); + if (areaid < B_OK) { + PRINT(("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); + PRINT(("couldn't map %s\n",name)); + return B_ERROR; + } + memset(logadr,0,size); + if (log) + *log = logadr; + if (phy) + *phy = pe.address; + LOG(("area = %d, size = %d, log = %#08X, phy = %#08X\n",areaid,size,logadr,pe.address)); + return areaid; +} + diff --git a/src/add-ons/kernel/drivers/audio/ac97/auvia/util.h b/src/add-ons/kernel/drivers/audio/ac97/auvia/util.h new file mode 100644 index 0000000000..7a367042ca --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auvia/util.h @@ -0,0 +1,40 @@ +/* + * BeOS Driver for Intel ICH AC'97 Link interface + * + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include + +area_id alloc_mem(void **phy, void **log, size_t size, const char *name); + +cpu_status lock(void); +void unlock(cpu_status status); + +extern spinlock slock; + +#endif