diff --git a/src/add-ons/kernel/drivers/audio/ac97/Jamfile b/src/add-ons/kernel/drivers/audio/ac97/Jamfile index 7722dcc0a3..2da9220303 100644 --- a/src/add-ons/kernel/drivers/audio/ac97/Jamfile +++ b/src/add-ons/kernel/drivers/audio/ac97/Jamfile @@ -1,5 +1,6 @@ SubDir OBOS_TOP src add-ons kernel drivers audio ac97 ; +SubInclude OBOS_TOP src add-ons kernel drivers audio ac97 auich ; SubInclude OBOS_TOP src add-ons kernel drivers audio ac97 auvia ; SubInclude OBOS_TOP src add-ons kernel drivers audio ac97 ich ; # SubInclude OBOS_TOP src add-ons kernel drivers audio ac97 ichaudio ; diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/Jamfile b/src/add-ons/kernel/drivers/audio/ac97/auich/Jamfile new file mode 100644 index 0000000000..882be1af5f --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/Jamfile @@ -0,0 +1,30 @@ +SubDir OBOS_TOP src add-ons kernel drivers audio ac97 auich ; + +R5KernelAddon auich : kernel drivers bin : + ac97.c + debug.c + auich.c + io.c + multi.c + util.c +; + +# Link to kernel/drivers/dev/audio/multi +{ + local dir = [ FDirName $(OBOS_ADDON_DIR) kernel drivers dev audio multi ] ; + local instDriver = auich ; + MakeLocate $(instDriver) : $(dir) ; + RelSymLink $(instDriver) : auich ; +} + +Package haiku-auich-cvs + : + auich + : + boot home config add-ons kernel drivers bin ; + +Package haiku-auich-cvs + : + auich + : + boot home config add-ons kernel drivers dev audio multi ; diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/ac97.c b/src/add-ons/kernel/drivers/audio/ac97/auich/ac97.c new file mode 100644 index 0000000000..fe81c3becb --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/ac97.c @@ -0,0 +1,296 @@ +/* + * Auich BeOS Driver for Intel Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include "ac97.h" + +#define REVERSE_EAMP_POLARITY 0 + +#include "debug.h" +#include "io.h" + +#define B_UTF8_REGISTERED "\xC2\xAE" + +const char * stereo_enhancement_technique[] = +{ + "No 3D Stereo Enhancement", + "Analog Devices", + "Creative Technology", + "National Semiconductor", + "Yamaha", + "BBE Sound", + "Crystal Semiconductor", + "Qsound Labs", + "Spatializer Audio Laboratories", + "SRS Labs", + "Platform Tech", + "AKM Semiconductor", + "Aureal", + "Aztech Labs", + "Binaura", + "ESS Technology", + "Harman International", + "Nvidea", + "Philips", + "Texas Instruments", + "VLSI Technology", + "TriTech", + "Realtek", + "Samsung", + "Wolfson Microelectronics", + "Delta Integration", + "SigmaTel", + "KS Waves", + "Rockwell", + "Unknown (29)", + "Unknown (30)", + "Unknown (31)" +}; + +typedef void (* codec_init)(device_config *); +typedef void (* codec_amp_enable)(device_config *, bool); + +typedef struct codec_ops_tag +{ + codec_init init; + codec_amp_enable amp_enable; +} codec_ops; + +typedef struct codec_table_tag +{ + uint32 id; + uint32 mask; + codec_ops *ops; + const char *info; +} codec_table; + +void default_init(device_config *); +void ad1886_init(device_config *); + +void default_amp_enable(device_config *, bool); +void cs4299_amp_enable(device_config *, bool); + +codec_ops default_ops = { default_init, default_amp_enable }; +codec_ops ad1886_ops = { ad1886_init, default_amp_enable }; +codec_ops cs4299_ops = { default_init, cs4299_amp_enable }; + +codec_table codecs[] = +{ + /* Vendor ID and description imported from FreeBSD src/sys/dev/sound/pcm/ac97.c */ + { 0x414b4d00, 0xffffffff, &default_ops, "Asahi Kasei AK4540" }, + { 0x414b4d01, 0xffffffff, &default_ops, "Asahi Kasei AK4542" }, + { 0x414b4d02, 0xffffffff, &default_ops, "Asahi Kasei AK4543" }, + { 0x43525900, 0xffffffff, &default_ops, "Cirrus Logic CS4297" }, + { 0x43525903, 0xffffffff, &default_ops, "Cirrus Logic CS4297" }, + { 0x43525913, 0xffffffff, &default_ops, "Cirrus Logic CS4297A" }, + { 0x43525914, 0xffffffff, &default_ops, "Cirrus Logic CS4297B" }, + { 0x43525923, 0xffffffff, &default_ops, "Cirrus Logic CS4294C" }, + { 0x4352592b, 0xffffffff, &default_ops, "Cirrus Logic CS4298C" }, + { 0x43525931, 0xffffffff, &cs4299_ops, "Cirrus Logic CS4299A" }, + { 0x43525933, 0xffffffff, &cs4299_ops, "Cirrus Logic CS4299C" }, + { 0x43525934, 0xffffffff, &cs4299_ops, "Cirrus Logic CS4299D" }, + { 0x43525941, 0xffffffff, &default_ops, "Cirrus Logic CS4201A" }, + { 0x43525951, 0xffffffff, &default_ops, "Cirrus Logic CS4205A" }, + { 0x43525961, 0xffffffff, &default_ops, "Cirrus Logic CS4291A" }, + { 0x45838308, 0xffffffff, &default_ops, "ESS Technology ES1921" }, + { 0x49434511, 0xffffffff, &default_ops, "ICEnsemble ICE1232" }, + { 0x4e534331, 0xffffffff, &default_ops, "National Semiconductor LM4549" }, + { 0x83847600, 0xffffffff, &default_ops, "SigmaTel STAC9700/9783/9784" }, + { 0x83847604, 0xffffffff, &default_ops, "SigmaTel STAC9701/9703/9704/9705" }, + { 0x83847605, 0xffffffff, &default_ops, "SigmaTel STAC9704" }, + { 0x83847608, 0xffffffff, &default_ops, "SigmaTel STAC9708/9711" }, + { 0x83847609, 0xffffffff, &default_ops, "SigmaTel STAC9721/9723" }, + { 0x83847644, 0xffffffff, &default_ops, "SigmaTel STAC9744" }, + { 0x83847656, 0xffffffff, &default_ops, "SigmaTel STAC9756/9757" }, + { 0x53494c22, 0xffffffff, &default_ops, "Silicon Laboratory Si3036" }, + { 0x53494c23, 0xffffffff, &default_ops, "Silicon Laboratory Si3038" }, + { 0x54524103, 0xffffffff, &default_ops, "TriTech TR?????" }, + { 0x54524106, 0xffffffff, &default_ops, "TriTech TR28026" }, + { 0x54524108, 0xffffffff, &default_ops, "TriTech TR28028" }, + { 0x54524123, 0xffffffff, &default_ops, "TriTech TR28602" }, + { 0x574d4c00, 0xffffffff, &default_ops, "Wolfson WM9701A" }, + { 0x574d4c03, 0xffffffff, &default_ops, "Wolfson WM9703/9704" }, + { 0x574d4c04, 0xffffffff, &default_ops, "Wolfson WM9704 (quad)" }, + /* Assembled from datasheets: */ + { 0x41445303, 0xffffffff, &default_ops, "Analog Devices AD1819B SoundPort"B_UTF8_REGISTERED }, + { 0x41445340, 0xffffffff, &default_ops, "Analog Devices AD1881 SoundMAX"B_UTF8_REGISTERED }, + { 0x41445348, 0xffffffff, &default_ops, "Analog Devices AD1881A SoundMAX"B_UTF8_REGISTERED }, + { 0x41445360, 0xffffffff, &default_ops, "Analog Devices AD1885 SoundMAX"B_UTF8_REGISTERED }, + { 0x41445361, 0xffffffff, &ad1886_ops, "Analog Devices AD1886 SoundMAX"B_UTF8_REGISTERED }, + { 0x41445362, 0xffffffff, &default_ops, "Analog Devices AD1887 SoundMAX"B_UTF8_REGISTERED }, + { 0x41445363, 0xffffffff, &default_ops, "Analog Devices AD1886A SoundMAX"B_UTF8_REGISTERED }, + { 0x41445371, 0xffffffff, &default_ops, "Analog Devices AD1981A SoundMAX"B_UTF8_REGISTERED }, + { 0x41445372, 0xffffffff, &default_ops, "Analog Devices AD1981A SoundMAX"B_UTF8_REGISTERED }, + { 0x414c4320, 0xfffffff0, &default_ops, "Avance Logic (Realtek) ALC100/ALC100P, RL5383/RL5522" }, + { 0x414c4730, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC101" }, +#if 0 + { 0x414c4710, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC200/ALC200A" }, /* datasheet says id2 = 4710 */ + { 0x414c4710, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC201/ALC201A" }, /* 4710 or 4720 */ + { 0x414c4720, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC650" }, /* datasheet says id2 = 4720 */ +#else + { 0x414c4710, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC200/ALC200A or ALC201/ALC201A" }, + { 0x414c4720, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC650 or ALC201/ALC201A" }, +#endif + { 0x414c4740, 0xffffffff, &default_ops, "Avance Logic (Realtek) ALC202/ALC202A" }, + /* Vendors only: */ + { 0x41445300, 0xffffff00, &default_ops, "Analog Devices" }, + { 0x414b4d00, 0xffffff00, &default_ops, "Asahi Kasei" }, + { 0x414c4700, 0xffffff00, &default_ops, "Avance Logic (Realtek)" }, + { 0x43525900, 0xffffff00, &default_ops, "Cirrus Logic" }, + { 0x45838300, 0xffffff00, &default_ops, "ESS Technology" }, + { 0x49434500, 0xffffff00, &default_ops, "ICEnsemble" }, + { 0x4e534300, 0xffffff00, &default_ops, "National Semiconductor" }, + { 0x83847600, 0xffffff00, &default_ops, "SigmaTel" }, + { 0x53494c00, 0xffffff00, &default_ops, "Silicon Laboratory" }, + { 0x54524100, 0xffffff00, &default_ops, "TriTech" }, + { 0x574d4c00, 0xffffff00, &default_ops, "Wolfson" }, + { 0x00000000, 0x00000000, &default_ops, "Unknown" } /* must be last one, matches every codec */ +}; + +static 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 = auich_codec_read(config, AC97_RESET); + data = (data >> 10) & 31; + return stereo_enhancement_technique[data]; +} + +const char * +ac97_get_vendor_id_description(device_config *config) +{ + uint32 id = ac97_get_vendor_id(config); + 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 = auich_codec_read(config, AC97_VENDOR_ID1); + data2 = auich_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); + + auich_codec_write(config, AC97_EXTENDED_AUDIO_STATUS, + auich_codec_read(config, AC97_EXTENDED_AUDIO_STATUS) | 1); + +} + +void default_init(device_config *config) +{ + LOG(("default_init\n")); +} + +void ad1886_init(device_config *config) +{ + LOG(("ad1886_init\n")); + auich_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",auich_codec_read(config, AC97_POWERDOWN))); + #if REVERSE_EAMP_POLARITY + yesno = !yesno; + LOG(("using reverse eamp polarity\n")); + #endif + if (yesno) + auich_codec_write(config, AC97_POWERDOWN, auich_codec_read(config, AC97_POWERDOWN) & ~0x8000); /* switch on (low active) */ + else + auich_codec_write(config, AC97_POWERDOWN, auich_codec_read(config, AC97_POWERDOWN) | 0x8000); /* switch off */ + LOG(("powerdown register is = %#04x\n", auich_codec_read(config, AC97_POWERDOWN))); +} + +void cs4299_amp_enable(device_config *config, bool yesno) +{ + LOG(("cs4299_amp_enable\n")); + if (yesno) + auich_codec_write(config, 0x68, 0x8004); + else + auich_codec_write(config, 0x68, 0); +} + +const ac97_source_info source_info[] = { + { "Record", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO|B_MIX_RECORDMUX, 100, AC97_RECORD_GAIN, 0x8000, 4, 0, 1, 0, 0.0, 22.5, 1.5 }, + { "Master", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 101, AC97_MASTER_VOLUME, 0x8000, 5, 0, 1, 1,-46.5, 0.0, 1.5 }, + //{ "Bass/Trebble", B_MIX_GAIN|B_MIX_STEREO, 102, AC97_MASTER_TONE, 0x0f0f, 4, 0, 1, 1,-12.0, 10.5, 1.5 }, + //{ "Aux Out", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 103, AC97_AUX_OUT_VOLUME, 0x8000, 5, 0, 1, 1,-46.5, 0.0, 1.5 }, + { "PCM Out", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 104, AC97_PCM_OUT_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 }, + { "CD", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 105, AC97_CD_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 }, + { "Aux In", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 106, AC97_AUX_IN_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 }, + { "TAD", B_MIX_GAIN|B_MIX_MUTE|B_MIX_MONO, 107, AC97_PHONE_VOLUME, 0x8008, 5, 0, 1, 1,-34.5, 12.0, 1.5 }, + { "Mic", B_MIX_GAIN|B_MIX_MUTE|B_MIX_MONO|B_MIX_MICBOOST, 108, AC97_MIC_VOLUME, 0x8008, 5, 0, 1, 1,-34.5, 12.0, 1.5 }, + { "Line In", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 109, AC97_LINE_IN_VOLUME, 0x8808, 5, 0, 1, 1,-34.5, 12.0, 1.5 }, + //{ "Center/Lfe", B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 111, AC97_CENTER_LFE_VOLUME, 0x8080, 5, 0, 1, 1,-46.5, 0.0, 1.5 }, + { "Center/Lfe" /* should be "Surround" but no */, B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 110, AC97_SURROUND_VOLUME, 0x8080, 5, 0, 1, 1,-46.5, 0.0, 1.5 } +}; + +const int32 source_info_size = (sizeof(source_info)/sizeof(source_info[0])); diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/ac97.h b/src/add-ons/kernel/drivers/audio/ac97/auich/ac97.h new file mode 100644 index 0000000000..9972b42a72 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/ac97.h @@ -0,0 +1,107 @@ +/* + * Auich BeOS Driver for Intel Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _AC97_H_ +#define _AC97_H_ + +#include "config.h" + +enum AC97_REGISTER { + AC97_RESET = 0x00, + AC97_MASTER_VOLUME = 0x02, + AC97_AUX_OUT_VOLUME = 0x04, + AC97_MONO_VOLUME = 0x06, + AC97_MASTER_TONE = 0x08, + AC97_PC_BEEP_VOLUME = 0x0A, + AC97_PHONE_VOLUME = 0x0C, + AC97_MIC_VOLUME = 0x0E, + AC97_LINE_IN_VOLUME = 0x10, + AC97_CD_VOLUME = 0x12, + AC97_VIDEO_VOLUME = 0x14, + AC97_AUX_IN_VOLUME = 0x16, + AC97_PCM_OUT_VOLUME = 0x18, + AC97_RECORD_SELECT = 0x1A, + AC97_RECORD_GAIN = 0x1C, + AC97_RECORD_GAIN_MIC = 0x1E, + AC97_GENERAL_PURPOSE = 0x20, + AC97_3D_CONTROL = 0x22, + AC97_PAGING = 0x24, + AC97_POWERDOWN = 0x26, + AC97_EXTENDED_AUDIO_ID = 0x28, + AC97_EXTENDED_AUDIO_STATUS = 0x2A, + AC97_PCM_FRONT_DAC_RATE = 0x2C, + AC97_PCM_SURR_DAC_RATE = 0x2E, + AC97_PCM_LFE_DAC_RATE = 0x30, + AC97_PCM_LR_ADC_RATE = 0x32, + AC97_MIC_ADC_RATE = 0x34, + AC97_CENTER_LFE_VOLUME = 0x36, + AC97_SURROUND_VOLUME = 0x38, + AC97_SPDIF_CONTROL = 0x3A, + AC97_VENDOR_ID1 = 0x7C, + AC97_VENDOR_ID2 = 0x7E +}; + +const char * ac97_get_3d_stereo_enhancement(device_config *config); +const char * ac97_get_vendor_id_description(device_config *config); +uint32 ac97_get_vendor_id(device_config *config); +void ac97_init(device_config *config); + +void ac97_amp_enable(device_config *config, bool yesno); + +typedef enum { + B_MIX_GAIN = 1 << 0, + B_MIX_MUTE = 1 << 1, + B_MIX_MONO = 1 << 2, + B_MIX_STEREO = 1 << 3, + B_MIX_MUX = 1 << 4, + B_MIX_MICBOOST = 1 << 5, + B_MIX_RECORDMUX = 1 << 6 +} ac97_mixer_type; + +typedef struct _ac97_source_info { + const char *name; + ac97_mixer_type type; + + int32 id; + uint8 reg; + uint16 default_value; + uint8 bits:3; + uint8 ofs:4; + uint8 mute:1; + uint8 polarity:1; // max_gain -> 0 + float min_gain; + float max_gain; + float granularity; +} ac97_source_info; + +extern const ac97_source_info source_info[]; +extern const int32 source_info_size; + +#endif diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/auich.c b/src/add-ons/kernel/drivers/audio/ac97/auich/auich.c new file mode 100644 index 0000000000..386cec2daf --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/auich.c @@ -0,0 +1,818 @@ +/* + * Auich BeOS Driver for Intel Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + + * 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 "auich.h" +#include "debug.h" +#include "config.h" +#include "util.h" +#include "io.h" +#include +#include +#include +#include "ac97.h" + +status_t init_hardware(void); +status_t init_driver(void); +void uninit_driver(void); +const char ** publish_devices(void); +device_hooks * find_device(const char *); +int32 auich_int(void *arg); +status_t auich_init(auich_dev * card); + +static char pci_name[] = B_PCI_MODULE_NAME; +pci_module_info *pci; + +int32 num_cards; +auich_dev cards[NUM_CARDS]; +int32 num_names; +char * names[NUM_CARDS*20+1]; + +extern device_hooks multi_hooks; + +/* The SIS7012 chipset has SR and PICB registers swapped when compared to Intel */ +#define GET_REG_PICB(x) (IS_SIS7012(x) ? AUICH_REG_X_SR : AUICH_REG_X_PICB) +#define GET_REG_SR(x) (IS_SIS7012(x) ? AUICH_REG_X_PICB : AUICH_REG_X_SR) + +static void +dump_hardware_regs(device_config *config) +{ + LOG(("GLOB_CNT = %#08x\n", auich_reg_read_32(config, AUICH_REG_GLOB_CNT))); + LOG(("GLOB_STA = %#08x\n", auich_reg_read_32(config, AUICH_REG_GLOB_STA))); + LOG(("PI AUICH_REG_X_BDBAR = %#x\n", auich_reg_read_32(config, AUICH_REG_X_BDBAR + AUICH_REG_PI_BASE))); + LOG(("PI AUICH_REG_X_CIV = %#x\n", auich_reg_read_8(config, AUICH_REG_X_CIV + AUICH_REG_PI_BASE))); + LOG(("PI AUICH_REG_X_LVI = %#x\n", auich_reg_read_8(config, AUICH_REG_X_LVI + AUICH_REG_PI_BASE))); + LOG(("PI REG_X_SR = %#x\n", auich_reg_read_16(config, AUICH_REG_X_SR + AUICH_REG_PI_BASE))); + LOG(("PI REG_X_PICB = %#x\n", auich_reg_read_16(config, AUICH_REG_X_PICB + AUICH_REG_PI_BASE))); + LOG(("PI AUICH_REG_X_PIV = %#x\n", auich_reg_read_8(config, AUICH_REG_X_PIV + AUICH_REG_PI_BASE))); + LOG(("PI AUICH_REG_X_CR = %#x\n", auich_reg_read_8(config, AUICH_REG_X_CR + AUICH_REG_PI_BASE))); + LOG(("PO AUICH_REG_X_BDBAR = %#x\n", auich_reg_read_32(config, AUICH_REG_X_BDBAR + AUICH_REG_PO_BASE))); + LOG(("PO AUICH_REG_X_CIV = %#x\n", auich_reg_read_8(config, AUICH_REG_X_CIV + AUICH_REG_PO_BASE))); + LOG(("PO AUICH_REG_X_LVI = %#x\n", auich_reg_read_8(config, AUICH_REG_X_LVI + AUICH_REG_PO_BASE))); + LOG(("PO REG_X_SR = %#x\n", auich_reg_read_16(config, AUICH_REG_X_SR + AUICH_REG_PO_BASE))); + LOG(("PO REG_X_PICB = %#x\n", auich_reg_read_16(config, AUICH_REG_X_PICB + AUICH_REG_PO_BASE))); + LOG(("PO AUICH_REG_X_PIV = %#x\n", auich_reg_read_8(config, AUICH_REG_X_PIV + AUICH_REG_PO_BASE))); + LOG(("PO AUICH_REG_X_CR = %#x\n", auich_reg_read_8(config, AUICH_REG_X_CR + AUICH_REG_PO_BASE))); +} + +/* auich Memory management */ + +static auich_mem * +auich_mem_new(auich_dev *card, size_t size) +{ + auich_mem *mem; + + if ((mem = malloc(sizeof(*mem))) == NULL) + return (NULL); + + mem->area = alloc_mem(&mem->phy_base, &mem->log_base, size, "auich buffer"); + mem->size = size; + if (mem->area < B_OK) { + free(mem); + return NULL; + } + return mem; +} + +static void +auich_mem_delete(auich_mem *mem) +{ + if(mem->area > B_OK) + delete_area(mem->area); + free(mem); +} + +static void * +auich_mem_alloc(auich_dev *card, size_t size) +{ + auich_mem *mem; + + mem = auich_mem_new(card, size); + if (mem == NULL) + return (NULL); + + LIST_INSERT_HEAD(&(card->mems), mem, next); + + return mem; +} + +static void +auich_mem_free(auich_dev *card, void *ptr) +{ + auich_mem *mem; + + LIST_FOREACH(mem, &card->mems, next) { + if (mem->log_base != ptr) + continue; + LIST_REMOVE(mem, next); + + auich_mem_delete(mem); + break; + } +} + +/* auich stream functions */ + +status_t +auich_stream_set_audioparms(auich_stream *stream, uint8 channels, + uint8 b16, uint32 sample_rate) +{ + uint8 sample_size, frame_size; + LOG(("auich_stream_set_audioparms\n")); + + if ((stream->channels == channels) && + (stream->b16 == b16) && + (stream->sample_rate == sample_rate)) + return B_OK; + + if(stream->buffer) + auich_mem_free(stream->card, stream->buffer->log_base); + + stream->b16 = b16; + stream->sample_rate = sample_rate; + stream->channels = channels; + + sample_size = stream->b16 + 1; + frame_size = sample_size * stream->channels; + + stream->buffer = auich_mem_alloc(stream->card, stream->bufframes * frame_size * stream->bufcount); + + stream->trigblk = 0; /* This shouldn't be needed */ + stream->blkmod = stream->bufcount; + stream->blksize = stream->bufframes * frame_size; + + return B_OK; +} + +status_t +auich_stream_commit_parms(auich_stream *stream) +{ + uint32 *page; + uint32 i; + LOG(("auich_stream_commit_parms\n")); + + auich_reg_write_8(&stream->card->config, stream->base + AUICH_REG_X_CR, 0); + snooze(10000); // 10 ms + + auich_reg_write_8(&stream->card->config, stream->base + AUICH_REG_X_CR, CR_RR); + for (i = 10000; i>=0; i--) { + if(0 == auich_reg_read_8(&stream->card->config, stream->base + AUICH_REG_X_CR)) { + LOG(("channel reset finished, %x, %d\n", stream->base, i)); + break; + } + snooze(1); + } + + if(i < 0) { + LOG(("channel reset failed after 10ms\n")); + } + + page = stream->dmaops_log_base; + + for(i=0; i < AUICH_DMALIST_MAX; i++) { + page[2*i] = ((uint32)stream->buffer->phy_base) + + (i % stream->bufcount) * stream->blksize; + page[2*i + 1] = AUICH_DMAF_IOC | (stream->blksize + / (IS_SIS7012(&stream->card->config) ? 1 : 2)); + } + + // set physical buffer descriptor base address + auich_reg_write_32(&stream->card->config, stream->base + AUICH_REG_X_BDBAR, + (uint32)stream->dmaops_phy_base); + + if(stream->use & AUICH_USE_RECORD) + auich_codec_write(&stream->card->config, AC97_PCM_LR_ADC_RATE, (uint16)stream->sample_rate); + else + auich_codec_write(&stream->card->config, AC97_PCM_FRONT_DAC_RATE, (uint16)stream->sample_rate); + + if(stream->use & AUICH_USE_RECORD) + LOG(("rate : %d\n", auich_codec_read(&stream->card->config, AC97_PCM_LR_ADC_RATE))); + else + LOG(("rate : %d\n", auich_codec_read(&stream->card->config, AC97_PCM_FRONT_DAC_RATE))); + return B_OK; +} + +status_t +auich_stream_get_nth_buffer(auich_stream *stream, uint8 chan, uint8 buf, + char** buffer, size_t *stride) +{ + uint8 sample_size, frame_size; + LOG(("auich_stream_get_nth_buffer\n")); + + sample_size = stream->b16 + 1; + frame_size = sample_size * stream->channels; + + *buffer = stream->buffer->log_base + (buf * stream->bufframes * frame_size) + + chan * sample_size; + *stride = frame_size; + + return B_OK; +} + +static uint8 +auich_stream_curaddr(auich_stream *stream) +{ + uint8 index = auich_reg_read_8(&stream->card->config, stream->base + AUICH_REG_X_CIV); + TRACE(("stream_curaddr %d\n", index)); + return index; +} + +void +auich_stream_start(auich_stream *stream, void (*inth) (void *), void *inthparam) +{ + int32 civ; + LOG(("auich_stream_start\n")); + + stream->inth = inth; + stream->inthparam = inthparam; + + stream->state |= AUICH_STATE_STARTED; + + civ = auich_reg_read_8(&stream->card->config, stream->base + AUICH_REG_X_CIV); + + // step 1: clear status bits + auich_reg_write_16(&stream->card->config, + stream->base + GET_REG_SR(&stream->card->config), + auich_reg_read_16(&stream->card->config, stream->base + GET_REG_SR(&stream->card->config))); + auich_reg_read_16(&stream->card->config, stream->base + GET_REG_SR(&stream->card->config)); + // step 2: prepare buffer transfer + auich_reg_write_8(&stream->card->config, stream->base + AUICH_REG_X_LVI, (civ + 2) % AUICH_DMALIST_MAX); + auich_reg_read_8(&stream->card->config, stream->base + AUICH_REG_X_LVI); + // step 3: enable interrupts & busmaster transfer + auich_reg_write_8(&stream->card->config, stream->base + AUICH_REG_X_CR, CR_RPBM | CR_LVBIE | CR_FEIE | CR_IOCE); + auich_reg_read_8(&stream->card->config, stream->base + AUICH_REG_X_CR); + +#ifdef DEBUG + dump_hardware_regs(&stream->card->config); +#endif +} + +void +auich_stream_halt(auich_stream *stream) +{ + LOG(("auich_stream_halt\n")); + + stream->state &= ~AUICH_STATE_STARTED; + + auich_reg_write_8(&stream->card->config, stream->base + AUICH_REG_X_CR, + auich_reg_read_8(&stream->card->config, stream->base + AUICH_REG_X_CR) & ~CR_RPBM); +} + +auich_stream * +auich_stream_new(auich_dev *card, uint8 use, uint32 bufframes, uint8 bufcount) +{ + auich_stream *stream; + cpu_status status; + LOG(("auich_stream_new\n")); + + stream = malloc(sizeof(auich_stream)); + if (stream == NULL) + return (NULL); + stream->card = card; + stream->use = use; + stream->state = !AUICH_STATE_STARTED; + stream->b16 = 0; + stream->sample_rate = 0; + stream->channels = 0; + stream->bufframes = bufframes; + stream->bufcount = bufcount; + stream->inth = NULL; + stream->inthparam = NULL; + stream->buffer = NULL; + stream->blksize = 0; + stream->trigblk = 0; + stream->blkmod = 0; + + if(use & AUICH_USE_PLAY) { + stream->base = AUICH_REG_PO_BASE; + stream->sta = STA_POINT; + } else { + stream->base = AUICH_REG_PI_BASE; + stream->sta = STA_PIINT; + } + + stream->frames_count = 0; + stream->real_time = 0; + stream->update_needed = false; + + /* allocate memory for our dma ops */ + stream->dmaops_area = alloc_mem(&stream->dmaops_phy_base, &stream->dmaops_log_base, + sizeof(auich_dmalist) * AUICH_DMALIST_MAX, "auich dmaops"); + + if (stream->dmaops_area < B_OK) { + PRINT(("couldn't allocate memory\n")); + free(stream); + return NULL; + } + + status = lock(); + LIST_INSERT_HEAD((&card->streams), stream, next); + unlock(status); + + return stream; +} + +void +auich_stream_delete(auich_stream *stream) +{ + cpu_status status; + int32 i; + LOG(("auich_stream_delete\n")); + + auich_stream_halt(stream); + + auich_reg_write_8(&stream->card->config, stream->base + AUICH_REG_X_CR, 0); + snooze(10000); // 10 ms + + auich_reg_write_8(&stream->card->config, stream->base + AUICH_REG_X_CR, CR_RR); + for (i = 10000; i>=0; i--) { + if(0 == auich_reg_read_8(&stream->card->config, stream->base + AUICH_REG_X_CR)) { + LOG(("channel reset finished, %x, %d\n", stream->base, i)); + break; + } + snooze(1); + } + + if(i < 0) { + LOG(("channel reset failed after 10ms\n")); + } + + auich_reg_write_32(&stream->card->config, stream->base + AUICH_REG_X_BDBAR, 0); + + if (stream->dmaops_area > B_OK) + delete_area(stream->dmaops_area); + + if(stream->buffer) + auich_mem_free(stream->card, stream->buffer->log_base); + + status = lock(); + LIST_REMOVE(stream, next); + unlock(status); + + free(stream); +} + +/* auich interrupt */ + +int32 +auich_int(void *arg) +{ + auich_dev *card = arg; + bool gotone = false; + uint8 curblk; + auich_stream *stream = NULL; + uint16 sr, sta; + + //TRACE(("auich_int(%p)\n", card)); + + sta = auich_reg_read_16(&card->config, AUICH_REG_GLOB_STA); + if(sta & card->interrupt_mask) { + + if (sta & (STA_S0RI | STA_S1RI | STA_S2RI)) { + // ignore and clear resume interrupt(s) + auich_reg_write_16(&card->config, AUICH_REG_GLOB_STA, sta & (STA_S0RI | STA_S1RI | STA_S2RI)); + TRACE(("interrupt !! %x\n", sta)); + gotone = true; + } + + //TRACE(("interrupt !! %x\n", sta)); + + LIST_FOREACH(stream, &card->streams, next) + if (sta & stream->sta) { + sr = auich_reg_read_16(&card->config, + stream->base + GET_REG_SR(&stream->card->config)); + sr &= SR_MASK; + + if(!sr) + continue; + + gotone = true; + + if (sr & SR_BCIS) { + curblk = auich_stream_curaddr(stream); + + auich_reg_write_8(&card->config, stream->base + AUICH_REG_X_LVI, + (curblk + 2) % AUICH_DMALIST_MAX); + + stream->trigblk = (curblk) % stream->blkmod; + + if(stream->inth) + stream->inth(stream->inthparam); + } else { + TRACE(("interrupt !! sta %x, sr %x\n", sta, sr)); + } + + auich_reg_write_16(&card->config, + stream->base + GET_REG_SR(&stream->card->config), sr); + } + } else { + TRACE(("interrupt masked %x, ", card->interrupt_mask)); + TRACE(("sta %x\n", sta)); + } + + if(gotone) + return B_HANDLED_INTERRUPT; + + TRACE(("Got unhandled interrupt\n")); + return B_UNHANDLED_INTERRUPT; +} + +/* auich driver functions */ + +static status_t +map_io_memory(device_config *config) +{ + if ((config->type & TYPE_ICH4) == 0) + return B_OK; + + config->area_mmbar = map_mem(&config->log_mmbar, (void *)config->mmbar, ICH4_MMBAR_SIZE, "auich mmbar io"); + if (config->area_mmbar <= B_OK) { + LOG(("mapping of mmbar io failed, error = %#x\n",config->area_mmbar)); + return B_ERROR; + } + LOG(("mapping of mmbar: area %#x, phys %#x, log %#x\n", config->area_mmbar, config->mmbar, config->log_mmbar)); + + config->area_mbbar = map_mem(&config->log_mbbar, (void *)config->mbbar, ICH4_MBBAR_SIZE, "auich mbbar io"); + if (config->area_mbbar <= B_OK) { + LOG(("mapping of mbbar io failed, error = %#x\n",config->area_mbbar)); + delete_area(config->area_mmbar); + config->area_mmbar = -1; + return B_ERROR; + } + LOG(("mapping of mbbar: area %#x, phys %#x, log %#x\n", config->area_mbbar, config->mbbar, config->log_mbbar)); + + return B_OK; +} + +static status_t +unmap_io_memory(device_config *config) +{ + status_t rv; + if ((config->type & TYPE_ICH4) == 0) + return B_OK; + rv = delete_area(config->area_mmbar); + rv |= delete_area(config->area_mbbar); + return rv; +} + +/* detect presence of our hardware */ +status_t +init_hardware(void) +{ + int ix=0; + pci_info info; + status_t err = ENODEV; + + LOG_CREATE(); + + PRINT(("init_hardware()\n")); + + if (get_module(pci_name, (module_info **)&pci)) + return ENOSYS; + + while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) { + if ((info.vendor_id == INTEL_VENDOR_ID && + (info.device_id == INTEL_82443MX_AC97_DEVICE_ID + || info.device_id == INTEL_82801AA_AC97_DEVICE_ID + || info.device_id == INTEL_82801AB_AC97_DEVICE_ID + || info.device_id == INTEL_82801BA_AC97_DEVICE_ID + || info.device_id == INTEL_82801CA_AC97_DEVICE_ID + || info.device_id == INTEL_82801DB_AC97_DEVICE_ID + )) + || (info.vendor_id == SIS_VENDOR_ID && + (info.device_id == SIS_SI7012_AC97_DEVICE_ID + )) + || (info.vendor_id == NVIDIA_VENDOR_ID && + (info.device_id == NVIDIA_nForce_AC97_DEVICE_ID + || info.device_id == NVIDIA_nForce2_AC97_DEVICE_ID + || info.device_id == NVIDIA_nForce3_AC97_DEVICE_ID + )) + || (info.vendor_id == AMD_VENDOR_ID && + (info.device_id == AMD_AMD8111_AC97_DEVICE_ID + || info.device_id == AMD_AMD768_AC97_DEVICE_ID + )) + ) + { + err = B_OK; + } + ix++; + } + + put_module(pci_name); + + return err; +} + +static void +make_device_names( + auich_dev * card) +{ + sprintf(card->name, "audio/multi/auich/%ld", card-cards+1); + names[num_names++] = card->name; + + names[num_names] = NULL; +} + + +status_t +auich_init(auich_dev * card) +{ + card->interrupt_mask = STA_PIINT | STA_POINT; //STA_INTMASK; + + /* Init streams list */ + LIST_INIT(&(card->streams)); + + /* Init mems list */ + LIST_INIT(&(card->mems)); + + return B_OK; +} + +static status_t +auich_setup(auich_dev * card) +{ + status_t err = B_OK; + status_t rv; + unsigned char cmd; + + PRINT(("auich_setup(%p)\n", card)); + + make_device_names(card); + + card->config.nabmbar = card->info.u.h0.base_registers[0]; + card->config.irq = card->info.u.h0.interrupt_line; + card->config.type = 0; + if(card->info.device_id == INTEL_82801DB_AC97_DEVICE_ID) + card->config.type |= TYPE_ICH4; + if(card->info.device_id == SIS_SI7012_AC97_DEVICE_ID) + card->config.type |= TYPE_SIS7012; + + PRINT(("%s deviceid = %#04x chiprev = %x model = %x enhanced at %lx\n", card->name, card->info.device_id, + card->info.revision, card->info.u.h0.subsystem_id, card->config.nabmbar)); + + if (IS_ICH4(&card->config)) { + // memory mapped access + card->config.mmbar = 0xfffffffe & (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, 0x18, 4); + card->config.mbbar = 0xfffffffe & (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, 0x1C, 4); + } else { + // pio access + card->config.nambar = 0xfffffffe & (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, 0x10, 4); + card->config.nabmbar = 0xfffffffe & (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, 0x14, 4); + } + + /* before doing anything else, map the IO memory */ + rv = map_io_memory(&card->config); + if (rv != B_OK) { + PRINT(("mapping of memory IO space failed\n")); + return B_ERROR; + } + + 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)); + + /* do a cold reset */ + LOG(("cold reset\n")); + auich_reg_write_32(&card->config, AUICH_REG_GLOB_CNT, 0); + snooze(50000); // 50 ms + auich_reg_write_32(&card->config, AUICH_REG_GLOB_CNT, CNT_COLD | CNT_PRIE); + LOG(("cold reset finished\n")); + rv = auich_reg_read_32(&card->config, AUICH_REG_GLOB_CNT); + if ((rv & CNT_COLD) == 0) { + LOG(("cold reset failed\n")); + } + + /* reset the codec */ + PRINT(("codec reset\n")); + auich_codec_write(&card->config, 0x00, 0x0000); + snooze(50000); // 50 ms + + ac97_init(&card->config); + ac97_amp_enable(&card->config, true); + + rv = auich_reg_read_32(&card->config, AUICH_REG_GLOB_STA); + if (!(rv & STA_S0CR)) { /* reset failure */ + /* It never return STA_S0CR in some cases */ + PRINT(("reset failure\n")); + } + /* Print capabilities though there are no supports for now */ + if ((rv & STA_SAMPLE_CAP) == STA_POM20) { + LOG(("20 bit precision support\n")); + } + if ((rv & STA_CHAN_CAP) == STA_PCM4) { + LOG(("4ch PCM output support\n")); + } + if ((rv & STA_CHAN_CAP) == STA_PCM6) { + LOG(("6ch PCM output support\n")); + } + + PRINT(("codec vendor id = %#08lx\n",ac97_get_vendor_id(&card->config))); + PRINT(("codec description = %s\n",ac97_get_vendor_id_description(&card->config))); + PRINT(("codec 3d enhancement = %s\n",ac97_get_3d_stereo_enhancement(&card->config))); + + PRINT(("installing interrupt : %lx\n", card->config.irq)); + install_io_interrupt_handler(card->config.irq, auich_int, card, 0); + + /*PRINT(("codec master output = %#04x\n",auich_codec_read(&card->config, 0x02))); + PRINT(("codec aux output = %#04x\n",auich_codec_read(&card->config, 0x04))); + PRINT(("codec mono output = %#04x\n",auich_codec_read(&card->config, 0x06))); + PRINT(("codec pcm output = %#04x\n",auich_codec_read(&card->config, 0x18))); + PRINT(("codec line in = %#04x\n",auich_codec_read(&card->config, 0x10))); + PRINT(("codec record line in= %#04x\n",auich_codec_read(&card->config, 0x1a))); + PRINT(("codec record gain = %#04x\n",auich_codec_read(&card->config, 0x1c)));*/ + + PRINT(("writing codec registers\n")); + // TODO : to move with AC97 + /* enable master output */ + auich_codec_write(&card->config, AC97_MASTER_VOLUME, 0x0000); + /* enable aux output */ + auich_codec_write(&card->config, AC97_AUX_OUT_VOLUME, 0x0000); + /* enable mono output */ + //auich_codec_write(&card->config, AC97_MONO_VOLUME, 0x0004); + /* enable pcm output */ + auich_codec_write(&card->config, AC97_PCM_OUT_VOLUME, 0x0808); + /* enable line in */ + //auich_codec_write(&card->config, AC97_LINE_IN_VOLUME, 0x8808); + /* set record line in */ + auich_codec_write(&card->config, AC97_RECORD_SELECT, 0x0404); + /* set record gain */ + //auich_codec_write(&card->config, AC97_RECORD_GAIN, 0x0000); + + PRINT(("codec master output = %#04x\n",auich_codec_read(&card->config, AC97_MASTER_VOLUME))); + PRINT(("codec aux output = %#04x\n",auich_codec_read(&card->config, AC97_AUX_OUT_VOLUME))); + PRINT(("codec mono output = %#04x\n",auich_codec_read(&card->config, AC97_MONO_VOLUME))); + PRINT(("codec pcm output = %#04x\n",auich_codec_read(&card->config, AC97_PCM_OUT_VOLUME))); + PRINT(("codec line in = %#04x\n",auich_codec_read(&card->config, AC97_LINE_IN_VOLUME))); + PRINT(("codec record line in= %#04x\n",auich_codec_read(&card->config, AC97_RECORD_SELECT))); + PRINT(("codec record gain = %#04x\n",auich_codec_read(&card->config, AC97_RECORD_GAIN))); + + if ((err = auich_init(card))) + return (err); + + PRINT(("init_driver done\n")); + + return err; +} + + +status_t +init_driver(void) +{ + int ix=0; + + pci_info info; + num_cards = 0; + + PRINT(("init_driver()\n")); + load_driver_symbols("auich"); + + if (get_module(pci_name, (module_info **) &pci)) + return ENOSYS; + + while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) { + if((info.vendor_id == INTEL_VENDOR_ID && + (info.device_id == INTEL_82443MX_AC97_DEVICE_ID + || info.device_id == INTEL_82801AA_AC97_DEVICE_ID + || info.device_id == INTEL_82801AB_AC97_DEVICE_ID + || info.device_id == INTEL_82801BA_AC97_DEVICE_ID + || info.device_id == INTEL_82801CA_AC97_DEVICE_ID + || info.device_id == INTEL_82801DB_AC97_DEVICE_ID + )) + || (info.vendor_id == SIS_VENDOR_ID && + (info.device_id == SIS_SI7012_AC97_DEVICE_ID + )) + || (info.vendor_id == NVIDIA_VENDOR_ID && + (info.device_id == NVIDIA_nForce_AC97_DEVICE_ID + || info.device_id == NVIDIA_nForce2_AC97_DEVICE_ID + || info.device_id == NVIDIA_nForce3_AC97_DEVICE_ID + )) + || (info.vendor_id == AMD_VENDOR_ID && + (info.device_id == AMD_AMD8111_AC97_DEVICE_ID + || info.device_id == AMD_AMD768_AC97_DEVICE_ID + )) + ) { + if (num_cards == NUM_CARDS) { + PRINT(("Too many auich cards installed!\n")); + break; + } + memset(&cards[num_cards], 0, sizeof(auich_dev)); + cards[num_cards].info = info; + if (auich_setup(&cards[num_cards])) { + PRINT(("Setup of auich %ld failed\n", num_cards+1)); + } + else { + num_cards++; + } + } + ix++; + } + if (!num_cards) { + PRINT(("no cards\n")); + put_module(pci_name); + PRINT(("no suitable cards found\n")); + return ENODEV; + } + + +#if DEBUG + //add_debugger_command("auich", auich_debug, "auich [card# (1-n)]"); +#endif + return B_OK; +} + + +static void +auich_shutdown(auich_dev *card) +{ + PRINT(("shutdown(%p)\n", card)); + card->interrupt_mask = 0; + + remove_io_interrupt_handler(card->config.irq, auich_int, card); + + unmap_io_memory(&card->config); +} + + +void +uninit_driver(void) +{ + int ix, cnt = num_cards; + num_cards = 0; + + PRINT(("uninit_driver()\n")); + //remove_debugger_command("auich", auich_debug); + + for (ix=0; ix +#include +#include +#include +#include "auichreg.h" +#include "config.h" +#include "queue.h" +#include "multi_audio.h" +#include "multi.h" + +#define INTEL_VENDOR_ID 0x8086 /* Intel */ +#define INTEL_82443MX_AC97_DEVICE_ID 0x7195 +#define INTEL_82801AA_AC97_DEVICE_ID 0x2415 +#define INTEL_82801AB_AC97_DEVICE_ID 0x2425 +#define INTEL_82801BA_AC97_DEVICE_ID 0x2445 +#define INTEL_82801CA_AC97_DEVICE_ID 0x2485 +#define INTEL_82801DB_AC97_DEVICE_ID 0x24C5 +#define SIS_VENDOR_ID 0x1039 /* Sis */ +#define SIS_SI7012_AC97_DEVICE_ID 0x7012 +#define NVIDIA_VENDOR_ID 0x10DE /* Nvidia */ +#define NVIDIA_nForce_AC97_DEVICE_ID 0x01B1 +#define NVIDIA_nForce2_AC97_DEVICE_ID 0x006A +#define NVIDIA_nForce3_AC97_DEVICE_ID 0x00DA +#define AMD_VENDOR_ID 0x1022 /* Amd */ +#define AMD_AMD8111_AC97_DEVICE_ID 0x764d +#define AMD_AMD768_AC97_DEVICE_ID 0x7445 + +#define VERSION "Version alpha 1, Copyright (c) 2003 Jérôme Duval, compiled on " ## __DATE__ ## " " ## __TIME__ +#define DRIVER_NAME "auich" +#define FRIENDLY_NAME "Auich" +#define AUTHOR "Jérôme Duval" + +#define FRIENDLY_NAME_ICH "Auich ICH" +#define FRIENDLY_NAME_SIS "Auich SiS" +#define FRIENDLY_NAME_NVIDIA "Auich nVidia" +#define FRIENDLY_NAME_AMD "Auich AMD" + +#define AUICH_DMALIST_MAX 32 +typedef struct _auich_dmalist { + uint32 base; + uint32 len; +#define AUICH_DMAF_IOC 0x80000000 /* 1-int on complete */ +#define AUICH_DMAF_BUP 0x40000000 /* 0-retrans last, 1-transmit 0 */ +} auich_dmalist; + +#define AUICH_USE_PLAY (1 << 0) +#define AUICH_USE_RECORD (1 << 1) +#define AUICH_STATE_STARTED (1 << 0) + +/* + * auich memory managment + */ + +typedef struct _auich_mem { + LIST_ENTRY(_auich_mem) next; + void *log_base; + void *phy_base; + area_id area; + size_t size; +} auich_mem; + +/* + * Streams + */ + +typedef struct _auich_stream { + struct _auich_dev *card; + uint8 use; + uint8 state; + uint8 b16; + uint32 sample_rate; + uint8 channels; + uint32 bufframes; + uint8 bufcount; + + uint32 base; + + LIST_ENTRY(_auich_stream) next; + + void (*inth) (void *); + void *inthparam; + + void *dmaops_log_base; + void *dmaops_phy_base; + area_id dmaops_area; + + auich_mem *buffer; + uint16 blksize; /* in samples */ + uint16 trigblk; /* blk on which to trigger inth */ + uint16 blkmod; /* Modulo value to wrap trigblk */ + + uint16 sta; /* GLOB_STA int bit */ + + /* 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; +} auich_stream; + +/* + * Devices + */ + +typedef struct _auich_dev { + char name[DEVNAME]; /* used for resources */ + pci_info info; + device_config config; + + void *ptb_log_base; + void *ptb_phy_base; + area_id ptb_area; + + sem_id buffer_ready_sem; + + uint32 interrupt_mask; + + LIST_HEAD(, _auich_stream) streams; + + LIST_HEAD(, _auich_mem) mems; + + auich_stream *pstream; + auich_stream *rstream; + + /* multi_audio */ + multi_dev multi; +} auich_dev; + +extern int32 num_cards; +extern auich_dev cards[NUM_CARDS]; + +status_t auich_stream_set_audioparms(auich_stream *stream, uint8 channels, + uint8 b16, uint32 sample_rate); +status_t auich_stream_commit_parms(auich_stream *stream); +status_t auich_stream_get_nth_buffer(auich_stream *stream, uint8 chan, uint8 buf, + char** buffer, size_t *stride); +void auich_stream_start(auich_stream *stream, void (*inth) (void *), void *inthparam); +void auich_stream_halt(auich_stream *stream); +auich_stream *auich_stream_new(auich_dev *card, uint8 use, uint32 bufframes, uint8 bufcount); +void auich_stream_delete(auich_stream *stream); + +#endif /* _DEV_PCI_AUICH_H_ */ diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/auichreg.h b/src/add-ons/kernel/drivers/audio/ac97/auich/auichreg.h new file mode 100644 index 0000000000..6e895ef22c --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/auichreg.h @@ -0,0 +1,127 @@ +/* + * Auich BeOS Driver for Intel Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _AUICHREG_H_ +#define _AUICHREG_H_ + +/* Native Audio Bus Master Control Registers */ +enum AUICH_GLOBAL_REGISTER +{ + AUICH_REG_GLOB_CNT = 0x2C, + AUICH_REG_GLOB_STA = 0x30, + AUICH_REG_ACC_SEMA = 0x34, + AUICH_REG_SDM = 0x80 +}; + +enum AUICH_X_REGISTER_BASE /* base addresses for the following offsets */ +{ + AUICH_REG_PI_BASE = 0x00, + AUICH_REG_PO_BASE = 0x10, + AUICH_REG_MC_BASE = 0x20 +}; + +enum AUICH_X_REGISTER_OFFSETS /* add base address to get the PI, PO or MC reg */ +{ + AUICH_REG_X_BDBAR = 0x00, + AUICH_REG_X_CIV = 0x04, + AUICH_REG_X_LVI = 0x05, + AUICH_REG_X_SR = 0x06, + AUICH_REG_X_PICB = 0x08, + AUICH_REG_X_PIV = 0x0A, + AUICH_REG_X_CR = 0x0B +}; + +/* AUICH_REG_X_SR (Status Register) Bits */ +enum REG_X_SR_BITS +{ + SR_DCH = 0x0001, + SR_CELV = 0x0002, + SR_LVBCI = 0x0004, + SR_BCIS = 0x0008, + SR_FIFOE = 0x0010, + SR_MASK = SR_FIFOE | SR_BCIS | SR_LVBCI | SR_CELV | SR_DCH //| 0x200 | 0x80 | 0x2 +}; + +/* AUICH_REG_X_CR (Control Register) Bits */ +enum REG_X_CR_BITS +{ + CR_RPBM = 0x01, + CR_RR = 0x02, + CR_LVBIE = 0x04, + CR_FEIE = 0x08, + CR_IOCE = 0x10 +}; + +/* AUICH_REG_GLOB_CNT (Global Control Register) Bits */ +enum REG_GLOB_CNT_BITS +{ + CNT_GIE = 0x01, + CNT_COLD = 0x02, + CNT_WARM = 0x04, + CNT_SHUT = 0x08, + CNT_PRIE = 0x10, + CNT_SRIE = 0x20 +}; + +/* AUICH_REG_GLOB_STA (Global Status Register) Bits */ +enum REG_GLOB_STA_BITS +{ + STA_GSCI = 0x00000001, /* GPI Status Change Interrupt */ + STA_MIINT = 0x00000002, /* Modem In Interrupt */ + STA_MOINT = 0x00000004, /* Modem Out Interrupt */ + STA_PIINT = 0x00000020, /* PCM In Interrupt */ + STA_POINT = 0x00000040, /* PCM Out Interrupt */ + STA_MINT = 0x00000080, /* Mic In Interrupt */ + STA_S0CR = 0x00000100, /* AC_SDIN0 Codec Ready */ + STA_S1CR = 0x00000200, /* AC_SDIN1 Codec Ready */ + STA_S0RI = 0x00000400, /* AC_SDIN0 Resume Interrupt */ + STA_S1RI = 0x00000800, /* AC_SDIN1 Resume Interrupt */ + STA_RCS = 0x00008000, /* Read Completition Status */ + STA_AD3 = 0x00010000, + STA_MD3 = 0x00020000, + STA_SAMPLE_CAP = 0x00c00000, /* sampling precision capability */ + STA_POM20 = 0x00400000, /* PCM out precision 20bit */ + STA_CHAN_CAP = 0x00300000, /* multi-channel capability */ + STA_PCM4 = 0x00100000, /* 4ch output */ + STA_PCM6 = 0x00200000, /* 6ch output */ + STA_M2INT = 0x01000000, /* Microphone 2 In Interrupt */ + STA_P2INT = 0x02000000, /* PCM In 2 Interrupt */ + STA_SPINT = 0x04000000, /* S/PDIF Interrupt */ + STA_BCS = 0x08000000, /* Bit Clock Stopped */ + STA_S2CR = 0x10000000, /* AC_SDIN2 Codec Ready */ + STA_S2RI = 0x20000000, /* AC_SDIN2 Resume Interrupt */ + STA_INTMASK = (STA_MIINT | STA_MOINT | STA_PIINT | STA_POINT | STA_MINT | STA_S0RI | STA_S1RI | STA_M2INT | STA_P2INT | STA_SPINT | STA_S2RI) +}; + +#define ICH4_MMBAR_SIZE 512 +#define ICH4_MBBAR_SIZE 256 + +#endif /* _AUICHREG_H_ */ diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/config.h b/src/add-ons/kernel/drivers/audio/ac97/auich/config.h new file mode 100644 index 0000000000..fbd30ff54c --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/config.h @@ -0,0 +1,56 @@ +/* + * Auich BeOS Driver for Intel Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#define NUM_CARDS 3 +#define DEVNAME 32 + +typedef struct +{ + uint32 nabmbar; + uint32 nambar; + uint32 irq; + uint32 type; + uint32 mmbar; // ich4 + uint32 mbbar; // ich4 + void * log_mmbar; // ich4 + void * log_mbbar; // ich4 + area_id area_mmbar; // ich4 + area_id area_mbbar; // ich4 +} device_config; + +#define TYPE_ICH4 0x01 +#define TYPE_SIS7012 0x02 + +#define IS_ICH4(x) ((x)->type & TYPE_ICH4) +#define IS_SIS7012(x) ((x)->type & TYPE_SIS7012) +#endif diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/debug.c b/src/add-ons/kernel/drivers/audio/ac97/auich/debug.c new file mode 100644 index 0000000000..790b2a2d97 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/debug.c @@ -0,0 +1,95 @@ +/* + * Auich BeOS Driver for Intel Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include +#include +#include +#include "debug.h" +#include "auich.h" + +#if DEBUG > 0 +static const char * logfile="/boot/home/auich.log"; +static sem_id loglock; +#endif + +void debug_printf(const char *text,...); +void log_printf(const char *text,...); +void log_create(void); + +void debug_printf(const char *text,...) +{ + char buf[1024]; + va_list ap; + + va_start(ap,text); + vsprintf(buf,text,ap); + va_end(ap); + + dprintf(DRIVER_NAME ": %s",buf); +} + +void log_create() +{ +#if DEBUG > 0 + int fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); + const char *text = DRIVER_NAME ", " VERSION "\n"; + loglock = create_sem(1,"logfile sem"); + write(fd,text,strlen(text)); + close(fd); +#endif +} + +void log_printf(const char *text,...) +{ +#if DEBUG > 0 + int fd; + char buf[1024]; + va_list ap; + + va_start(ap,text); + vsprintf(buf,text,ap); + va_end(ap); + + dprintf(DRIVER_NAME ": %s",buf); + + acquire_sem(loglock); + fd = open(logfile, O_WRONLY | O_APPEND); + write(fd,buf,strlen(buf)); + close(fd); + release_sem(loglock); + + #if DEBUG > 1 + snooze(150000); + #endif +#endif +} + diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/debug.h b/src/add-ons/kernel/drivers/audio/ac97/auich/debug.h new file mode 100644 index 0000000000..c52ce0705a --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/debug.h @@ -0,0 +1,70 @@ +/* + * Auich BeOS Driver for Intel Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +/* + * PRINT() executes dprintf if DEBUG = 0 (disabled), or expands to LOG() when DEBUG > 0 + * TRACE() executes dprintf if DEBUG > 0 + * LOG() executes dprintf and writes to the logfile if DEBUG > 0 + */ + +/* DEBUG == 0, no debugging, PRINT writes to syslog + * DEBUG == 1, TRACE & LOG, PRINT + * DEBUG == 2, TRACE & LOG, PRINT with snooze() + */ +#ifndef DEBUG + #define DEBUG 0 +#endif + +#undef PRINT +#undef TRACE +#undef ASSERT + +#if DEBUG > 0 + #define PRINT(a) log_printf a + #define TRACE(a) debug_printf a + #define LOG(a) log_printf a + #define LOG_CREATE() log_create() + #define ASSERT(a) if (a) {} else LOG(("ASSERT failed! file = %s, line = %d\n",__FILE__,__LINE__)) + void log_create(); + void log_printf(const char *text,...); + void debug_printf(const char *text,...); +#else + void debug_printf(const char *text,...); + #define PRINT(a) debug_printf a + #define TRACE(a) ((void)(0)) + #define ASSERT(a) ((void)(0)) + #define LOG(a) ((void)(0)) + #define LOG_CREATE() +#endif + +#endif diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/io.c b/src/add-ons/kernel/drivers/audio/ac97/auich/io.c new file mode 100644 index 0000000000..49b975d49d --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/io.c @@ -0,0 +1,149 @@ +/* + * Auich BeOS Driver for Intel Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include +#include +#include "io.h" +#include "auichreg.h" +#include "debug.h" +#include + +extern pci_module_info *pci; + +uint8 +auich_reg_read_8(device_config *config, int regno) +{ + ASSERT(regno >= 0); + ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63); + if (config->type & TYPE_ICH4) + return *(uint8 *)(((char *)config->log_mbbar) + regno); + else + return pci->read_io_8(config->nabmbar + regno); +} + +uint16 +auich_reg_read_16(device_config *config, int regno) +{ + ASSERT(regno >= 0); + ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63); + if (config->type & TYPE_ICH4) + return *(uint16 *)(((char *)config->log_mbbar) + regno); + else + return pci->read_io_16(config->nabmbar + regno); +} + +uint32 +auich_reg_read_32(device_config *config, int regno) +{ + ASSERT(regno >= 0); + ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63); + if (config->type & TYPE_ICH4) + return *(uint32 *)(((char *)config->log_mbbar) + regno); + else + return pci->read_io_32(config->nabmbar + regno); +} + +void +auich_reg_write_8(device_config *config, int regno, uint8 value) +{ + ASSERT(regno >= 0); + ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63); + if (config->type & TYPE_ICH4) + *(uint8 *)(((char *)config->log_mbbar) + regno) = value; + else + pci->write_io_8(config->nabmbar + regno, value); +} + +void +auich_reg_write_16(device_config *config, int regno, uint16 value) +{ + ASSERT(regno >= 0); + ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63); + if (config->type & TYPE_ICH4) + *(uint16 *)(((char *)config->log_mbbar) + regno) = value; + else + pci->write_io_16(config->nabmbar + regno, value); +} + +void +auich_reg_write_32(device_config *config, int regno, uint32 value) +{ + ASSERT(regno >= 0); + ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 255) || regno <= 63); + if (config->type & TYPE_ICH4) + *(uint32 *)(((char *)config->log_mbbar) + regno) = value; + else + pci->write_io_32(config->nabmbar + regno, value); +} + +/* codec */ +static int +auich_codec_wait(device_config *config) +{ + int i; + for (i = 0; i < 1100; i++) { + if ((auich_reg_read_8(config, AUICH_REG_ACC_SEMA) & 0x01) == 0) + return B_OK; + if (i > 100) + snooze(1); + } + return B_TIMED_OUT; +} + +uint16 +auich_codec_read(device_config *config, int regno) +{ + ASSERT(regno >= 0); + ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 511) || regno <= 255); + if(auich_codec_wait(config)!=B_OK) { + PRINT(("codec busy (2)\n")); + return -1; + } + + if (config->type & TYPE_ICH4) + return *(uint16 *)(((char *)config->log_mmbar) + regno); + else + return pci->read_io_16(config->nambar + regno); +} + +void +auich_codec_write(device_config *config, int regno, uint16 value) +{ + ASSERT(regno >= 0); + ASSERT(((config->type & TYPE_ICH4) != 0 && regno <= 511) || regno <= 255); + if(auich_codec_wait(config)!=B_OK) { + PRINT(("codec busy (4)\n")); + return; + } + if (config->type & TYPE_ICH4) + *(uint16 *)(((char *)config->log_mmbar) + regno) = value; + else + pci->write_io_16(config->nambar + regno, value); +} diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/io.h b/src/add-ons/kernel/drivers/audio/ac97/auich/io.h new file mode 100644 index 0000000000..1653095120 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/io.h @@ -0,0 +1,47 @@ +/* + * Auich BeOS Driver for Intel Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _IO_H_ +#define _IO_H_ + +#include "config.h" + +uint8 auich_reg_read_8(device_config *config, int regno); +uint16 auich_reg_read_16(device_config *config, int regno); +uint32 auich_reg_read_32(device_config *config, int regno); + +void auich_reg_write_8(device_config *config, int regno, uint8 value); +void auich_reg_write_16(device_config *config, int regno, uint16 value); +void auich_reg_write_32(device_config *config, int regno, uint32 value); + +uint16 auich_codec_read(device_config *config, int regno); +void auich_codec_write(device_config *config, int regno, uint16 value); + +#endif diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/multi.c b/src/add-ons/kernel/drivers/audio/ac97/auich/multi.c new file mode 100644 index 0000000000..c203a2e192 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/multi.c @@ -0,0 +1,1120 @@ +/* + * Auich BeOS Driver for Intel Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include "multi_audio.h" +#include "multi.h" +#include "ac97.h" + +//#define DEBUG 1 + +#include "debug.h" +#include "auich.h" +#include "util.h" +#include "io.h" + +static void +auich_ac97_get_mix(void *card, const void *cookie, int32 type, float *values) { + auich_dev *dev = (auich_dev*)card; + ac97_source_info *info = (ac97_source_info *)cookie; + uint16 value, mask; + float gain; + + switch(type) { + case B_MIX_GAIN: + value = auich_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 = auich_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 = auich_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 = auich_codec_read(&dev->config, AC97_RECORD_SELECT); + value &= mask; + //PRINT(("B_MIX_MUX value : %u\n", value)); + values[0] = (float)value; + break; + } +} + +static void +auich_ac97_set_mix(void *card, const void *cookie, int32 type, float *values) { + auich_dev *dev = (auich_dev*)card; + ac97_source_info *info = (ac97_source_info *)cookie; + uint16 value, mask; + float gain; + + switch(type) { + case B_MIX_GAIN: + value = auich_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)); + auich_codec_write(&dev->config, info->reg, value); + break; + case B_MIX_MUTE: + mask = ((1 << 1) - 1) << 15; + value = auich_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)); + auich_codec_write(&dev->config, info->reg, value); + break; + case B_MIX_MICBOOST: + mask = ((1 << 1) - 1) << 6; + value = auich_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)); + auich_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)); + auich_codec_write(&dev->config, AC97_RECORD_SELECT, value); + break; + } + +} + +static int32 +auich_create_group_control(multi_dev *multi, int32 *index, int32 parent, + int32 string, const char* name) { + int32 i = *index; + (*index)++; + multi->controls[i].mix_control.id = EMU_MULTI_CONTROL_FIRSTID + i; + multi->controls[i].mix_control.parent = parent; + multi->controls[i].mix_control.flags = B_MULTI_MIX_GROUP; + multi->controls[i].mix_control.master = EMU_MULTI_CONTROL_MASTERID; + multi->controls[i].mix_control.string = string; + if(name) + strcpy(multi->controls[i].mix_control.name, name); + + return multi->controls[i].mix_control.id; +} + +static status_t +auich_create_controls_list(multi_dev *multi) +{ + uint32 i = 0, index = 0, count, id, parent, parent2, parent3; + const ac97_source_info *info; + + parent = auich_create_group_control(multi, &index, 0, 0, "Record"); + + /* AC97 Record */ + info = &source_info[0]; + PRINT(("name : %s\n", info->name)); + + parent2 = auich_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 = &auich_ac97_get_mix; + multi->controls[index].set = &auich_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 = &auich_ac97_get_mix; + multi->controls[index].set = &auich_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 = &auich_ac97_get_mix; + multi->controls[index].set = &auich_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 = &auich_ac97_get_mix; + multi->controls[index].set = &auich_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 = auich_create_group_control(multi, &index, 0, 0, "AC97 Mixer"); + + count = source_info_size; + count--; + + for(i=1; i < count ; i++) { + info = &source_info[i]; + PRINT(("name : %s\n", info->name)); + + parent2 = auich_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 = &auich_ac97_get_mix; + multi->controls[index].set = &auich_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 = &auich_ac97_get_mix; + multi->controls[index].set = &auich_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 = &auich_ac97_get_mix; + multi->controls[index].set = &auich_ac97_set_mix; + index++; + } + } + } + + parent = auich_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 = &auich_ac97_get_mix; + multi->controls[index].set = &auich_ac97_set_mix; + index++; + } + + multi->control_count = index; + PRINT(("multi->control_count %lu\n", multi->control_count)); + return B_OK; +} + +static status_t +auich_get_mix(auich_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(("auich_get_mix : invalid control id requested : %li\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 +auich_set_mix(auich_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(("auich_set_mix : invalid control id requested : %li\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(("auich_set_mix : invalid control id requested : %li\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 +auich_list_mix_controls(auich_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(auich_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 +auich_list_mix_connections(auich_dev *card, multi_mix_connection_info * data) +{ + return B_ERROR; +} + +static status_t +auich_list_mix_channels(auich_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 +auich_create_channels_list(multi_dev *multi) +{ + auich_stream *stream; + uint32 index, i, mode, designations; + multi_channel_info *chans; + uint32 chan_designations[] = { + B_CHANNEL_LEFT, + B_CHANNEL_RIGHT, + B_CHANNEL_REARLEFT, + B_CHANNEL_REARRIGHT, + B_CHANNEL_CENTER, + B_CHANNEL_SUB + }; + + chans = multi->chans; + index = 0; + + for(mode=AUICH_USE_PLAY; mode!=-1; + mode = (mode == AUICH_USE_PLAY) ? AUICH_USE_RECORD : -1) { + LIST_FOREACH(stream, &((auich_dev*)multi->card)->streams, next) { + if ((stream->use & mode) == 0) + continue; + + if(stream->channels == 2) + designations = B_CHANNEL_STEREO_BUS; + else + designations = B_CHANNEL_SURROUND_BUS; + + for(i=0; ichannels; i++) { + chans[index].channel_id = index; + chans[index].kind = (mode == AUICH_USE_PLAY) ? B_MULTI_OUTPUT_CHANNEL : B_MULTI_INPUT_CHANNEL; + chans[index].designations = designations | chan_designations[i]; + chans[index].connectors = 0; + index++; + } + } + + if(mode==AUICH_USE_PLAY) { + multi->output_channel_count = index; + } else { + multi->input_channel_count = index - multi->output_channel_count; + } + } + + chans[index].channel_id = index; + chans[index].kind = B_MULTI_OUTPUT_BUS; + chans[index].designations = B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS; + chans[index].connectors = B_CHANNEL_MINI_JACK_STEREO; + index++; + + chans[index].channel_id = index; + chans[index].kind = B_MULTI_OUTPUT_BUS; + chans[index].designations = B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS; + chans[index].connectors = B_CHANNEL_MINI_JACK_STEREO; + index++; + + multi->output_bus_channel_count = index - multi->output_channel_count + - multi->input_channel_count; + + chans[index].channel_id = index; + chans[index].kind = B_MULTI_INPUT_BUS; + chans[index].designations = B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS; + chans[index].connectors = B_CHANNEL_MINI_JACK_STEREO; + index++; + + chans[index].channel_id = index; + chans[index].kind = B_MULTI_INPUT_BUS; + chans[index].designations = B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS; + chans[index].connectors = B_CHANNEL_MINI_JACK_STEREO; + index++; + + multi->input_bus_channel_count = index - multi->output_channel_count + - multi->input_channel_count - multi->output_bus_channel_count; + + multi->aux_bus_channel_count = 0; +} + + +static status_t +auich_get_description(auich_dev *card, multi_description *data) +{ + uint32 size; + + data->interface_version = B_CURRENT_INTERFACE_VERSION; + data->interface_minimum = B_CURRENT_INTERFACE_VERSION; + + switch(card->info.vendor_id) { + case INTEL_VENDOR_ID: + strncpy(data->friendly_name, FRIENDLY_NAME_ICH, 32); + break; + case SIS_VENDOR_ID: + strncpy(data->friendly_name, FRIENDLY_NAME_SIS, 32); + break; + case NVIDIA_VENDOR_ID: + strncpy(data->friendly_name, FRIENDLY_NAME_NVIDIA, 32); + break; + case AMD_VENDOR_ID: + strncpy(data->friendly_name, FRIENDLY_NAME_AMD, 32); + break; + } + + strcpy(data->vendor_info, AUTHOR); + + /*data->output_channel_count = 6; + data->input_channel_count = 4; + data->output_bus_channel_count = 2; + data->input_bus_channel_count = 2; + data->aux_bus_channel_count = 0;*/ + + data->output_channel_count = card->multi.output_channel_count; + data->input_channel_count = card->multi.input_channel_count; + data->output_bus_channel_count = card->multi.output_bus_channel_count; + data->input_bus_channel_count = card->multi.input_bus_channel_count; + data->aux_bus_channel_count = card->multi.aux_bus_channel_count; + + size = card->multi.output_channel_count + card->multi.input_channel_count + + card->multi.output_bus_channel_count + card->multi.input_bus_channel_count + + card->multi.aux_bus_channel_count; + + // for each channel, starting with the first output channel, + // then the second, third..., followed by the first input + // channel, second, third, ..., followed by output bus + // channels and input bus channels and finally auxillary channels, + + LOG(("request_channel_count = %d\n",data->request_channel_count)); + if (data->request_channel_count >= size) { + LOG(("copying data\n")); + memcpy(data->channels, card->multi.chans, size * sizeof(card->multi.chans[0])); + } + + data->output_rates = B_SR_48000;// | B_SR_44100 | B_SR_CVSR; + data->input_rates = B_SR_48000;// | B_SR_44100 | B_SR_CVSR; + /*data->output_rates = B_SR_44100; + data->input_rates = B_SR_44100;*/ + data->min_cvsr_rate = 0; + data->max_cvsr_rate = 48000; + //data->max_cvsr_rate = 44100; + + data->output_formats = B_FMT_16BIT; + data->input_formats = B_FMT_16BIT; + data->lock_sources = B_MULTI_LOCK_INTERNAL; + data->timecode_sources = 0; + data->interface_flags = B_MULTI_INTERFACE_PLAYBACK | B_MULTI_INTERFACE_RECORD; + data->start_latency = 3000; + + strcpy(data->control_panel,""); + + return B_OK; +} + +static status_t +auich_get_enabled_channels(auich_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 +auich_set_enabled_channels(auich_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 +auich_get_global_format(auich_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 +auich_get_buffers(auich_dev *card, multi_buffer_list *data) +{ + uint8 i, j, pchannels, rchannels, bufcount; + + LOG(("flags = %#x\n",data->flags)); + LOG(("request_playback_buffers = %#x\n",data->request_playback_buffers)); + LOG(("request_playback_channels = %#x\n",data->request_playback_channels)); + LOG(("request_playback_buffer_size = %#x\n",data->request_playback_buffer_size)); + LOG(("request_record_buffers = %#x\n",data->request_record_buffers)); + LOG(("request_record_channels = %#x\n",data->request_record_channels)); + LOG(("request_record_buffer_size = %#x\n",data->request_record_buffer_size)); + + pchannels = card->pstream->channels; + rchannels = card->rstream->channels; + + if (data->request_playback_buffers < BUFFER_COUNT || + data->request_playback_channels < (pchannels) || + data->request_record_buffers < BUFFER_COUNT || + data->request_record_channels < (rchannels)) { + LOG(("not enough channels/buffers\n")); + } + + ASSERT(BUFFER_COUNT == 2); + + data->flags = B_MULTI_BUFFER_PLAYBACK | B_MULTI_BUFFER_RECORD; // XXX ??? +// data->flags = 0; + + data->return_playback_buffers = BUFFER_COUNT; /* playback_buffers[b][] */ + data->return_playback_channels = pchannels; /* playback_buffers[][c] */ + data->return_playback_buffer_size = BUFFER_FRAMES; /* frames */ + + bufcount = BUFFER_COUNT; + if(bufcount > data->request_playback_buffers) + bufcount = data->request_playback_buffers; + + for(i=0; ipstream, j, i, + &data->playback_buffers[i][j].base, + &data->playback_buffers[i][j].stride); + + data->return_record_buffers = BUFFER_COUNT; + data->return_record_channels = rchannels; + data->return_record_buffer_size = BUFFER_FRAMES; /* frames */ + + bufcount = BUFFER_COUNT; + if(bufcount > data->request_record_buffers) + bufcount = data->request_record_buffers; + + for(i=0; irstream, j, i, + &data->record_buffers[i][j].base, + &data->record_buffers[i][j].stride); + + return B_OK; +} + + +static void +auich_play_inth(void* inthparams) +{ + auich_stream *stream = (auich_stream *)inthparams; + //int32 count; + + acquire_spinlock(&slock); + stream->real_time = system_time(); + stream->frames_count += BUFFER_FRAMES; + stream->buffer_cycle = (stream->trigblk + + stream->blkmod - 1) % stream->blkmod; + stream->update_needed = true; + release_spinlock(&slock); + + TRACE(("auich_play_inth : cycle : %d\n", stream->buffer_cycle)); + + //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 void +auich_record_inth(void* inthparams) +{ + auich_stream *stream = (auich_stream *)inthparams; + //int32 count; + + acquire_spinlock(&slock); + stream->real_time = system_time(); + stream->frames_count += BUFFER_FRAMES; + stream->buffer_cycle = (stream->trigblk + + stream->blkmod - 1) % stream->blkmod; + stream->update_needed = true; + release_spinlock(&slock); + + TRACE(("auich_record_inth : cycle : %d\n", stream->buffer_cycle)); + + //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 +auich_buffer_exchange(auich_dev *card, multi_buffer_info *data) +{ + cpu_status status; + auich_stream *pstream, *rstream; + + data->flags = B_MULTI_BUFFER_PLAYBACK | B_MULTI_BUFFER_RECORD; + + if (!(card->pstream->state & AUICH_STATE_STARTED)) + auich_stream_start(card->pstream, auich_play_inth, card->pstream); + + if (!(card->rstream->state & AUICH_STATE_STARTED)) + auich_stream_start(card->rstream, auich_record_inth, card->rstream); + + if(acquire_sem_etc(card->buffer_ready_sem, 1, B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, 50000) + == B_TIMED_OUT) { + LOG(("buffer_exchange timeout ff\n")); + } + + status = lock(); + + LIST_FOREACH(pstream, &card->streams, next) { + if ((pstream->use & AUICH_USE_PLAY) == 0 || + (pstream->state & AUICH_STATE_STARTED) == 0) + continue; + if(pstream->update_needed) + break; + } + + LIST_FOREACH(rstream, &card->streams, next) { + if ((rstream->use & AUICH_USE_RECORD) == 0 || + (rstream->state & AUICH_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 +auich_buffer_force_stop(auich_dev *card) +{ + //auich_voice_halt(card->pvoice); + return B_OK; +} + +static status_t +auich_multi_control(void *cookie, uint32 op, void *data, size_t length) +{ + auich_dev *card = (auich_dev *)cookie; + + switch (op) { + case B_MULTI_GET_DESCRIPTION: + LOG(("B_MULTI_GET_DESCRIPTION\n")); + return auich_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 auich_get_enabled_channels(card, (multi_channel_enable *)data); + case B_MULTI_SET_ENABLED_CHANNELS: + LOG(("B_MULTI_SET_ENABLED_CHANNELS\n")); + return auich_set_enabled_channels(card, (multi_channel_enable *)data); + case B_MULTI_GET_GLOBAL_FORMAT: + LOG(("B_MULTI_GET_GLOBAL_FORMAT\n")); + return auich_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 auich_get_mix(card, (multi_mix_value_info *)data); + case B_MULTI_SET_MIX: + LOG(("B_MULTI_SET_MIX\n")); + return auich_set_mix(card, (multi_mix_value_info *)data); + case B_MULTI_LIST_MIX_CHANNELS: + LOG(("B_MULTI_LIST_MIX_CHANNELS\n")); + return auich_list_mix_channels(card, (multi_mix_channel_info *)data); + case B_MULTI_LIST_MIX_CONTROLS: + LOG(("B_MULTI_LIST_MIX_CONTROLS\n")); + return auich_list_mix_controls(card, (multi_mix_control_info *)data); + case B_MULTI_LIST_MIX_CONNECTIONS: + LOG(("B_MULTI_LIST_MIX_CONNECTIONS\n")); + return auich_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 auich_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 auich_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 auich_buffer_force_stop(card); + } + LOG(("ERROR: unknown multi_control %#x\n",op)); + return B_ERROR; +} + +static status_t auich_open(const char *name, uint32 flags, void** cookie); +static status_t auich_close(void* cookie); +static status_t auich_free(void* cookie); +static status_t auich_control(void* cookie, uint32 op, void* arg, size_t len); +static status_t auich_read(void* cookie, off_t position, void *buf, size_t* num_bytes); +static status_t auich_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes); + +device_hooks multi_hooks = { + auich_open, /* -> open entry point */ + auich_close, /* -> close entry point */ + auich_free, /* -> free cookie */ + auich_control, /* -> control entry point */ + auich_read, /* -> read entry point */ + auich_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 +auich_open(const char *name, uint32 flags, void** cookie) +{ + auich_dev *card = NULL; + int ix; + + LOG(("open()\n")); + + for (ix=0; ixpstream !=NULL) + return B_ERROR; + if(card->rstream !=NULL) + return B_ERROR; + + *cookie = card; + card->multi.card = card; + + LOG(("stream_new\n")); + + card->rstream = auich_stream_new(card, AUICH_USE_RECORD, BUFFER_FRAMES, BUFFER_COUNT); + card->pstream = auich_stream_new(card, AUICH_USE_PLAY, BUFFER_FRAMES, BUFFER_COUNT); + + card->buffer_ready_sem = create_sem(0, "pbuffer ready"); + + LOG(("stream_setaudio\n")); + + auich_stream_set_audioparms(card->pstream, 2, true, 48000); + auich_stream_set_audioparms(card->rstream, 2, true, 48000); + + card->pstream->first_channel = 0; + card->rstream->first_channel = 2; + + auich_stream_commit_parms(card->pstream); + auich_stream_commit_parms(card->rstream); + + auich_create_channels_list(&card->multi); + + return B_OK; +} + +static status_t +auich_close(void* cookie) +{ + //auich_dev *card = cookie; + LOG(("close()\n")); + + return B_OK; +} + +static status_t +auich_free(void* cookie) +{ + auich_dev *card = cookie; + auich_stream *stream; + LOG(("free()\n")); + + if (card->buffer_ready_sem > B_OK) + delete_sem(card->buffer_ready_sem); + + LIST_FOREACH(stream, &card->streams, next) { + auich_stream_halt(stream); + } + + while(!LIST_EMPTY(&card->streams)) { + auich_stream_delete(LIST_FIRST(&card->streams)); + } + + return B_OK; +} + +static status_t +auich_control(void* cookie, uint32 op, void* arg, size_t len) +{ + return auich_multi_control(cookie, op, arg, len); +} + +static status_t +auich_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 +auich_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes) +{ + *num_bytes = 0; /* tell caller nothing was written */ + return B_IO_ERROR; +} + diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/multi.h b/src/add-ons/kernel/drivers/audio/ac97/auich/multi.h new file mode 100644 index 0000000000..04d0fd35d1 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/multi.h @@ -0,0 +1,64 @@ +/* + * Auich BeOS Driver for Intel Southbridge audio + * + * Copyright (c) 2003, Jerome Duval (jerome.duval@free.fr) + * + * Original code : BeOS Driver for Intel ICH AC'97 Link interface + * Copyright (c) 2002, Marcus Overhagen + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _MULTI_H_ +#define _MULTI_H_ + +#define BUFFER_FRAMES 256 +#define BUFFER_COUNT 4 + +typedef struct _multi_mixer_control { + struct _multi_dev *multi; + void (*get) (void *card, const void *cookie, int32 type, float *values); + void (*set) (void *card, const void *cookie, int32 type, float *values); + const void *cookie; + int32 type; + multi_mix_control mix_control; +} multi_mixer_control; + +#define EMU_MULTI_CONTROL_FIRSTID 1024 +#define EMU_MULTI_CONTROL_MASTERID 0 + +typedef struct _multi_dev { + void *card; +#define EMU_MULTICONTROLSNUM 64 + multi_mixer_control controls[EMU_MULTICONTROLSNUM]; + uint32 control_count; + +#define EMU_MULTICHANNUM 64 + multi_channel_info chans[EMU_MULTICHANNUM]; + uint32 output_channel_count; + uint32 input_channel_count; + uint32 output_bus_channel_count; + uint32 input_bus_channel_count; + uint32 aux_bus_channel_count; +} multi_dev; + +#endif diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/multi_audio.h b/src/add-ons/kernel/drivers/audio/ac97/auich/multi_audio.h new file mode 100644 index 0000000000..5701022e6a --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/multi_audio.h @@ -0,0 +1,701 @@ +/* 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 +#ifndef ASSERT +#include +#endif + +#define B_MULTI_DRIVER_BASE (B_AUDIO_DRIVER_BASE+20) + +enum { /* open() modes */ + /* O_RDONLY is 0, O_WRONLY is 1, O_RDWR is 2 */ + B_MULTI_CONTROL = 3 +}; + +/* ioctl codes */ +enum { + /* multi_description */ + B_MULTI_GET_DESCRIPTION = B_MULTI_DRIVER_BASE, + /* multi_event_handling */ + B_MULTI_GET_EVENT_INFO, + B_MULTI_SET_EVENT_INFO, + B_MULTI_GET_EVENT, + /* multi_channel_enable */ + B_MULTI_GET_ENABLED_CHANNELS, + B_MULTI_SET_ENABLED_CHANNELS, + /* multi_format_info */ + B_MULTI_GET_GLOBAL_FORMAT, + B_MULTI_SET_GLOBAL_FORMAT, /* always sets for all channels, always implemented */ + /* multi_channel_formats */ + B_MULTI_GET_CHANNEL_FORMATS, + B_MULTI_SET_CHANNEL_FORMATS, /* only implemented if possible */ + /* multi_mix_value_info */ + B_MULTI_GET_MIX, + B_MULTI_SET_MIX, + /* multi_mix_channel_info */ + B_MULTI_LIST_MIX_CHANNELS, + /* multi_mix_control_info */ + B_MULTI_LIST_MIX_CONTROLS, + /* multi_mix_connection_info */ + B_MULTI_LIST_MIX_CONNECTIONS, + /* multi_buffer_list */ + B_MULTI_GET_BUFFERS, /* Fill out the struct for the first time; doesn't start anything. */ + B_MULTI_SET_BUFFERS, /* Set what buffers to use, if the driver supports soft buffers. */ + /* bigtime_t */ + B_MULTI_SET_START_TIME, /* When to actually start */ + /* multi_buffer_info */ + B_MULTI_BUFFER_EXCHANGE, /* stop and go are derived from this being called */ + B_MULTI_BUFFER_FORCE_STOP, /* force stop of playback */ + /* extension protocol */ + B_MULTI_LIST_EXTENSIONS, /* get a list of supported extensions */ + B_MULTI_GET_EXTENSION, /* get the value of an extension (or return error if not supported) */ + B_MULTI_SET_EXTENSION, /* set the value of an extension */ + /* multi_mode_list */ + B_MULTI_LIST_MODES, /* get a list of possible modes (multi_mode_list * arg) */ + B_MULTI_GET_MODE, /* get the current mode (int32 * arg) */ + B_MULTI_SET_MODE /* set a new mode (int32 * arg) */ +}; + +/* sample rate values */ +/* various fixed sample rates we support (for hard-sync clocked values) */ +#define B_SR_8000 0x1 +#define B_SR_11025 0x2 +#define B_SR_12000 0x4 +#define B_SR_16000 0x8 +#define B_SR_22050 0x10 +#define B_SR_24000 0x20 +#define B_SR_32000 0x40 +#define B_SR_44100 0x80 +#define B_SR_48000 0x100 +#define B_SR_64000 0x200 +#define B_SR_88200 0x400 +#define B_SR_96000 0x800 +#define B_SR_176400 0x1000 +#define B_SR_192000 0x2000 +#define B_SR_384000 0x4000 +#define B_SR_1536000 0x10000 +/* continuously variable sample rate (typically board-generated) */ +#define B_SR_CVSR 0x10000000UL +/* sample rate parameter global to all channels (input and output rates respectively) */ +#define B_SR_IS_GLOBAL 0x80000000UL +/* output sample rate locked to input sample rate (output_rates only; the common case!) */ +#define B_SR_SAME_AS_INPUT 0x40000000UL + + +/* format values */ +/* signed char */ +#define B_FMT_8BIT_S 0x01 +/* unsigned char -- this is special case */ +#define B_FMT_8BIT_U 0x02 +/* traditional 16 bit signed format (host endian) */ +#define B_FMT_16BIT 0x10 +/* left-adjusted in 32 bit signed word */ +#define B_FMT_18BIT 0x20 +#define B_FMT_20BIT 0x40 +#define B_FMT_24BIT 0x100 +#define B_FMT_32BIT 0x1000 +/* 32-bit floating point, -1.0 to 1.0 */ +#define B_FMT_FLOAT 0x20000 +/* 64-bit floating point, -1.0 to 1.0 */ +#define B_FMT_DOUBLE 0x40000 +/* 80-bit floating point, -1.0 to 1.0 */ +#define B_FMT_EXTENDED 0x80000 +/* bit stream */ +#define B_FMT_BITSTREAM 0x1000000 +/* format parameter global to all channels (input and output formats respectively) */ +#define B_FMT_IS_GLOBAL 0x80000000UL +/* output format locked to input format (output_formats) */ +#define B_FMT_SAME_AS_INPUT 0x40000000UL + +/* possible sample lock sources */ +#define B_MULTI_LOCK_INPUT_CHANNEL 0x0 /* lock_source_data is channel id */ +#define B_MULTI_LOCK_INTERNAL 0x1 +#define B_MULTI_LOCK_WORDCLOCK 0x2 +#define B_MULTI_LOCK_SUPERCLOCK 0x4 +#define B_MULTI_LOCK_LIGHTPIPE 0x8 +#define B_MULTI_LOCK_VIDEO 0x10 /* or blackburst */ +#define B_MULTI_LOCK_FIRST_CARD 0x20 /* if you have more than one card */ +#define B_MULTI_LOCK_MTC 0x40 +#define B_MULTI_LOCK_SPDIF 0x80 + +/* possible timecode sources */ +#define B_MULTI_TIMECODE_MTC 0x1 +#define B_MULTI_TIMECODE_VTC 0x2 +#define B_MULTI_TIMECODE_SMPTE 0x4 +#define B_MULTI_TIMECODE_SUPERCLOCK 0x8 +#define B_MULTI_TIMECODE_FIREWIRE 0x10 + +/* interface_flags values */ +/* Available functions on this device. */ +#define B_MULTI_INTERFACE_PLAYBACK 0x1 +#define B_MULTI_INTERFACE_RECORD 0x2 +#define B_MULTI_INTERFACE_TRANSPORT 0x4 +#define B_MULTI_INTERFACE_TIMECODE 0x8 +/* "Soft" buffers means you can change the pointer values and the driver will still be happy. */ +#define B_MULTI_INTERFACE_SOFT_PLAY_BUFFERS 0x10000 +#define B_MULTI_INTERFACE_SOFT_REC_BUFFERS 0x20000 +/* Whether the data stream is interrupted when changing channel enables. */ +#define B_MULTI_INTERFACE_CLICKS_WHEN_ENABLING_OUTPUTS 0x40000 +#define B_MULTI_INTERFACE_CLICKS_WHEN_ENABLING_INPUTS 0x80000 + +#define B_CURRENT_INTERFACE_VERSION 0x4502 +#define B_MINIMUM_INTERFACE_VERSION 0x4502 + +typedef struct multi_description multi_description; +typedef struct multi_channel_info multi_channel_info; +struct multi_description { + + size_t info_size; /* sizeof(multi_description) */ + uint32 interface_version; /* current version of interface that's implemented */ + uint32 interface_minimum; /* minimum version required to understand driver */ + + char friendly_name[32]; /* name displayed to user (C string) */ + char vendor_info[32]; /* name used internally by vendor (C string) */ + + int32 output_channel_count; + int32 input_channel_count; + int32 output_bus_channel_count; + int32 input_bus_channel_count; + int32 aux_bus_channel_count; + + int32 request_channel_count; /* how many channel_infos are there */ + multi_channel_info * + channels; + + uint32 output_rates; + uint32 input_rates; + float min_cvsr_rate; + float max_cvsr_rate; + uint32 output_formats; + uint32 input_formats; + uint32 lock_sources; + uint32 timecode_sources; + + uint32 interface_flags; + bigtime_t start_latency; /* how much in advance driver needs SET_START_TIME */ + + uint32 _reserved_[11]; + + char control_panel[64]; /* MIME type of control panel application */ +}; + +#if !defined(_MEDIA_DEFS_H) /* enum in MediaDefs.h */ +/* designation values */ +/* mono channels have no designation */ +#define B_CHANNEL_LEFT 0x1 +#define B_CHANNEL_RIGHT 0x2 +#define B_CHANNEL_CENTER 0x4 /* 5.1+ or fake surround */ +#define B_CHANNEL_SUB 0x8 /* 5.1+ */ +#define B_CHANNEL_REARLEFT 0x10 /* quad surround or 5.1+ */ +#define B_CHANNEL_REARRIGHT 0x20 /* quad surround or 5.1+ */ +#define B_CHANNEL_FRONT_LEFT_CENTER 0x40 +#define B_CHANNEL_FRONT_RIGHT_CENTER 0x80 +#define B_CHANNEL_BACK_CENTER 0x100 /* 6.1 or fake surround */ +#define B_CHANNEL_SIDE_LEFT 0x200 +#define B_CHANNEL_SIDE_RIGHT 0x400 +#define B_CHANNEL_TOP_CENTER 0x800 +#define B_CHANNEL_TOP_FRONT_LEFT 0x1000 +#define B_CHANNEL_TOP_FRONT_CENTER 0x2000 +#define B_CHANNEL_TOP_FRONT_RIGHT 0x4000 +#define B_CHANNEL_TOP_BACK_LEFT 0x8000 +#define B_CHANNEL_TOP_BACK_CENTER 0x10000 +#define B_CHANNEL_TOP_BACK_RIGHT 0x20000 +#endif + +#define B_CHANNEL_MONO_BUS 0x4000000 +#define B_CHANNEL_STEREO_BUS 0x2000000 /* + left/right */ +#define B_CHANNEL_SURROUND_BUS 0x1000000 /* multichannel */ + +/* If you have interactions where some inputs can not be used when some */ +/* outputs are used, mark both inputs and outputs with this flag. */ +#define B_CHANNEL_INTERACTION 0x80000000UL +/* If input channel #n is simplexed with output channel #n, they should both */ +/* have this flag set (different from the previous flag, which is more vague). */ +#define B_CHANNEL_SIMPLEX 0x40000000UL + +/* connector values */ +/* analog connectors */ +#define B_CHANNEL_RCA 0x1 +#define B_CHANNEL_XLR 0x2 +#define B_CHANNEL_TRS 0x4 +#define B_CHANNEL_QUARTER_INCH_MONO 0x8 +#define B_CHANNEL_MINI_JACK_STEREO 0x10 +#define B_CHANNEL_QUARTER_INCH_STEREO 0x20 +#define B_CHANNEL_ANALOG_HEADER 0x100 /* internal on card */ +#define B_CHANNEL_SNAKE 0x200 /* or D-sub */ +/* digital connectors (stereo) */ +#define B_CHANNEL_OPTICAL_SPDIF 0x1000 +#define B_CHANNEL_COAX_SPDIF 0x2000 +#define B_CHANNEL_COAX_EBU 0x4000 +#define B_CHANNEL_XLR_EBU 0x8000 +#define B_CHANNEL_TRS_EBU 0x10000 +#define B_CHANNEL_SPDIF_HEADER 0x20000 /* internal on card */ +/* multi-channel digital connectors */ +#define B_CHANNEL_LIGHTPIPE 0x100000 +#define B_CHANNEL_TDIF 0x200000 +#define B_CHANNEL_FIREWIRE 0x400000 +#define B_CHANNEL_USB 0x800000 +/* If you have multiple output connectors, only one of which can */ +/* be active at a time. */ +#define B_CHANNEL_EXCLUSIVE_SELECTION 0x80000000UL + + +typedef enum { + B_MULTI_NO_CHANNEL_KIND, + B_MULTI_OUTPUT_CHANNEL = 0x1, + B_MULTI_INPUT_CHANNEL = 0x2, + B_MULTI_OUTPUT_BUS = 0x4, + B_MULTI_INPUT_BUS = 0x8, + B_MULTI_AUX_BUS = 0x10 +} channel_kind; + +struct multi_channel_info { + int32 channel_id; + channel_kind kind; + uint32 designations; + uint32 connectors; + uint32 _reserved_[4]; +}; + + +/* Constants */ +#define B_MULTI_EVENT_MINMAX 16 +/* Event flags/masks */ +#define B_MULTI_EVENT_TRANSPORT 0x40000000UL +#define B_MULTI_EVENT_HAS_TIMECODE 0x80000000UL + +/* possible transport events */ +#define B_MULTI_EVENT_NONE 0x00000000UL +#define B_MULTI_EVENT_START 0x40010000UL +#define B_MULTI_EVENT_LOCATION 0x40020000UL /* location when shuttling or locating */ +#define B_MULTI_EVENT_SHUTTLING 0x40040000UL +#define B_MULTI_EVENT_STOP 0x40080000UL +#define B_MULTI_EVENT_RECORD 0x40100000UL +#define B_MULTI_EVENT_PAUSE 0x40200000UL +#define B_MULTI_EVENT_RUNNING 0x40400000UL /* location when running */ +/* possible device events */ +enum { + B_MULTI_EVENT_STARTED = 0x1, + B_MULTI_EVENT_STOPPED = 0x2, + B_MULTI_EVENT_CHANNEL_FORMAT_CHANGED= 0x4, + B_MULTI_EVENT_BUFFER_OVERRUN = 0x8, + B_MULTI_EVENT_SIGNAL_LOST = 0x10, + B_MULTI_EVENT_SIGNAL_DETECTED = 0x20, + B_MULTI_EVENT_CLOCK_LOST = 0x40, + B_MULTI_EVENT_CLOCK_DETECTED = 0x80, + B_MULTI_EVENT_NEW_MODE = 0x100, + B_MULTI_EVENT_CONTROL_CHANGED = 0x200 +}; + +typedef struct multi_get_event_info multi_get_event_info; +struct multi_get_event_info { + size_t info_size; /* sizeof(multi_get_event_info) */ + uint32 supported_mask; /* what events h/w supports */ + uint32 current_mask; /* current driver value */ + uint32 queue_size; /* current queue size */ + uint32 event_count; /* number of events currently in queue*/ + uint32 _reserved[3]; +}; + +typedef struct multi_set_event_info multi_set_event_info; +struct multi_set_event_info { + size_t info_size; /* sizeof(multi_set_event_info) */ + uint32 in_mask; /* what events to wait for */ + int32 semaphore; /* semaphore app will wait on */ + uint32 queue_size; /* minimum number of events to save */ + uint32 _reserved[4]; +}; + +typedef struct multi_get_event multi_get_event; +struct multi_get_event { + size_t info_size; /* sizeof(multi_get_event) */ + uint32 event; + bigtime_t timestamp; /* real time at which event was received */ + int32 count; /* used for configuration events */ + union { + int32 channels[100]; + uint32 clocks; + int32 mode; + int32 controls[100]; + struct { /* transport event */ + float out_rate; /* what rate it's now playing at */ + int32 out_hours; /* location at the time given */ + int32 out_minutes; + int32 out_seconds; + int32 out_frames; + }transport; + char _reserved_[400]; + #if defined(__cplusplus) + }; + #else + } u; + #endif + uint32 _reserved_1[10]; +}; + +typedef struct multi_channel_enable multi_channel_enable; +struct multi_channel_enable { + size_t info_size; /* sizeof(multi_channel_enable) */ + /* this must have bytes for all channels (see multi_description) */ + /* channel 0 is lowest bit of first byte */ + uchar * enable_bits; + + uint32 lock_source; + int32 lock_data; + uint32 timecode_source; + uint32 * connectors; /* which connector(s) is/are active, per channel */ +}; + +#include + +#if defined(__cplusplus) + inline void B_SET_CHANNEL(void * bits, int channel, bool value) + { + ASSERT(channel>=0); + (((uchar *)(bits))[((channel)&0x7fff)>>3] = + (((uchar *)(bits))[((channel)&0x7fff)>>3] & ~(1<<((channel)&0x7))) | + ((value) ? (1<<((channel)&0x7)) : 0)); + } + inline bool B_TEST_CHANNEL(const void * bits, int channel) + { + return ((((uchar *)(bits))[((channel)&0x7fff)>>3] >> ((channel)&0x7)) & 1); + } +#else + #define B_SET_CHANNEL(bits, channel, value) \ + ASSERT(channel>=0); \ + (((uchar *)(bits))[((channel)&0x7fff)>>3] = \ + (((uchar *)(bits))[((channel)&0x7fff)>>3] & ~(1<<((channel)&0x7))) | \ + ((value) ? (1<<((channel)&0x7)) : 0)) + #define B_TEST_CHANNEL(bits, channel) \ + ((((uchar *)(bits))[((channel)&0x7fff)>>3] >> ((channel)&0x7)) & 1) +#endif + +typedef struct multi_channel_formats multi_channel_formats; +typedef struct multi_format_info multi_format_info; +typedef struct _multi_format _multi_format; + +struct _multi_format { + uint32 rate; + float cvsr; + uint32 format; + uint32 _reserved_[3]; +}; +enum { /* timecode kinds */ + B_MULTI_NO_TIMECODE, + B_MULTI_TIMECODE_30, /* MIDI */ + B_MULTI_TIMECODE_30_DROP_2, /* NTSC */ + B_MULTI_TIMECODE_30_DROP_4, /* Brazil */ + B_MULTI_TIMECODE_25, /* PAL */ + B_MULTI_TIMECODE_24 /* Film */ +}; +struct multi_format_info { + size_t info_size; /* sizeof(multi_format_info) */ + bigtime_t output_latency; + bigtime_t input_latency; + int32 timecode_kind; + uint32 _reserved_[7]; + _multi_format input; + _multi_format output; +}; +struct multi_channel_formats { + size_t info_size; /* sizeof(multi_channel_formats) */ + int32 request_channel_count; + int32 request_first_channel; + int32 returned_channel_count; + int32 timecode_kind; + int32 _reserved_[4]; + _multi_format * + channels; + bigtime_t * latencies; /* DMA/hardware latencies; client calculates for buffers */ +}; + + +typedef struct multi_mix_value multi_mix_value; +struct multi_mix_value { + int32 id; + union { + float gain; + uint32 mux; /* bitmask of mux points */ + bool enable; + uint32 _reserved_[2]; +#if defined(__cplusplus) + }; +#else + } u; +#endif + int32 ramp; + uint32 _reserved_2[2]; +}; + +typedef struct multi_mix_value_info multi_mix_value_info; +struct multi_mix_value_info { + size_t info_size; /* sizeof(multi_mix_value_info) */ + int32 item_count; + multi_mix_value * + values; + int32 at_frame; /* time at which to start the change */ +}; + +// only one of these should be set +#define B_MULTI_MIX_JUNCTION 0x1 +#define B_MULTI_MIX_GAIN 0x2 +#define B_MULTI_MIX_MUX 0x4 +#define B_MULTI_MIX_ENABLE 0x8 +#define B_MULTI_MIX_GROUP 0x10 +#define B_MULTI_MIX_KIND_MASK 0xffff + +#define B_MULTI_MIX_MUX_VALUE 0x0104 +// any combination of these can be set +#define B_MULTI_MIX_RAMP 0x10000 + +enum strind_id { + S_null = 0, S_OUTPUT, S_INPUT, S_SETUP, S_TONE_CONTROL, S_EXTENDED_SETUP, + S_ENHANDED_SETUP, S_MASTER, S_BEEP, S_PHONE, S_MIC, S_LINE, S_CD, S_VIDEO, + S_AUX, S_WAVE, S_GAIN, S_LEVEL, S_VOLUME, S_MUTE, S_ENABLE, S_STEREO_MIX, + S_MONO_MIX, S_OUTPUT_STEREO_MIX, S_OUTPUT_MONO_MIX, S_OUTPUT_BASS, + S_OUTPUT_TREBLE, S_OUTPUT_3D_CENTER, S_OUTPUT_3D_DEPTH, + S_USERID = 1000000 +}; + +typedef struct multi_mix_control multi_mix_control; +struct multi_mix_control { + int32 id; /* unique for device -- not same id as any channel/bus ! */ + uint32 flags; /* including kind */ + int32 master; /* or 0 if it's not slaved */ + union { + struct { + float min_gain; /* dB */ + float max_gain; /* dB */ + float granularity; /* dB */ + } gain; + struct { + uint32 _reserved; + } mux; + struct { + uint32 _reserved; + } enable; + uint32 _reserved[12]; +#if defined(__cplusplus) + }; +#else + } u; +#endif + enum strind_id string; /* string id (S_null : use name) */ + int32 parent; /* parent id */ + char name[48]; +}; + +typedef struct multi_mix_channel_info multi_mix_channel_info; +struct multi_mix_channel_info { + size_t info_size; /* sizeof(multi_mix_channel_info) */ + int32 channel_count; + int32 * channels; /* allocated by caller, lists requested channels */ + int32 max_count; /* in: control ids per channel */ + int32 actual_count; /* out: actual max # controls for any individual requested channel */ + int32 ** controls; +}; + +typedef struct multi_mix_control_info multi_mix_control_info; +struct multi_mix_control_info { + size_t info_size; /* sizeof(multi_mix_control_info) */ + int32 control_count; /* in: number of controls */ + multi_mix_control * + controls; /* allocated by caller, returns control description for each */ +}; + +typedef struct multi_mix_connection multi_mix_connection; +struct multi_mix_connection { + int32 from; + int32 to; + uint32 _reserved_[2]; +}; + +typedef struct multi_mix_connection_info multi_mix_connection_info; +struct multi_mix_connection_info { + size_t info_size; + int32 max_count; /* in: available space */ + int32 actual_count; /* out: actual count */ + multi_mix_connection * + connections; /* allocated by caller, returns connections */ +}; + + +/* possible flags values for what is available (in and out) */ +#define B_MULTI_BUFFER_PLAYBACK 0x1 +#define B_MULTI_BUFFER_RECORD 0x2 +#define B_MULTI_BUFFER_METERING 0x4 +#define B_MULTI_BUFFER_TIMECODE 0x40000 + +typedef struct multi_buffer_list multi_buffer_list; +typedef struct buffer_desc buffer_desc; +/* This struct is used to query the driver about what buffers it will use, */ +/* and to tell it what buffers to use if it supports soft buffers. */ +struct multi_buffer_list { + + size_t info_size; /* sizeof(multi_buffer_list) */ + uint32 flags; + + int32 request_playback_buffers; + int32 request_playback_channels; + uint32 request_playback_buffer_size; /* frames per buffer */ + int32 return_playback_buffers; /* playback_buffers[b][] */ + int32 return_playback_channels; /* playback_buffers[][c] */ + uint32 return_playback_buffer_size; /* frames */ + buffer_desc ** playback_buffers; + void * _reserved_1; + + int32 request_record_buffers; + int32 request_record_channels; + uint32 request_record_buffer_size; /* frames per buffer */ + int32 return_record_buffers; + int32 return_record_channels; + uint32 return_record_buffer_size; /* frames */ + buffer_desc ** record_buffers; + void * _reserved_2; + +}; + +struct buffer_desc { + char * base; /* pointer to first sample for channel for buffer */ + size_t stride; /* offset to next sample */ + uint32 _reserved_[2]; +}; + + +/* This struct is used when actually queuing data to be played, and/or */ +/* receiving data from a recorder. */ +typedef struct multi_buffer_info multi_buffer_info; +struct multi_buffer_info { + + size_t info_size; /* sizeof(multi_buffer_info) */ + uint32 flags; + + bigtime_t played_real_time; + bigtime_t played_frames_count; + int32 _reserved_0; + int32 playback_buffer_cycle; + + bigtime_t recorded_real_time; + bigtime_t recorded_frames_count; + int32 _reserved_1; + int32 record_buffer_cycle; + + int32 meter_channel_count; + char * meters_peak; /* in the same format as the data; allocated by caller */ + char * meters_average; /* in the same format as the data; allocated by caller */ + + /* timecode sent and received at buffer swap */ + int32 hours; + int32 minutes; + int32 seconds; + int32 tc_frames; /* for timecode frames as opposed to sample frames */ + int32 at_frame_delta; /* how far into buffer (or before buffer for negative) */ + +}; + + +typedef struct multi_mode_info multi_mode_info; +typedef struct multi_mode_list multi_mode_list; + +struct multi_mode_list { + size_t info_size; /* sizeof(multi_mode_list) */ + int32 in_request_count; + int32 out_actual_count; + int32 out_current_mode; + multi_mode_info * + io_modes; +}; + +struct multi_mode_info { + int32 mode_id; + uint32 flags; + char mode_name[64]; + int32 input_channel_count; + int32 output_channel_count; + float best_frame_rate_in; + float best_frame_rate_out; + uint32 sample_formats_in; + uint32 sample_formats_out; + char _reserved[160]; +}; + + +/* This extension protocol can grow however much you want. */ +/* Good extensions should be put into this header; really */ +/* good extensions should become part of the regular API. */ +/* For developer-developed extensions, use all lowercase */ +/* and digits (no upper case). If we then bless a third- */ +/* party extension, we can just upper-case the selector. */ + +typedef struct multi_extension_list multi_extension_list; +typedef struct multi_extension_info multi_extension_info; +struct multi_extension_info { + uint32 code; + uint32 flags; + char name[24]; +}; + +#define B_MULTI_MAX_EXTENSION_COUNT 31 +struct multi_extension_list { /* MULTI_LIST_EXTENSIONS */ + size_t info_size; /* sizeof(multi_extension_list) */ + uint32 max_count; + int32 actual_count; /* return # of actual extensions */ + multi_extension_info * + extensions; /* allocated by caller */ +}; + +typedef struct multi_extension_cmd multi_extension_cmd; +struct multi_extension_cmd { /* MULTI_GET_EXTENSION and MULTI_SET_EXTENSION */ + size_t info_size; /* sizeof(multi_extension_cmd) */ + uint32 code; + uint32 _reserved_1; + void * in_data; + size_t in_size; + void * out_data; + size_t out_size; +}; + +enum { + B_MULTI_EX_CLOCK_GENERATION = 'CLGE', + B_MULTI_EX_DIGITAL_FORMAT = 'DIFO', + B_MULTI_EX_OUTPUT_NOMINAL = 'OUNO', + B_MULTI_EX_INPUT_NOMINAL = 'INNO' +}; + +typedef struct multi_ex_clock_generation multi_ex_clock_generation; +struct multi_ex_clock_generation { + int32 channel; /* if specific, or -1 for all */ + uint32 clock; /* WORDCLOCK or SUPERCLOCK, typically */ +}; + +typedef struct multi_ex_digital_format multi_ex_digital_format; +struct multi_ex_digital_format { + int32 channel; /* if specific, or -1 for all */ + uint32 format; /* B_CHANNEL_*_SPDIF or B_CHANNEL_*_EBU */ +}; + +enum { + B_MULTI_NOMINAL_MINUS_10 = 1, + B_MULTI_NOMINAL_PLUS_4 +}; + +typedef struct multi_ex_nominal_level multi_ex_nominal_level; +struct multi_ex_nominal_level { + int32 channel; /* if specific, or -1 for all */ + int32 level; +}; + +#endif /* _MULTI_AUDIO_H */ + diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/queue.h b/src/add-ons/kernel/drivers/audio/ac97/auich/queue.h new file mode 100644 index 0000000000..396c70fb2e --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/queue.h @@ -0,0 +1,529 @@ +/* $NetBSD: queue.h,v 1.31 2002/06/01 23:51:05 lukem Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so only elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ + if ((head)->lh_first && \ + (head)->lh_first->field.le_prev != &(head)->lh_first) \ + panic("LIST_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_OP(elm, field) \ + if ((elm)->field.le_next && \ + (elm)->field.le_next->field.le_prev != \ + &(elm)->field.le_next) \ + panic("LIST_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ + if (*(elm)->field.le_prev != (elm)) \ + panic("LIST_* back %p %s:%d", (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ + (elm)->field.le_next = (void *)1L; \ + (elm)->field.le_prev = (void *)1L; +#else +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_LIST_OP(elm, field) +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) +#endif + +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + QUEUEDEBUG_LIST_OP((elm), field) \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var); \ + (var) = ((var)->field.le_next)) + +/* + * List access methods. + */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) + +#define SLIST_INIT(head) do { \ + (head)->slh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var); \ + (var) = ((var)->field.sqe_next)) + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue functions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ + if ((head)->tqh_first && \ + (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ + panic("TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ + if (*(head)->tqh_last != NULL) \ + panic("TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_OP(elm, field) \ + if ((elm)->field.tqe_next && \ + (elm)->field.tqe_next->field.tqe_prev != \ + &(elm)->field.tqe_next) \ + panic("TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ + (elm)->field.tqe_next = (void *)1L; \ + (elm)->field.tqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) +#define QUEUEDEBUG_TAILQ_OP(elm, field) +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) +#endif + +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((elm), field) \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +/* + * Tail queue access methods. + */ +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ + (var); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { (void *)&head, (void *)&head } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != (void *)(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != (void *)(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#endif /* !_SYS_QUEUE_H_ */ diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/util.c b/src/add-ons/kernel/drivers/audio/ac97/auich/util.c new file mode 100644 index 0000000000..59fd55d6f8 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/util.c @@ -0,0 +1,113 @@ +/* + * 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; +} + +/* This is not the most advanced method to map physical memory for io access. + * Perhaps using B_ANY_KERNEL_ADDRESS instead of B_ANY_KERNEL_BLOCK_ADDRESS + * makes the whole offset calculation and relocation obsolete. But the code + * below does work, and I can't test if using B_ANY_KERNEL_ADDRESS also works. + */ +area_id map_mem(void **log, void *phy, size_t size, const char *name) +{ + uint32 offset; + void *phyadr; + void *mapadr; + area_id area; + + LOG(("mapping physical address %p with %#x bytes for %s\n",phy,size,name)); + + offset = (uint32)phy & (B_PAGE_SIZE - 1); + phyadr = phy - offset; + size = round_to_pagesize(size + offset); + area = map_physical_memory(name, phyadr, size, B_ANY_KERNEL_BLOCK_ADDRESS, B_READ_AREA | B_WRITE_AREA, &mapadr); + *log = mapadr + offset; + + LOG(("physical = %p, logical = %p, offset = %#x, phyadr = %p, mapadr = %p, size = %#x, area = %#x\n", + phy, *log, offset, phyadr, mapadr, size, area)); + + return area; +} diff --git a/src/add-ons/kernel/drivers/audio/ac97/auich/util.h b/src/add-ons/kernel/drivers/audio/ac97/auich/util.h new file mode 100644 index 0000000000..79a2c69809 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/auich/util.h @@ -0,0 +1,41 @@ +/* + * 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); +area_id map_mem(void **log, void *phy, size_t size, const char *name); + +cpu_status lock(void); +void unlock(cpu_status status); + +extern spinlock slock; + +#endif