diff --git a/src/add-ons/kernel/drivers/audio/emuxki/Jamfile b/src/add-ons/kernel/drivers/audio/emuxki/Jamfile new file mode 100644 index 0000000000..2bdcd5870f --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/Jamfile @@ -0,0 +1,19 @@ +SubDir OBOS_TOP src add-ons kernel drivers audio emuxki ; + +R5KernelAddon emuxki : kernel drivers bin : + ac97.c + debug.c + emuxki.c + io.c + midi.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 = emuxki ; + MakeLocate $(instDriver) : $(dir) ; + RelSymLink $(instDriver) : emuxki ; +} diff --git a/src/add-ons/kernel/drivers/audio/emuxki/ac97.c b/src/add-ons/kernel/drivers/audio/emuxki/ac97.c new file mode 100644 index 0000000000..88dfddb946 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/ac97.c @@ -0,0 +1,292 @@ +/* + * Emuxki BeOS Driver for Creative Labs SBLive!/Audigy series + * + * Copyright (c) 2002, 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 = emuxki_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 = emuxki_codec_read(config, AC97_VENDOR_ID1); + data2 = emuxki_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")); + emuxki_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",emuxki_codec_read(config, AC97_POWERDOWN))); + #if REVERSE_EAMP_POLARITY + yesno = !yesno; + LOG(("using reverse eamp polarity\n")); + #endif + if (yesno) + emuxki_codec_write(config, AC97_POWERDOWN, emuxki_codec_read(config, AC97_POWERDOWN) & ~0x8000); /* switch on (low active) */ + else + emuxki_codec_write(config, AC97_POWERDOWN, emuxki_codec_read(config, AC97_POWERDOWN) | 0x8000); /* switch off */ + LOG(("powerdown register is = %#04x\n", emuxki_codec_read(config, AC97_POWERDOWN))); +} + +void cs4299_amp_enable(device_config *config, bool yesno) +{ + LOG(("cs4299_amp_enable\n")); + if (yesno) + emuxki_codec_write(config, 0x68, 0x8004); + else + emuxki_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/emuxki/ac97.h b/src/add-ons/kernel/drivers/audio/emuxki/ac97.h new file mode 100644 index 0000000000..f286f95a87 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/ac97.h @@ -0,0 +1,107 @@ +/* + * Emuxki BeOS Driver for Creative Labs SBLive!/Audigy series + * + * Copyright (c) 2002, 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/emuxki/config.h b/src/add-ons/kernel/drivers/audio/emuxki/config.h new file mode 100644 index 0000000000..0ac1d8b6d0 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/config.h @@ -0,0 +1,57 @@ +/* + * Emuxki BeOS Driver for Creative Labs SBLive!/Audigy series + * + * Copyright (c) 2002, 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_ + +#include + +#define NUM_CARDS 3 +#define DEVNAME 32 + +typedef struct +{ + const char *name; + uint32 nambar; + uint32 nabmbar; + uint32 irq; + int sample_size; + bool swap_reg; + uint32 type; +} device_config; + +#define TYPE_AUDIGY 0x01 +#define TYPE_AUDIGY2 0x02 +#define TYPE_LIVE_5_1 0x04 + +#define IS_AUDIGY(x) ((x)->type & TYPE_AUDIGY) +#define IS_AUDIGY2(x) ((x)->type & TYPE_AUDIGY2) +#define IS_LIVE_5_1(x) ((x)->type & TYPE_LIVE_5_1) +#endif diff --git a/src/add-ons/kernel/drivers/audio/emuxki/debug.c b/src/add-ons/kernel/drivers/audio/emuxki/debug.c new file mode 100644 index 0000000000..a5ba865f1c --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/debug.c @@ -0,0 +1,95 @@ +/* + * Emuxki BeOS Driver for Creative Labs SBLive!/Audigy series + * + * Copyright (c) 2002, 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 "emuxki.h" + +#if DEBUG > 0 +static const char * logfile="/boot/home/emuxki.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/emuxki/debug.h b/src/add-ons/kernel/drivers/audio/emuxki/debug.h new file mode 100644 index 0000000000..6e94ed0ea5 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/debug.h @@ -0,0 +1,70 @@ +/* + * Emuxki BeOS Driver for Creative Labs SBLive!/Audigy series + * + * Copyright (c) 2002, 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/emuxki/emuxki.c b/src/add-ons/kernel/drivers/audio/emuxki/emuxki.c new file mode 100644 index 0000000000..f66c04a569 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/emuxki.c @@ -0,0 +1,2865 @@ +/* + * Emuxki BeOS Driver for Creative Labs SBLive!/Audigy series + * + * Copyright (c) 2002, Jerome Duval (jerome.duval@free.fr) + * +*/ +/* This code is derived from the NetBSD driver for Creative Labs SBLive! series + * + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Yannick Montulet. + * + * 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 +#include "emuxki.h" +#include "debug.h" +#include "config.h" +#include "util.h" +#include "io.h" +#include +#include +#include +#include "multi.h" +#include "ac97.h" +#include "midi_driver.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; +static char mpu401_name[] = B_MPU_401_MODULE_NAME; +generic_mpu401_module * mpu401; + + +int32 num_cards; +emuxki_dev cards[NUM_CARDS]; +int32 num_names; +char * names[NUM_CARDS*20+1]; + +status_t emuxki_init(emuxki_dev * card); + +extern device_hooks multi_hooks; +extern device_hooks midi_hooks; + +/* Hardware Dump */ + +void +dump_hardware_regs(device_config *config) +{ + LOG(("EMU_IPR = %#08x\n",emuxki_reg_read_32(config, EMU_IPR))); + LOG(("EMU_INTE = %#08x\n",emuxki_reg_read_32(config, EMU_INTE))); + LOG(("EMU_HCFG = %#08x\n",emuxki_reg_read_32(config, EMU_HCFG))); + snooze(100); + /*emuxki_reg_write_8(config, EMU_AC97ADDRESS, EMU_AC97ADDRESS_READY); + LOG(("EMU_AC97ADDRESS_READY = %#08x\n", emuxki_reg_read_16(config, EMU_AC97DATA)));*/ + + /*emuxki_reg_write_8(config, EMU_AC97ADDRESS, EMU_AC97ADDRESS_ADDRESS); + LOG(("EMU_AC97ADDRESS_ADDRESS = %#08x\n", emuxki_reg_read_16(config, EMU_AC97DATA)));*/ + + /*LOG(("EMU_CHAN_CPF_STEREO = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_CPF_STEREO))); + LOG(("EMU_CHAN_FXRT = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_FXRT))); + LOG(("EMU_CHAN_PTRX = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_PTRX))); + LOG(("EMU_CHAN_DSL = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_DSL))); + LOG(("EMU_CHAN_PSST = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_PSST))); + LOG(("EMU_CHAN_CCCA = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_CCCA))); + LOG(("EMU_CHAN_Z1 = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_Z1))); + LOG(("EMU_CHAN_Z2 = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_Z2))); + LOG(("EMU_CHAN_MAPA = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_MAPA))); + LOG(("EMU_CHAN_MAPB = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_MAPB))); + LOG(("EMU_CHAN_CVCF_CURRENTFILTER = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_CVCF_CURRENTFILTER))); + LOG(("EMU_CHAN_VTFT_FILTERTARGET = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_VTFT_FILTERTARGET))); + LOG(("EMU_CHAN_ATKHLDM = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_ATKHLDM))); + LOG(("EMU_CHAN_DCYSUSM = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_DCYSUSM))); + LOG(("EMU_CHAN_LFOVAL1 = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_LFOVAL1))); + LOG(("EMU_CHAN_LFOVAL2 = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_LFOVAL2))); + LOG(("EMU_CHAN_FMMOD = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_FMMOD))); + LOG(("EMU_CHAN_TREMFRQ = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_TREMFRQ))); + LOG(("EMU_CHAN_FM2FRQ2 = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_FM2FRQ2))); + LOG(("EMU_CHAN_ENVVAL = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_ENVVAL))); + LOG(("EMU_CHAN_ATKHLDV = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_ATKHLDV))); + LOG(("EMU_CHAN_ENVVOL = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_ENVVOL))); + LOG(("EMU_CHAN_PEFE = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_PEFE)));*/ + +} + +void +trace_hardware_regs(device_config *config) +{ + TRACE(("EMU_IPR = %#08x\n",emuxki_reg_read_32(config, EMU_IPR))); + TRACE(("EMU_INTE = %#08x\n",emuxki_reg_read_32(config, EMU_INTE))); + TRACE(("EMU_HCFG = %#08x\n",emuxki_reg_read_32(config, EMU_HCFG))); +} + +/* Misc stuff relative to Emuxki */ + +int emu10k1_recbuf_sizes[] = { + 0, 384, 448, 512, 640, 768, 896, 1024, 1280, 1536, 1792, + 2048, 2560, 3072, 3584, 4096, 5120, 6144, 7168, 8192, 10240, 12288, + 14366, 16384, 20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536 +}; + +static uint32 +emuxki_rate_to_pitch(uint32 rate) +{ + static uint32 logMagTable[128] = { + 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, + 0x13aa2, 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, + 0x2655d, 0x28ed5, 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, + 0x381b6, 0x3a93d, 0x3d081, 0x3f782, 0x41e42, 0x444c1, 0x46b01, + 0x49101, 0x4b6c4, 0x4dc49, 0x50191, 0x5269e, 0x54b6f, 0x57006, + 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, 0x646ee, 0x66a00, + 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, 0x759d4, + 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e, + 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, + 0x93d26, 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, + 0xa11d8, 0xa2f9d, 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, + 0xadf26, 0xafbe7, 0xb1885, 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, + 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, 0xc1404, 0xc2f50, 0xc4a7b, + 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, 0xceaec, 0xd053f, + 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, 0xdba4a, + 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3, + 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, + 0xf2c83, 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, + 0xfd1a7, 0xfe8df + }; + static uint8 logSlopeTable[128] = { + 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58, + 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, + 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, + 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, + 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47, + 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, + 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, + 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, + 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, + 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, + 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35, + 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34, + 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, + 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f + }; + int8 i; + + if (rate == 0) + return 0; /* Bail out if no leading "1" */ + rate *= 11185; /* Scale 48000 to 0x20002380 */ + for (i = 31; i > 0; i--) { + if (rate & 0x80000000) { /* Detect leading "1" */ + return (((uint32) (i - 15) << 20) + + logMagTable[0x7f & (rate >> 24)] + + (0x7f & (rate >> 17)) * + logSlopeTable[0x7f & (rate >> 24)]); + } + rate <<= 1; + } + return 0; /* Should never reach this point */ +} + +/* Emuxki Memory management */ + +static emuxki_mem * +emuxki_mem_new(emuxki_dev *card, int ptbidx, size_t size) +{ + emuxki_mem *mem; + + if ((mem = malloc(sizeof(*mem))) == NULL) + return (NULL); + + mem->ptbidx = ptbidx; + mem->area = alloc_mem(&mem->phy_base, &mem->log_base, size, "emuxki buffer"); + mem->size = size; + if (mem->area < B_OK) { + free(mem); + return NULL; + } + return mem; +} + +static void +emuxki_mem_delete(emuxki_mem *mem) +{ + if(mem->area > B_OK) + delete_area(mem->area); + free(mem); +} + +void * +emuxki_pmem_alloc(emuxki_dev *card, size_t size) +{ + int i, j;//, s; + size_t numblocks; + emuxki_mem *mem; + uint32 *ptb, silentpage; + + ptb = card->ptb_log_base; + silentpage = ((uint32)card->silentpage_phy_base) << 1; + numblocks = size / EMU_PTESIZE; + if (size % EMU_PTESIZE) + numblocks++; + + PRINT(("emuxki_pmem_alloc : numblocks : %d\n", numblocks)); + + for (i = 0; i < EMU_MAXPTE; i++) { + PRINT(("emuxki_pmem_alloc : %d\n", i)); + if ((ptb[i] & EMU_CHAN_MAP_PTE_MASK) == silentpage) { + /* We look for a free PTE */ + //s = splaudio(); + for (j = 0; j < numblocks; j++) + if ((ptb[i + j] & EMU_CHAN_MAP_PTE_MASK) + != silentpage) + break; + if (j == numblocks) { + PRINT(("emuxki_pmem_alloc : j == numblocks %d\n", j)); + if ((mem = emuxki_mem_new(card, i, size)) == NULL) { + //splx(s); + return (NULL); + } + PRINT(("emuxki_pmem_alloc : j == numblocks emuxki_mem_new ok\n")); + for (j = 0; j < numblocks; j++) + ptb[i + j] = (uint32) ( + (( ((uint32)mem->phy_base) + + j * EMU_PTESIZE) << 1) + | (i + j)); + LIST_INSERT_HEAD(&(card->mem), mem, next); + PRINT(("emuxki_pmem_alloc : j == numblocks returning\n")); + + //splx(s); + return mem->log_base; + } else { + PRINT(("emuxki_pmem_alloc : j != numblocks %d\n", j)); + i += j; + } + //splx(s); + } + } + return NULL; +} + +void * +emuxki_rmem_alloc(emuxki_dev *card, size_t size) +{ + emuxki_mem *mem; + //int s; + + mem = emuxki_mem_new(card, EMU_RMEM, size); + if (mem == NULL) + return (NULL); + + //s = splaudio(); + LIST_INSERT_HEAD(&(card->mem), mem, next); + //splx(s); + + return mem->log_base; +} + +void +emuxki_mem_free(emuxki_dev *card, void *ptr) +{ + int i;//, s; + emuxki_mem *mem; + size_t numblocks; + uint32 *ptb, silentpage; + + ptb = card->ptb_log_base; + silentpage = ((uint32)card->silentpage_phy_base) << 1; + LOG(("emuxki_mem_free 1\n")); + LIST_FOREACH(mem, &card->mem, next) { + LOG(("emuxki_mem_free 2\n")); + if (mem->log_base != ptr) + continue; + LOG(("emuxki_mem_free 3\n")); + //s = splaudio(); + if (mem->ptbidx != EMU_RMEM) { + LOG(("mem_size : %i\n", mem->size)); + numblocks = mem->size / EMU_PTESIZE; + if (mem->size % EMU_PTESIZE) + numblocks++; + for (i = 0; i < numblocks; i++) + ptb[mem->ptbidx + i] = + silentpage | (mem->ptbidx + i); + } + LIST_REMOVE(mem, next); + //splx(s); + + LOG(("freeing mem\n")); + emuxki_mem_delete(mem); + break; + } +} + +/* Emuxki channel functions */ + +static void +emuxki_chanparms_set_defaults(emuxki_channel *chan) +{ + chan->fxsend.a.level = chan->fxsend.b.level = + chan->fxsend.c.level = chan->fxsend.d.level = + /* for audigy */ + chan->fxsend.e.level = chan->fxsend.f.level = + chan->fxsend.g.level = chan->fxsend.h.level = 0xc0; /* not max */ + + chan->fxsend.a.dest = 0x0; + chan->fxsend.b.dest = 0x1; + chan->fxsend.c.dest = 0x2; + chan->fxsend.d.dest = 0x3; + /* for audigy */ + chan->fxsend.e.dest = 0x4; + chan->fxsend.f.dest = 0x5; + chan->fxsend.g.dest = 0x6; + chan->fxsend.h.dest = 0x7; + + chan->pitch.intial = 0x0000; /* shouldn't it be 0xE000 ? */ + chan->pitch.current = 0x0000; /* should it be 0x0400 */ + chan->pitch.target = 0x0000; /* the unity pitch shift ? */ + chan->pitch.envelope_amount = 0x00; /* none */ + + chan->initial_attenuation = 0x00; /* no attenuation */ + chan->volume.current = 0x0000; /* no volume */ + chan->volume.target = 0xffff; + chan->volume.envelope.current_state = 0x8000; /* 0 msec delay */ + chan->volume.envelope.hold_time = 0x7f; /* 0 msec */ + chan->volume.envelope.attack_time = 0x7f; /* 5.5msec */ + chan->volume.envelope.sustain_level = 0x7f; /* full */ + chan->volume.envelope.decay_time = 0x7f; /* 22msec */ + + chan->filter.initial_cutoff_frequency = 0xff; /* no filter */ + chan->filter.current_cutoff_frequency = 0xffff; /* no filtering */ + chan->filter.target_cutoff_frequency = 0xffff; /* no filtering */ + chan->filter.lowpass_resonance_height = 0x0; + chan->filter.interpolation_ROM = 0x1; /* full band */ + chan->filter.envelope_amount = 0x7f; /* none */ + chan->filter.LFO_modulation_depth = 0x00; /* none */ + + chan->loop.start = 0x000000; + chan->loop.end = 0x000010; /* Why ? */ + + chan->modulation.envelope.current_state = 0x8000; + chan->modulation.envelope.hold_time = 0x00; /* 127 better ? */ + chan->modulation.envelope.attack_time = 0x00; /* infinite */ + chan->modulation.envelope.sustain_level = 0x00; /* off */ + chan->modulation.envelope.decay_time = 0x7f; /* 22 msec */ + chan->modulation.LFO_state = 0x8000; + + chan->vibrato_LFO.state = 0x8000; + chan->vibrato_LFO.modulation_depth = 0x00; /* none */ + chan->vibrato_LFO.vibrato_depth = 0x00; + chan->vibrato_LFO.frequency = 0x00; /* Why set to 24 when + * initialized ? */ + + chan->tremolo_depth = 0x00; +} + +static emuxki_channel * +emuxki_channel_new(emuxki_voice *voice, uint8 num) +{ + emuxki_channel *chan; + + chan = (emuxki_channel *) malloc(sizeof(emuxki_channel)); + if (chan == NULL) + return NULL; + chan->voice = voice; + chan->num = num; + emuxki_chanparms_set_defaults(chan); + chan->voice->stream->card->channel[num] = chan; + return chan; +} + +static void +emuxki_channel_delete(emuxki_channel *chan) +{ + chan->voice->stream->card->channel[chan->num] = NULL; + free(chan); +} + +static void +emuxki_channel_set_fxsend(emuxki_channel *chan, + emuxki_chanparms_fxsend *fxsend) +{ + /* Could do a memcpy ...*/ + chan->fxsend.a.level = fxsend->a.level; + chan->fxsend.b.level = fxsend->b.level; + chan->fxsend.c.level = fxsend->c.level; + chan->fxsend.d.level = fxsend->d.level; + chan->fxsend.a.dest = fxsend->a.dest; + chan->fxsend.b.dest = fxsend->b.dest; + chan->fxsend.c.dest = fxsend->c.dest; + chan->fxsend.d.dest = fxsend->d.dest; + + /* for audigy */ + chan->fxsend.e.level = fxsend->e.level; + chan->fxsend.f.level = fxsend->f.level; + chan->fxsend.g.level = fxsend->g.level; + chan->fxsend.h.level = fxsend->h.level; + chan->fxsend.e.dest = fxsend->e.dest; + chan->fxsend.f.dest = fxsend->f.dest; + chan->fxsend.g.dest = fxsend->g.dest; + chan->fxsend.h.dest = fxsend->h.dest; +} + +static void +emuxki_channel_set_srate(emuxki_channel *chan, uint32 srate) +{ + chan->pitch.target = (srate << 8) / 375; + chan->pitch.target = (chan->pitch.target >> 1) + + (chan->pitch.target & 1); + chan->pitch.target &= 0xffff; + chan->pitch.current = chan->pitch.target; + chan->pitch.intial = + (emuxki_rate_to_pitch(srate) >> 8) & EMU_CHAN_IP_MASK; +} + +/* voice params must be set before calling this */ +static void +emuxki_channel_set_bufparms(emuxki_channel *chan, + uint32 start, uint32 end) +{ + //uint8 shift; + //emuxki_voice *voice = chan->voice; + + //shift = voice->stereo + voice->b16; + chan->loop.start = start & EMU_CHAN_PSST_LOOPSTARTADDR_MASK; + chan->loop.end = end & EMU_CHAN_DSL_LOOPENDADDR_MASK; +} + +static void +emuxki_channel_commit_fx(emuxki_channel *chan) +{ + emuxki_dev *card = chan->voice->stream->card; + uint8 chano = chan->num; + + if(IS_AUDIGY(&card->config)) { + emuxki_chan_write(&card->config, chano, EMU_A_CHAN_FXRT1, + (chan->fxsend.d.dest << 24) | + (chan->fxsend.c.dest << 16) | + (chan->fxsend.b.dest << 8) | + (chan->fxsend.a.dest)); + emuxki_chan_write(&card->config, chano, EMU_A_CHAN_FXRT2, + (chan->fxsend.h.dest << 24) | + (chan->fxsend.g.dest << 16) | + (chan->fxsend.f.dest << 8) | + (chan->fxsend.e.dest)); + emuxki_chan_write(&card->config, chano, EMU_A_CHAN_SENDAMOUNTS, + (chan->fxsend.e.level << 24) | + (chan->fxsend.f.level << 16) | + (chan->fxsend.g.level << 8) | + (chan->fxsend.h.level)); + } else { + emuxki_chan_write(&card->config, chano, EMU_CHAN_FXRT, + (chan->fxsend.d.dest << 28) | + (chan->fxsend.c.dest << 24) | + (chan->fxsend.b.dest << 20) | + (chan->fxsend.a.dest << 16)); + } + + emuxki_chan_write(&card->config, chano, 0x10000000 | EMU_CHAN_PTRX, + (chan->fxsend.a.level << 8) | chan->fxsend.b.level); + emuxki_chan_write(&card->config, chano, EMU_CHAN_DSL, + (chan->fxsend.d.level << 24) | chan->loop.end); + emuxki_chan_write(&card->config, chano, EMU_CHAN_PSST, + (chan->fxsend.c.level << 24) | chan->loop.start); +} + +static void +emuxki_channel_commit_parms(emuxki_channel *chan) +{ + emuxki_voice *voice = chan->voice; + emuxki_dev *card = chan->voice->stream->card; + uint32 start, mapval; + uint8 chano = chan->num; + //int s; + + start = chan->loop.start + + (voice->stereo ? 28 : 30) * (voice->b16 + 1); + mapval = ((uint32)card->silentpage_phy_base) << 1 | EMU_CHAN_MAP_PTI_MASK; + + //s = splaudio(); + emuxki_chan_write(&card->config, chano, EMU_CHAN_CPF_STEREO, voice->stereo); + + emuxki_channel_commit_fx(chan); + + emuxki_chan_write(&card->config, chano, EMU_CHAN_CCCA, + (chan->filter.lowpass_resonance_height << 28) | + (chan->filter.interpolation_ROM << 25) | + (voice->b16 ? 0 : EMU_CHAN_CCCA_8BITSELECT) | start); + emuxki_chan_write(&card->config, chano, EMU_CHAN_Z1, 0); + emuxki_chan_write(&card->config, chano, EMU_CHAN_Z2, 0); + emuxki_chan_write(&card->config, chano, EMU_CHAN_MAPA, mapval); + emuxki_chan_write(&card->config, chano, EMU_CHAN_MAPB, mapval); + emuxki_chan_write(&card->config, chano, EMU_CHAN_CVCF_CURRFILTER, + chan->filter.current_cutoff_frequency); + emuxki_chan_write(&card->config, chano, EMU_CHAN_VTFT_FILTERTARGET, + chan->filter.target_cutoff_frequency); + emuxki_chan_write(&card->config, chano, EMU_CHAN_ATKHLDM, + (chan->modulation.envelope.hold_time << 8) | + chan->modulation.envelope.attack_time); + emuxki_chan_write(&card->config, chano, EMU_CHAN_DCYSUSM, + (chan->modulation.envelope.sustain_level << 8) | + chan->modulation.envelope.decay_time); + emuxki_chan_write(&card->config, chano, EMU_CHAN_LFOVAL1, + chan->modulation.LFO_state); + emuxki_chan_write(&card->config, chano, EMU_CHAN_LFOVAL2, + chan->vibrato_LFO.state); + emuxki_chan_write(&card->config, chano, EMU_CHAN_FMMOD, + (chan->vibrato_LFO.modulation_depth << 8) | + chan->filter.LFO_modulation_depth); + emuxki_chan_write(&card->config, chano, EMU_CHAN_TREMFRQ, + (chan->tremolo_depth << 8)); + emuxki_chan_write(&card->config, chano, EMU_CHAN_FM2FRQ2, + (chan->vibrato_LFO.vibrato_depth << 8) | + chan->vibrato_LFO.frequency); + emuxki_chan_write(&card->config, chano, EMU_CHAN_ENVVAL, + chan->modulation.envelope.current_state); + emuxki_chan_write(&card->config, chano, EMU_CHAN_ATKHLDV, + (chan->volume.envelope.hold_time << 8) | + chan->volume.envelope.attack_time); + emuxki_chan_write(&card->config, chano, EMU_CHAN_ENVVOL, + chan->volume.envelope.current_state); + emuxki_chan_write(&card->config, chano, EMU_CHAN_PEFE, + (chan->pitch.envelope_amount << 8) | + chan->filter.envelope_amount); + //splx(s); +} + +static void +emuxki_channel_start(emuxki_channel *chan) +{ + emuxki_voice *voice = chan->voice; + emuxki_dev *card = chan->voice->stream->card; + uint8 cache_sample, cache_invalid_size, chano = chan->num; + uint32 sample; + //int s; + + cache_sample = voice->stereo ? 4 : 2; + sample = voice->b16 ? 0x00000000 : 0x80808080; + cache_invalid_size = (voice->stereo ? 28 : 30) * (voice->b16 + 1); + + //s = splaudio(); + while (cache_sample--) + emuxki_chan_write(&card->config, chano, + EMU_CHAN_CD0 + cache_sample, sample); + emuxki_chan_write(&card->config, chano, EMU_CHAN_CCR_CACHEINVALIDSIZE, 0); + emuxki_chan_write(&card->config, chano, EMU_CHAN_CCR_READADDRESS, 64); + emuxki_chan_write(&card->config, chano, EMU_CHAN_CCR_CACHEINVALIDSIZE, + cache_invalid_size); + emuxki_chan_write(&card->config, chano, EMU_CHAN_IFATN, + (chan->filter.target_cutoff_frequency << 8) | + chan->initial_attenuation); + emuxki_chan_write(&card->config, chano, EMU_CHAN_VTFT_VOLUMETARGET, + chan->volume.target); + emuxki_chan_write(&card->config, chano, EMU_CHAN_CVCF_CURRVOL, + chan->volume.current); + emuxki_chan_write(&card->config, 0, EMU_MKSUBREG(1, chano, + EMU_SOLEL + (chano >> 5)), + 0); /* Clear stop on loop */ + emuxki_chan_write(&card->config, 0, EMU_MKSUBREG(1, chano, + EMU_CLIEL + (chano >> 5)), + 0); /* Clear loop interrupt */ + emuxki_chan_write(&card->config, chano, EMU_CHAN_DCYSUSV, + (chan->volume.envelope.sustain_level << 8) | + chan->volume.envelope.decay_time); + emuxki_chan_write(&card->config, chano, EMU_CHAN_PTRX_PITCHTARGET, + chan->pitch.target); + emuxki_chan_write(&card->config, chano, EMU_CHAN_CPF_PITCH, + chan->pitch.current); + emuxki_chan_write(&card->config, chano, EMU_CHAN_IP, chan->pitch.intial); + + //splx(s); +} + +static void +emuxki_channel_stop(emuxki_channel *chan) +{ + emuxki_dev *card = chan->voice->stream->card; + //int s; + uint8 chano = chan->num; + + //s = splaudio(); + + emuxki_chan_write(&card->config, chano, EMU_CHAN_PTRX_PITCHTARGET, 0); + emuxki_chan_write(&card->config, chano, EMU_CHAN_CPF_PITCH, 0); + emuxki_chan_write(&card->config, chano, EMU_CHAN_IFATN_ATTENUATION, 0xff); + emuxki_chan_write(&card->config, chano, EMU_CHAN_VTFT_VOLUMETARGET, 0); + emuxki_chan_write(&card->config, chano, EMU_CHAN_CVCF_CURRVOL, 0); + emuxki_chan_write(&card->config, chano, EMU_CHAN_IP, 0); + + //splx(s); +} + +/* Emuxki voice functions */ + +void emuxki_dump_voice(emuxki_voice *voice) +{ + LOG(("voice->use = %#u\n", voice->use)); + LOG(("voice->state = %#u\n", voice->state)); + LOG(("voice->stereo = %#u\n", voice->stereo)); + LOG(("voice->b16 = %#u\n", voice->b16)); + LOG(("voice->sample_rate = %#lu\n", voice->sample_rate)); + LOG(("voice->buffer = %#08x\n", voice->buffer)); + if(voice->buffer) { + LOG(("voice->buffer->ptbidx = %#u\n", voice->buffer->ptbidx)); + LOG(("voice->buffer->log_base = %#08x\n", voice->buffer->log_base)); + LOG(("voice->buffer->phy_base = %#08x\n", voice->buffer->phy_base)); + LOG(("voice->buffer->size = %#08x\n", voice->buffer->size)); + LOG(("voice->buffer->area = %#08x\n", voice->buffer->area)); + } + LOG(("voice->blksize = %#u\n", voice->blksize)); + LOG(("voice->trigblk = %#u\n", voice->trigblk)); + LOG(("voice->blkmod = %#u\n", voice->blkmod)); + LOG(("voice->timerate = %#u\n", voice->timerate)); +} + +/* Allocate channels for voice in case of play voice */ +static status_t +emuxki_voice_channel_create(emuxki_voice *voice) +{ + emuxki_channel **channel = voice->stream->card->channel; + uint8 i, stereo = voice->stereo; + //int s; + + for (i = 0; i < EMU_NUMCHAN; i += stereo + 1) { + if ((stereo && (channel[i + 1] != NULL)) || + (channel[i] != NULL)) /* Looking for free channels */ + continue; + //s = splaudio(); + if (stereo) { + voice->dataloc.chan[1] = + emuxki_channel_new(voice, i + 1); + if (voice->dataloc.chan[1] == NULL) { + //splx(s); + return ENOMEM; + } + } + voice->dataloc.chan[0] = emuxki_channel_new(voice, i); + if (voice->dataloc.chan[0] == NULL) { + if (stereo) { + emuxki_channel_delete(voice->dataloc.chan[1]); + voice->dataloc.chan[1] = NULL; + } + //splx(s); + return ENOMEM; + } + //splx(s); + return B_OK; + } + return EAGAIN; +} + +/* When calling this function we assume no one can access the voice */ +static void +emuxki_voice_channel_destroy(emuxki_voice *voice) +{ + emuxki_channel_delete(voice->dataloc.chan[0]); + voice->dataloc.chan[0] = NULL; + if (voice->stereo) + emuxki_channel_delete(voice->dataloc.chan[1]); + voice->dataloc.chan[1] = NULL; +} + +static status_t +emuxki_voice_dataloc_create(emuxki_voice *voice) +{ + status_t error; + + if (voice->use & EMU_USE_PLAY) { + if ((error = emuxki_voice_channel_create(voice))) + return (error); + } else { + //return B_ERROR; + } + return B_OK; +} + +static void +emuxki_voice_dataloc_destroy(emuxki_voice *voice) +{ + if (voice->use & EMU_USE_PLAY) { + if (voice->dataloc.chan[0] != NULL) + emuxki_voice_channel_destroy(voice); + } else { + uint32 buffaddr_reg, buffsize_reg; + switch (voice->dataloc.source) { + case EMU_RECSRC_MIC: + buffaddr_reg = EMU_MICBA; + buffsize_reg = EMU_MICBS; + break; + case EMU_RECSRC_ADC: + buffaddr_reg = EMU_ADCBA; + buffsize_reg = EMU_ADCBS; + break; + case EMU_RECSRC_FX: + buffaddr_reg = EMU_FXBA; + buffsize_reg = EMU_FXBS; + break; + default: + return; + } + emuxki_chan_write(&voice->stream->card->config, 0, buffaddr_reg, 0); + emuxki_chan_write(&voice->stream->card->config, 0, buffsize_reg, EMU_RECBS_BUFSIZE_NONE); + } +} + +static void +emuxki_voice_fxupdate(emuxki_voice *voice) +{ + emuxki_chanparms_fxsend fxsend; + + if (voice->use & EMU_USE_PLAY) { + fxsend.a.dest = 0x3f; + fxsend.b.dest = 0x3f; + fxsend.c.dest = 0x3f; + fxsend.d.dest = 0x3f; + /* for audigy */ + fxsend.e.dest = 0x3f; + fxsend.f.dest = 0x3f; + fxsend.g.dest = 0x3f; + fxsend.h.dest = 0x3f; + + fxsend.a.level = fxsend.b.level = fxsend.c.level = fxsend.d.level = + fxsend.e.level = fxsend.g.level = fxsend.f.level = fxsend.h.level = 0x00; + + if (voice->stereo) { + switch(voice->stream->card->play_mode) { + case 2: + if(voice->stream->nstereo == 1) { + fxsend.a.dest = voice->voicenum * 2; + fxsend.a.level = 0xc0; + } else if((voice->stream->nstereo == 2) || + ((voice->stream->nstereo == 3)&&(voice->voicenum < 2))) { + fxsend.a.dest = voice->voicenum * 2; + fxsend.a.level = 0xc0; + if(voice->voicenum > 1 - 1) + fxsend.a.dest-=2; + } else if(voice->stream->nstereo == 3 && voice->voicenum > 1) { + fxsend.a.dest = 0x0; + fxsend.a.level = 0xc0 / 2; + fxsend.b.dest = 0x1; + fxsend.b.level = 0xc0 / 2; + } else { + LOG(("emuxki_voice_set_stereo case 2 badly managed\n")); + } + break; + case 4: + if(voice->stream->nstereo == 1) { + fxsend.a.dest = voice->voicenum * 2; + fxsend.a.level = 0xc0; + fxsend.b.dest = voice->voicenum * 2 + 2; + fxsend.b.level = 0xc0; + } else if((voice->stream->nstereo == 2) || + ((voice->stream->nstereo == 3)&&(voice->voicenum < 2))) { + fxsend.a.dest = voice->voicenum * 2; + fxsend.a.level = 0xc0; + } else if(voice->stream->nstereo == 3 && voice->voicenum > 1) { + fxsend.a.dest = 0x0; + fxsend.a.level = 0xc0 / 2; + fxsend.b.dest = 0x1; + fxsend.b.level = 0xc0 / 2; + } else { + LOG(("emuxki_voice_set_stereo case 4 badly managed\n")); + } + break; + case 6: // only on audigy + if(voice->stream->nstereo == 1) { + fxsend.a.dest = voice->voicenum * 2; + fxsend.a.level = 0xc0; + fxsend.b.dest = voice->voicenum * 2 + 2; + fxsend.b.level = 0xc0; + fxsend.c.dest = 0x4; + fxsend.c.level = 0xc0 / 2; + fxsend.d.dest = 0x5; + fxsend.d.level = 0xc0 / 2; + } else if(voice->stream->nstereo == 2) { + fxsend.a.dest = voice->voicenum * 2; + fxsend.a.level = 0xc0; + if(voice->voicenum < 1) { + fxsend.b.dest = 0x4; + fxsend.b.level = 0xc0 / 2; + fxsend.c.dest = 0x5; + fxsend.c.level = 0xc0 / 2; + } + } else if(voice->stream->nstereo == 3) { + fxsend.a.dest = voice->voicenum * 2; + fxsend.a.level = 0xc0; + } else { + LOG(("emuxki_voice_set_stereo case 6 badly managed\n")); + } + break; + } + + emuxki_channel_set_fxsend(voice->dataloc.chan[0], + &fxsend); + + switch(voice->stream->card->play_mode) { + case 2: + if(voice->stream->nstereo == 1) { + fxsend.a.dest = voice->voicenum * 2 + 1; + fxsend.a.level = 0xc0; + } else if((voice->stream->nstereo == 2) || + ((voice->stream->nstereo == 3)&&(voice->voicenum < 2))) { + fxsend.a.dest = voice->voicenum * 2 + 1; + fxsend.a.level = 0xc0; + if(voice->voicenum > 1 - 1) + fxsend.a.dest-=2; + } else if(voice->stream->nstereo == 3 && voice->voicenum > 1) { + fxsend.a.dest = 0x0; + fxsend.a.level = 0xc0 / 2; + fxsend.b.dest = 0x1; + fxsend.b.level = 0xc0 / 2; + } else { + LOG(("emuxki_voice_set_stereo case 2 badly managed\n")); + } + break; + case 4: + if(voice->stream->nstereo == 1) { + fxsend.a.dest = voice->voicenum * 2 + 1; + fxsend.a.level = 0xc0; + fxsend.b.dest = voice->voicenum * 2 + 3; + fxsend.b.level = 0xc0; + } else if((voice->stream->nstereo == 2) || + ((voice->stream->nstereo == 3)&&(voice->voicenum < 2))) { + fxsend.a.dest = voice->voicenum * 2 + 1; + fxsend.a.level = 0xc0; + } else if(voice->stream->nstereo == 3 && voice->voicenum > 1) { + fxsend.a.dest = 0x0; + fxsend.a.level = 0xc0 / 2; + fxsend.b.dest = 0x1; + fxsend.b.level = 0xc0 / 2; + } else { + LOG(("emuxki_voice_set_stereo case 4 badly managed\n")); + } + break; + case 6: // only on audigy + if(voice->stream->nstereo == 1) { + fxsend.a.dest = voice->voicenum * 2 + 1; + fxsend.a.level = 0xc0; + fxsend.b.dest = voice->voicenum * 2 + 3; + fxsend.b.level = 0xc0; + fxsend.c.dest = 0x4; + fxsend.c.level = 0xc0 / 2; + fxsend.d.dest = 0x5; + fxsend.d.level = 0xc0 / 2; + } else if(voice->stream->nstereo == 2) { + fxsend.a.dest = voice->voicenum * 2 + 1; + fxsend.a.level = 0xc0; + if(voice->voicenum < 1) { + fxsend.b.dest = 0x4; + fxsend.b.level = 0xc0 / 2; + fxsend.c.dest = 0x5; + fxsend.c.level = 0xc0 / 2; + } + } else if(voice->stream->nstereo == 3) { + fxsend.a.dest = voice->voicenum * 2 + 1; + fxsend.a.level = 0xc0; + } else { + LOG(("emuxki_voice_set_stereo case 6 badly managed\n")); + } + break; + } + + emuxki_channel_set_fxsend(voice->dataloc.chan[1], + &fxsend); + } else { + switch(voice->stream->card->play_mode) { + case 2: + if(voice->stream->nmono == 1) { + fxsend.a.dest = voice->voicenum; + fxsend.a.level = 0xc0; + fxsend.b.dest = voice->voicenum + 1; + fxsend.b.level = 0xc0; + } else if(voice->stream->nmono == 2) { + fxsend.a.dest = voice->voicenum; + fxsend.a.level = 0xc0; + } else if((voice->stream->nmono == 4) || + ((voice->stream->nmono == 6)&&(voice->voicenum < 4))) { + fxsend.a.dest = voice->voicenum; + fxsend.a.level = 0xc0; + if(voice->voicenum > 2 - 1) + fxsend.a.dest-=2; + } else if(voice->stream->nmono == 6 && voice->voicenum > 3) { + fxsend.a.dest = 0x0; + fxsend.a.level = 0xc0 / 2; + fxsend.b.dest = 0x1; + fxsend.b.level = 0xc0 / 2; + } else { + LOG(("emuxki_voice_set_stereo case 2 badly managed\n")); + } + break; + case 4: + if(voice->stream->nmono == 1) { + fxsend.a.dest = voice->voicenum; + fxsend.a.level = 0xc0; + fxsend.b.dest = voice->voicenum + 1; + fxsend.b.level = 0xc0; + fxsend.c.dest = voice->voicenum + 2; + fxsend.c.level = 0xc0; + fxsend.d.dest = voice->voicenum + 3; + fxsend.d.level = 0xc0; + } else if(voice->stream->nmono == 2) { + fxsend.a.dest = voice->voicenum; + fxsend.a.level = 0xc0; + fxsend.b.dest = voice->voicenum + 2; + fxsend.b.level = 0xc0; + } else if((voice->stream->nmono == 4) || + ((voice->stream->nmono == 6)&&(voice->voicenum < 4))) { + fxsend.a.dest = voice->voicenum; + fxsend.a.level = 0xc0; + } else if(voice->stream->nmono == 6 && voice->voicenum > 3) { + fxsend.a.dest = 0x0; + fxsend.a.level = 0xc0 / 2; + fxsend.b.dest = 0x1; + fxsend.b.level = 0xc0 / 2; + } else { + LOG(("emuxki_voice_set_stereo case 4 badly managed\n")); + } + break; + case 6: // only on audigy + if(voice->stream->nmono == 1) { + fxsend.a.dest = voice->voicenum; + fxsend.a.level = 0xc0; + fxsend.b.dest = voice->voicenum + 1; + fxsend.b.level = 0xc0; + fxsend.c.dest = voice->voicenum + 2; + fxsend.c.level = 0xc0; + fxsend.d.dest = voice->voicenum + 3; + fxsend.d.level = 0xc0; + fxsend.e.dest = voice->voicenum + 4; + fxsend.e.level = 0xc0; + fxsend.f.dest = voice->voicenum + 5; + fxsend.f.level = 0xc0; + } else if(voice->stream->nmono == 2) { + fxsend.a.dest = voice->voicenum; + fxsend.a.level = 0xc0; + fxsend.b.dest = voice->voicenum + 2; + fxsend.b.level = 0xc0; + fxsend.c.dest = 0x4; + fxsend.c.level = 0xc0 / 2; + fxsend.d.dest = 0x5; + fxsend.d.level = 0xc0 / 2; + } else if(voice->stream->nmono == 4) { + fxsend.a.dest = voice->voicenum; + fxsend.a.level = 0xc0; + if(voice->voicenum < 2) { + fxsend.b.dest = 0x4; + fxsend.b.level = 0xc0 / 2; + fxsend.c.dest = 0x5; + fxsend.c.level = 0xc0 / 2; + } + } else if(voice->stream->nmono == 6) { + fxsend.a.dest = voice->voicenum; + fxsend.a.level = 0xc0; + } else { + LOG(("emuxki_voice_set_stereo case 6 badly managed\n")); + } + break; + } + + emuxki_channel_set_fxsend(voice->dataloc.chan[0], + &fxsend); + } + } +} + +static status_t +emuxki_voice_set_stereo(emuxki_voice *voice, uint8 stereo) +{ + status_t error; + + emuxki_voice_dataloc_destroy(voice); + voice->stereo = stereo; + if ((error = emuxki_voice_dataloc_create(voice))) + return error; + emuxki_voice_fxupdate(voice); + return B_OK; +} + +static status_t +emuxki_voice_set_srate(emuxki_voice *voice, uint32 srate) +{ + voice->sample_rate = srate; + if (voice->use & EMU_USE_PLAY) { + if ((srate < 4000) || (srate > 48000)) + return (EINVAL); + emuxki_channel_set_srate(voice->dataloc.chan[0], srate); + if (voice->stereo) + emuxki_channel_set_srate(voice->dataloc.chan[1], + srate); + } + return B_OK; +} + +status_t +emuxki_voice_set_audioparms(emuxki_voice *voice, uint8 stereo, + uint8 b16, uint32 srate) +{ + status_t error; + + if (voice->stereo == stereo && voice->b16 == b16 && + voice->sample_rate == srate) + return B_OK; + + if (voice->stereo != stereo) { + if ((error = emuxki_voice_set_stereo(voice, stereo))) + return error; + } + voice->b16 = b16; + if (voice->sample_rate != srate) + emuxki_voice_set_srate(voice, srate); + return B_OK; +} + +status_t +emuxki_voice_set_recparms(emuxki_voice *voice, emuxki_recsrc_t recsrc, + emuxki_recparams *recparams) +{ + if(voice->use & EMU_USE_RECORD) { + switch(recsrc) { + case EMU_RECSRC_MIC: + break; + case EMU_RECSRC_ADC: + break; + case EMU_RECSRC_FX: + if(!recparams) + return B_ERROR; + voice->recparams.efx_voices[0] = recparams->efx_voices[0]; + voice->recparams.efx_voices[1] = recparams->efx_voices[1]; + break; + default: + return B_ERROR; + break; + } + voice->dataloc.source = recsrc; + } + return B_OK; +} + +/* voice audio parms (see just before) must be set prior to this */ +status_t +emuxki_voice_set_bufparms(emuxki_voice *voice, void *ptr, + uint32 bufsize, uint16 blksize) +{ + emuxki_mem *mem; + emuxki_channel **chan; + uint32 start, end; + uint8 sample_size; + status_t error = EFAULT; + + LIST_FOREACH(mem, &voice->stream->card->mem, next) { + if (mem->log_base != ptr) + continue; + + voice->buffer = mem; + sample_size = (voice->b16 + 1) * (voice->stereo + 1); + voice->trigblk = 0; /* This shouldn't be needed */ + voice->blkmod = bufsize / blksize; + if (bufsize % blksize) /* This should not happen */ + voice->blkmod++; + error = 0; + + if (voice->use & EMU_USE_PLAY) { + voice->blksize = blksize / sample_size; + chan = voice->dataloc.chan; + start = (mem->ptbidx << 12) / sample_size; + end = start + bufsize / sample_size; + emuxki_channel_set_bufparms(chan[0], + start, end); + if (voice->stereo) + emuxki_channel_set_bufparms(chan[1], + start, end); + voice->timerate = (uint32) 48000 * + voice->blksize / voice->sample_rate / 2; + if (voice->timerate < 5) + error = EINVAL; + } else { + voice->blksize = blksize; + } + + break; + } + + return error; +} + +status_t +emuxki_voice_commit_parms(emuxki_voice *voice) +{ + if (voice->use & EMU_USE_PLAY) { + emuxki_channel_commit_parms(voice->dataloc.chan[0]); + if (voice->stereo) + emuxki_channel_commit_parms(voice->dataloc.chan[1]); + } else { + uint32 buffaddr_reg, buffsize_reg, idx_reg; + switch (voice->dataloc.source) { + case EMU_RECSRC_MIC: + buffaddr_reg = EMU_MICBA; + buffsize_reg = EMU_MICBS; + idx_reg = EMU_MICIDX; + break; + case EMU_RECSRC_ADC: + buffaddr_reg = EMU_ADCBA; + buffsize_reg = EMU_ADCBS; + idx_reg = EMU_ADCIDX; + break; + case EMU_RECSRC_FX: + buffaddr_reg = EMU_FXBA; + buffsize_reg = EMU_FXBS; + idx_reg = EMU_FXIDX; + break; + default: + return B_ERROR; + } + emuxki_chan_write(&voice->stream->card->config, 0, buffaddr_reg, (uint32)voice->buffer->phy_base); + emuxki_chan_write(&voice->stream->card->config, 0, buffsize_reg, EMU_RECBS_BUFSIZE_NONE); + emuxki_chan_write(&voice->stream->card->config, 0, buffsize_reg, EMU_RECBS_BUFSIZE_4096); + + LOG(("emuxki_voice_commit_parms idx_reg : %u\n", idx_reg)); + + idx_reg = EMU_RECIDX(idx_reg); + while(emuxki_chan_read(&voice->stream->card->config, 0, idx_reg)) + snooze(5); + } + return B_OK; +} + +static uint32 +emuxki_voice_curaddr(emuxki_voice *voice) +{ + if (voice->use & EMU_USE_PLAY) + return (emuxki_chan_read(&voice->stream->card->config, + voice->dataloc.chan[0]->num, + EMU_CHAN_CCCA_CURRADDR) - + voice->dataloc.chan[0]->loop.start); + else { + uint32 idx_reg = 0; + switch (voice->dataloc.source) { + case EMU_RECSRC_MIC: + idx_reg = IS_AUDIGY(&voice->stream->card->config) ? EMU_A_MICIDX : EMU_MICIDX; + break; + case EMU_RECSRC_ADC: + idx_reg = IS_AUDIGY(&voice->stream->card->config) ? EMU_A_ADCIDX : EMU_ADCIDX; + break; + case EMU_RECSRC_FX: + idx_reg = EMU_FXIDX; + break; + default: + TRACE(("emuxki_voice_curaddr : BUG!!\n")); + } + //TRACE(("emuxki_voice_curaddr idx_reg : %u\n", idx_reg)); + //TRACE(("emuxki_voice_curaddr : %lu\n", emuxki_chan_read(&voice->stream->card->config, 0, idx_reg))); + return emuxki_chan_read(&voice->stream->card->config, + 0, + idx_reg); + } +} + +static void +emuxki_resched_timer(emuxki_dev *card) +{ + emuxki_voice *voice; + emuxki_stream *stream; + uint16 timerate = 1024; + uint8 active = 0; + //int s = splaudio(); + + LOG(("emuxki_resched_timer begin\n")); + + LIST_FOREACH(stream, &card->streams, next) { + LIST_FOREACH(voice, &stream->voices, next) { + if ((voice->use & EMU_USE_PLAY) == 0 || + (voice->state & EMU_STATE_STARTED) == 0) + continue; + active = 1; + if (voice->timerate < timerate) + timerate = voice->timerate; + } + } + if (timerate & ~EMU_TIMER_RATE_MASK) + timerate = 0; + + if(card->timerate > timerate) { + LOG(("emuxki_resched_timer written (old %u, new %u)\n", card->timerate, timerate)); + card->timerate = timerate; + emuxki_reg_write_16(&card->config, EMU_TIMER, timerate); + } + if (!active && (card->timerstate & EMU_TIMER_STATE_ENABLED)) { + emuxki_inte_disable(&card->config, EMU_INTE_INTERTIMERENB); + card->timerstate &= ~EMU_TIMER_STATE_ENABLED; + LOG(("emuxki_resched_timer : timer disabled\n")); + } else + if (active && !(card->timerstate & EMU_TIMER_STATE_ENABLED)) { + emuxki_inte_enable(&card->config, EMU_INTE_INTERTIMERENB); + card->timerstate |= EMU_TIMER_STATE_ENABLED; + LOG(("emuxki_resched_timer : timer enabled\n")); + } + LOG(("emuxki_resched_timer state : %x\n", card->timerstate)); + //splx(s); +} + +static uint32 +emuxki_voice_adc_rate(emuxki_voice *voice) +{ + switch(voice->sample_rate) { + case 48000: + return EMU_ADCCR_SAMPLERATE_48; + break; + case 44100: + return EMU_ADCCR_SAMPLERATE_44; + break; + case 32000: + return EMU_ADCCR_SAMPLERATE_32; + break; + case 24000: + return EMU_ADCCR_SAMPLERATE_24; + break; + case 22050: + return EMU_ADCCR_SAMPLERATE_22; + break; + case 16000: + return EMU_ADCCR_SAMPLERATE_16; + break; + case 12000: + if(IS_AUDIGY(&voice->stream->card->config)) + return EMU_A_ADCCR_SAMPLERATE_12; + else + PRINT(("recording sample_rate not supported : %u\n", voice->sample_rate)); + break; + case 11000: + if(IS_AUDIGY(&voice->stream->card->config)) + return EMU_A_ADCCR_SAMPLERATE_11; + else + return EMU_ADCCR_SAMPLERATE_11; + break; + case 8000: + if(IS_AUDIGY(&voice->stream->card->config)) + return EMU_A_ADCCR_SAMPLERATE_8; + else + return EMU_ADCCR_SAMPLERATE_8; + break; + default: + PRINT(("recording sample_rate not supported : %u\n", voice->sample_rate)); + } + return 0; +} + +void +emuxki_voice_start(emuxki_voice *voice) +{ + LOG(("emuxki_voice_start\n")); + + if (voice->use & EMU_USE_PLAY) { + voice->trigblk = 1; + emuxki_channel_start(voice->dataloc.chan[0]); + if (voice->stereo) + emuxki_channel_start(voice->dataloc.chan[1]); + } else { + + switch (voice->dataloc.source) { + case EMU_RECSRC_MIC: + emuxki_inte_enable(&voice->stream->card->config, EMU_INTE_MICBUFENABLE); + break; + case EMU_RECSRC_ADC: { + uint32 adccr_value = 0; + adccr_value = emuxki_voice_adc_rate(voice); + LOG(("emuxki_voice_start adccr_value : %u\n", adccr_value)); + if(voice->stereo) + adccr_value |= ( (IS_AUDIGY(&voice->stream->card->config) ? EMU_A_ADCCR_LCHANENABLE : EMU_ADCCR_LCHANENABLE ) + | ( IS_AUDIGY(&voice->stream->card->config) ? EMU_A_ADCCR_RCHANENABLE : EMU_ADCCR_RCHANENABLE )); + else + adccr_value |= IS_AUDIGY(&voice->stream->card->config) ? EMU_A_ADCCR_LCHANENABLE : EMU_ADCCR_LCHANENABLE; + + LOG(("emuxki_voice_start adccr_value : %u, %u\n", adccr_value, EMU_ADCCR_LCHANENABLE | EMU_ADCCR_RCHANENABLE)); + emuxki_chan_write(&voice->stream->card->config, 0, EMU_ADCCR, adccr_value); + + emuxki_inte_enable(&voice->stream->card->config, EMU_INTE_ADCBUFENABLE); + } + break; + case EMU_RECSRC_FX: + if(IS_AUDIGY(&voice->stream->card->config)) { + emuxki_chan_write(&voice->stream->card->config, 0, EMU_A_FXWC1, + voice->recparams.efx_voices[0]); + emuxki_chan_write(&voice->stream->card->config, 0, EMU_A_FXWC2, + voice->recparams.efx_voices[1]); + } else { + emuxki_chan_write(&voice->stream->card->config, 0, EMU_FXWC, + voice->recparams.efx_voices[0]); + } + emuxki_inte_enable(&voice->stream->card->config, EMU_INTE_EFXBUFENABLE); + break; + default: + PRINT(("emuxki_voice_start BUG\n")); + } + } + voice->state |= EMU_STATE_STARTED; + if (voice->use & EMU_USE_PLAY) { + emuxki_resched_timer(voice->stream->card); + + //emuxki_chan_write(&voice->stream->card->config, 0, 0x66, 1); + + //emuxki_chan_write(&voice->stream->card->config, 0, EMU_CLIEL, 1); + + /*emuxki_chan_write(&voice->stream->card->config, 0, EMU_MKSUBREG(1, 0, + EMU_CLIEL + (0 >> 5)), 1);*/ + } +} + +void +emuxki_voice_halt(emuxki_voice *voice) +{ + LOG(("emuxki_voice_halt\n")); + + if (voice->use & EMU_USE_PLAY) { + emuxki_channel_stop(voice->dataloc.chan[0]); + if (voice->stereo) + emuxki_channel_stop(voice->dataloc.chan[1]); + } else { + + switch (voice->dataloc.source) { + case EMU_RECSRC_MIC: + emuxki_inte_disable(&voice->stream->card->config, EMU_INTE_MICBUFENABLE); + break; + case EMU_RECSRC_ADC: + emuxki_chan_write(&voice->stream->card->config, 0, EMU_ADCCR, 0); + emuxki_inte_disable(&voice->stream->card->config, EMU_INTE_ADCBUFENABLE); + break; + case EMU_RECSRC_FX: + if(IS_AUDIGY(&voice->stream->card->config)) { + emuxki_chan_write(&voice->stream->card->config, 0, EMU_A_FXWC1, 0); + emuxki_chan_write(&voice->stream->card->config, 0, EMU_A_FXWC2, 0); + } else + emuxki_chan_write(&voice->stream->card->config, 0, EMU_FXWC, 0); + emuxki_inte_disable(&voice->stream->card->config, EMU_INTE_EFXBUFENABLE); + break; + default: + PRINT(("emuxki_voice_halt BUG\n")); + } + } + voice->state &= ~EMU_STATE_STARTED; + if (voice->use & EMU_USE_PLAY) { + emuxki_resched_timer(voice->stream->card); + + //emuxki_chan_write(&voice->stream->card->config, 0, EMU_CLIEL, 0); + } +} + +emuxki_voice * +emuxki_voice_new(emuxki_stream *stream, uint8 use, uint8 voicenum) +{ + emuxki_voice *voice; + //int s; + + LOG(("emuxki_voice_new\n")); + + voice = malloc(sizeof(emuxki_voice)); + if (voice == NULL) + return (NULL); + voice->stream = stream; + voice->use = use; + voice->state = !EMU_STATE_STARTED; + voice->stereo = EMU_STEREO_NOTSET; + voice->b16 = 0; + voice->sample_rate = 0; + if (use & EMU_USE_PLAY) + voice->dataloc.chan[0] = voice->dataloc.chan[1] = NULL; + else + voice->dataloc.source = EMU_RECSRC_NOTSET; + voice->buffer = NULL; + voice->blksize = 0; + voice->trigblk = 0; + voice->blkmod = 0; + voice->timerate = 0; + voice->voicenum = voicenum; + return voice; +} + +void +emuxki_voice_delete(emuxki_voice *voice) +{ + if (voice->state & EMU_STATE_STARTED) + emuxki_voice_halt(voice); + emuxki_voice_dataloc_destroy(voice); + free(voice); +} + +/* Emuxki stream functions */ + +status_t +emuxki_stream_set_audioparms(emuxki_stream *stream, bool stereo, uint8 channels, + uint8 b16, uint32 sample_rate) +{ + status_t error; + emuxki_voice *voice; + uint8 i, nvoices; + char *buffer; + uint8 sample_size, frame_size; + LOG(("emuxki_stream_set_audioparms\n")); + + if (stream->stereo == stereo && + ((stream->nmono + 2*stream->nstereo) == channels) && + (stream->b16 == b16) && + (stream->sample_rate == sample_rate)) + return B_OK; + + LIST_FOREACH(voice, &stream->voices, next) { + if(voice->buffer) + emuxki_mem_free(stream->card, voice->buffer->log_base); + emuxki_voice_delete(voice); + } + stream->first_voice = NULL; + LIST_INIT(&(stream->voices)); + + stream->b16 = b16; + stream->sample_rate = sample_rate; + + if(stereo && (channels % 2 == 0)) { + stream->stereo = true; + stream->nstereo = channels / 2; + stream->nmono = 0; + nvoices = stream->nstereo; + } else { + stream->stereo = false; + stream->nstereo = 0; + stream->nmono = channels; + nvoices = stream->nmono; + } + + sample_size = stream->b16 + 1; + frame_size = sample_size * (stream->stereo ? 2 : 1); + + for(i=0; iuse, i); + if(voice) { + if(!stream->first_voice) + stream->first_voice = voice; + LIST_INSERT_HEAD((&stream->voices), voice, next); + if ((error = emuxki_voice_set_audioparms(voice, stream->stereo, stream->b16, stream->sample_rate))) + return error; + + if (stream->use & EMU_USE_PLAY) + buffer = emuxki_pmem_alloc(stream->card, stream->bufframes * frame_size * stream->bufcount); + else + buffer = emuxki_rmem_alloc(stream->card, stream->bufframes * frame_size * stream->bufcount); + + emuxki_voice_set_bufparms(voice, buffer, + stream->bufframes * frame_size * stream->bufcount, + stream->bufframes * frame_size); + } + } + + return B_OK; +} + +status_t +emuxki_stream_set_recparms(emuxki_stream *stream, emuxki_recsrc_t recsrc, + emuxki_recparams *recparams) +{ + emuxki_voice *voice; + LOG(("emuxki_stream_set_recparms\n")); + + if(stream->use & EMU_USE_RECORD) { + switch(recsrc) { + case EMU_RECSRC_MIC: + break; + case EMU_RECSRC_ADC: + break; + case EMU_RECSRC_FX: + if(!recparams) + return B_ERROR; + LIST_FOREACH(voice, &stream->voices, next) { + voice->recparams.efx_voices[0] = recparams->efx_voices[0]; + voice->recparams.efx_voices[1] = recparams->efx_voices[1]; + } + break; + default: + return B_ERROR; + break; + } + LIST_FOREACH(voice, &stream->voices, next) + voice->dataloc.source = recsrc; + } + return B_OK; +} + +status_t +emuxki_stream_commit_parms(emuxki_stream *stream) +{ + emuxki_voice *voice; + status_t error; + LOG(("emuxki_stream_commit_parms\n")); + + LIST_FOREACH(voice, &stream->voices, next) + if ((error = emuxki_voice_commit_parms(voice))) + return error; + + return B_OK; +} + +status_t +emuxki_stream_get_nth_buffer(emuxki_stream *stream, uint8 chan, uint8 buf, + char** buffer, size_t *stride) +{ + emuxki_voice *voice = NULL; + uint8 i, sample_size; + LOG(("emuxki_stream_get_nth_buffer\n")); + + sample_size = stream->b16 + 1; + if(buf >= stream->bufcount) + return B_BAD_INDEX; + + if(stream->stereo) { + i = stream->nstereo - 1; + if(chan/2 > i) + return B_BAD_INDEX; + LIST_FOREACH(voice, &stream->voices, next) + if(i != chan/2) + i--; + else + break; + if(voice) { + *buffer = voice->buffer->log_base + (buf * stream->bufframes * sample_size * 2); + if(chan % 2 == 1) + *buffer += sample_size; + *stride = sample_size * 2; + } else + return B_ERROR; + } else { + i = stream->nmono - 1; + if(chan > i) + return B_BAD_INDEX; + LIST_FOREACH(voice, &stream->voices, next) + if(i != chan) + i--; + else + break; + if(voice) { + *buffer = voice->buffer->log_base + (buf * stream->bufframes * sample_size); + *stride = sample_size; + } else + return B_ERROR; + } + + return B_OK; +} + +void +emuxki_stream_start(emuxki_stream *stream, void (*inth) (void *), void *inthparam) +{ + emuxki_voice *voice; + LOG(("emuxki_stream_start\n")); + + stream->inth = inth; + stream->inthparam = inthparam; + + LIST_FOREACH(voice, &stream->voices, next) { + emuxki_voice_start(voice); + } + stream->state |= EMU_STATE_STARTED; +} + +void +emuxki_stream_halt(emuxki_stream *stream) +{ + emuxki_voice *voice; + LOG(("emuxki_stream_halt\n")); + + LIST_FOREACH(voice, &stream->voices, next) { + emuxki_voice_halt(voice); + } + stream->state &= ~EMU_STATE_STARTED; +} + +emuxki_stream * +emuxki_stream_new(emuxki_dev *card, uint8 use, uint32 bufframes, uint8 bufcount) +{ + emuxki_stream *stream; + cpu_status status; + LOG(("emuxki_stream_new\n")); + + stream = malloc(sizeof(emuxki_stream)); + if (stream == NULL) + return (NULL); + stream->card = card; + stream->use = use; + stream->state = !EMU_STATE_STARTED; + stream->stereo = EMU_STEREO_NOTSET; + stream->b16 = 0; + stream->sample_rate = 0; + stream->nmono = 0; + stream->nstereo = 0; + stream->bufframes = bufframes; + stream->bufcount = bufcount; + stream->first_voice = NULL; + stream->inth = NULL; + stream->inthparam = NULL; + + stream->frames_count = 0; + stream->real_time = 0; + stream->update_needed = false; + + /* Init voices list */ + LIST_INIT(&(stream->voices)); + + status = lock(); + LIST_INSERT_HEAD((&card->streams), stream, next); + unlock(status); + + return stream; +} + +void +emuxki_stream_delete(emuxki_stream *stream) +{ + emuxki_voice *voice; + cpu_status status; + LOG(("emuxki_stream_delete\n")); + + emuxki_stream_halt(stream); + + status = lock(); + LIST_REMOVE(stream, next); + unlock(status); + + while(!LIST_EMPTY(&stream->voices)) { + voice = LIST_FIRST(&stream->voices); + LIST_REMOVE(voice, next); + if(voice->buffer) + emuxki_mem_free(stream->card, voice->buffer->log_base); + emuxki_voice_delete(voice); + } + + free(stream); +} + +/* Emuxki gprs */ +// 87 values from 0.0dB to -xdB (-0.75dB each) +static uint32 db_table[] = { + 2147483647, 1969835071, 1806882308, 1657409659, 1520301995, 1394536435, 1279174712, + 1173356181, 1076291388, 987256190, 905586345, 830672562, 761955951, 698923858, + 641106035, 588071138, 539423503, 494800198, 453868315, 416322483, 381882595, + 350291714, 321314160, 294733747, 270352173, 247987542, 227473005, 208655513, + 191394681, 175561735, 161038555, 147716791, 135497057, 124288190, 114006566, + 104575479, 95924570, 87989300, 80710468, 74033770, 67909395, 62291654, + 57138635, 52411895, 48076170, 44099114, 40451056, 37104780, 34035322, + 31219781, 28637154, 26268172, 24095162, 22101913, 20273552, 18596442, + 17058068, 15646956, 14352576, 13165272, 12076188, 11077196, 10160845, + 9320299, 8549286, 7842054, 7193328, 6598266, 6052431, 5551749, + 5092486, 4671215, 4284793, 3930337, 3605204, 3306967, 3033401, + 2782465, 2552289, 2341153, 2147483, 1969835, 1806882, 1657409, + 1520301, 1394536, 1279174 +}; + +void +emuxki_gpr_set(emuxki_dev *card, emuxki_gpr *gpr, int32 type, float *values) +{ + uint8 count = gpr->type & EMU_MIX_STEREO ? 2 : 1; + uint8 i; + uint32 index; + + LOG(("emuxki_set_gpr\n")); + + switch(type) { + case EMU_MIX_MUTE: + gpr->mute = (values[0] == 1.0); + if(gpr->mute) { + for(i=0; iconfig, gpr->gpr + i, 0); + break; + } + for(i=0; icurrent[i]; + } + case EMU_MIX_GAIN: + for(i=0; igpr->max_gain || values[i]min_gain) + return; + index = (int32)(values[i] / gpr->granularity); + if(index > sizeof(db_table)/sizeof(db_table[0])) + index = sizeof(db_table)/sizeof(db_table[0]); + else if(index < 0) + index = 0; + LOG(("emuxki_set_gpr gpr: %d \n", gpr->gpr + i)); + LOG(("emuxki_set_gpr values[i]: %g \n", values[i])); + LOG(("emuxki_set_gpr index: %u \n", index)); + if(!gpr->mute) + emuxki_write_gpr(&card->config, gpr->gpr + i, db_table[index]); + gpr->current[i] = index * gpr->granularity; + } + break; + } +} + +void +emuxki_gpr_get(emuxki_dev *card, emuxki_gpr *gpr, int32 type, float *values) +{ + uint8 count = gpr->type & EMU_MIX_STEREO ? 2 : 1; + uint16 i; + + LOG(("emuxki_get_gpr\n")); + + switch(type) { + case EMU_MIX_GAIN: + for(i=0; icurrent[i]; + } + break; + case EMU_MIX_MUTE: + values[0] = (gpr->mute ? 1.0 : 0.0); + break; + } +} + +void +emuxki_gpr_dump(emuxki_dev * card, uint16 count) +{ + uint16 pc; + uint32 value; + + LOG(("emuxki_dump_gprs\n")); + + for(pc = 0; pc < count; pc++) { + value = emuxki_read_gpr(&card->config, pc); + LOG(("dsp_gpr pc=%x, value=%x\n", pc, value)); + } +} + +static emuxki_gpr * +emuxki_gpr_new(emuxki_dev *card, const char *name, emuxki_gpr_type type, uint16 *gpr_num, + float default_value, float default_mute, float min_gain, float max_gain, float granularity) +{ + emuxki_gpr *gpr; + float values[2]; + + LOG(("emuxki_gpr_new\n")); + + gpr = &card->gpr[*gpr_num]; + strncpy(gpr->name, name, 32); + gpr->type = type; + gpr->gpr = *gpr_num; + gpr->default_value = default_value; + gpr->min_gain = min_gain; + gpr->max_gain = max_gain; + gpr->granularity = granularity; + gpr->mute = false; + (*gpr_num)++; + if(gpr->type & EMU_MIX_STEREO) + (*gpr_num)++; + + if(default_mute == 1.0) { + values[0] = default_mute; + emuxki_gpr_set(card, gpr, EMU_MIX_MUTE, values); + } + + values[0] = gpr->default_value; + if(gpr->type & EMU_MIX_STEREO) + values[1] = gpr->default_value; + emuxki_gpr_set(card, gpr, EMU_MIX_GAIN, values); + + + return gpr; +} + +/* Emuxki parameter */ + +void +emuxki_parameter_set(emuxki_dev *card, const void* cookie, int32 type, int32 *value) +{ + emuxki_stream *stream; + emuxki_voice *voice; + LOG(("emuxki_parameter_set\n")); + + switch(type) { + case EMU_DIGITAL_MODE: + card->digital_enabled = *value == 1; + if(IS_AUDIGY(&card->config)) + if(IS_AUDIGY2(&card->config)) { + // this disables analog, not enough + emuxki_reg_write_32(&card->config, EMU_A_IOCFG, + (card->digital_enabled ? 0 : EMU_A_IOCFG_GPOUT0) | + (emuxki_reg_read_32(&card->config, EMU_A_IOCFG) + & ~(EMU_A_IOCFG_GPOUT0 | EMU_A_IOCFG_GPOUT1) ) ); + } else { + // this disables analog, and enables digital + emuxki_reg_write_32(&card->config, EMU_A_IOCFG, + (card->digital_enabled ? EMU_A_IOCFG_GPOUT0 | EMU_A_IOCFG_GPOUT1 : 0) | + (emuxki_reg_read_32(&card->config, EMU_A_IOCFG) + & ~(EMU_A_IOCFG_GPOUT0 | EMU_A_IOCFG_GPOUT1) ) ); + } + else { + // this enables digital, not enough + emuxki_reg_write_32(&card->config, EMU_HCFG, + (card->digital_enabled ? EMU_HCFG_GPOUTPUT0 : 0) | + (emuxki_reg_read_32(&card->config, EMU_HCFG) & ~EMU_HCFG_GPOUTPUT0)); + } + + break; + case EMU_AUDIO_MODE: + if(*value!=0 && *value!=1 && *value!=2) { + PRINT(("emuxki_parameter_set error value unexpected\n")); + return; + } + card->play_mode = (*value + 1) * 2; + LIST_FOREACH(stream, &card->streams, next) { + if ((stream->use & EMU_USE_PLAY) == 0 || + (stream->state & EMU_STATE_STARTED) == 0) + continue; + LIST_FOREACH(voice, &stream->voices, next) { + emuxki_voice_fxupdate(voice); + emuxki_channel_commit_fx(voice->dataloc.chan[0]); + if (voice->stereo) + emuxki_channel_commit_fx(voice->dataloc.chan[1]); + } + } + break; + } +} + +void +emuxki_parameter_get(emuxki_dev *card, const void* cookie, int32 type, int32 *value) +{ + LOG(("emuxki_parameter_get\n")); + + switch(type) { + case EMU_DIGITAL_MODE: + *value = card->digital_enabled ? 1 : 0; + break; + case EMU_AUDIO_MODE: + *value = card->play_mode / 2 - 1; + break; + } +} + +/* Emuxki interrupt */ + +int32 emuxki_int(void *arg) +{ + emuxki_dev *card = arg; + uint32 ipr, curblk; + bool gotone = false; + emuxki_voice *voice; + emuxki_stream *stream; + + while ((ipr = emuxki_reg_read_32(&card->config, EMU_IPR))) { + gotone = true; + if (ipr & EMU_IPR_INTERVALTIMER) { + TRACE(("EMU_IPR_INTERVALTIMER\n")); + LIST_FOREACH(stream, &card->streams, next) { + if ((stream->use & EMU_USE_PLAY) == 0 || + (stream->state & EMU_STATE_STARTED) == 0 || + (stream->inth == NULL)) + continue; + + voice = stream->first_voice; + TRACE(("voice %p\n", voice)); + curblk = emuxki_voice_curaddr(voice) / + voice->blksize; + //TRACE(("EMU_IPR_INTERVALTIMER at trigblk %lu\n", curblk)); + //TRACE(("EMU_IPR_INTERVALTIMER at voice->trigblk %lu\n", voice->trigblk)); + if (curblk == voice->trigblk) { + //TRACE(("EMU_IPR_INTERVALTIMER at trigblk %lu\n", curblk)); + //dump_voice(voice); + //trace_hardware_regs(&card->config); + TRACE(("voice pointer %p\n", voice)); + + if(stream->inth) + stream->inth(stream->inthparam); + + voice->trigblk++; + voice->trigblk %= voice->blkmod; + } + } + } + if (ipr & (/*EMU_IPR_MIDITRANSBUFE | */EMU_IPR_MIDIRECVBUFE)) { + midi_interrupt(card); + } + if (ipr & (EMU_IPR_MIDITRANSBUFE)) { + if(!midi_interrupt(card)) { + emuxki_inte_disable(&card->config, EMU_INTE_MIDITXENABLE); + TRACE(("EMU_INTE_MIDITXENABLE disabled\n")); + } + } + if (ipr & (EMU_IPR_ADCBUFHALFFULL | EMU_IPR_ADCBUFFULL + | EMU_IPR_MICBUFHALFFULL | EMU_IPR_MICBUFFULL + | EMU_IPR_EFXBUFHALFFULL | EMU_IPR_EFXBUFFULL)) { + //TRACE(("EMU_IPR_ADCBUF\n")); + LIST_FOREACH(stream, &card->streams, next) { + if ((stream->use & EMU_USE_RECORD) == 0 || + (stream->state & EMU_STATE_STARTED) == 0 || + (stream->inth == NULL)) + continue; + voice = stream->first_voice; + curblk = emuxki_voice_curaddr(voice) / + voice->blksize; + //TRACE(("EMU_IPR_ADCBUF at trigblk %lu\n", curblk)); + //TRACE(("EMU_IPR_ADCBUF at voice->trigblk %lu\n", voice->trigblk)); + if (curblk == voice->trigblk) { + //TRACE(("EMU_IPR_ADCBUF at trigblk %lu\n", curblk)); + //dump_voice(voice); + //trace_hardware_regs(&card->config); + + if(stream->inth) + stream->inth(stream->inthparam); + + voice->trigblk++; + voice->trigblk %= voice->blkmod; + } + } + } + + /*if (ipr & (EMU_IPR_CHANNELLOOP)) { + TRACE(("EMU_IPR_CHANNELLOOP pending channel : %u\n", ipr & EMU_IPR_CHNOMASK)); + LIST_FOREACH(stream, &card->streams, next) + LIST_FOREACH(voice, &stream->voices, next) { + if ((voice->use & EMU_USE_PLAY) == 0 || + (voice->state & EMU_STATE_STARTED) == 0) + continue; + TRACE(("EMU_IPR_CHANNELLOOP at trigblk %lu\n", emuxki_voice_curaddr(voice))); + TRACE(("EMU_IPR_CHANNELLOOP read %x\n", emuxki_chan_read(&voice->card->config, 0, EMU_CLIPL))); + emuxki_chan_write(&voice->card->config, 0, EMU_CLIPL, emuxki_chan_read(&voice->card->config, 0, EMU_CLIPL)); + } + }*/ + + if (ipr & ~(EMU_IPR_RATETRCHANGE | EMU_IPR_INTERVALTIMER + | EMU_IPR_MIDITRANSBUFE | EMU_IPR_MIDIRECVBUFE + | EMU_IPR_ADCBUFHALFFULL | EMU_IPR_ADCBUFFULL + | EMU_IPR_MICBUFHALFFULL | EMU_IPR_MICBUFFULL + | EMU_IPR_EFXBUFHALFFULL | EMU_IPR_EFXBUFFULL)) + TRACE(("Got interrupt 0x%08x !!!\n", + ipr & ~(EMU_IPR_RATETRCHANGE | + EMU_IPR_INTERVALTIMER))); + + emuxki_reg_write_32(&card->config, EMU_IPR, ipr); + } + + if(gotone) + return B_HANDLED_INTERRUPT; + + TRACE(("Got unhandled interrupt\n")); + return B_UNHANDLED_INTERRUPT; +} + + + +/* Emu10k1 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(("emuxki: 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 == CREATIVELABS_VENDOR_ID && + (info.device_id == CREATIVELABS_SBLIVE_DEVICE_ID +#if AUDIGY + || info.device_id == CREATIVELABS_AUDIGY_DEVICE_ID +#endif + )) { + err = B_OK; + } + ix++; + } + + put_module(pci_name); + + return err; +} + +static void +make_device_names( + emuxki_dev * card) +{ + /*char * name = card->name; + sprintf(name, "emuxki/%ld", card-cards+1);*/ + + sprintf(card->midi.name, "midi/emuxki/%ld", card-cards+1); + names[num_names++] = card->midi.name; + + sprintf(card->name, "audio/multi/emuxki/%ld", card-cards+1); + names[num_names++] = card->name; + + names[num_names] = NULL; +} + + +static status_t +emuxki_setup(emuxki_dev * card) +{ + status_t err = B_OK; + unsigned char cmd; + + PRINT(("setup_emuxki(%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 == CREATIVELABS_AUDIGY_DEVICE_ID) + card->config.type |= TYPE_AUDIGY; + if(IS_AUDIGY(&card->config) && card->info.revision == 4) + card->config.type |= TYPE_AUDIGY2; + + 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)); + + dump_hardware_regs(&card->config); + + emuxki_reg_write_32(&card->config, EMU_HCFG, EMU_HCFG_LOCKSOUNDCACHE | EMU_HCFG_LOCKTANKCACHE_MASK| + EMU_HCFG_MUTEBUTTONENABLE); + emuxki_reg_write_32(&card->config, EMU_INTE, EMU_INTE_SAMPLERATER | EMU_INTE_PCIERRENABLE); + + dump_hardware_regs(&card->config); + + if ((err = (*mpu401->create_device)(card->config.nabmbar + IS_AUDIGY(&card->config) ? EMU_A_MUDATA1 : EMU_MUDATA, + &card->midi.driver, 0, midi_interrupt_op, &card->midi)) < B_OK) + return (err); + + card->midi.card = card; + + /* reset the codec */ + PRINT(("codec reset\n")); + emuxki_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())); + + if(IS_AUDIGY2(&card->config)) { + emuxki_reg_write_32(&card->config, EMU_HCFG, EMU_HCFG_AUDIOENABLE | + EMU_HCFG_AC3ENABLE_CDSPDIF | EMU_HCFG_AC3ENABLE_GPSPDIF| + EMU_HCFG_JOYENABLE | EMU_HCFG_AUTOMUTE); + } else { + emuxki_reg_write_32(&card->config, EMU_HCFG, EMU_HCFG_AUDIOENABLE | + EMU_HCFG_LOCKTANKCACHE_MASK | EMU_HCFG_JOYENABLE | EMU_HCFG_AUTOMUTE); + } + emuxki_inte_enable(&card->config, EMU_INTE_VOLINCRENABLE | EMU_INTE_VOLDECRENABLE + | EMU_INTE_MUTEENABLE | EMU_INTE_FXDSPENABLE); + + if(IS_AUDIGY2(&card->config)) { + emuxki_reg_write_32(&card->config, EMU_A_IOCFG, + EMU_A_IOCFG_GPOUT0 | emuxki_reg_read_32(&card->config, EMU_A_IOCFG)); + } + + dump_hardware_regs(&card->config); + + PRINT(("installing interrupt : %x\n", card->config.irq)); + install_io_interrupt_handler(card->config.irq, emuxki_int, card, 0); + + /*PRINT(("codec master output = %#04x\n",emuxki_codec_read(&card->config, 0x02))); + PRINT(("codec aux output = %#04x\n",emuxki_codec_read(&card->config, 0x04))); + PRINT(("codec mono output = %#04x\n",emuxki_codec_read(&card->config, 0x06))); + PRINT(("codec pcm output = %#04x\n",emuxki_codec_read(&card->config, 0x18))); + PRINT(("codec line in = %#04x\n",emuxki_codec_read(&card->config, 0x10))); + PRINT(("codec record line in= %#04x\n",emuxki_codec_read(&card->config, 0x1a))); + PRINT(("codec record gain = %#04x\n",emuxki_codec_read(&card->config, 0x1c)));*/ + + /*PRINT(("adc index = %#08x\n",emuxki_chan_read(&card->config, EMU_ADCIDX, 0))); + PRINT(("micro index = %#08x\n",emuxki_chan_read(&card->config, EMU_MICIDX, 0))); + PRINT(("fx index = %#08x\n",emuxki_chan_read(&card->config, EMU_FXIDX, 0))); + PRINT(("adc addr = %#08x\n",emuxki_chan_read(&card->config, EMU_ADCBA, 0))); + PRINT(("micro addr = %#08x\n",emuxki_chan_read(&card->config, EMU_MICBA, 0))); + PRINT(("fx addr = %#08x\n",emuxki_chan_read(&card->config, EMU_FXBA, 0))); + PRINT(("adc size = %#08x\n",emuxki_chan_read(&card->config, EMU_ADCBS, 0))); + PRINT(("micro size = %#08x\n",emuxki_chan_read(&card->config, EMU_MICBS, 0))); + PRINT(("fx size = %#08x\n",emuxki_chan_read(&card->config, EMU_FXBS, 0))); + + PRINT(("EMU_ADCCR = %#08x\n",emuxki_chan_read(&card->config, EMU_ADCCR, 0))); + PRINT(("EMU_FXWC = %#08x\n",emuxki_chan_read(&card->config, EMU_FXWC, 0))); + PRINT(("EMU_FXWC = %#08x\n",emuxki_reg_read_32(&card->config, EMU_FXWC)));*/ + + PRINT(("writing codec registers\n")); + // TODO : to move with AC97 + /* enable master output */ + emuxki_codec_write(&card->config, AC97_MASTER_VOLUME, 0x0000); + /* enable aux output */ + emuxki_codec_write(&card->config, AC97_AUX_OUT_VOLUME, 0x0000); + /* enable mono output */ + //emuxki_codec_write(&card->config, AC97_MONO_VOLUME, 0x0004); + /* enable pcm output */ + emuxki_codec_write(&card->config, AC97_PCM_OUT_VOLUME, 0x0808); + /* enable line in */ + //emuxki_codec_write(&card->config, AC97_LINE_IN_VOLUME, 0x8808); + /* set record line in */ + emuxki_codec_write(&card->config, AC97_RECORD_SELECT, 0x0404); + /* set record gain */ + //emuxki_codec_write(&card->config, AC97_RECORD_GAIN, 0x0000); + + PRINT(("codec master output = %#04x\n",emuxki_codec_read(&card->config, AC97_MASTER_VOLUME))); + PRINT(("codec aux output = %#04x\n",emuxki_codec_read(&card->config, AC97_AUX_OUT_VOLUME))); + PRINT(("codec mono output = %#04x\n",emuxki_codec_read(&card->config, AC97_MONO_VOLUME))); + PRINT(("codec pcm output = %#04x\n",emuxki_codec_read(&card->config, AC97_PCM_OUT_VOLUME))); + PRINT(("codec line in = %#04x\n",emuxki_codec_read(&card->config, AC97_LINE_IN_VOLUME))); + PRINT(("codec record line in= %#04x\n",emuxki_codec_read(&card->config, AC97_RECORD_SELECT))); + PRINT(("codec record gain = %#04x\n",emuxki_codec_read(&card->config, AC97_RECORD_GAIN))); + + if (emuxki_codec_read(&card->config, AC97_EXTENDED_AUDIO_ID) & 0x0080) { + card->config.type |= TYPE_LIVE_5_1; + emuxki_chan_write(&card->config, 0, EMU_AC97SLOT, EMU_AC97SLOT_CENTER | EMU_AC97SLOT_LFE); + emuxki_codec_write(&card->config, AC97_SURROUND_VOLUME, 0x0000); + } + + if ((err = emuxki_init(card))) + return (err); + + if(IS_AUDIGY(&card->config) || IS_LIVE_5_1(&card->config)) { + card->play_mode = 6; // mode 5.1 + } else { + card->play_mode = 4; // mode 4.0 + } + + PRINT(("init_driver done\n")); + + return err; +} + +void +emuxki_dump_fx(emuxki_dev * card) +{ + uint16 pc = 0; + uint8 op; + uint16 r,a,x,y,zero; + + LOG(("emuxki_dump_fx\n")); + + zero = IS_AUDIGY(&card->config) ? EMU_A_DSP_CST(0) : EMU_DSP_CST(0); + + while (pc < 512) { + emuxki_dsp_getop(&card->config, &pc, &op, &r, &a, &x, &y); + if(op!=EMU_DSP_OP_ACC3 || r!=zero || a!=zero || x!=zero || y!=zero) { + LOG(("dsp_op pc=%u, op=%x, r=%x, a=%x, x=%x, y=%x\n", + pc, op, r, a, x, y)); + } + } +} + +static void +emuxki_initfx(emuxki_dev * card) +{ + uint16 pc, gpr; + emuxki_gpr *a_front_gpr, *a_rear_gpr, *a_center_sub_gpr = NULL; + emuxki_gpr *p_ac97_in_gpr, *p_cd_in_gpr, *r_ac97_in_gpr, *r_cd_in_gpr; + emuxki_gpr *d_front_gpr, *d_rear_gpr, *d_center_sub_gpr; + + /* Set all GPRs to 0 */ + for (pc = 0; pc < 256; pc++) { + emuxki_write_gpr(&card->config, pc, 0); + card->gpr[pc].gpr = -1; + } + + for (pc = 0; pc < 160; pc++) { + emuxki_chan_write(&card->config, 0, EMU_TANKMEMDATAREGBASE + pc, 0); + emuxki_chan_write(&card->config, 0, EMU_TANKMEMADDRREGBASE + pc, 0); + } + pc = 0; + gpr = EMU_GPR_FIRST_MIX; // we reserve 16 gprs for processing +#define EMU_DSP_TMPGPR_FRONT_LEFT 0 +#define EMU_DSP_TMPGPR_FRONT_RIGHT 1 +#define EMU_DSP_TMPGPR_REAR_LEFT 2 +#define EMU_DSP_TMPGPR_REAR_RIGHT 3 +#define EMU_DSP_TMPGPR_CENTER 4 +#define EMU_DSP_TMPGPR_SUB 5 +#define EMU_DSP_TMPGPR_DSP_IN_L 6 +#define EMU_DSP_TMPGPR_DSP_IN_R 7 + + a_front_gpr = emuxki_gpr_new(card, "Analog Front", + EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75); + a_rear_gpr = emuxki_gpr_new(card, "Analog Rear", + EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75); + if(IS_AUDIGY(&card->config) || IS_LIVE_5_1(&card->config)) + a_center_sub_gpr = emuxki_gpr_new(card, "Analog Center/Sub", + EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75); + + d_front_gpr = emuxki_gpr_new(card, "Digital Front", + EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75); + d_rear_gpr = emuxki_gpr_new(card, "Digital Rear", + EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75); + d_center_sub_gpr = emuxki_gpr_new(card, "Digital Center/Sub", + EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75); + + /* playback in gprs */ + p_ac97_in_gpr = emuxki_gpr_new(card, "AC97 Record In", + EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 1.0, -46.5, 0.0, -0.75); + p_cd_in_gpr = emuxki_gpr_new(card, "CD Spdif In", + EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 1.0, -46.5, 0.0, -0.75); + + /* record in gprs */ + r_ac97_in_gpr = emuxki_gpr_new(card, "AC97 Record In", + EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_RECORD, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75); + r_cd_in_gpr = emuxki_gpr_new(card, "CD Spdif In", + EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_RECORD, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75); + + card->gpr_count = gpr; + + if(IS_AUDIGY(&card->config)) { + /* DSP_IN_GPR(l/r) = 0 + AC97In(l/r) * P_AC97_IN_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_A_DSP_CST(0), + EMU_A_DSP_INL(EMU_DSP_IN_AC97), EMU_A_DSP_GPR(p_ac97_in_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_A_DSP_CST(0), + EMU_A_DSP_INR(EMU_DSP_IN_AC97), EMU_A_DSP_GPR(p_ac97_in_gpr->gpr + 1)); + + /* DSP_IN_GPR(l/r) = DSP_IN_GPR(l/r) + CDIn(l/r) * P_CD_IN_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_A_DSP_INL(EMU_DSP_IN_CDSPDIF), EMU_A_DSP_GPR(p_cd_in_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_A_DSP_INR(EMU_DSP_IN_CDSPDIF), EMU_A_DSP_GPR(p_cd_in_gpr->gpr + 1)); + + /* Front GPR(l/r) = DSP_IN_GPR(l/r) + FX(0/1) + 0 */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_FRONT_LEFT), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_DSP_FX(0), EMU_A_DSP_CST(0)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_FRONT_RIGHT), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_DSP_FX(1), EMU_A_DSP_CST(0)); + + /* Rear GPR(l/r) = DSP_IN_GPR(l/r) + FX(2/3) + 0 */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_REAR_LEFT), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_DSP_FX(2), EMU_A_DSP_CST(0)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_REAR_RIGHT), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_DSP_FX(3), EMU_A_DSP_CST(0)); + + /* Center/Sub GPR = 0 + FX(4/5) + 0 */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_CENTER), + EMU_A_DSP_CST(0), + EMU_DSP_FX(4), EMU_A_DSP_CST(0)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_SUB), + EMU_A_DSP_CST(0), + EMU_DSP_FX(5), EMU_A_DSP_CST(0)); + + /* Analog Front Output l/r = 0 + Front GPR(l/r) * A_FRONT_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTL(EMU_A_DSP_OUT_A_FRONT), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_FRONT_LEFT), EMU_A_DSP_GPR(a_front_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTR(EMU_A_DSP_OUT_A_FRONT), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_FRONT_RIGHT), EMU_A_DSP_GPR(a_front_gpr->gpr+1)); + + /* Analog Rear Output l/r = 0 + Rear GPR(l/r) * A_REAR_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTL(EMU_A_DSP_OUT_A_REAR), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_REAR_LEFT), EMU_A_DSP_GPR(a_rear_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTR(EMU_A_DSP_OUT_A_REAR), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_REAR_RIGHT), EMU_A_DSP_GPR(a_rear_gpr->gpr+1)); + + /* Analog Center/Sub = 0 + Center/Sub GPR(l/r) * A_CENTER_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTL(EMU_A_DSP_OUT_A_CENTER), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_CENTER), EMU_A_DSP_GPR(a_center_sub_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTR(EMU_A_DSP_OUT_A_CENTER), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_SUB), EMU_A_DSP_GPR(a_center_sub_gpr->gpr+1)); + + /* Digital Front Output l/r = 0 + Front GPR(l/r) * D_FRONT_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTL(EMU_A_DSP_OUT_D_FRONT), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_FRONT_LEFT), EMU_A_DSP_GPR(d_front_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTR(EMU_A_DSP_OUT_D_FRONT), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_FRONT_RIGHT), EMU_A_DSP_GPR(d_front_gpr->gpr+1)); + + /* Digital Rear Output l/r = 0 + Rear GPR(l/r) * D_REAR_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTL(EMU_A_DSP_OUT_D_REAR), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_REAR_LEFT), EMU_A_DSP_GPR(d_rear_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTR(EMU_A_DSP_OUT_D_REAR), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_REAR_RIGHT), EMU_A_DSP_GPR(d_rear_gpr->gpr+1)); + + /* Digital Center/Sub = 0 + Center/Sub GPR(l/r) * D_CENTER_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTL(EMU_A_DSP_OUT_D_CENTER), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_CENTER), EMU_A_DSP_GPR(d_center_sub_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTR(EMU_A_DSP_OUT_D_CENTER), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_SUB), EMU_A_DSP_GPR(d_center_sub_gpr->gpr+1)); + + /* DSP_IN_GPR(l/r) = 0 + AC97In(l/r) * R_AC97_IN_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_A_DSP_CST(0), + EMU_A_DSP_INL(EMU_DSP_IN_AC97), EMU_A_DSP_GPR(r_ac97_in_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_A_DSP_CST(0), + EMU_A_DSP_INR(EMU_DSP_IN_AC97), EMU_A_DSP_GPR(r_ac97_in_gpr->gpr + 1)); + + /* DSP_IN_GPR(l/r) = 0 + DSP_IN_GPR(l/r) * 4 */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), EMU_A_DSP_CST(4)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_A_DSP_CST(0), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), EMU_A_DSP_CST(4)); + + /* ADC recording buffer (l/r) = DSP_IN_GPR(l/r) + CDIn(l/r) * R_CD_IN_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTL(EMU_A_DSP_OUT_ADC), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_A_DSP_INL(EMU_DSP_IN_CDSPDIF), EMU_A_DSP_GPR(r_cd_in_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_A_DSP_OUTR(EMU_A_DSP_OUT_ADC), + EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_A_DSP_INR(EMU_DSP_IN_CDSPDIF), EMU_A_DSP_GPR(r_cd_in_gpr->gpr + 1)); + + /* zero out the rest of the microcode */ + while (pc < 512) + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_A_DSP_CST(0), EMU_A_DSP_CST(0), + EMU_A_DSP_CST(0), EMU_A_DSP_CST(0)); + + emuxki_chan_write(&card->config, 0, EMU_A_DBG, 0); /* Is it really necessary ? */ + + } else { + /* DSP_IN_GPR(l/r) = 0 + AC97In(l/r) * P_AC97_IN_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_DSP_CST(0), + EMU_DSP_INL(EMU_DSP_IN_AC97), EMU_DSP_GPR(p_ac97_in_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_DSP_CST(0), + EMU_DSP_INR(EMU_DSP_IN_AC97), EMU_DSP_GPR(p_ac97_in_gpr->gpr + 1)); + + /* DSP_IN_GPR(l/r) = DSP_IN_GPR(l/r) + CDIn(l/r) * P_CD_IN_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_DSP_INL(EMU_DSP_IN_CDSPDIF), EMU_DSP_GPR(p_cd_in_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_DSP_INR(EMU_DSP_IN_CDSPDIF), EMU_DSP_GPR(p_cd_in_gpr->gpr + 1)); + + /* Front GPR(l/r) = DSP_IN_GPR(l/r) + FX(0/1) + 0 */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_DSP_GPR(EMU_DSP_TMPGPR_FRONT_LEFT), + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_DSP_FX(0), EMU_DSP_CST(0)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_DSP_GPR(EMU_DSP_TMPGPR_FRONT_RIGHT), + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_DSP_FX(1), EMU_DSP_CST(0)); + + /* Rear GPR(l/r) = DSP_IN_GPR(l/r) + FX(2/3) + 0 */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_DSP_GPR(EMU_DSP_TMPGPR_REAR_LEFT), + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_DSP_FX(2), EMU_DSP_CST(0)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_DSP_GPR(EMU_DSP_TMPGPR_REAR_RIGHT), + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_DSP_FX(3), EMU_DSP_CST(0)); + + /* Center/Sub GPR = 0 + FX(4/5) + 0 */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_DSP_GPR(EMU_DSP_TMPGPR_CENTER), + EMU_DSP_CST(0), + EMU_DSP_FX(4), EMU_DSP_CST(0)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_DSP_GPR(EMU_DSP_TMPGPR_SUB), + EMU_DSP_CST(0), + EMU_DSP_FX(5), EMU_DSP_CST(0)); + + /* Analog Front Output l/r = 0 + Front GPR(l/r) * A_FRONT_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUTL(EMU_DSP_OUT_A_FRONT), + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_FRONT_LEFT), EMU_DSP_GPR(a_front_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUTR(EMU_DSP_OUT_A_FRONT), + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_FRONT_RIGHT), EMU_DSP_GPR(a_front_gpr->gpr+1)); + + /* Analog Rear Output l/r = 0 + Rear GPR(l/r) * A_REAR_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUTL(EMU_DSP_OUT_AD_REAR), + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_REAR_LEFT), EMU_DSP_GPR(a_rear_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUTR(EMU_DSP_OUT_AD_REAR), + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_REAR_RIGHT), EMU_DSP_GPR(a_rear_gpr->gpr+1)); + + /* Analog Center/Sub = 0 + Center/Sub GPR(l/r) * A_CENTER_GPR(l/r) */ + if(IS_LIVE_5_1(&card->config)) { + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUT_A_CENTER, + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_CENTER), EMU_DSP_GPR(a_center_sub_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUT_A_SUB, + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_SUB), EMU_DSP_GPR(a_center_sub_gpr->gpr+1)); + } + + /* Digital Front Output l/r = 0 + Front GPR(l/r) * D_FRONT_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUTL(EMU_DSP_OUT_D_FRONT), + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_FRONT_LEFT), EMU_DSP_GPR(d_front_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUTR(EMU_DSP_OUT_D_FRONT), + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_FRONT_RIGHT), EMU_DSP_GPR(d_front_gpr->gpr+1)); + + /* Digital Rear Output l/r = 0 + Rear GPR(l/r) * D_REAR_GPR(l/r) */ + /*emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUTL(EMU_DSP_OUT_D_REAR), + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_REAR_LEFT), EMU_DSP_GPR(d_rear_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUTR(EMU_DSP_OUT_D_REAR), + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_REAR_RIGHT), EMU_DSP_GPR(d_rear_gpr->gpr+1));*/ + + /* Digital Center/Sub = 0 + Center/Sub GPR(l/r) * D_CENTER_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUTL(EMU_DSP_OUT_D_CENTER), + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_CENTER), EMU_DSP_GPR(d_center_sub_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUTR(EMU_DSP_OUT_D_CENTER), + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_SUB), EMU_DSP_GPR(d_center_sub_gpr->gpr+1)); + + /* DSP_IN_GPR(l/r) = 0 + AC97In(l/r) * R_AC97_IN_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_DSP_CST(0), + EMU_DSP_INL(EMU_DSP_IN_AC97), EMU_DSP_GPR(r_ac97_in_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_DSP_CST(0), + EMU_DSP_INR(EMU_DSP_IN_AC97), EMU_DSP_GPR(r_ac97_in_gpr->gpr + 1)); + + /* DSP_IN_GPR(l/r) = 0 + DSP_IN_GPR(l/r) * 4 */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS, + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), EMU_DSP_CST(4)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS, + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_DSP_CST(0), + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), EMU_DSP_CST(4)); + + /* ADC recording buffer (l/r) = DSP_IN_GPR(l/r) + CDIn(l/r) * R_CD_IN_GPR(l/r) */ + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUTL(EMU_DSP_OUT_ADC), + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), + EMU_DSP_INL(EMU_DSP_IN_CDSPDIF), EMU_DSP_GPR(r_cd_in_gpr->gpr)); + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS, + EMU_DSP_OUTR(EMU_DSP_OUT_ADC), + EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), + EMU_DSP_INR(EMU_DSP_IN_CDSPDIF), EMU_DSP_GPR(r_cd_in_gpr->gpr + 1)); + + /* zero out the rest of the microcode */ + while (pc < 512) + emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3, + EMU_DSP_CST(0), EMU_DSP_CST(0), + EMU_DSP_CST(0), EMU_DSP_CST(0)); + + emuxki_chan_write(&card->config, 0, EMU_DBG, 0); /* Is it really necessary ? */ + } + + emuxki_dump_fx(card); +} + +status_t +emuxki_init(emuxki_dev * card) +{ + uint16 i; + uint32 spcs, *ptb; + uint32 silentpage; + + /* disable any channel interrupt */ + emuxki_chan_write(&card->config, 0, EMU_CLIEL, 0); + emuxki_chan_write(&card->config, 0, EMU_CLIEH, 0); + emuxki_chan_write(&card->config, 0, EMU_SOLEL, 0); + emuxki_chan_write(&card->config, 0, EMU_SOLEH, 0); + + /* Set recording buffers sizes to zero */ + emuxki_chan_write(&card->config, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE); + emuxki_chan_write(&card->config, 0, EMU_MICBA, 0); + emuxki_chan_write(&card->config, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE); + emuxki_chan_write(&card->config, 0, EMU_FXBA, 0); + emuxki_chan_write(&card->config, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE); + emuxki_chan_write(&card->config, 0, EMU_ADCBA, 0); + + /* Initialize all channels to stopped and no effects */ + for (i = 0; i < EMU_NUMCHAN; i++) { + emuxki_chan_write(&card->config, i, EMU_CHAN_DCYSUSV, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_IP, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_VTFT, 0xffff); + emuxki_chan_write(&card->config, i, EMU_CHAN_CVCF, 0xffff); + emuxki_chan_write(&card->config, i, EMU_CHAN_PTRX, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_CPF, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_CCR, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_PSST, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_DSL, 0x10); /* Why 16 ? */ + emuxki_chan_write(&card->config, i, EMU_CHAN_CCCA, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_Z1, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_Z2, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_FXRT, 0x32100000); + emuxki_chan_write(&card->config, i, EMU_CHAN_ATKHLDM, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_DCYSUSM, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_IFATN, 0xffff); + emuxki_chan_write(&card->config, i, EMU_CHAN_PEFE, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_FMMOD, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_TREMFRQ, 24); + emuxki_chan_write(&card->config, i, EMU_CHAN_FM2FRQ2, 24); + emuxki_chan_write(&card->config, i, EMU_CHAN_TEMPENV, 0); + /*** these are last so OFF prevents writing ***/ + emuxki_chan_write(&card->config, i, EMU_CHAN_LFOVAL2, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_LFOVAL1, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_ATKHLDV, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_ENVVOL, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_ENVVAL, 0); + } + + /* set digital outputs format */ + spcs = (EMU_SPCS_CLKACCY_1000PPM | EMU_SPCS_SAMPLERATE_48 | + EMU_SPCS_CHANNELNUM_LEFT | EMU_SPCS_SOURCENUM_UNSPEC | + EMU_SPCS_GENERATIONSTATUS | 0x00001200 /* Cat code. */ | + 0x00000000 /* IEC-958 Mode */ | EMU_SPCS_EMPHASIS_NONE | + EMU_SPCS_COPYRIGHT); + emuxki_chan_write(&card->config, 0, EMU_SPCS0, spcs); + emuxki_chan_write(&card->config, 0, EMU_SPCS1, spcs); + emuxki_chan_write(&card->config, 0, EMU_SPCS2, spcs); + + if(IS_AUDIGY2(&card->config)) { + emuxki_chan_write(&card->config, 0, EMU_A2_SPDIF_SAMPLERATE, EMU_A2_SPDIF_UNKNOWN); + + emuxki_reg_write_32(&card->config, EMU_A2_PTR, EMU_A2_SRCSEL); + emuxki_reg_write_32(&card->config, EMU_A2_DATA, + EMU_A2_SRCSEL_ENABLE_SPDIF | EMU_A2_SRCSEL_ENABLE_SRCMULTI); + + emuxki_reg_write_32(&card->config, EMU_A2_PTR, EMU_A2_SRCMULTI); + emuxki_reg_write_32(&card->config, EMU_A2_DATA, EMU_A2_SRCMULTI_ENABLE_INPUT); + } + + /* Let's play with sound processor */ + emuxki_initfx(card); + + /* allocate memory for our Page Table */ + card->ptb_area = alloc_mem(&card->ptb_phy_base, &card->ptb_log_base, + EMU_MAXPTE * sizeof(uint32), "emuxki ptb"); + + /* This is necessary unless you like Metallic noise... */ + card->silentpage_area = alloc_mem(&card->silentpage_phy_base, &card->silentpage_log_base, + EMU_PTESIZE, "emuxki sp"); + + if (card->ptb_area < B_OK || card->silentpage_area < B_OK) { + PRINT(("couldn't allocate memory\n")); + if (card->ptb_area > B_OK) + delete_area(card->ptb_area); + if (card->silentpage_area > B_OK) + delete_area(card->silentpage_area); + return B_ERROR; + } + + /* Zero out the silent page */ + /* This might not be always true, it might be 128 for 8bit channels */ + memset(card->ptb_log_base, 0, EMU_PTESIZE); + + /* + * Set all the PTB Entries to the silent page We shift the physical + * address by one and OR it with the page number. I don't know what + * the ORed index is for, might be a very useful unused feature... + */ + silentpage = ((uint32)card->silentpage_phy_base) << 1; + ptb = card->ptb_log_base; + for (i = 0; i < EMU_MAXPTE; i++) + ptb[i] = silentpage | i; + + /* Write PTB address and set TCB to none */ + emuxki_chan_write(&card->config, 0, EMU_PTB, (uint32)card->ptb_phy_base); + emuxki_chan_write(&card->config, 0, EMU_TCBS, 0); /* This means 16K TCB */ + emuxki_chan_write(&card->config, 0, EMU_TCB, 0); /* No TCB use for now */ + + /* + * Set channels MAPs to the silent page. + * I don't know what MAPs are for. + */ + silentpage |= EMU_CHAN_MAP_PTI_MASK; + for (i = 0; i < EMU_NUMCHAN; i++) { + emuxki_chan_write(&card->config, i, EMU_CHAN_MAPA, silentpage); + emuxki_chan_write(&card->config, i, EMU_CHAN_MAPB, silentpage); + card->channel[i] = NULL; + } + + /* Init streams list */ + LIST_INIT(&(card->streams)); + + /* Timer is stopped */ + card->timerstate &= ~EMU_TIMER_STATE_ENABLED; + card->timerate = 0xffff; + + return B_OK; +} + + + +status_t +init_driver(void) +{ + int ix=0; + + pci_info info; + num_cards = 0; + + PRINT(("init_driver()\n")); + load_driver_symbols("emuxki"); + + if (get_module(pci_name, (module_info **) &pci)) + return ENOSYS; + + if (get_module(mpu401_name, (module_info **) &mpu401)) { + put_module(pci_name); + return ENOSYS; + } + + while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) { + if (info.vendor_id == CREATIVELABS_VENDOR_ID && + (info.device_id == CREATIVELABS_SBLIVE_DEVICE_ID +#if AUDIGY + || info.device_id == CREATIVELABS_AUDIGY_DEVICE_ID +#endif + )) { + if (num_cards == NUM_CARDS) { + PRINT(("Too many emuxki cards installed!\n")); + break; + } + memset(&cards[num_cards], 0, sizeof(emuxki_dev)); + cards[num_cards].info = info; + if (emuxki_setup(&cards[num_cards])) { + PRINT(("Setup of emuxki %ld failed\n", num_cards+1)); + } + else { + num_cards++; + } + } + ix++; + } + if (!num_cards) { + PRINT(("no cards\n")); + put_module(mpu401_name); + put_module(pci_name); + PRINT(("emuxki: no suitable cards found\n")); + return ENODEV; + } + + +#if DEBUG + //add_debugger_command("emuxki", emuxki_debug, "emuxki [card# (1-n)]"); +#endif + return B_OK; +} + + +static void +emuxki_shutdown(emuxki_dev *card) +{ + uint32 i; + + PRINT(("shutdown(%p)\n", card)); + remove_io_interrupt_handler(card->config.irq, emuxki_int, card); + + emuxki_reg_write_32(&card->config, EMU_HCFG, EMU_HCFG_LOCKSOUNDCACHE | + EMU_HCFG_LOCKTANKCACHE_MASK | EMU_HCFG_MUTEBUTTONENABLE); + emuxki_reg_write_32(&card->config, EMU_INTE, 0); + + dump_hardware_regs(&card->config); + + /* Disable any Channels interrupts */ + emuxki_chan_write(&card->config, 0, EMU_CLIEL, 0); + emuxki_chan_write(&card->config, 0, EMU_CLIEH, 0); + emuxki_chan_write(&card->config, 0, EMU_SOLEL, 0); + emuxki_chan_write(&card->config, 0, EMU_SOLEH, 0); + + /* Stop all channels */ + /* This shouldn't be necessary, i'll remove once everything works */ + for (i = 0; i < EMU_NUMCHAN; i++) + emuxki_chan_write(&card->config, i, EMU_CHAN_DCYSUSV, 0); + for (i = 0; i < EMU_NUMCHAN; i++) { + emuxki_chan_write(&card->config, i, EMU_CHAN_VTFT, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_CVCF, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_PTRX, 0); + emuxki_chan_write(&card->config, i, EMU_CHAN_CPF, 0); + } + + /* + * deallocate Emu10k1 caches and recording buffers Again it will be + * removed because it will be done in voice shutdown. + */ + emuxki_chan_write(&card->config, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE); + emuxki_chan_write(&card->config, 0, EMU_MICBA, 0); + emuxki_chan_write(&card->config, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE); + emuxki_chan_write(&card->config, 0, EMU_FXBA, 0); + if(IS_AUDIGY(&card->config)) { + emuxki_chan_write(&card->config, 0, EMU_A_FXWC1, 0); + emuxki_chan_write(&card->config, 0, EMU_A_FXWC2, 0); + } else { + emuxki_chan_write(&card->config, 0, EMU_FXWC, 0); + } + emuxki_chan_write(&card->config, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE); + emuxki_chan_write(&card->config, 0, EMU_ADCBA, 0); + + /* + * I don't know yet how i will handle tank cache buffer, + * I don't even clearly know what it is for + */ + emuxki_chan_write(&card->config, 0, EMU_TCB, 0); /* 16K again */ + emuxki_chan_write(&card->config, 0, EMU_TCBS, 0); + + emuxki_chan_write(&card->config, 0, EMU_DBG, 0x8000); /* necessary ? */ + + PRINT(("freeing ptb_area\n")); + if (card->ptb_area > B_OK) + delete_area(card->ptb_area); + PRINT(("freeing silentpage_area\n")); + if (card->silentpage_area > B_OK) + delete_area(card->silentpage_area); +} + + +void +uninit_driver(void) +{ + int ix, cnt = num_cards; + num_cards = 0; + + PRINT(("uninit_driver()\n")); + //remove_debugger_command("emuxki", emuxki_debug); + + for (ix=0; ix +#include +#include +#include "emuxkireg.h" +#include "config.h" +#include "queue.h" +#include "multi_audio.h" +#include "multi.h" + + +#define CREATIVELABS_VENDOR_ID 0x1102 /* Creative Labs */ +#define CREATIVELABS_SBLIVE_DEVICE_ID 0x0002 /* Creative Labs SoundBlaster Live */ +#define CREATIVELABS_AUDIGY_DEVICE_ID 0x0004 /* Creative Labs Audigy and Audigy 2 */ + +#define AUDIGY 1 // Audigy is not tested for now, but Audigy 2 is + +#define VERSION "Version alpha 4, Copyright (c) 2002 Jérôme Duval, compiled on " ## __DATE__ ## " " ## __TIME__ +#define DRIVER_NAME "emuxki" +#define FRIENDLY_NAME "Emuxki" +#define FRIENDLY_NAME_LIVE FRIENDLY_NAME" SB Live" +#define FRIENDLY_NAME_LIVE_5_1 FRIENDLY_NAME_LIVE" 5.1" +#define FRIENDLY_NAME_AUDIGY FRIENDLY_NAME" Audigy" +#define FRIENDLY_NAME_AUDIGY2 FRIENDLY_NAME_AUDIGY" 2" +#define AUTHOR "Jérôme Duval" + +/* + * Emu10k1 midi + */ + +typedef struct _midi_dev { + struct _emuxki_dev * + card; + void * driver; + void * cookie; + int32 count; + char name[64]; +} midi_dev; + + +/* + * Emu10k1 hardware limits + */ + +#define EMU_PTESIZE 4096 +#define EMU_MAXPTE ((EMU_CHAN_PSST_LOOPSTARTADDR_MASK + 1) / \ + EMU_PTESIZE) +#define EMU_NUMCHAN 64 +#define EMU_NUMRECSRCS 3 + +#define EMU_DMA_ALIGN 4096 +#define EMU_DMAMEM_NSEG 1 + +/* + * Emu10k1 memory managment + */ + +typedef struct _emuxki_mem { + LIST_ENTRY(_emuxki_mem) next; + uint16 ptbidx; + void *log_base; + void *phy_base; + area_id area; + size_t size; +#define EMU_RMEM 0xffff /* recording memory */ +} emuxki_mem; + +/* + * Emu10k1 play channel params + */ + +typedef struct _emuxki_chanparms_fxsend { + struct { + uint8 level, dest; + } a, b, c, d, e, f, g, h; +} emuxki_chanparms_fxsend; + +typedef struct _emuxki_chanparms_pitch { + uint16 intial; /* 4 bits of octave, 12 bits of fractional + * octave */ + uint16 current;/* 0x4000 == unity pitch shift */ + uint16 target; /* 0x4000 == unity pitch shift */ + uint8 envelope_amount; /* Signed 2's complement, +/- + * one octave peak extremes */ +} emuxki_chanparms_pitch; + +typedef struct _emuxki_chanparms_envelope { + uint16 current_state; /* 0x8000-n == 666*n usec delay */ + uint8 hold_time; /* 127-n == n*(volume ? 88.2 : + * 42)msec */ + uint8 attack_time; /* 0 = infinite, 1 = (volume ? 11 : + * 10.9) msec, 0x7f = 5.5msec */ + uint8 sustain_level; /* 127 = full, 0 = off, 0.75dB + * increments */ + uint8 decay_time; /* 0 = 43.7msec, 1 = 21.8msec, 0x7f = + * 22msec */ +} emuxki_chanparms_envelope; + +typedef struct _emuxki_chanparms_volume { + uint16 current; + uint16 target; + emuxki_chanparms_envelope envelope; +} emuxki_chanparms_volume; + +typedef struct _emuxki_chanparms_filter { + uint16 initial_cutoff_frequency; + /* + * 6 most significant bits are semitones, 2 least significant bits + * are fractions + */ + uint16 current_cutoff_frequency; + uint16 target_cutoff_frequency; + uint8 lowpass_resonance_height; + uint8 interpolation_ROM; /* 1 = full band, 7 = low + * pass */ + uint8 envelope_amount; /* Signed 2's complement, +/- + * six octaves peak extremes */ + uint8 LFO_modulation_depth; /* Signed 2's complement, +/- + * three octave extremes */ +} emuxki_chanparms_filter; + +typedef struct _emuxki_chanparms_loop { + uint32 start; /* index in the PTB (in samples) */ + uint32 end; /* index in the PTB (in samples) */ +} emuxki_chanparms_loop; + +typedef struct _emuxki_chanparms_modulation { + emuxki_chanparms_envelope envelope; + uint16 LFO_state; /* 0x8000-n = 666*n usec delay */ +} emuxki_chanparms_modulation; + +typedef struct _emuxki_chanparms_vibrato_LFO { + uint16 state; /* 0x8000-n == 666*n usec delay */ + uint8 modulation_depth; /* Signed 2's complement, +/- + * one octave extremes */ + uint8 vibrato_depth; /* Signed 2's complement, +/- one + * octave extremes */ + uint8 frequency; /* 0.039Hz steps, maximum of 9.85 Hz */ +} emuxki_chanparms_vibrato_LFO; + +typedef struct _emuxki_channel { + uint8 num; /* voice number */ + struct _emuxki_voice *voice; + emuxki_chanparms_fxsend fxsend; + emuxki_chanparms_pitch pitch; + uint16 initial_attenuation; /* 0.375dB steps */ + emuxki_chanparms_volume volume; + emuxki_chanparms_filter filter; + emuxki_chanparms_loop loop; + emuxki_chanparms_modulation modulation; + emuxki_chanparms_vibrato_LFO vibrato_LFO; + uint8 tremolo_depth; +} emuxki_channel; + +typedef struct _emuxki_recparams { + uint32 efx_voices[2]; +} emuxki_recparams; + + +/* + * Voices + */ + +typedef enum { + EMU_RECSRC_MIC = 0, + EMU_RECSRC_ADC, + EMU_RECSRC_FX, + EMU_RECSRC_NOTSET +} emuxki_recsrc_t; + +#define EMU_USE_PLAY (1 << 0) +#define EMU_USE_RECORD (1 << 1) +#define EMU_STATE_STARTED (1 << 0) +#define EMU_STEREO_NOTSET 0xFF + +typedef struct _emuxki_voice { + struct _emuxki_stream *stream; + uint8 use; + uint8 voicenum; + uint8 state; + uint8 stereo; + uint8 b16; + uint32 sample_rate; + union { + emuxki_channel *chan[2]; + emuxki_recsrc_t source; + } dataloc; + emuxki_recparams recparams; + emuxki_mem *buffer; + uint16 blksize;/* in samples */ + uint16 trigblk;/* blk on which to trigger inth */ + uint16 blkmod; /* Modulo value to wrap trigblk */ + uint16 timerate; + + LIST_ENTRY(_emuxki_voice) next; + +} emuxki_voice; + +/* + * Streams + */ + +typedef struct _emuxki_stream { + struct _emuxki_dev *card; + uint8 use; + uint8 state; + uint8 stereo; + uint8 b16; + uint32 sample_rate; + uint8 nmono; + uint8 nstereo; + uint32 bufframes; + uint8 bufcount; + LIST_HEAD(, _emuxki_voice) voices; + emuxki_voice *first_voice; + + LIST_ENTRY(_emuxki_stream) next; + + void (*inth) (void *); + void *inthparam; + + /* 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; +} emuxki_stream; + +/* + * Mixer controls gpr + */ +#define EMU_GPR_FIRST_MIX 16 + +typedef enum { + EMU_MIX_GAIN = 1 << 0, + EMU_MIX_MONO = 1 << 1, + EMU_MIX_STEREO = 1 << 2, + EMU_MIX_TMP = 1 << 3, + EMU_MIX_MUTE = 1 << 4, + EMU_MIX_PLAYBACK = 1 << 5, + EMU_MIX_RECORD = 1 << 6 +} emuxki_gpr_type; + +typedef struct _emuxki_gpr { + char name[32]; + emuxki_gpr_type type; + uint16 gpr; + float default_value; + float min_gain; + float max_gain; + float granularity; + float current[2]; + bool mute; +} emuxki_gpr; + +typedef enum { + EMU_DIGITAL_MODE = 1 << 0, + EMU_AUDIO_MODE = 1 << 1 +} emuxki_parameter_type; + +/* + * Devices + */ + +typedef struct _emuxki_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; + void *silentpage_log_base; + void *silentpage_phy_base; + area_id silentpage_area; + + emuxki_channel *channel[EMU_NUMCHAN]; + emuxki_voice *recsrc[EMU_NUMRECSRCS]; + + LIST_HEAD(, _emuxki_mem) mem; + + LIST_HEAD(, _emuxki_stream) streams; + + uint8 timerstate; + uint16 timerate; +#define EMU_TIMER_STATE_ENABLED 1 + uint8 play_mode; // number of channels to be played : 2, 4, 6 + bool digital_enabled; // if digital is enabled and analog is disabled + + emuxki_stream *pstream; + emuxki_stream *pstream2; + emuxki_stream *rstream; + emuxki_stream *rstream2; + + sem_id buffer_ready_sem; + + midi_dev midi; + + /* mixer controls */ + emuxki_gpr gpr[256]; + uint32 gpr_count; + + /* multi_audio */ + multi_dev multi; +} emuxki_dev; + +extern int32 num_cards; +extern emuxki_dev cards[NUM_CARDS]; + +void emuxki_mem_free(emuxki_dev *card, void *ptr); +void * emuxki_pmem_alloc(emuxki_dev *card, size_t size); +void * emuxki_rmem_alloc(emuxki_dev *card, size_t size); +status_t emuxki_voice_commit_parms(emuxki_voice *voice); +status_t emuxki_voice_set_audioparms(emuxki_voice *voice, uint8 stereo, + uint8 b16, uint32 srate); +status_t emuxki_voice_set_recparms(emuxki_voice *voice, emuxki_recsrc_t recsrc, + emuxki_recparams *recparams); +status_t emuxki_voice_set_bufparms(emuxki_voice *voice, void *ptr, + uint32 bufsize, uint16 blksize); +void emuxki_voice_start(emuxki_voice *voice); +void emuxki_voice_halt(emuxki_voice *voice); +emuxki_voice *emuxki_voice_new(emuxki_stream *stream, uint8 use, uint8 voicenum); +void emuxki_voice_delete(emuxki_voice *voice); + +status_t emuxki_stream_set_audioparms(emuxki_stream *stream, bool stereo, uint8 channels, + uint8 b16, uint32 sample_rate); +status_t emuxki_stream_set_recparms(emuxki_stream *stream, emuxki_recsrc_t recsrc, + emuxki_recparams *recparams); +status_t emuxki_stream_commit_parms(emuxki_stream *stream); +status_t emuxki_stream_get_nth_buffer(emuxki_stream *stream, uint8 chan, uint8 buf, + char** buffer, size_t *stride); +void emuxki_stream_start(emuxki_stream *stream, void (*inth) (void *), void *inthparam); +void emuxki_stream_halt(emuxki_stream *stream); +emuxki_stream *emuxki_stream_new(emuxki_dev *card, uint8 use, uint32 bufframes, uint8 bufcount); +void emuxki_stream_delete(emuxki_stream *stream); + +void emuxki_dump_fx(emuxki_dev * card); +void emuxki_gpr_dump(emuxki_dev * card, uint16 count); +void emuxki_gpr_set(emuxki_dev *card, emuxki_gpr *gpr, int32 type, float *values); +void emuxki_gpr_get(emuxki_dev *card, emuxki_gpr *gpr, int32 type, float *values); + +void emuxki_parameter_set(emuxki_dev *card, const void*, int32 type, int32 *value); +void emuxki_parameter_get(emuxki_dev *card, const void*, int32 type, int32 *value); + +extern void midi_interrupt_op(int32 op, void * data); +extern bool midi_interrupt(emuxki_dev *card); + +#endif + diff --git a/src/add-ons/kernel/drivers/audio/emuxki/emuxkireg.h b/src/add-ons/kernel/drivers/audio/emuxki/emuxkireg.h new file mode 100644 index 0000000000..fc1b92d40b --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/emuxkireg.h @@ -0,0 +1,702 @@ +/* + * Emuxki BeOS Driver for Creative Labs SBLive!/Audigy series + * + * Copyright (c) 2002, Jerome Duval (jerome.duval@free.fr) + * + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Yannick Montulet. + * + * 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_EMUXKIREG_H_ +#define _DEV_PCI_EMUXKIREG_H_ + +/* + * Register values for Creative EMU10000. The register values have been + * taken from GPLed SBLive! header file published by Creative. The comments + * have been stripped to avoid GPL pollution in kernel. The Creative version + * including comments is available in Linux 2.4.* kernel as file + * drivers/sound/emu10k1/8010.h + */ + +// Audigy specific registers contain a 'A_' +// Audigy(2) specific registers contain a 'A2_' + +#define EMU_MKSUBREG(sz, idx, reg) (((sz) << 24) | ((idx) << 16) | (reg)) + +#define EMU_PTR 0x00 +#define EMU_PTR_CHNO_MASK 0x0000003f +#define EMU_PTR_ADDR_MASK 0x07ff0000 +#define EMU_A_PTR_ADDR_MASK 0x0fff0000 + +#define EMU_DATA 0x04 + +#define EMU_IPR 0x08 +#define EMU_IPR_A_MIDITRANSBUFE2 0x10000000 +#define EMU_IPR_A_MIDIRECVBUFE2 0x08000000 +#define EMU_IPR_RATETRCHANGE 0x01000000 +#define EMU_IPR_FXDSP 0x00800000 +#define EMU_IPR_FORCEINT 0x00400000 +#define EMU_PCIERROR 0x00200000 +#define EMU_IPR_VOLINCR 0x00100000 +#define EMU_IPR_VOLDECR 0x00080000 +#define EMU_IPR_MUTE 0x00040000 +#define EMU_IPR_MICBUFFULL 0x00020000 +#define EMU_IPR_MICBUFHALFFULL 0x00010000 +#define EMU_IPR_ADCBUFFULL 0x00008000 +#define EMU_IPR_ADCBUFHALFFULL 0x00004000 +#define EMU_IPR_EFXBUFFULL 0x00002000 +#define EMU_IPR_EFXBUFHALFFULL 0x00001000 +#define EMU_IPR_GPSPDIFSTCHANGE 0x00000800 +#define EMU_IPR_CDROMSTCHANGE 0x00000400 +#define EMU_IPR_INTERVALTIMER 0x00000200 +#define EMU_IPR_MIDITRANSBUFE 0x00000100 +#define EMU_IPR_MIDIRECVBUFE 0x00000080 +#define EMU_IPR_CHANNELLOOP 0x00000040 +#define EMU_IPR_CHNOMASK 0x0000003f + +#define EMU_INTE 0x0c + +#define EMU_INTE_VSB_MASK 0xc0000000 +#define EMU_INTE_VSB_220 0x00000000 +#define EMU_INTE_VSB_240 0x40000000 +#define EMU_INTE_VSB_260 0x80000000 +#define EMU_INTE_VSB_280 0xc0000000 + +#define EMU_INTE_VMPU_MASK 0x30000000 +#define EMU_INTE_VMPU_300 0x00000000 +#define EMU_INTE_VMPU_310 0x10000000 +#define EMU_INTE_VMPU_320 0x20000000 +#define EMU_INTE_VMPU_330 0x30000000 +#define EMU_INTE_MDMAENABLE 0x08000000 +#define EMU_INTE_SDMAENABLE 0x04000000 +#define EMU_INTE_MPICENABLE 0x02000000 +#define EMU_INTE_SPICENABLE 0x01000000 +#define EMU_INTE_VSBENABLE 0x00800000 +#define EMU_INTE_ADLIBENABLE 0x00400000 +#define EMU_INTE_MPUENABLE 0x00200000 +#define EMU_INTE_FORCEINT 0x00100000 +#define EMU_INTE_MRHANDENABLE 0x00080000 +#define EMU_INTE_SAMPLERATER 0x00002000 +#define EMU_INTE_FXDSPENABLE 0x00001000 +#define EMU_INTE_PCIERRENABLE 0x00000800 +#define EMU_INTE_VOLINCRENABLE 0x00000400 +#define EMU_INTE_VOLDECRENABLE 0x00000200 +#define EMU_INTE_MUTEENABLE 0x00000100 +#define EMU_INTE_MICBUFENABLE 0x00000080 +#define EMU_INTE_ADCBUFENABLE 0x00000040 +#define EMU_INTE_EFXBUFENABLE 0x00000020 +#define EMU_INTE_GPSPDIFENABLE 0x00000010 +#define EMU_INTE_CDSPDIFENABLE 0x00000008 +#define EMU_INTE_INTERTIMERENB 0x00000004 +#define EMU_INTE_MIDITXENABLE 0x00000002 +#define EMU_INTE_MIDIRXENABLE 0x00000001 +#define EMU_INTE_A_MIDITXENABLE2 0x00020000 +#define EMU_INTE_A_MIDIRXENABLE2 0x00010000 + +#define EMU_WC 0x10 +#define EMU_WC_SAMPLECOUNTER_MASK 0x03FFFFC0 +#define EMU_WC_SAMPLECOUNTER EMU_MKSUBREG(20, 6, EMU_WC) +#define EMU_WC_CURRENTCHANNEL 0x0000003F + +#define EMU_HCFG 0x14 +#define EMU_HCFG_LEGACYFUNC_MASK 0xe0000000 +#define EMU_HCFG_LEGACYFUNC_MPU 0x00000000 +#define EMU_HCFG_LEGACYFUNC_SB 0x40000000 +#define EMU_HCFG_LEGACYFUNC_AD 0x60000000 +#define EMU_HCFG_LEGACYFUNC_MPIC 0x80000000 +#define EMU_HCFG_LEGACYFUNC_MDMA 0xa0000000 +#define EMU_HCFG_LEGACYFUNC_SPCI 0xc0000000 +#define EMU_HCFG_LEGACYFUNC_SDMA 0xe0000000 +#define EMU_HCFG_IOCAPTUREADDR 0x1f000000 +#define EMU_HCFG_LEGACYWRITE 0x00800000 +#define EMU_HCFG_LEGACYWORD 0x00400000 +#define EMU_HCFG_LEGACYINT 0x00200000 + +#define EMU_HCFG_CODECFMT_MASK 0x00070000 +#define EMU_HCFG_CODECFMT_AC97 0x00000000 +#define EMU_HCFG_CODECFMT_I2S 0x00010000 +#define EMU_HCFG_GPINPUT0 0x00004000 +#define EMU_HCFG_GPINPUT1 0x00002000 +#define EMU_HCFG_GPOUTPUT_MASK 0x00001c00 +#define EMU_HCFG_GPOUTPUT0 0x00001000 +#define EMU_HCFG_JOYENABLE 0x00000200 +#define EMU_HCFG_PHASETRACKENABLE 0x00000100 +#define EMU_HCFG_AC3ENABLE_MASK 0x000000e0 +#define EMU_HCFG_AC3ENABLE_ZVIDEO 0x00000080 +#define EMU_HCFG_AC3ENABLE_CDSPDIF 0x00000040 +#define EMU_HCFG_AC3ENABLE_GPSPDIF 0x00000020 +#define EMU_HCFG_AUTOMUTE 0x00000010 +#define EMU_HCFG_LOCKSOUNDCACHE 0x00000008 +#define EMU_HCFG_LOCKTANKCACHE_MASK 0x00000004 +#define EMU_HCFG_LOCKTANKCACHE EMU_MKSUBREG(1, 2, EMU_HCFG) +#define EMU_HCFG_MUTEBUTTONENABLE 0x00000002 +#define EMU_HCFG_AUDIOENABLE 0x00000001 + +#define EMU_MUDATA 0x18 +#define EMU_MUCMD 0x19 +#define EMU_MUCMD_RESET 0xff +#define EMU_MUCMD_ENTERUARTMODE 0x3f + +#define EMU_MUSTAT EMU_MUCMD +#define EMU_MUSTAT_IRDYN 0x80 +#define EMU_MUSTAT_ORDYN 0x40 + +#define EMU_A_IOCFG 0x18 +#define EMU_A_GPINPUT_MASK 0xff00 +#define EMU_A_GPOUTPUT_MASK 0x00ff +#define EMU_A_IOCFG_GPOUT0 0x0040 +#define EMU_A_IOCFG_GPOUT1 0x0004 + +#define EMU_TIMER 0x1a +#define EMU_TIMER_RATE_MASK 0x000003ff +#define EMU_TIMER_RATE EMU_MKSUBREG(10, 0, EMU_TIMER) + +#define EMU_AC97DATA 0x1c +#define EMU_AC97ADDR 0x1e +#define EMU_AC97ADDR_RDY 0x80 +#define EMU_AC97ADDR_ADDR 0x7f + +#define EMU_A2_PTR 0x20 +#define EMU_A2_DATA 0x24 + +#define EMU_A2_SRCSEL 0x600000 +#define EMU_A2_SRCSEL_ENABLE_SPDIF 0x00000004 +#define EMU_A2_SRCSEL_ENABLE_SRCMULTI 0x00000010 +#define EMU_A2_SRCMULTI 0x6e0000 +#define EMU_A2_SRCMULTI_ENABLE_INPUT 0xff00ff00 + + +/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */ + +#define EMU_CHAN_CPF 0x00 + +#define EMU_CHAN_CPF_PITCH_MASK 0xffff0000 +#define EMU_CHAN_CPF_PITCH EMU_MKSUBREG(16, 16, EMU_CHAN_CPF) +#define EMU_CHAN_CPF_STEREO_MASK 0x00008000 +#define EMU_CHAN_CPF_STEREO EMU_MKSUBREG(1, 15, EMU_CHAN_CPF) +#define EMU_CHAN_CPF_STOP_MASK 0x00004000 +#define EMU_CHAN_CPF_FRACADDRESS_MASK 0x00003fff + + +#define EMU_CHAN_PTRX 0x01 +#define EMU_CHAN_PTRX_PITCHTARGET_MASK 0xffff0000 +#define EMU_CHAN_PTRX_PITCHTARGET EMU_MKSUBREG(16, 16, EMU_CHAN_PTRX) +#define EMU_CHAN_PTRX_FXSENDAMOUNT_A_MASK 0x0000ff00 +#define EMU_CHAN_PTRX_FXSENDAMOUNT_A EMU_MKSUBREG(8, 8, EMU_CHAN_PTRX) +#define EMU_CHAN_PTRX_FXSENDAMOUNT_B_MASK 0x000000ff +#define EMU_CHAN_PTRX_FXSENDAMOUNT_B EMU_MKSUBREG(8, 0, EMU_CHAN_PTRX) + +#define EMU_CHAN_CVCF 0x02 +#define EMU_CHAN_CVCF_CURRVOL_MASK 0xffff0000 +#define EMU_CHAN_CVCF_CURRVOL EMU_MKSUBREG(16, 16, EMU_CHAN_CVCF) +#define EMU_CHAN_CVCF_CURRFILTER_MASK 0x0000ffff +#define EMU_CHAN_CVCF_CURRFILTER EMU_MKSUBREG(16, 0, EMU_CHAN_CVCF) + +#define EMU_CHAN_VTFT 0x03 +#define EMU_CHAN_VTFT_VOLUMETARGET_MASK 0xffff0000 +#define EMU_CHAN_VTFT_VOLUMETARGET EMU_MKSUBREG(16, 16, EMU_CHAN_VTFT) +#define EMU_CHAN_VTFT_FILTERTARGET_MASK 0x0000ffff +#define EMU_CHAN_VTFT_FILTERTARGET EMU_MKSUBREG(16, 0, EMU_CHAN_VTFT) + +#define EMU_CHAN_Z1 0x05 +#define EMU_CHAN_Z2 0x04 + +#define EMU_CHAN_PSST 0x06 +#define EMU_CHAN_PSST_FXSENDAMOUNT_C_MASK 0xff000000 +#define EMU_CHAN_PSST_FXSENDAMOUNT_C EMU_MKSUBREG(8, 24, EMU_CHAN_PSST) +#define EMU_CHAN_PSST_LOOPSTARTADDR_MASK 0x00ffffff +#define EMU_CHAN_PSST_LOOPSTARTADDR EMU_MKSUBREG(24, 0, EMU_CHAN_PSST) + +#define EMU_CHAN_DSL 0x07 +#define EMU_CHAN_DSL_FXSENDAMOUNT_D_MASK 0xff000000 +#define EMU_CHAN_DSL_FXSENDAMOUNT_D EMU_MKSUBREG(8, 24, EMU_CHAN_DSL) +#define EMU_CHAN_DSL_LOOPENDADDR_MASK 0x00ffffff +#define EMU_CHAN_DSL_LOOPENDADDR EMU_MKSUBREG(24, 0, EMU_CHAN_DSL) + +#define EMU_CHAN_CCCA 0x08 +#define EMU_CHAN_CCCA_RESONANCE 0xf0000000 +#define EMU_CHAN_CCCA_INTERPROMMASK 0x0e000000 +#define EMU_CHAN_CCCA_INTERPROM_0 0x00000000 +#define EMU_CHAN_CCCA_INTERPROM_1 0x02000000 +#define EMU_CHAN_CCCA_INTERPROM_2 0x04000000 +#define EMU_CHAN_CCCA_INTERPROM_3 0x06000000 +#define EMU_CHAN_CCCA_INTERPROM_4 0x08000000 +#define EMU_CHAN_CCCA_INTERPROM_5 0x0a000000 +#define EMU_CHAN_CCCA_INTERPROM_6 0x0c000000 +#define EMU_CHAN_CCCA_INTERPROM_7 0x0e000000 +#define EMU_CHAN_CCCA_8BITSELECT 0x01000000 +#define EMU_CHAN_CCCA_CURRADDR_MASK 0x00ffffff +#define EMU_CHAN_CCCA_CURRADDR EMU_MKSUBREG(24, 0, EMU_CHAN_CCCA) + +#define EMU_CHAN_CCR 0x09 +#define EMU_CHAN_CCR_CACHEINVALIDSIZE_MASK 0xfe000000 +#define EMU_CHAN_CCR_CACHEINVALIDSIZE EMU_MKSUBREG(7, 25, EMU_CHAN_CCR) +#define EMU_CHAN_CCR_CACHELOOPFLAG 0x01000000 +#define EMU_CHAN_CCR_INTERLEAVEDSAMPLES 0x00800000 +#define EMU_CHAN_CCR_WORDSIZEDSAMPLES 0x00400000 +#define EMU_CHAN_CCR_READADDRESS_MASK 0x003f0000 +#define EMU_CHAN_CCR_READADDRESS EMU_MKSUBREG(6, 16, EMU_CHAN_CCR) +#define EMU_CHAN_CCR_LOOPINVALSIZE 0x0000fe00 +#define EMU_CHAN_CCR_LOOPFLAG 0x00000100 +#define EMU_CHAN_CCR_CACHELOOPADDRHI 0x000000ff + +#define EMU_CHAN_CLP 0x0a +#define EMU_CHAN_CLP_CACHELOOPADDR 0x0000ffff + +#define EMU_CHAN_FXRT 0x0b +#define EMU_CHAN_FXRT_CHANNELA 0x000f0000 +#define EMU_CHAN_FXRT_CHANNELB 0x00f00000 +#define EMU_CHAN_FXRT_CHANNELC 0x0f000000 +#define EMU_CHAN_FXRT_CHANNELD 0xf0000000 + +#define EMU_CHAN_MAPA 0x0c +#define EMU_CHAN_MAPB 0x0d + +#define EMU_CHAN_MAP_PTE_MASK 0xffffe000 +#define EMU_CHAN_MAP_PTI_MASK 0x00001fff + + +#define EMU_CHAN_ENVVOL 0x10 +#define EMU_CHAN_ENVVOL_MASK 0x0000ffff + + +#define EMU_CHAN_ATKHLDV 0x11 +#define EMU_CHAN_ATKHLDV_PHASE0 0x00008000 +#define EMU_CHAN_ATKHLDV_HOLDTIME_MASK 0x00007f00 +#define EMU_CHAN_ATKHLDV_ATTACKTIME_MASK 0x0000007f + + +#define EMU_CHAN_DCYSUSV 0x12 +#define EMU_CHAN_DCYSUSV_PHASE1_MASK 0x00008000 +#define EMU_CHAN_DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00 +#define EMU_CHAN_DCYSUSV_CHANNELENABLE_MASK 0x00000080 +#define EMU_CHAN_DCYSUSV_DECAYTIME_MASK 0x0000007f + + +#define EMU_CHAN_LFOVAL1 0x13 +#define EMU_CHAN_LFOVAL_MASK 0x0000ffff + +#define EMU_CHAN_ENVVAL 0x14 +#define EMU_CHAN_ENVVAL_MASK 0x0000ffff + +#define EMU_CHAN_ATKHLDM 0x15 +#define EMU_CHAN_ATKHLDM_PHASE0 0x00008000 +#define EMU_CHAN_ATKHLDM_HOLDTIME 0x00007f00 +#define EMU_CHAN_ATKHLDM_ATTACKTIME 0x0000007f + +#define EMU_CHAN_DCYSUSM 0x16 +#define EMU_CHAN_DCYSUSM_PHASE1_MASK 0x00008000 +#define EMU_CHAN_DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00 +#define EMU_CHAN_DCYSUSM_DECAYTIME_MASK 0x0000007f + +#define EMU_CHAN_LFOVAL2 0x17 +#define EMU_CHAN_LFOVAL2_MASK 0x0000ffff + +#define EMU_CHAN_IP 0x18 +#define EMU_CHAN_IP_MASK 0x0000ffff +#define EMU_CHAN_IP_UNITY 0x0000e000 + +#define EMU_CHAN_IFATN 0x19 +#define EMU_CHAN_IFATN_FILTERCUTOFF_MASK 0x0000ff00 +#define EMU_CHAN_IFATN_FILTERCUTOFF EMU_MKSUBREG(8, 8, EMU_CHAN_IFATN) +#define EMU_CHAN_IFATN_ATTENUATION_MASK 0x000000ff +#define EMU_CHAN_IFATN_ATTENUATION EMU_MKSUBREG(8, 0, EMU_CHAN_IFATN) + +#define EMU_CHAN_PEFE 0x1a +#define EMU_CHAN_PEFE_PITCHAMOUNT_MASK 0x0000ff00 +#define EMU_CHAN_PEFE_PITCHAMOUNT EMU_MKSUBREG(8, 8, EMU_CHAN_PEFE) +#define EMU_CHAN_PEFE_FILTERAMOUNT_MASK 0x000000ff +#define EMU_CHAN_PEFE_FILTERAMOUNT EMU_MKSUBREG(8, 0, EMU_CHAN_PEFE) + +#define EMU_CHAN_FMMOD 0x1b +#define EMU_CHAN_FMMOD_MODVIBRATO 0x0000ff00 +#define EMU_CHAN_FMMOD_MOFILTER 0x000000ff + +#define EMU_CHAN_TREMFRQ 0x1c +#define EMU_CHAN_TREMFRQ_DEPTH 0x0000ff00 + +#define EMU_CHAN_FM2FRQ2 0x1d +#define EMU_CHAN_FM2FRQ2_DEPTH 0x0000ff00 +#define EMU_CHAN_FM2FRQ2_FREQUENCY 0x000000ff + +#define EMU_CHAN_TEMPENV 0x1e +#define EMU_CHAN_TEMPENV_MASK 0x0000ffff + +#define EMU_CHAN_CD0 0x20 +#define EMU_CHAN_CD1 0x21 +#define EMU_CHAN_CD2 0x22 +#define EMU_CHAN_CD3 0x23 +#define EMU_CHAN_CD4 0x24 +#define EMU_CHAN_CD5 0x25 +#define EMU_CHAN_CD6 0x26 +#define EMU_CHAN_CD7 0x27 +#define EMU_CHAN_CD8 0x28 +#define EMU_CHAN_CD9 0x29 +#define EMU_CHAN_CDA 0x2a +#define EMU_CHAN_CDB 0x2b +#define EMU_CHAN_CDC 0x2c +#define EMU_CHAN_CDD 0x2d +#define EMU_CHAN_CDE 0x2e +#define EMU_CHAN_CDF 0x2f + +/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */ + +#define EMU_PTB 0x40 +#define EMU_PTB_MASK 0xfffff000 + +#define EMU_TCB 0x41 +#define EMU_TCB_MASK 0xfffff000 + +#define EMU_ADCCR 0x42 +#define EMU_ADCCR_RCHANENABLE 0x00000010 +#define EMU_ADCCR_LCHANENABLE 0x00000008 +#define EMU_ADCCR_SAMPLERATE_MASK 0x00000007 +#define EMU_A_ADCCR_RCHANENABLE 0x00000020 +#define EMU_A_ADCCR_LCHANENABLE 0x00000010 +#define EMU_A_ADCCR_SAMPLERATE_MASK 0x0000000F +#define EMU_ADCCR_SAMPLERATE_48 0x00000000 +#define EMU_ADCCR_SAMPLERATE_44 0x00000001 +#define EMU_ADCCR_SAMPLERATE_32 0x00000002 +#define EMU_ADCCR_SAMPLERATE_24 0x00000003 +#define EMU_ADCCR_SAMPLERATE_22 0x00000004 +#define EMU_ADCCR_SAMPLERATE_16 0x00000005 +#define EMU_ADCCR_SAMPLERATE_11 0x00000006 +#define EMU_ADCCR_SAMPLERATE_8 0x00000007 +#define EMU_A_ADCCR_SAMPLERATE_12 0x00000006 +#define EMU_A_ADCCR_SAMPLERATE_11 0x00000007 +#define EMU_A_ADCCR_SAMPLERATE_8 0x00000008 + +#define EMU_FXWC 0x43 +#define EMU_TCBS 0x44 +#define EMU_TCBS_MASK 0x00000007 +#define EMU_TCBS_BUFFSIZE_16K 0x00000000 +#define EMU_TCBS_BUFFSIZE_32K 0x00000001 +#define EMU_TCBS_BUFFSIZE_64K 0x00000002 +#define EMU_TCBS_BUFFSIZE_128K 0x00000003 +#define EMU_TCBS_BUFFSIZE_256K 0x00000004 +#define EMU_TCBS_BUFFSIZE_512K 0x00000005 +#define EMU_TCBS_BUFFSIZE_1024K 0x00000006 +#define EMU_TCBS_BUFFSIZE_2048K 0x00000007 + +#define EMU_MICBA 0x45 +#define EMU_ADCBA 0x46 +#define EMU_FXBA 0x47 +#define EMU_RECBA_MASK 0xfffff000 + +#define EMU_MICBS 0x49 +#define EMU_ADCBS 0x4a +#define EMU_FXBS 0x4b +#define EMU_RECBS_BUFSIZE_NONE 0x00000000 +#define EMU_RECBS_BUFSIZE_384 0x00000001 +#define EMU_RECBS_BUFSIZE_448 0x00000002 +#define EMU_RECBS_BUFSIZE_512 0x00000003 +#define EMU_RECBS_BUFSIZE_640 0x00000004 +#define EMU_RECBS_BUFSIZE_768 0x00000005 +#define EMU_RECBS_BUFSIZE_896 0x00000006 +#define EMU_RECBS_BUFSIZE_1024 0x00000007 +#define EMU_RECBS_BUFSIZE_1280 0x00000008 +#define EMU_RECBS_BUFSIZE_1536 0x00000009 +#define EMU_RECBS_BUFSIZE_1792 0x0000000a +#define EMU_RECBS_BUFSIZE_2048 0x0000000b +#define EMU_RECBS_BUFSIZE_2560 0x0000000c +#define EMU_RECBS_BUFSIZE_3072 0x0000000d +#define EMU_RECBS_BUFSIZE_3584 0x0000000e +#define EMU_RECBS_BUFSIZE_4096 0x0000000f +#define EMU_RECBS_BUFSIZE_5120 0x00000010 +#define EMU_RECBS_BUFSIZE_6144 0x00000011 +#define EMU_RECBS_BUFSIZE_7168 0x00000012 +#define EMU_RECBS_BUFSIZE_8192 0x00000013 +#define EMU_RECBS_BUFSIZE_10240 0x00000014 +#define EMU_RECBS_BUFSIZE_12288 0x00000015 +#define EMU_RECBS_BUFSIZE_14366 0x00000016 +#define EMU_RECBS_BUFSIZE_16384 0x00000017 +#define EMU_RECBS_BUFSIZE_20480 0x00000018 +#define EMU_RECBS_BUFSIZE_24576 0x00000019 +#define EMU_RECBS_BUFSIZE_28672 0x0000001a +#define EMU_RECBS_BUFSIZE_32768 0x0000001b +#define EMU_RECBS_BUFSIZE_40960 0x0000001c +#define EMU_RECBS_BUFSIZE_49152 0x0000001d +#define EMU_RECBS_BUFSIZE_57344 0x0000001e +#define EMU_RECBS_BUFSIZE_65536 0x0000001f + +#define EMU_CDCS 0x50 +#define EMU_GPSCS 0x51 + +#define EMU_DBG 0x52 +#define EMU_DBG_ZC 0x80000000 +#define EMU_DBG_SATURATION_OCCURED 0x02000000 +#define EMU_DBG_SATURATION_ADDR 0x01ff0000 +#define EMU_DBG_SINGLE_STEP 0x00008000 +#define EMU_DBG_STEP 0x00004000 +#define EMU_DBG_CONDITION_CODE 0x00003e00 +#define EMU_DBG_SINGLE_STEP_ADDR 0x000001ff +#define EMU_REG53 0x53 + +#define EMU_A_DBG 0x53 +#define EMU_A_DBG_SINGLE_STEP 0x00020000 +#define EMU_A_DBG_ZC 0x40000000 +#define EMU_A_DBG_STEP_ADDR 0x000003ff +#define EMU_A_DBG_SATURATION_OCCRD 0x20000000 +#define EMU_A_DBG_SATURATION_ADDR 0x0ffc0000 + +#define EMU_SPCS0 0x54 +#define EMU_SPCS1 0x55 +#define EMU_SPCS2 0x56 +#define EMU_SPCS_CLKACCYMASK 0x30000000 +#define EMU_SPCS_CLKACCY_1000PPM 0x00000000 +#define EMU_SPCS_CLKACCY_50PPM 0x10000000 +#define EMU_SPCS_CLKACCY_VARIABLE 0x20000000 +#define EMU_SPCS_SAMPLERATEMASK 0x0f000000 +#define EMU_SPCS_SAMPLERATE_44 0x00000000 +#define EMU_SPCS_SAMPLERATE_48 0x02000000 +#define EMU_SPCS_SAMPLERATE_32 0x03000000 +#define EMU_SPCS_CHANNELNUMMASK 0x00f00000 +#define EMU_SPCS_CHANNELNUM_UNSPEC 0x00000000 +#define EMU_SPCS_CHANNELNUM_LEFT 0x00100000 +#define EMU_SPCS_CHANNELNUM_RIGHT 0x00200000 +#define EMU_SPCS_SOURCENUMMASK 0x000f0000 +#define EMU_SPCS_SOURCENUM_UNSPEC 0x00000000 +#define EMU_SPCS_GENERATIONSTATUS 0x00008000 +#define EMU_SPCS_CATEGORYCODEMASK 0x00007f00 +#define EMU_SPCS_MODEMASK 0x000000c0 +#define EMU_SPCS_EMPHASISMASK 0x00000038 +#define EMU_SPCS_EMPHASIS_NONE 0x00000000 +#define EMU_SPCS_EMPHASIS_50_15 0x00000008 +#define EMU_SPCS_COPYRIGHT 0x00000004 +#define EMU_SPCS_NOTAUDIODATA 0x00000002 +#define EMU_SPCS_PROFESSIONAL 0x00000001 + +#define EMU_CLIEL 0x58 +#define EMU_CLIEH 0x59 +#define EMU_CLIPL 0x5a +#define EMU_CLIPH 0x5b +#define EMU_SOLEL 0x5c +#define EMU_SOLEH 0x5d + +#define EMU_SPBYPASS 0x5e +#define EMU_SPBYPASS_ENABLE 0x00000001 + +#define EMU_AC97SLOT 0x5f +#define EMU_AC97SLOT_CENTER 0x00000010 +#define EMU_AC97SLOT_LFE 0x00000020 + +#define EMU_CDSRCS 0x60 +#define EMU_GPSRCS 0x61 +#define EMU_ZVSRCS 0x62 +#define EMU_SRCS_SPDIFLOCKED 0x02000000 +#define EMU_SRCS_RATELOCKED 0x01000000 +#define EMU_SRCS_ESTSAMPLERATE 0x0007ffff + +#define EMU_MICIDX 0x63 +#define EMU_A_MICIDX 0x64 +#define EMU_ADCIDX 0x64 +#define EMU_A_ADCIDX 0x63 +#define EMU_FXIDX 0x65 +#define EMU_RECIDX_MASK 0x0000ffff +#define EMU_RECIDX(idxreg) (0x10000000|(idxreg)) + +#define EMU_A_MUDATA1 0x70 +#define EMU_A_MUCMD1 0x71 +#define EMU_A_MUSTAT1 EMU_A_MUCMD1 + +#define EMU_A_MUDATA2 0x72 +#define EMU_A_MUCMD2 0x73 +#define EMU_A_MUSTAT2 EMU_A_MUCMD2 + +#define EMU_A_FXWC1 0x74 +#define EMU_A_FXWC2 0x75 + +#define EMU_A_SPDIF_SAMPLERATE 0x76 +#define EMU_A_SPDIF_48000 0x00000080 +#define EMU_A_SPDIF_44100 0x00000000 +#define EMU_A_SPDIF_96000 0x00000040 +#define EMU_A2_SPDIF_SAMPLERATE EMU_MKSUBREG(3, 9, EMU_A_SPDIF_SAMPLERATE) +#define EMU_A2_SPDIF_MASK 0x00000e00 +#define EMU_A2_SPDIF_UNKNOWN 0x2 + +#define EMU_A_CHAN_FXRT2 0x7c +#define EMU_A_CHAN_FXRT_CHANNELE 0x0000003f +#define EMU_A_CHAN_FXRT_CHANNELF 0x00003f00 +#define EMU_A_CHAN_FXRT_CHANNELG 0x003f0000 +#define EMU_A_CHAN_FXRT_CHANNELH 0x3f000000 + +#define EMU_A_CHAN_SENDAMOUNTS 0x7d +#define EMU_A_CHAN_FXSENDAMOUNTS_E_MASK 0xff000000 +#define EMU_A_CHAN_FXSENDAMOUNTS_F_MASK 0x00ff0000 +#define EMU_A_CHAN_FXSENDAMOUNTS_G_MASK 0x0000ff00 +#define EMU_A_CHAN_FXSENDAMOUNTS_H_MASK 0x000000ff + +#define EMU_A_CHAN_FXRT1 0x7e +#define EMU_A_CHAN_FXRT_CHANNELA 0x0000003f +#define EMU_A_CHAN_FXRT_CHANNELB 0x00003f00 +#define EMU_A_CHAN_FXRT_CHANNELC 0x003f0000 +#define EMU_A_CHAN_FXRT_CHANNELD 0x3f000000 + +#define EMU_FXGPREGBASE 0x100 +#define EMU_A_FXGPREGBASE 0x400 + +#define EMU_TANKMEMDATAREGBASE 0x200 +#define EMU_TANKMEMDATAREG_MASK 0x000fffff + +#define EMU_TANKMEMADDRREGBASE 0x300 +#define EMU_TANKMEMADDRREG_ADDR_MASK 0x000fffff +#define EMU_TANKMEMADDRREG_CLEAR 0x00800000 +#define EMU_TANKMEMADDRREG_ALIGN 0x00400000 +#define EMU_TANKMEMADDRREG_WRITE 0x00200000 +#define EMU_TANKMEMADDRREG_READ 0x00100000 + +#define EMU_MICROCODEBASE 0x400 +#define EMU_DSP_LOWORD_OPX_MASK 0x000ffc00 +#define EMU_DSP_LOWORD_OPY_MASK 0x000003ff +#define EMU_DSP_HIWORD_OPCODE_MASK 0x00f00000 +#define EMU_DSP_HIWORD_RESULT_MASK 0x000ffc00 +#define EMU_DSP_HIWORD_OPA_MASK 0x000003ff + +#define EMU_A_MICROCODEBASE 0x600 +#define EMU_A_DSP_LOWORD_OPX_MASK 0x007ff000 +#define EMU_A_DSP_LOWORD_OPY_MASK 0x000007ff +#define EMU_A_DSP_HIWORD_OPCODE_MASK 0x0f000000 +#define EMU_A_DSP_HIWORD_RESULT_MASK 0x007ff000 +#define EMU_A_DSP_HIWORD_OPA_MASK 0x000007ff + +#define EMU_DSP_OP_MACS 0x0 +#define EMU_DSP_OP_MACS1 0x1 +#define EMU_DSP_OP_MACW 0x2 +#define EMU_DSP_OP_MACW1 0x3 +#define EMU_DSP_OP_MACINTS 0x4 +#define EMU_DSP_OP_MACINTW 0x5 +#define EMU_DSP_OP_ACC3 0x6 +#define EMU_DSP_OP_MACMV 0x7 +#define EMU_DSP_OP_ANDXOR 0x8 +#define EMU_DSP_OP_TSTNEG 0x9 +#define EMU_DSP_OP_LIMIT 0xa +#define EMU_DSP_OP_LIMIT1 0xb +#define EMU_DSP_OP_LOG 0xc +#define EMU_DSP_OP_EXP 0xd +#define EMU_DSP_OP_INTERP 0xe +#define EMU_DSP_OP_SKIP 0xf + + +#define EMU_DSP_FX(num) (num) + +#define EMU_DSP_IOL(base, num) (base + (num << 1)) +#define EMU_DSP_IOR(base, num) (EMU_DSP_IOL(base, num) + 1) + +#define EMU_DSP_INL_BASE 0x010 +#define EMU_DSP_INL(num) (EMU_DSP_IOL(EMU_DSP_INL_BASE, num)) +#define EMU_DSP_INR(num) (EMU_DSP_IOR(EMU_DSP_INL_BASE, num)) +#define EMU_DSP_IN_AC97 0 +#define EMU_DSP_IN_CDSPDIF 1 +#define EMU_DSP_IN_ZOOM 2 +#define EMU_DSP_IN_TOSOPT 3 +#define EMU_DSP_IN_LVDLM1 4 +#define EMU_DSP_IN_LVDCOS 5 +#define EMU_DSP_IN_LVDLM2 6 +#define EMU_DSP_IN_UNKOWN 7 + +#define EMU_A_DSP_INL_BASE 0x040 +#define EMU_A_DSP_INL(num) (EMU_DSP_IOL(EMU_A_DSP_INL_BASE, num)) +#define EMU_A_DSP_INR(num) (EMU_DSP_IOR(EMU_A_DSP_INL_BASE, num)) + +#define EMU_DSP_OUTL_BASE 0x020 +#define EMU_DSP_OUTL(num) (EMU_DSP_IOL(EMU_DSP_OUTL_BASE, num)) +#define EMU_DSP_OUTR(num) (EMU_DSP_IOR(EMU_DSP_OUTL_BASE, num)) +#define EMU_DSP_OUT_A_FRONT 0 +#define EMU_DSP_OUT_D_FRONT 1 // not tested +#define EMU_DSP_OUT_D_CENTER 2 // not tested +#define EMU_DSP_OUT_DRIVE_HP 3 // not tested +#define EMU_DSP_OUT_AD_REAR 4 +#define EMU_DSP_OUT_ADC 5 +#define EMU_DSP_OUTL_MIC 6 +#define EMU_DSP_OUT_A_CENTER (EMU_DSP_IOR(EMU_DSP_OUTL_BASE, 8)) // Live 5.1 only +#define EMU_DSP_OUT_A_SUB (EMU_DSP_IOL(EMU_DSP_OUTL_BASE, 9)) // Live 5.1 only + +#define EMU_A_DSP_OUTL_BASE 0x060 +#define EMU_A_DSP_OUTL(num) (EMU_DSP_IOL(EMU_A_DSP_OUTL_BASE, num)) +#define EMU_A_DSP_OUTR(num) (EMU_DSP_IOR(EMU_A_DSP_OUTL_BASE, num)) +#define EMU_A_DSP_OUT_D_FRONT 0 // not tested +#define EMU_A_DSP_OUT_D_CENTER 1 // not tested +#define EMU_A_DSP_OUT_DRIVE_HP 2 // not tested +#define EMU_A_DSP_OUT_D_REAR 3 // not tested +#define EMU_A_DSP_OUT_A_FRONT 4 +#define EMU_A_DSP_OUT_A_CENTER 5 +#define EMU_A_DSP_OUT_A_REAR 7 +#define EMU_A_DSP_OUT_ADC 11 + + +#define EMU_DSP_CST_BASE 0x40 +#define EMU_A_DSP_CST_BASE 0xc0 +#define EMU_DSP_CST(num) (EMU_DSP_CST_BASE + num) +#define EMU_A_DSP_CST(num) (EMU_A_DSP_CST_BASE + num) +/* +00 = 0x00000000 +01 = 0x00000001 +02 = 0x00000002 +03 = 0x00000003 +04 = 0x00000004 +05 = 0x00000008 +06 = 0x00000010 +07 = 0x00000020 +08 = 0x00000100 +09 = 0x00010000 +0A = 0x00080000 +0B = 0x10000000 +0C = 0x20000000 +0D = 0x40000000 +0E = 0x80000000 +0F = 0x7FFFFFFF +10 = 0xFFFFFFFF +11 = 0xFFFFFFFE +12 = 0xC0000000 +13 = 0x4F1BBCDC +14 = 0x5A7EF9DB +15 = 0x00100000 +*/ + +#define EMU_DSP_HWR_ACC 0x056 +#define EMU_DSP_HWR_CCR 0x057 +#define EMU_DSP_HWR_CCR_S 0x04 +#define EMU_DSP_HWR_CCR_Z 0x03 +#define EMU_DSP_HWR_CCR_M 0x02 +#define EMU_DSP_HWR_CCR_N 0x01 +#define EMU_DSP_HWR_CCR_B 0x00 +#define EMU_DSP_HWR_NOISE0 0x058 +#define EMU_DSP_HWR_NOISE1 0x059 +#define EMU_DSP_HWR_INTR 0x05A +#define EMU_DSP_HWR_DBAC 0x05B + +#define EMU_DSP_GPR(num) (EMU_FXGPREGBASE + num) +#define EMU_A_DSP_GPR(num) (EMU_A_FXGPREGBASE + num) + +#endif /* _DEV_PCI_EMUXKIREG_H_ */ diff --git a/src/add-ons/kernel/drivers/audio/emuxki/io.c b/src/add-ons/kernel/drivers/audio/emuxki/io.c new file mode 100644 index 0000000000..b34709b279 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/io.c @@ -0,0 +1,236 @@ +/* + * Emuxki BeOS Driver for Creative Labs SBLive!/Audigy series + * + * Copyright (c) 2002, 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 "emuxkireg.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 +emuxki_reg_read_8(device_config *config, int regno) +{ + return read_io_8(config->nabmbar + regno); +} + +uint16 +emuxki_reg_read_16(device_config *config, int regno) +{ + return read_io_16(config->nabmbar + regno); +} + +uint32 +emuxki_reg_read_32(device_config *config, int regno) +{ + return read_io_32(config->nabmbar + regno); +} + +void +emuxki_reg_write_8(device_config *config, int regno, uint8 value) +{ + write_io_8(config->nabmbar + regno, value); +} + +void +emuxki_reg_write_16(device_config *config, int regno, uint16 value) +{ + write_io_16(config->nabmbar + regno, value); +} + +void +emuxki_reg_write_32(device_config *config, int regno, uint32 value) +{ + write_io_32(config->nabmbar + regno, value); +} + +/* Emu10k1 Low level */ + +uint32 +emuxki_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 +emuxki_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) | + (emuxki_chan_read(config, chano, reg & 0xFFFF) & ~mask); + } + write_io_32(config->nabmbar + EMU_PTR, ptr); + write_io_32(config->nabmbar + EMU_DATA, data); +} + +/* Microcode */ + +void +emuxki_write_micro(device_config *config, uint32 pc, uint32 data) +{ + emuxki_chan_write(config, 0, (IS_AUDIGY(config) ? EMU_A_MICROCODEBASE : + EMU_MICROCODEBASE ) + pc, data); +} + +uint32 +emuxki_read_micro(device_config *config, uint32 pc) +{ + return emuxki_chan_read(config, 0, (IS_AUDIGY(config) ? EMU_A_MICROCODEBASE : + EMU_MICROCODEBASE ) + pc); +} + +void +emuxki_dsp_addop(device_config *config, uint16 *pc, uint8 op, + uint16 r, uint16 a, uint16 x, uint16 y) +{ + if(IS_AUDIGY(config)) { + emuxki_write_micro(config, *pc << 1, + ((x << 12) & EMU_A_DSP_LOWORD_OPX_MASK) | + (y & EMU_A_DSP_LOWORD_OPY_MASK)); + emuxki_write_micro(config, (*pc << 1) + 1, + ((op << 24) & EMU_A_DSP_HIWORD_OPCODE_MASK) | + ((r << 12) & EMU_A_DSP_HIWORD_RESULT_MASK) | + (a & EMU_A_DSP_HIWORD_OPA_MASK)); + } else { + emuxki_write_micro(config, *pc << 1, + ((x << 10) & EMU_DSP_LOWORD_OPX_MASK) | + (y & EMU_DSP_LOWORD_OPY_MASK)); + emuxki_write_micro(config, (*pc << 1) + 1, + ((op << 20) & EMU_DSP_HIWORD_OPCODE_MASK) | + ((r << 10) & EMU_DSP_HIWORD_RESULT_MASK) | + (a & EMU_DSP_HIWORD_OPA_MASK)); + } + (*pc)++; +} + +void +emuxki_dsp_getop(device_config *config, uint16 *pc, uint8 *op, + uint16 *r, uint16 *a, uint16 *x, uint16 *y) +{ + uint32 value; + if(IS_AUDIGY(config)) { + value = emuxki_read_micro(config, *pc << 1); + *x = (value & EMU_A_DSP_LOWORD_OPX_MASK) >> 12; + *y = value & EMU_A_DSP_LOWORD_OPY_MASK; + value = emuxki_read_micro(config, (*pc << 1) + 1); + *op = (value & EMU_A_DSP_HIWORD_OPCODE_MASK) >> 24; + *r = (value & EMU_A_DSP_HIWORD_RESULT_MASK) >> 12; + *a = value & EMU_A_DSP_HIWORD_OPA_MASK; + } else { + value = emuxki_read_micro(config, *pc << 1); + *x = (value & EMU_DSP_LOWORD_OPX_MASK) >> 10; + *y = value & EMU_DSP_LOWORD_OPY_MASK; + value = emuxki_read_micro(config, (*pc << 1) + 1); + *op = (value & EMU_DSP_HIWORD_OPCODE_MASK) >> 20; + *r = (value & EMU_DSP_HIWORD_RESULT_MASK) >> 10; + *a = value & EMU_DSP_HIWORD_OPA_MASK; + } + (*pc)++; +} + +/* Gpr */ + +void +emuxki_write_gpr(device_config *config, uint32 pc, uint32 data) +{ + emuxki_chan_write(config, 0, IS_AUDIGY(config) ? EMU_A_DSP_GPR(pc) : + EMU_DSP_GPR(pc), data); +} + +uint32 +emuxki_read_gpr(device_config *config, uint32 pc) +{ + return emuxki_chan_read(config, 0, IS_AUDIGY(config) ? EMU_A_DSP_GPR(pc) : + EMU_DSP_GPR(pc)); +} + +/* codec */ + +uint16 +emuxki_codec_read(device_config *config, int regno) +{ + write_io_8(config->nabmbar + EMU_AC97ADDR, regno); + return read_io_16(config->nabmbar + EMU_AC97DATA); +} + +void +emuxki_codec_write(device_config *config, int regno, uint16 value) +{ + write_io_8(config->nabmbar + EMU_AC97ADDR, regno); + write_io_16(config->nabmbar + EMU_AC97DATA, value); +} + +/* inte */ + +void +emuxki_inte_enable(device_config *config, uint32 value) +{ + emuxki_reg_write_32(config, EMU_INTE, + emuxki_reg_read_32(config, EMU_INTE) | value); +} + +void +emuxki_inte_disable(device_config *config, uint32 value) +{ + emuxki_reg_write_32(config, EMU_INTE, + emuxki_reg_read_32(config, EMU_INTE) & ~value); +} diff --git a/src/add-ons/kernel/drivers/audio/emuxki/io.h b/src/add-ons/kernel/drivers/audio/emuxki/io.h new file mode 100644 index 0000000000..679c1b1e55 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/io.h @@ -0,0 +1,61 @@ +/* + * Emuxki BeOS Driver for Creative Labs SBLive!/Audigy series + * + * Copyright (c) 2002, 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 emuxki_reg_read_8(device_config *config, int regno); +uint16 emuxki_reg_read_16(device_config *config, int regno); +uint32 emuxki_reg_read_32(device_config *config, int regno); + +void emuxki_reg_write_8(device_config *config, int regno, uint8 value); +void emuxki_reg_write_16(device_config *config, int regno, uint16 value); +void emuxki_reg_write_32(device_config *config, int regno, uint32 value); + +uint32 emuxki_chan_read(device_config *config, uint16 chano, uint32 reg); +void emuxki_chan_write(device_config *config, uint16 chano, uint32 reg, uint32 data); + +void emuxki_dsp_addop(device_config *config, uint16 *pc, uint8 op, + uint16 r, uint16 a, uint16 x, uint16 y); +void emuxki_dsp_getop(device_config *config, uint16 *pc, uint8 *op, + uint16 *r, uint16 *a, uint16 *x, uint16 *y); + +void emuxki_write_gpr(device_config *config, uint32 pc, uint32 data); +uint32 emuxki_read_gpr(device_config *config, uint32 pc); + +uint16 emuxki_codec_read(device_config *config, int regno); +void emuxki_codec_write(device_config *config, int regno, uint16 value); + +void emuxki_inte_enable(device_config *config, uint32 value); +void emuxki_inte_disable(device_config *config, uint32 value); + +#endif diff --git a/src/add-ons/kernel/drivers/audio/emuxki/midi.c b/src/add-ons/kernel/drivers/audio/emuxki/midi.c new file mode 100644 index 0000000000..4b71e4d78b --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/midi.c @@ -0,0 +1,226 @@ +/* + * Emuxki BeOS Driver for Creative Labs SBLive!/Audigy series + * + * Copyright (c) 2002, Jerome Duval (jerome.duval@free.fr) + * + * 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. + * + * + * Original code : Copyright 1999, Be Incorporated. All Rights Reserved. + * This file may be used under the terms of the Be Sample Code License. +*/ + +#include +#include +#include + +#include "emuxki.h" +#include "io.h" +#include "debug.h" +#include "midi_driver.h" +#include "util.h" + +extern generic_mpu401_module * mpu401; + +void +midi_interrupt_op( + int32 op, + void * data) +{ + // dprint seems to be disabled here, will have to enable it to trace + + midi_dev * port = (midi_dev *)data; + TRACE(("port = %p\n", port)); + if (op == B_MPU_401_ENABLE_CARD_INT) { + // sample code + /*cpu_status cp; + ddprintf(("sonic_vibes: B_MPU_401_ENABLE_CARD_INT\n")); + cp = disable_interrupts(); + acquire_spinlock(&port->card->hardware); + increment_interrupt_handler(port->card); + set_direct(port->card, 0x01, 0x00, 0x80); + set_indirect(port->card, 0x2A, 0x04, 0xff); + release_spinlock(&port->card->hardware); + restore_interrupts(cp);*/ + + //real code + cpu_status status; + status = lock(); + emuxki_reg_write_32(&(port->card->config), EMU_INTE, + emuxki_reg_read_32(&(port->card->config), EMU_INTE) | + EMU_INTE_MIDITXENABLE | EMU_INTE_MIDIRXENABLE ); + unlock(status); + } + else if (op == B_MPU_401_DISABLE_CARD_INT) { + // sample code + /* turn off MPU interrupts */ + /*cpu_status cp; + ddprintf(("sonic_vibes: B_MPU_401_DISABLE_CARD_INT\n")); + cp = disable_interrupts(); + acquire_spinlock(&port->card->hardware); + set_direct(port->card, 0x01, 0x80, 0x80);*/ + /* remove interrupt handler if necessary */ + /*decrement_interrupt_handler(port->card); + release_spinlock(&port->card->hardware); + restore_interrupts(cp);*/ + + //real code + cpu_status status; + status = lock(); + emuxki_reg_write_32(&port->card->config, EMU_INTE, + emuxki_reg_read_32(&port->card->config, EMU_INTE) & + ~ (EMU_INTE_MIDITXENABLE | EMU_INTE_MIDIRXENABLE ) ); + unlock(status); + } + TRACE(("midi_interrupt_op() done\n")); +} + +static status_t midi_open(const char *name, uint32 flags, void **cookie); +static status_t midi_close(void *cookie); +static status_t midi_free(void *cookie); +static status_t midi_control(void *cookie, uint32 op, void *data, size_t len); +static status_t midi_read(void *cookie, off_t pos, void *data, size_t *len); +static status_t midi_write(void *cookie, off_t pos, const void *data, size_t *len); + + +device_hooks midi_hooks = { + &midi_open, + &midi_close, + &midi_free, + &midi_control, + &midi_read, + &midi_write, + NULL, /* select */ + NULL, /* deselect */ + NULL, /* readv */ + NULL /* writev */ +}; + +static status_t +midi_open( + const char * name, + uint32 flags, + void ** cookie) +{ + int ix; + int ret; + + LOG(("midi_open()\n")); + + *cookie = NULL; + for (ix=0; ix= num_cards) { + LOG(("bad device\n")); + return ENODEV; + } + + LOG(("mpu401: %p open(): %p driver: %p\n", mpu401, mpu401->open_hook, cards[ix].midi.driver)); + ret = (*mpu401->open_hook)(cards[ix].midi.driver, flags, cookie); + if (ret >= B_OK) { + cards[ix].midi.cookie = *cookie; + atomic_add(&cards[ix].midi.count, 1); + } + LOG(("mpu401: open returns %x / %p\n", ret, *cookie)); + return ret; +} + + +static status_t +midi_close( + void * cookie) +{ + LOG(("midi_close()\n")); + return (*mpu401->close_hook)(cookie); +} + + +static status_t +midi_free( + void * cookie) +{ + int ix; + status_t f; + LOG(("midi_free()\n")); + f = (*mpu401->free_hook)(cookie); + for (ix=0; ixcontrol_hook)(cookie, iop, data, len); +} + + +static status_t +midi_read( + void * cookie, + off_t pos, + void * ptr, + size_t * nread) +{ + return (*mpu401->read_hook)(cookie, pos, ptr, nread); +} + + +static status_t +midi_write( + void * cookie, + off_t pos, + const void * ptr, + size_t * nwritten) +{ + return (*mpu401->write_hook)(cookie, pos, ptr, nwritten); +} + + +bool +midi_interrupt(emuxki_dev *card) +{ + TRACE(("midi_interrupt\n")); + if (!card->midi.driver) { +// kprintf("aiigh\n"); + return false; + } + + return (*mpu401->interrupt_hook)(card->midi.driver); +} + diff --git a/src/add-ons/kernel/drivers/audio/emuxki/midi_driver.h b/src/add-ons/kernel/drivers/audio/emuxki/midi_driver.h new file mode 100644 index 0000000000..147dce4915 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/midi_driver.h @@ -0,0 +1,118 @@ +/* ++++++++++ + FILE: midi_driver.h + REVS: $Revision: 1.1 $ + NAME: herold + DATE: Tue Jun 4 15:23:29 PDT 1996 + + Interface to /dev/midi, the midi driver ++++++ */ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef _MIDI_DRIVER_H +#define _MIDI_DRIVER_H + +#include +#include + +/* ----- + ioctl codes +----- */ + +/* the old opcodes are deprecated, and may or may not work + in newer drivers */ +enum { + B_MIDI_GET_READ_TIMEOUT = B_MIDI_DRIVER_BASE, + B_MIDI_SET_READ_TIMEOUT, + B_MIDI_TIMED_READ, + B_MIDI_TIMED_WRITE, + B_MIDI_WRITE_SYNC, + B_MIDI_WRITE_CLEAR, + B_MIDI_GET_READ_TIMEOUT_OLD = B_DEVICE_OP_CODES_END + 1, + B_MIDI_SET_READ_TIMEOUT_OLD +}; + +/* the default timeout when you open a midi driver */ +#define B_MIDI_DEFAULT_TIMEOUT 1000000000000000LL + +/* Usage: + To read, set "data" to a pointer to your buffer, and "size" to the + maximum size of this buffer. On return, "when" will contain the time + at which the data was received (first byte) and "size" will contain + the actual amount of data read. + Call ioctl(fd, B_MIDI_TIMED_READ, &midi_timed_data, sizeof(midi_timed_data)); + To write, set "when" to when you want the first byte to go out the + wire, set "data" to point to your data, and set "size" to the size + of your data. + Call ioctl(fd, B_MIDI_TIMED_WRITE, &midi_timed_data, sizeof(midi_timed_data)); +*/ +typedef struct { + bigtime_t when; + size_t size; + unsigned char * data; +} midi_timed_data; + + +/* The MIDI parser returns the number of bytes that a message contains, given the */ +/* initial byte. For some messages, this is not known until the second byte is seen. */ +/* For such messages, a state > 0 is returned as well as some count > 0. When state */ +/* is > 0, you should call (*parse) for the next byte as well, which might modify */ +/* the returned message size. Message size will always be returned with the current */ +/* byte being counted as byte 1. A return of 0 means that the byte initiates a new */ +/* message. SysX is handled by returning max_size until the end or next initial message */ +/* is seen. So your loop looks something like: */ +/* + uint32 state = 0; // Only set this to 0 the first time you call the parser. + // preserve the 'state' between invocations of your read() hook. + int todo = 0; + unsigned char * end = in_buf+buf_size + unsigned char * out_buf = in_buf; + while (true) { + uchar byte = read_midi(); + if (!todo || state) { + todo = (*parser->parse)(&state, byte, end-out_buf); + } + if (todo < 1) { + unput_midi(byte); + } else { + *(out_buf++) = byte; + todo--; + } + if (todo < 1 || out_buf >= end) { + received_midi_message(in_buf, out_buf-in_buf); + todo = 0; + } + } + */ + +#define B_MIDI_PARSER_MODULE_NAME "media/midiparser/v1" + +typedef struct _midi_parser_module_info { + module_info minfo; + int (*parse)(uint32 * state, uchar byte, size_t max_size); + int _reserved_; +} midi_parser_module_info; + +#define B_MPU_401_MODULE_NAME "generic/mpu401/v1" + +enum { + B_MPU_401_ENABLE_CARD_INT = 1, + B_MPU_401_DISABLE_CARD_INT +}; +typedef struct _generic_mpu401_module { + module_info minfo; + status_t (*create_device)(int port, void ** out_storage, uint32 workarounds, void (*interrupt_op)(int32 op, void * card), void * card); + status_t (*delete_device)(void * storage); + status_t (*open_hook)(void * storage, uint32 flags, void ** out_cookie); + status_t (*close_hook)(void * cookie); + status_t (*free_hook)(void * cookie); + status_t (*control_hook)(void * cookie, uint32 op, void * data, size_t len); + status_t (*read_hook)(void * cookie, off_t pos, void * data, size_t * len); + status_t (*write_hook)(void * cookie, off_t pos, const void * data, size_t * len); + bool (*interrupt_hook)(void * cookie); + int _reserved_; +} generic_mpu401_module; + +#endif diff --git a/src/add-ons/kernel/drivers/audio/emuxki/multi.c b/src/add-ons/kernel/drivers/audio/emuxki/multi.c new file mode 100644 index 0000000000..fa35054854 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/multi.c @@ -0,0 +1,1281 @@ +/* + * Emuxki BeOS Driver for Creative Labs SBLive!/Audigy series + * + * Copyright (c) 2002, 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 "emuxki.h" +#include "util.h" +#include "io.h" + +static void +emuxki_ac97_get_mix(void *card, const void *cookie, int32 type, float *values) { + emuxki_dev *dev = (emuxki_dev*)card; + ac97_source_info *info = (ac97_source_info *)cookie; + uint16 value, mask; + float gain; + + switch(type) { + case B_MIX_GAIN: + value = emuxki_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 = emuxki_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 = emuxki_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 = emuxki_codec_read(&dev->config, AC97_RECORD_SELECT); + value &= mask; + //PRINT(("B_MIX_MUX value : %u\n", value)); + values[0] = (float)value; + break; + } +} + +static void +emuxki_ac97_set_mix(void *card, const void *cookie, int32 type, float *values) { + emuxki_dev *dev = (emuxki_dev*)card; + ac97_source_info *info = (ac97_source_info *)cookie; + uint16 value, mask; + float gain; + + switch(type) { + case B_MIX_GAIN: + value = emuxki_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)); + emuxki_codec_write(&dev->config, info->reg, value); + break; + case B_MIX_MUTE: + mask = ((1 << 1) - 1) << 15; + value = emuxki_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)); + emuxki_codec_write(&dev->config, info->reg, value); + break; + case B_MIX_MICBOOST: + mask = ((1 << 1) - 1) << 6; + value = emuxki_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)); + emuxki_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)); + emuxki_codec_write(&dev->config, AC97_RECORD_SELECT, value); + break; + } + +} + +static void +emuxki_gpr_get_mix(void *card, const void *cookie, int32 type, float *values) { + emuxki_gpr_get((emuxki_dev*)card, (emuxki_gpr *)cookie, type, values); +} + +static void +emuxki_gpr_set_mix(void *card, const void *cookie, int32 type, float *values) { + emuxki_gpr_set((emuxki_dev*)card, (emuxki_gpr *)cookie, type, values); +} + +static void +emuxki_parameter_get_mix(void *card, const void *cookie, int32 type, float *values) { + int32 value; + emuxki_parameter_get((emuxki_dev*)card, cookie, type, &value); + values[0] = (float)value; +} + +static void +emuxki_parameter_set_mix(void *card, const void *cookie, int32 type, float *values) { + int32 value; + value = (int32)values[0]; + emuxki_parameter_set((emuxki_dev*)card, cookie, type, &value); +} + +static int32 +emuxki_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 void +emuxki_create_gpr_control(multi_dev *multi, int32 *index, int32 parent, int32 string, + const emuxki_gpr *gpr) { + int32 i = *index, id; + multi_mixer_control control; + + control.mix_control.master = EMU_MULTI_CONTROL_MASTERID; + control.mix_control.parent = parent; + control.cookie = gpr; + control.get = &emuxki_gpr_get_mix; + control.set = &emuxki_gpr_set_mix; + control.mix_control.u.gain.min_gain = gpr->min_gain; + control.mix_control.u.gain.max_gain = gpr->max_gain; + control.mix_control.u.gain.granularity = gpr->granularity; + + if(gpr->type & EMU_MIX_GAIN) { + if(gpr->type & EMU_MIX_MUTE) { + control.mix_control.id = EMU_MULTI_CONTROL_FIRSTID + i; + control.mix_control.flags = B_MULTI_MIX_ENABLE; + control.mix_control.string = S_MUTE; + control.type = EMU_MIX_MUTE; + multi->controls[i] = control; + i++; + } + + control.mix_control.id = EMU_MULTI_CONTROL_FIRSTID + i; + control.mix_control.flags = B_MULTI_MIX_GAIN; + strcpy(control.mix_control.name, gpr->name); + control.type = EMU_MIX_GAIN; + multi->controls[i] = control; + id = control.mix_control.id; + i++; + + if(gpr->type & EMU_MIX_STEREO) { + control.mix_control.id = EMU_MULTI_CONTROL_FIRSTID + i; + control.mix_control.master = id; + multi->controls[i] = control; + i++; + } + } + *index = i; +} + +static status_t +emuxki_create_controls_list(multi_dev *multi) +{ + uint32 i = 0, index = 0, count, id, parent, parent2, parent3; + emuxki_dev *card = (emuxki_dev*)multi->card; + const ac97_source_info *info; + + parent = emuxki_create_group_control(multi, &index, 0, 0, "Playback"); + + for(i=EMU_GPR_FIRST_MIX; igpr_count; i++) { + const emuxki_gpr *gpr = &card->gpr[i]; + if((gpr->type & EMU_MIX_PLAYBACK) == 0) + continue; + + parent2 = emuxki_create_group_control(multi, &index, parent, 0, gpr->name); + + emuxki_create_gpr_control(multi, &index, parent2, 0, gpr); + if(gpr->type & EMU_MIX_GAIN && gpr->type & EMU_MIX_STEREO) + i++; + } + + parent = emuxki_create_group_control(multi, &index, 0, 0, "Record"); + + for(i=EMU_GPR_FIRST_MIX; igpr_count; i++) { + const emuxki_gpr *gpr = &card->gpr[i]; + if((gpr->type & EMU_MIX_RECORD) == 0) + continue; + parent2 = emuxki_create_group_control(multi, &index, parent, 0, gpr->name); + + emuxki_create_gpr_control(multi, &index, parent2, 0, gpr); + if(gpr->type & EMU_MIX_GAIN && gpr->type & EMU_MIX_STEREO) + i++; + } + + /* AC97 Record */ + info = &source_info[0]; + PRINT(("name : %s\n", info->name)); + + parent2 = emuxki_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 = &emuxki_ac97_get_mix; + multi->controls[index].set = &emuxki_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 = &emuxki_ac97_get_mix; + multi->controls[index].set = &emuxki_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 = &emuxki_ac97_get_mix; + multi->controls[index].set = &emuxki_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 = &emuxki_ac97_get_mix; + multi->controls[index].set = &emuxki_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 = emuxki_create_group_control(multi, &index, 0, 0, "AC97 Mixer"); + + count = source_info_size; + if(IS_AUDIGY2(&card->config)) + count = 1; + if(!IS_LIVE_5_1(&card->config) && !IS_AUDIGY(&card->config)) + count--; + + for(i=1; i < count ; i++) { + info = &source_info[i]; + PRINT(("name : %s\n", info->name)); + + parent2 = emuxki_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 = &emuxki_ac97_get_mix; + multi->controls[index].set = &emuxki_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 = &emuxki_ac97_get_mix; + multi->controls[index].set = &emuxki_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 = &emuxki_ac97_get_mix; + multi->controls[index].set = &emuxki_ac97_set_mix; + index++; + } + } + } + + parent = emuxki_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 = &emuxki_ac97_get_mix; + multi->controls[index].set = &emuxki_ac97_set_mix; + index++; + } + + if(true) { + 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, "Enable digital"); + multi->controls[index].cookie = NULL; + multi->controls[index].type = EMU_DIGITAL_MODE; + multi->controls[index].get = &emuxki_parameter_get_mix; + multi->controls[index].set = &emuxki_parameter_set_mix; + index++; + } + + if(true) { + 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 = parent; + strcpy(multi->controls[index].mix_control.name, "Audio mode"); + multi->controls[index].cookie = NULL; + multi->controls[index].type = EMU_AUDIO_MODE; + multi->controls[index].get = &emuxki_parameter_get_mix; + multi->controls[index].set = &emuxki_parameter_set_mix; + parent2 = 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 = parent2; + strcpy(multi->controls[index].mix_control.name, "2.0"); + 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 = parent2; + strcpy(multi->controls[index].mix_control.name, "4.0"); + 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 = parent2; + strcpy(multi->controls[index].mix_control.name, "5.1"); + index++; + } + + multi->control_count = index; + PRINT(("multi->control_count %u\n", multi->control_count)); + return B_OK; +} + +static status_t +emuxki_get_mix(emuxki_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(("emuxki_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 +emuxki_set_mix(emuxki_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(("emuxki_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(("emuxki_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 +emuxki_list_mix_controls(emuxki_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(emuxki_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 +emuxki_list_mix_connections(emuxki_dev *card, multi_mix_connection_info * data) +{ + return B_ERROR; +} + +static status_t +emuxki_list_mix_channels(emuxki_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 +emuxki_create_channels_list(multi_dev *multi) +{ + emuxki_stream *stream; + uint32 index, i, mode, designations, nchannels; + 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=EMU_USE_PLAY; mode!=-1; + mode = (mode == EMU_USE_PLAY) ? EMU_USE_RECORD : -1) { + LIST_FOREACH(stream, &((emuxki_dev*)multi->card)->streams, next) { + if ((stream->use & mode) == 0) + continue; + + nchannels = stream->nmono + 2 * stream->nstereo; + if(nchannels == 2) + designations = B_CHANNEL_STEREO_BUS; + else + designations = B_CHANNEL_SURROUND_BUS; + + for(i=0; ioutput_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 +emuxki_get_description(emuxki_dev *card, multi_description *data) +{ + uint32 size; + + data->interface_version = B_CURRENT_INTERFACE_VERSION; + data->interface_minimum = B_CURRENT_INTERFACE_VERSION; + + if(IS_AUDIGY2(&card->config)) + strncpy(data->friendly_name, FRIENDLY_NAME_AUDIGY2, 32); + else if(IS_AUDIGY(&card->config)) + strncpy(data->friendly_name, FRIENDLY_NAME_AUDIGY, 32); + else if(IS_LIVE_5_1(&card->config)) + strncpy(data->friendly_name, FRIENDLY_NAME_LIVE_5_1, 32); + else + strncpy(data->friendly_name, FRIENDLY_NAME_LIVE, 32); + 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 +emuxki_get_enabled_channels(emuxki_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 +emuxki_set_enabled_channels(emuxki_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 +emuxki_get_global_format(emuxki_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 +emuxki_get_buffers(emuxki_dev *card, multi_buffer_list *data) +{ + uint32 i, j, pchannels, pchannels2, rchannels, rchannels2; + + 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->nmono + card->pstream->nstereo * 2; + pchannels2 = card->pstream2->nmono + card->pstream2->nstereo * 2; + rchannels = card->rstream->nmono + card->rstream->nstereo * 2; + rchannels2 = card->rstream2->nmono + card->rstream2->nstereo * 2; + + if (data->request_playback_buffers < BUFFER_COUNT || + data->request_playback_channels < (pchannels + pchannels2) || + data->request_record_buffers < BUFFER_COUNT || + data->request_record_channels < (rchannels + rchannels2)) { + 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 + pchannels2; /* 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); + + for(i=0; ipstream2, j, i, + &data->playback_buffers[i][pchannels + j].base, + &data->playback_buffers[i][pchannels + j].stride); + + data->return_record_buffers = BUFFER_COUNT; + data->return_record_channels = rchannels + rchannels2; + 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); + + for(i=0; irstream2, j, i, + &data->record_buffers[i][rchannels + j].base, + &data->record_buffers[i][rchannels + j].stride); + + return B_OK; +} + + +void +emuxki_play_inth(void* inthparams) +{ + emuxki_stream *stream = (emuxki_stream *)inthparams; + //int32 count; + + acquire_spinlock(&slock); + stream->real_time = system_time(); + stream->frames_count += BUFFER_FRAMES; + stream->buffer_cycle = stream->first_voice->trigblk; + 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 +emuxki_record_inth(void* inthparams) +{ + emuxki_stream *stream = (emuxki_stream *)inthparams; + //int32 count; + + //TRACE(("emuxki_record_inth\n")); + + acquire_spinlock(&slock); + stream->real_time = system_time(); + stream->frames_count += BUFFER_FRAMES; + stream->buffer_cycle = (stream->first_voice->trigblk + + stream->first_voice->blkmod -1) % stream->first_voice->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 +emuxki_buffer_exchange(emuxki_dev *card, multi_buffer_info *data) +{ + cpu_status status; + emuxki_stream *pstream, *rstream; + + data->flags = B_MULTI_BUFFER_PLAYBACK | B_MULTI_BUFFER_RECORD; + + if (!(card->pstream->state & EMU_STATE_STARTED)) + emuxki_stream_start(card->pstream, emuxki_play_inth, card->pstream); + + if (!(card->pstream2->state & EMU_STATE_STARTED)) + emuxki_stream_start(card->pstream2, emuxki_play_inth, card->pstream2); + + if (!(card->rstream->state & EMU_STATE_STARTED)) + emuxki_stream_start(card->rstream, emuxki_record_inth, card->rstream); + + if (!(card->rstream2->state & EMU_STATE_STARTED)) + emuxki_stream_start(card->rstream2, emuxki_record_inth, card->rstream2); + + + 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")); + LOG(("EMU_IPR = %#08x\n",emuxki_reg_read_32(&card->config, EMU_IPR))); + LOG(("EMU_INTE = %#08x\n",emuxki_reg_read_32(&card->config, EMU_INTE))); + LOG(("EMU_HCFG = %#08x\n",emuxki_reg_read_32(&card->config, EMU_HCFG))); + } + + status = lock(); + + LIST_FOREACH(pstream, &card->streams, next) { + if ((pstream->use & EMU_USE_PLAY) == 0 || + (pstream->state & EMU_STATE_STARTED) == 0) + continue; + if(pstream->update_needed) + break; + } + + LIST_FOREACH(rstream, &card->streams, next) { + if ((rstream->use & EMU_USE_RECORD) == 0 || + (rstream->state & EMU_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 +emuxki_buffer_force_stop(emuxki_dev *card) +{ + //emuxki_voice_halt(card->pvoice); + return B_OK; +} + +static status_t +emuxki_multi_control(void *cookie, uint32 op, void *data, size_t length) +{ + emuxki_dev *card = (emuxki_dev *)cookie; + + switch (op) { + case B_MULTI_GET_DESCRIPTION: + LOG(("B_MULTI_GET_DESCRIPTION\n")); + return emuxki_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 emuxki_get_enabled_channels(card, (multi_channel_enable *)data); + case B_MULTI_SET_ENABLED_CHANNELS: + LOG(("B_MULTI_SET_ENABLED_CHANNELS\n")); + return emuxki_set_enabled_channels(card, (multi_channel_enable *)data); + case B_MULTI_GET_GLOBAL_FORMAT: + LOG(("B_MULTI_GET_GLOBAL_FORMAT\n")); + return emuxki_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 emuxki_get_mix(card, (multi_mix_value_info *)data); + case B_MULTI_SET_MIX: + LOG(("B_MULTI_SET_MIX\n")); + return emuxki_set_mix(card, (multi_mix_value_info *)data); + case B_MULTI_LIST_MIX_CHANNELS: + LOG(("B_MULTI_LIST_MIX_CHANNELS\n")); + return emuxki_list_mix_channels(card, (multi_mix_channel_info *)data); + case B_MULTI_LIST_MIX_CONTROLS: + LOG(("B_MULTI_LIST_MIX_CONTROLS\n")); + return emuxki_list_mix_controls(card, (multi_mix_control_info *)data); + case B_MULTI_LIST_MIX_CONNECTIONS: + LOG(("B_MULTI_LIST_MIX_CONNECTIONS\n")); + return emuxki_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 emuxki_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 emuxki_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 emuxki_buffer_force_stop(card); + } + LOG(("ERROR: unknown multi_control %#x\n",op)); + return B_ERROR; +} + +static status_t emuxki_open(const char *name, uint32 flags, void** cookie); +static status_t emuxki_close(void* cookie); +static status_t emuxki_free(void* cookie); +static status_t emuxki_control(void* cookie, uint32 op, void* arg, size_t len); +static status_t emuxki_read(void* cookie, off_t position, void *buf, size_t* num_bytes); +static status_t emuxki_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes); + +device_hooks multi_hooks = { + emuxki_open, /* -> open entry point */ + emuxki_close, /* -> close entry point */ + emuxki_free, /* -> free cookie */ + emuxki_control, /* -> control entry point */ + emuxki_read, /* -> read entry point */ + emuxki_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 +emuxki_open(const char *name, uint32 flags, void** cookie) +{ + emuxki_dev *card = NULL; + emuxki_recparams recparams; + int ix; + + LOG(("open()\n")); + + for (ix=0; ixpstream !=NULL) + return B_ERROR; + if(card->pstream2 !=NULL) + return B_ERROR; + if(card->rstream !=NULL) + return B_ERROR; + if(card->rstream2 !=NULL) + return B_ERROR; + + *cookie = card; + card->multi.card = card; + + LOG(("voice_new\n")); + + card->rstream2 = emuxki_stream_new(card, EMU_USE_RECORD, BUFFER_FRAMES, BUFFER_COUNT); + card->rstream = emuxki_stream_new(card, EMU_USE_RECORD, BUFFER_FRAMES, BUFFER_COUNT); + card->pstream2 = emuxki_stream_new(card, EMU_USE_PLAY, BUFFER_FRAMES, BUFFER_COUNT); + card->pstream = emuxki_stream_new(card, EMU_USE_PLAY, BUFFER_FRAMES, BUFFER_COUNT); + + card->buffer_ready_sem = create_sem(0,"pbuffer ready"); + + LOG(("voice_setaudio\n")); + + emuxki_stream_set_audioparms(card->pstream, true, 2, true, 48000); + emuxki_stream_set_audioparms(card->pstream2, false, 4, true, 48000); + emuxki_stream_set_audioparms(card->rstream, true, 2, true, 48000); + emuxki_stream_set_audioparms(card->rstream2, true, 2, true, 48000); + recparams.efx_voices[0] = 3; // channels 1,2 + //emuxki_voice_set_recparms(card->rvoice, EMU_RECSRC_FX, &recparams); + emuxki_stream_set_recparms(card->rstream, EMU_RECSRC_ADC, NULL); + emuxki_stream_set_recparms(card->rstream2, EMU_RECSRC_FX, &recparams); + + card->pstream->first_channel = 0; + card->pstream2->first_channel = 2; + card->rstream->first_channel = 6; + card->rstream2->first_channel = 8; + + emuxki_stream_commit_parms(card->pstream); + emuxki_stream_commit_parms(card->pstream2); + emuxki_stream_commit_parms(card->rstream); + emuxki_stream_commit_parms(card->rstream2); + + emuxki_create_channels_list(&card->multi); + + return B_OK; +} + +static status_t +emuxki_close(void* cookie) +{ + //emuxki_dev *card = cookie; + LOG(("close()\n")); + + return B_OK; +} + +static status_t +emuxki_free(void* cookie) +{ + emuxki_dev *card = cookie; + emuxki_stream *stream; + LOG(("free()\n")); + + if (card->buffer_ready_sem > B_OK) + delete_sem(card->buffer_ready_sem); + + LIST_FOREACH(stream, &card->streams, next) { + emuxki_stream_halt(stream); + } + + while(!LIST_EMPTY(&card->streams)) { + emuxki_stream_delete(LIST_FIRST(&card->streams)); + } + + return B_OK; +} + +static status_t +emuxki_control(void* cookie, uint32 op, void* arg, size_t len) +{ + return emuxki_multi_control(cookie, op, arg, len); +} + +static status_t +emuxki_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 +emuxki_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/emuxki/multi.h b/src/add-ons/kernel/drivers/audio/emuxki/multi.h new file mode 100644 index 0000000000..c1f1ae8092 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/multi.h @@ -0,0 +1,65 @@ +/* + * BeOS Driver for Creative Labs SBLive! series + * + * Copyright (c) 2002, 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_FRAMES 32 // this is the minimum which works +#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/emuxki/multi_audio.h b/src/add-ons/kernel/drivers/audio/emuxki/multi_audio.h new file mode 100644 index 0000000000..5964973d61 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/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/emuxki/queue.h b/src/add-ons/kernel/drivers/audio/emuxki/queue.h new file mode 100644 index 0000000000..396c70fb2e --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/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/emuxki/util.c b/src/add-ons/kernel/drivers/audio/emuxki/util.c new file mode 100644 index 0000000000..d71521def3 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/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/emuxki/util.h b/src/add-ons/kernel/drivers/audio/emuxki/util.h new file mode 100644 index 0000000000..7a367042ca --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/emuxki/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