diff --git a/src/add-ons/kernel/drivers/audio/ac97/Jamfile b/src/add-ons/kernel/drivers/audio/ac97/Jamfile index 0773a9e59e..211f6f005b 100644 --- a/src/add-ons/kernel/drivers/audio/ac97/Jamfile +++ b/src/add-ons/kernel/drivers/audio/ac97/Jamfile @@ -2,5 +2,6 @@ SubDir HAIKU_TOP src add-ons kernel drivers audio ac97 ; SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 auich ; SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 auvia ; +SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 es1370 ; SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 ich ; # SubInclude HAIKU_TOP src add-ons kernel drivers audio ac97 ichaudio ; diff --git a/src/add-ons/kernel/drivers/audio/ac97/es1370/Jamfile b/src/add-ons/kernel/drivers/audio/ac97/es1370/Jamfile new file mode 100644 index 0000000000..cd44061601 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/Jamfile @@ -0,0 +1,14 @@ +SubDir HAIKU_TOP src add-ons kernel drivers audio ac97 es1370 ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +UsePrivateHeaders media ; + +KernelAddon es1370 : + debug.c + es1370.c + io.c + multi.c + util.c +; + diff --git a/src/add-ons/kernel/drivers/audio/ac97/es1370/ac97.c b/src/add-ons/kernel/drivers/audio/ac97/es1370/ac97.c new file mode 100644 index 0000000000..086cf91e44 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/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[] = { + { "Recording", 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/Treble", 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/es1370/ac97.h b/src/add-ons/kernel/drivers/audio/ac97/es1370/ac97.h new file mode 100644 index 0000000000..9972b42a72 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/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/es1370/config.h b/src/add-ons/kernel/drivers/audio/ac97/es1370/config.h new file mode 100644 index 0000000000..9856277f27 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/config.h @@ -0,0 +1,24 @@ +/* + * ES1370 Haiku Driver for ES1370 audio + * + * Copyright 2002-2007, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Marcus Overhagen, marcus@overhagen.de + * Jerome Duval, jerome.duval@free.fr + */ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#define NUM_CARDS 3 +#define DEVNAME 32 + +typedef struct +{ + uint32 base; + uint32 irq; + uint32 type; +} device_config; + +#endif diff --git a/src/add-ons/kernel/drivers/audio/ac97/es1370/debug.c b/src/add-ons/kernel/drivers/audio/ac97/es1370/debug.c new file mode 100644 index 0000000000..5fb661ac51 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/debug.c @@ -0,0 +1,76 @@ +/* + * ES1370 Haiku Driver for ES1370 audio + * + * Copyright 2002-2007, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Marcus Overhagen, marcus@overhagen.de + * Jerome Duval, jerome.duval@free.fr + */ +#include +#include +#include +#include +#include +#include +#include "debug.h" +#include "es1370.h" + +#if DEBUG > 0 +static const char * logfile="/boot/home/es1370.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/es1370/debug.h b/src/add-ons/kernel/drivers/audio/ac97/es1370/debug.h new file mode 100644 index 0000000000..e6b45ddf2e --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/debug.h @@ -0,0 +1,51 @@ +/* + * ES1370 Haiku Driver for ES1370 audio + * + * Copyright 2002-2007, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Marcus Overhagen, marcus@overhagen.de + * Jerome Duval, jerome.duval@free.fr + */ + +#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 1 +#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/es1370/es1370.c b/src/add-ons/kernel/drivers/audio/ac97/es1370/es1370.c new file mode 100644 index 0000000000..1744d774d5 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/es1370.c @@ -0,0 +1,613 @@ +/* + * ES1370 Haiku Driver for ES1370 audio + * + * Copyright 2002-2007, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jerome Duval, jerome.duval@free.fr + */ + +#include +#include +#include +#include +#include +#include +#include "es1370.h" +#include "debug.h" +#include "config.h" +#include "util.h" +#include "io.h" +#include +#include +#include + +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 *); +status_t es1370_init(es1370_dev * card); + +static char pci_name[] = B_PCI_MODULE_NAME; +pci_module_info *pci; + +int32 num_cards; +es1370_dev cards[NUM_CARDS]; +int32 num_names; +char * names[NUM_CARDS*20+1]; + +extern device_hooks multi_hooks; + +es1370_settings current_settings = { + 44100, // sample rate + 512, // buffer frames + 2, // buffer count +}; + + +/* es1370 Memory management */ + +static es1370_mem * +es1370_mem_new(es1370_dev *card, size_t size) +{ + es1370_mem *mem; + + if ((mem = malloc(sizeof(*mem))) == NULL) + return (NULL); + + mem->area = alloc_mem(&mem->phy_base, &mem->log_base, size, "es1370 buffer"); + mem->size = size; + if (mem->area < B_OK) { + free(mem); + return NULL; + } + return mem; +} + +static void +es1370_mem_delete(es1370_mem *mem) +{ + if(mem->area > B_OK) + delete_area(mem->area); + free(mem); +} + +static void * +es1370_mem_alloc(es1370_dev *card, size_t size) +{ + es1370_mem *mem; + + mem = es1370_mem_new(card, size); + if (mem == NULL) + return (NULL); + + LIST_INSERT_HEAD(&(card->mems), mem, next); + + return mem; +} + +static void +es1370_mem_free(es1370_dev *card, void *ptr) +{ + es1370_mem *mem; + + LIST_FOREACH(mem, &card->mems, next) { + if (mem->log_base != ptr) + continue; + LIST_REMOVE(mem, next); + + es1370_mem_delete(mem); + break; + } +} + +/* es1370 stream functions */ + +status_t +es1370_stream_set_audioparms(es1370_stream *stream, uint8 channels, + uint8 b16, uint32 sample_rate) +{ + uint8 sample_size, frame_size; + LOG(("es1370_stream_set_audioparms\n")); + + if ((stream->channels == channels) && + (stream->b16 == b16) && + (stream->sample_rate == sample_rate)) + return B_OK; + + if(stream->buffer) + es1370_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 = es1370_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 +es1370_stream_commit_parms(es1370_stream *stream) +{ + uint8 sample_size, frame_size; + uint32 ctrl; + es1370_dev *card = stream->card; + LOG(("es1370_stream_commit_parms\n")); + + ctrl = es1370_reg_read_32(&card->config, ES1370_REG_CONTROL) & ~CTRL_PCLKDIV; + ctrl |= DAC2_SRTODIV((uint16)stream->sample_rate) << CTRL_SH_PCLKDIV; + es1370_reg_write_32(&card->config, ES1370_REG_CONTROL, ctrl); + + sample_size = stream->b16 + 1; + frame_size = sample_size * stream->channels; + + if (stream->use & ES1370_USE_RECORD) { + es1370_reg_write_32(&card->config, ES1370_REG_MEMPAGE, 0xd); + es1370_reg_write_32(&card->config, ES1370_REG_ADC_FRAMEADR & 0xff, (uint32)stream->buffer->phy_base); + es1370_reg_write_32(&card->config, ES1370_REG_ADC_FRAMECNT & 0xff, ((stream->blksize * stream->bufcount) >> 2) - 1); + es1370_reg_write_32(&card->config, ES1370_REG_ADC_SCOUNT & 0xff, stream->bufframes - 1); + } else { + es1370_reg_write_32(&card->config, ES1370_REG_MEMPAGE, 0xc); + es1370_reg_write_32(&card->config, ES1370_REG_DAC2_FRAMEADR & 0xff, (uint32)stream->buffer->phy_base); + es1370_reg_write_32(&card->config, ES1370_REG_DAC2_FRAMECNT & 0xff, ((stream->blksize * stream->bufcount) >> 2) - 1); + es1370_reg_write_32(&card->config, ES1370_REG_DAC2_SCOUNT & 0xff, stream->bufframes - 1); + LOG(("es1370_stream_commit_parms %ld %ld\n", ((stream->blksize * stream->bufcount) >> 2) - 1, (stream->blksize / frame_size) - 1)); + } + + return B_OK; +} + +status_t +es1370_stream_get_nth_buffer(es1370_stream *stream, uint8 chan, uint8 buf, + char** buffer, size_t *stride) +{ + uint8 sample_size, frame_size; + LOG(("es1370_stream_get_nth_buffer\n")); + + sample_size = stream->b16 + 1; + frame_size = sample_size * stream->channels; + + *buffer = stream->buffer->log_base + (buf * stream->bufframes * frame_size) + + chan * sample_size; + *stride = frame_size; + + return B_OK; +} + +static uint32 +es1370_stream_curaddr(es1370_stream *stream) +{ + es1370_dev *card = stream->card; + uint32 reg = 0, cnt = 0; + if (stream->use & ES1370_USE_RECORD) { + reg = ES1370_REG_ADC_FRAMECNT; + } else { + reg = ES1370_REG_DAC2_FRAMECNT; + } + es1370_reg_write_32(&card->config, ES1370_REG_MEMPAGE, reg >> 8); + cnt = es1370_reg_read_32(&card->config, reg & 0xff) >> 16; + //TRACE(("stream_curaddr %lx\n", (cnt << 2) / stream->blksize)); + return (cnt << 2) / stream->blksize; +} + +void +es1370_stream_start(es1370_stream *stream, void (*inth) (void *), void *inthparam) +{ + uint32 sctrl = 0, ctrl = 0; + es1370_dev *card = stream->card; + LOG(("es1370_stream_start\n")); + + stream->inth = inth; + stream->inthparam = inthparam; + + stream->state |= ES1370_STATE_STARTED; + + sctrl = es1370_reg_read_32(&card->config, ES1370_REG_SERIAL_CONTROL); + ctrl = es1370_reg_read_32(&card->config, ES1370_REG_CONTROL); + + if (stream->use & ES1370_USE_RECORD) { + sctrl &= ~(SCTRL_R1SEB | SCTRL_R1SMB); + if (stream->b16) + sctrl |= SCTRL_R1SEB; + if (stream->channels == 2) + sctrl |= SCTRL_R1SMB; + es1370_reg_write_32(&card->config, ES1370_REG_SERIAL_CONTROL, sctrl & ~SCTRL_R1INTEN); + es1370_reg_write_32(&card->config, ES1370_REG_SERIAL_CONTROL, sctrl | SCTRL_R1INTEN); + + ctrl |= CTRL_ADC_EN; + } else { + sctrl &= ~(SCTRL_P2SEB | SCTRL_P2SMB | 0x003f0000); + if (stream->b16) + sctrl |= SCTRL_P2SEB; + if (stream->channels == 2) + sctrl |= SCTRL_P2SMB; + sctrl |= (stream->b16 + 1) << SCTRL_SH_P2ENDINC; + es1370_reg_write_32(&card->config, ES1370_REG_SERIAL_CONTROL, sctrl & ~SCTRL_P2INTEN); + es1370_reg_write_32(&card->config, ES1370_REG_SERIAL_CONTROL, sctrl | SCTRL_P2INTEN); + + ctrl |= CTRL_DAC2_EN; + } + + es1370_reg_write_32(&card->config, ES1370_REG_SERIAL_CONTROL, sctrl); + es1370_reg_write_32(&card->config, ES1370_REG_CONTROL, ctrl); + +#ifdef DEBUG + //dump_hardware_regs(&stream->card->config); +#endif +} + +void +es1370_stream_halt(es1370_stream *stream) +{ + uint32 ctrl; + es1370_dev *card = stream->card; + LOG(("es1370_stream_halt\n")); + + stream->state &= ~ES1370_STATE_STARTED; + + ctrl = es1370_reg_read_32(&card->config, ES1370_REG_CONTROL); + if (stream->use & ES1370_USE_RECORD) + ctrl &= ~CTRL_ADC_EN; + else + ctrl &= ~CTRL_DAC2_EN; + es1370_reg_write_32(&card->config, ES1370_REG_CONTROL, ctrl); +} + +es1370_stream * +es1370_stream_new(es1370_dev *card, uint8 use, uint32 bufframes, uint8 bufcount) +{ + es1370_stream *stream; + cpu_status status; + LOG(("es1370_stream_new\n")); + + stream = malloc(sizeof(es1370_stream)); + if (stream == NULL) + return (NULL); + stream->card = card; + stream->use = use; + stream->state = !ES1370_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; + + stream->buffer_cycle = 0; + stream->frames_count = 0; + stream->real_time = 0; + stream->update_needed = false; + + status = lock(); + LIST_INSERT_HEAD((&card->streams), stream, next); + unlock(status); + + return stream; +} + +void +es1370_stream_delete(es1370_stream *stream) +{ + cpu_status status; + int32 i; + LOG(("es1370_stream_delete\n")); + + es1370_stream_halt(stream); + + if(stream->buffer) + es1370_mem_free(stream->card, stream->buffer->log_base); + + status = lock(); + LIST_REMOVE(stream, next); + unlock(status); + + free(stream); +} + +/* es1370 interrupt */ + +static int32 +es1370_int(void *arg) +{ + es1370_dev *card = arg; + bool gotone = false; + uint32 curblk; + es1370_stream *stream = NULL; + uint32 sta, sctrl; + + // TRACE(("es1370_int(%p)\n", card)); + + sta = es1370_reg_read_32(&card->config, ES1370_REG_STATUS); + if (sta & card->interrupt_mask) { + + //TRACE(("interrupt !! %x\n", sta)); + sctrl = es1370_reg_read_32(&card->config, ES1370_REG_SERIAL_CONTROL); + + LIST_FOREACH(stream, &card->streams, next) { + if (stream->use & ES1370_USE_RECORD) { + if ((sta & STAT_ADC) == 0) + continue; + es1370_reg_write_32(&card->config, ES1370_REG_SERIAL_CONTROL, sctrl & ~SCTRL_R1INTEN); + es1370_reg_write_32(&card->config, ES1370_REG_SERIAL_CONTROL, sctrl | SCTRL_R1INTEN); + } else { + if ((sta & STAT_DAC2) == 0) + continue; + es1370_reg_write_32(&card->config, ES1370_REG_SERIAL_CONTROL, sctrl & ~SCTRL_P2INTEN); + es1370_reg_write_32(&card->config, ES1370_REG_SERIAL_CONTROL, sctrl | SCTRL_P2INTEN); + } + + curblk = es1370_stream_curaddr(stream); + // TRACE(("INTR at trigblk %lu, stream->trigblk %lu\n", curblk, stream->trigblk)); + if (curblk == stream->trigblk) { + stream->trigblk++; + stream->trigblk = stream->trigblk % stream->blkmod; + if (stream->inth) + stream->inth(stream->inthparam); + } + gotone = true; + } + } else { + //TRACE(("interrupt masked %x, ", card->interrupt_mask)); + //TRACE(("sta %x\n", sta)); + } + + if (gotone) + return B_INVOKE_SCHEDULER; + + //TRACE(("Got unhandled interrupt\n")); + return B_UNHANDLED_INTERRUPT; +} + + +/* es1370 driver functions */ + + +/* detect presence of our hardware */ +status_t +init_hardware(void) +{ + int ix=0; + pci_info info; + status_t err = ENODEV; + + LOG_CREATE(); + + PRINT(("init_hardware()\n")); + + if (get_module(pci_name, (module_info **)&pci)) + return ENOSYS; + + while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) { + if (info.vendor_id == 0x1274 + && (info.device_id == 0x5000 + /*|| info.device_id == 0x1371 + || info.device_id == 0x5880*/) + ) + { + err = B_OK; + } + ix++; + } + + put_module(pci_name); + + return err; +} + +static void +make_device_names( + es1370_dev * card) +{ + sprintf(card->name, "audio/hmulti/es1370/%ld", card-cards+1); + names[num_names++] = card->name; + + names[num_names] = NULL; +} + + +status_t +es1370_init(es1370_dev * card) +{ + card->interrupt_mask = STAT_DAC2 | STAT_ADC; + + /* Init streams list */ + LIST_INIT(&(card->streams)); + + /* Init mems list */ + LIST_INIT(&(card->mems)); + + return B_OK; +} + +static status_t +es1370_setup(es1370_dev * card) +{ + status_t err = B_OK; + status_t rv; + unsigned char cmd; + + PRINT(("es1370_setup(%p)\n", card)); + + make_device_names(card); + + card->config.base = card->info.u.h0.base_registers[0]; + card->config.irq = card->info.u.h0.interrupt_line; + card->config.type = 0; + + 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.base)); + + 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)); + + es1370_reg_write_32(&card->config, ES1370_REG_SERIAL_CONTROL, SCTRL_P2INTEN | SCTRL_R1INTEN); + es1370_reg_write_32(&card->config, ES1370_REG_CONTROL, CTRL_CDC_EN); + + /* reset the codec */ + PRINT(("codec reset\n")); + es1370_codec_write(&card->config, CODEC_RES_PD, 0x2); + es1370_codec_write(&card->config, CODEC_RES_PD, 0x3); + es1370_codec_write(&card->config, CODEC_CSEL, 0x0); + + es1370_codec_write(&card->config, 0x0, 0x0); + es1370_codec_write(&card->config, 0x1, 0x0); + es1370_codec_write(&card->config, 0x2, 0x0); + es1370_codec_write(&card->config, 0x3, 0x0); + + snooze(50000); // 50 ms + + PRINT(("installing interrupt : %lx\n", card->config.irq)); + install_io_interrupt_handler(card->config.irq, es1370_int, card, 0); + + if ((err = es1370_init(card))) + return (err); + + PRINT(("init_driver done\n")); + + return err; +} + + +status_t +init_driver(void) +{ + int ix=0; + void *settings_handle; + + pci_info info; + num_cards = 0; + + PRINT(("init_driver()\n")); + load_driver_symbols("es1370"); + + // get driver settings + settings_handle = load_driver_settings ("es1370.settings"); + if (settings_handle != NULL) { + const char *item; + char *end; + uint32 value; + + item = get_driver_parameter (settings_handle, "sample_rate", "44100", "44100"); + value = strtoul (item, &end, 0); + if (*end == '\0') current_settings.sample_rate = value; + + item = get_driver_parameter (settings_handle, "buffer_frames", "512", "512"); + value = strtoul (item, &end, 0); + if (*end == '\0') current_settings.buffer_frames = value; + + item = get_driver_parameter (settings_handle, "buffer_count", "2", "2"); + value = strtoul (item, &end, 0); + if (*end == '\0') current_settings.buffer_count = value; + + unload_driver_settings (settings_handle); + } + + if (get_module(pci_name, (module_info **) &pci)) + return ENOSYS; + + while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) { + if (info.vendor_id == 0x1274 + && (info.device_id == 0x5000 + /*|| info.device_id == 0x1371 + || info.device_id == 0x5880*/) + ) { + if (num_cards == NUM_CARDS) { + PRINT(("Too many es1370 cards installed!\n")); + break; + } + memset(&cards[num_cards], 0, sizeof(es1370_dev)); + cards[num_cards].info = info; + if (es1370_setup(&cards[num_cards])) { + PRINT(("Setup of es1370 %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; + } + + return B_OK; +} + + +static void +es1370_shutdown(es1370_dev *card) +{ + PRINT(("shutdown(%p)\n", card)); + //ac97_amp_enable(&card->config, false); + card->interrupt_mask = 0; + + remove_io_interrupt_handler(card->config.irq, es1370_int, card); +} + + +void +uninit_driver(void) +{ + int ix, cnt = num_cards; + num_cards = 0; + + PRINT(("uninit_driver()\n")); + for (ix=0; ix +#include +#include +#include +#include "es1370reg.h" +#include "config.h" +#include "queue.h" +#include "hmulti_audio.h" +#include "multi.h" + + +#define VERSION "Version alpha 1, Copyright (c) 2007 Jérôme Duval, compiled on " __DATE__ " " __TIME__ +#define DRIVER_NAME "es1370" +#define FRIENDLY_NAME "ES1370" +#define AUTHOR "Jérôme Duval" + +#define FRIENDLY_NAME_ES1370 "ES1370" + +#define ES1370_USE_PLAY (1 << 0) +#define ES1370_USE_RECORD (1 << 1) +#define ES1370_STATE_STARTED (1 << 0) + +/* + * es1370 memory managment + */ + +typedef struct _es1370_mem { + LIST_ENTRY(_es1370_mem) next; + void *log_base; + void *phy_base; + area_id area; + size_t size; +} es1370_mem; + +/* + * Streams + */ + +typedef struct _es1370_stream { + struct _es1370_dev *card; + uint8 use; + uint8 state; + uint8 b16; + uint32 sample_rate; + uint8 channels; + uint32 bufframes; + uint8 bufcount; + + uint32 base; + + LIST_ENTRY(_es1370_stream) next; + + void (*inth) (void *); + void *inthparam; + + void *dmaops_log_base; + void *dmaops_phy_base; + area_id dmaops_area; + + es1370_mem *buffer; + uint16 blksize; /* in samples */ + uint16 trigblk; /* blk on which to trigger inth */ + uint16 blkmod; /* Modulo value to wrap trigblk */ + + /* multi_audio */ + volatile int64 frames_count; // for play or record + volatile bigtime_t real_time; // for play or record + volatile int32 buffer_cycle; // for play or record + int32 first_channel; + bool update_needed; +} es1370_stream; + +/* + * Devices + */ + +typedef struct _es1370_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(, _es1370_stream) streams; + + LIST_HEAD(, _es1370_mem) mems; + + es1370_stream *pstream; + es1370_stream *rstream; + + /* multi_audio */ + multi_dev multi; +} es1370_dev; + + +typedef struct { + uint32 sample_rate; + uint32 buffer_frames; + int32 buffer_count; +} es1370_settings; + +extern es1370_settings current_settings; + +extern int32 num_cards; +extern es1370_dev cards[NUM_CARDS]; + +status_t es1370_stream_set_audioparms(es1370_stream *stream, uint8 channels, + uint8 b16, uint32 sample_rate); +status_t es1370_stream_commit_parms(es1370_stream *stream); +status_t es1370_stream_get_nth_buffer(es1370_stream *stream, uint8 chan, uint8 buf, + char** buffer, size_t *stride); +void es1370_stream_start(es1370_stream *stream, void (*inth) (void *), void *inthparam); +void es1370_stream_halt(es1370_stream *stream); +es1370_stream *es1370_stream_new(es1370_dev *card, uint8 use, uint32 bufframes, uint8 bufcount); +void es1370_stream_delete(es1370_stream *stream); + +#endif /* _DEV_PCI_ES1370_H_ */ diff --git a/src/add-ons/kernel/drivers/audio/ac97/es1370/es1370.settings b/src/add-ons/kernel/drivers/audio/ac97/es1370/es1370.settings new file mode 100644 index 0000000000..3f90c1b153 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/es1370.settings @@ -0,0 +1,9 @@ +# Settings file for the es1370 driver +# +# This file should be moved to the directory +# ~/config/settings/kernel/drivers/ +# + +#sample_rate 48000 +#buffer_frames 512 +#buffer_count 2 diff --git a/src/add-ons/kernel/drivers/audio/ac97/es1370/es1370reg.h b/src/add-ons/kernel/drivers/audio/ac97/es1370/es1370reg.h new file mode 100644 index 0000000000..2d39c6d803 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/es1370reg.h @@ -0,0 +1,194 @@ +/*- + * This supports the ENSONIQ AudioPCI board based on the ES1370. + * + * Copyright (c) 1998 Joachim Kuebart + * 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 immediately at the beginning of the file, without modification, + * 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. Absolutely no warranty of function or purpose is made by the author + * Joachim Kuebart. + * 4. Modifications may be freely made to this file if the above conditions + * are met. + * + * $FreeBSD: /repoman/r/ncvs/src/sys/dev/sound/pci/es137x.h,v 1.6 2005/07/31 13:19:38 netchild Exp $ + */ + +#ifndef _ES1370_REG_H +#define _ES1370_REG_H + +#define ES1370_REG_CONTROL 0x00 +#define ES1370_REG_STATUS 0x04 +#define ES1370_REG_UART_DATA 0x08 +#define ES1370_REG_UART_STATUS 0x09 +#define ES1370_REG_UART_CONTROL 0x09 +#define ES1370_REG_UART_TEST 0x0a +#define ES1370_REG_MEMPAGE 0x0c +#define ES1370_REG_CODEC 0x10 +#define CODEC_INDEX_SHIFT 8 +#define ES1370_REG_SERIAL_CONTROL 0x20 +#define ES1370_REG_DAC1_SCOUNT 0x24 +#define ES1370_REG_DAC2_SCOUNT 0x28 +#define ES1370_REG_ADC_SCOUNT 0x2c + +#define ES1370_REG_DAC1_FRAMEADR 0xc30 +#define ES1370_REG_DAC1_FRAMECNT 0xc34 +#define ES1370_REG_DAC2_FRAMEADR 0xc38 +#define ES1370_REG_DAC2_FRAMECNT 0xc3c +#define ES1370_REG_ADC_FRAMEADR 0xd30 +#define ES1370_REG_ADC_FRAMECNT 0xd34 + +#define DAC2_SRTODIV(x) (((1411200 + (x) / 2) / (x) - 2) & 0x1fff) +#define DAC2_DIVTOSR(x) (1411200 / ((x) + 2)) + +#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */ +#define CTRL_XCTL1 0x40000000 /* SERR pin if enabled */ +#define CTRL_OPEN 0x20000000 /* no function, can be read and + * written */ +#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */ +#define CTRL_SH_PCLKDIV 16 +#define CTRL_MSFMTSEL 0x00008000 /* MPEG serial data fmt: 0 = Sony, 1 + * = I2S */ +#define CTRL_M_SBB 0x00004000 /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */ +#define CTRL_WTSRSEL 0x00003000 /* DAC1 clock freq: 0=5512, 1=11025, + * 2=22050, 3=44100 */ +#define CTRL_SH_WTSRSEL 12 +#define CTRL_DAC_SYNC 0x00000800 /* 1 = DAC2 runs off DAC1 clock */ +#define CTRL_CCB_INTRM 0x00000400 /* 1 = CCB "voice" ints enabled */ +#define CTRL_M_CB 0x00000200 /* recording source: 0 = ADC, 1 = + * MPEG */ +#define CTRL_XCTL0 0x00000100 /* 0 = Line in, 1 = Line out */ +#define CTRL_BREQ 0x00000080 /* 1 = test mode (internal mem test) */ +#define CTRL_DAC1_EN 0x00000040 /* enable DAC1 */ +#define CTRL_DAC2_EN 0x00000020 /* enable DAC2 */ +#define CTRL_ADC_EN 0x00000010 /* enable ADC */ +#define CTRL_UART_EN 0x00000008 /* enable MIDI uart */ +#define CTRL_JYSTK_EN 0x00000004 /* enable Joystick port (presumably + * at address 0x200) */ +#define CTRL_CDC_EN 0x00000002 /* enable serial (CODEC) interface */ +#define CTRL_SERR_DIS 0x00000001 /* 1 = disable PCI SERR signal */ + +#define SCTRL_P2ENDINC 0x00380000 /* */ +#define SCTRL_SH_P2ENDINC 19 +#define SCTRL_P2STINC 0x00070000 /* */ +#define SCTRL_SH_P2STINC 16 +#define SCTRL_R1LOOPSEL 0x00008000 /* 0 = loop mode */ +#define SCTRL_P2LOOPSEL 0x00004000 /* 0 = loop mode */ +#define SCTRL_P1LOOPSEL 0x00002000 /* 0 = loop mode */ +#define SCTRL_P2PAUSE 0x00001000 /* 1 = pause mode */ +#define SCTRL_P1PAUSE 0x00000800 /* 1 = pause mode */ +#define SCTRL_R1INTEN 0x00000400 /* enable interrupt */ +#define SCTRL_P2INTEN 0x00000200 /* enable interrupt */ +#define SCTRL_P1INTEN 0x00000100 /* enable interrupt */ +#define SCTRL_P1SCTRLD 0x00000080 /* reload sample count register for + * DAC1 */ +#define SCTRL_P2DACSEN 0x00000040 /* 1 = DAC2 play back last sample + * when disabled */ +#define SCTRL_R1SEB 0x00000020 /* 1 = 16bit */ +#define SCTRL_R1SMB 0x00000010 /* 1 = stereo */ +#define SCTRL_R1FMT 0x00000030 /* format mask */ +#define SCTRL_SH_R1FMT 4 +#define SCTRL_P2SEB 0x00000008 /* 1 = 16bit */ +#define SCTRL_P2SMB 0x00000004 /* 1 = stereo */ +#define SCTRL_P2FMT 0x0000000c /* format mask */ +#define SCTRL_SH_P2FMT 2 +#define SCTRL_P1SEB 0x00000002 /* 1 = 16bit */ +#define SCTRL_P1SMB 0x00000001 /* 1 = stereo */ +#define SCTRL_P1FMT 0x00000003 /* format mask */ +#define SCTRL_SH_P1FMT 0 + +#define STAT_INTR 0x80000000 /* wired or of all interrupt bits */ +#define STAT_CSTAT 0x00000400 /* 1 = codec busy or codec write in + * progress */ +#define STAT_CBUSY 0x00000200 /* 1 = codec busy */ +#define STAT_CWRIP 0x00000100 /* 1 = codec write in progress */ +#define STAT_VC 0x00000060 /* CCB int source, 0=DAC1, 1=DAC2, + * 2=ADC, 3=undef */ +#define STAT_SH_VC 5 +#define STAT_MCCB 0x00000010 /* CCB int pending */ +#define STAT_UART 0x00000008 /* UART int pending */ +#define STAT_DAC1 0x00000004 /* DAC1 int pending */ +#define STAT_DAC2 0x00000002 /* DAC2 int pending */ +#define STAT_ADC 0x00000001 /* ADC int pending */ + +#define CODEC_OMIX1 0x10 +#define CODEC_OMIX2 0x11 +#define CODEC_LIMIX1 0x12 +#define CODEC_RIMIX1 0x13 +#define CODEC_LIMIX2 0x14 +#define CODEC_RIMIX2 0x15 +#define CODEC_RES_PD 0x16 +#define CODEC_CSEL 0x17 +#define CODEC_ADSEL 0x18 +#define CODEC_MGAIN 0x19 + +/* ES1371 specific */ + +#define CODEC_ID_SESHIFT 10 +#define CODEC_ID_SEMASK 0x1f + +#define CODEC_PIRD 0x00800000 /* 0 = write AC97 register */ +#define CODEC_PIADD_MASK 0x007f0000 +#define CODEC_PIADD_SHIFT 16 +#define CODEC_PIDAT_MASK 0x0000ffff +#define CODEC_PIDAT_SHIFT 0 + +#define CODEC_PORD 0x00800000 /* 0 = write AC97 register */ +#define CODEC_POADD_MASK 0x007f0000 +#define CODEC_POADD_SHIFT 16 +#define CODEC_PODAT_MASK 0x0000ffff +#define CODEC_PODAT_SHIFT 0 + +#define CODEC_RDY 0x80000000 /* AC97 read data valid */ +#define CODEC_WIP 0x40000000 /* AC97 write in progress */ + +#define ES1370_REG_CONTROL 0x00 +#define ES1370_REG_SERIAL_CONTROL 0x20 +#define ES1371_REG_CODEC 0x14 +#define ES1371_REG_LEGACY 0x18 /* W/R: Legacy control/status register */ +#define ES1371_REG_SMPRATE 0x10 /* W/R: Codec rate converter interface register */ + +#define ES1371_SYNC_RES (1<<14) /* Warm AC97 reset */ +#define ES1371_DIS_R1 (1<<19) /* record channel accumulator update disable */ +#define ES1371_DIS_P2 (1<<20) /* playback channel 2 accumulator update disable */ +#define ES1371_DIS_P1 (1<<21) /* playback channel 1 accumulator update disable */ +#define ES1371_DIS_SRC (1<<22) /* sample rate converter disable */ +#define ES1371_SRC_RAM_BUSY (1<<23) /* R/O: sample rate memory is busy */ +#define ES1371_SRC_RAM_WE (1<<24) /* R/W: read/write control for sample rate converter */ +#define ES1371_SRC_RAM_ADDRO(o) (((o)&0x7f)<<25) /* address of the sample rate converter */ +#define ES1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0) /* current value of the sample rate converter */ +#define ES1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff) /* current value of the sample rate converter */ + +/* + * S/PDIF specific + */ + +/* Use ES1370_REG_CONTROL */ +#define RECEN_B 0x08000000 /* Used to control mixing of analog with digital data */ +#define SPDIFEN_B 0x04000000 /* Reset to switch digital output mux to "THRU" mode */ +/* Use ES1370_REG_STATUS */ +#define ENABLE_SPDIF 0x00040000 /* Used to enable the S/PDIF circuitry */ +#define TEST_SPDIF 0x00020000 /* Used to put the S/PDIF module in "test mode" */ + +/* + * Sample rate converter addresses + */ + +#define ES_SMPREG_DAC1 0x70 +#define ES_SMPREG_DAC2 0x74 +#define ES_SMPREG_ADC 0x78 +#define ES_SMPREG_TRUNC_N 0x00 +#define ES_SMPREG_INT_REGS 0x01 +#define ES_SMPREG_VFREQ_FRAC 0x03 +#define ES_SMPREG_VOL_ADC 0x6c +#define ES_SMPREG_VOL_DAC1 0x7c +#define ES_SMPREG_VOL_DAC2 0x7e + +#endif diff --git a/src/add-ons/kernel/drivers/audio/ac97/es1370/io.c b/src/add-ons/kernel/drivers/audio/ac97/es1370/io.c new file mode 100644 index 0000000000..0214fbbab1 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/io.c @@ -0,0 +1,97 @@ +/* + * ES1370 Haiku Driver for ES1370 audio + * + * Copyright 2002-2007, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Marcus Overhagen, marcus@overhagen.de + * Jerome Duval, jerome.duval@free.fr + */ +#include +#include +#include "io.h" +#include "es1370reg.h" +#include "debug.h" +#include + +extern pci_module_info *pci; + +uint8 +es1370_reg_read_8(device_config *config, int regno) +{ + ASSERT(regno >= 0); + return pci->read_io_8(config->base + regno); +} + +uint16 +es1370_reg_read_16(device_config *config, int regno) +{ + ASSERT(regno >= 0); + return pci->read_io_16(config->base + regno); +} + +uint32 +es1370_reg_read_32(device_config *config, int regno) +{ + ASSERT(regno >= 0); + return pci->read_io_32(config->base + regno); +} + +void +es1370_reg_write_8(device_config *config, int regno, uint8 value) +{ + ASSERT(regno >= 0); + pci->write_io_8(config->base + regno, value); +} + +void +es1370_reg_write_16(device_config *config, int regno, uint16 value) +{ + ASSERT(regno >= 0); + pci->write_io_16(config->base + regno, value); +} + +void +es1370_reg_write_32(device_config *config, int regno, uint32 value) +{ + ASSERT(regno >= 0); + pci->write_io_32(config->base + regno, value); +} + +/* codec */ +static int +es1370_codec_wait(device_config *config) +{ + int i; + for (i = 0; i < 1100; i++) { + if ((es1370_reg_read_32(config, ES1370_REG_STATUS) & STAT_CWRIP) == 0) + return B_OK; + if (i > 100) + snooze(1); + } + return B_TIMED_OUT; +} + +uint16 +es1370_codec_read(device_config *config, int regno) +{ + ASSERT(regno >= 0); + if(es1370_codec_wait(config)!=B_OK) { + PRINT(("codec busy (2)\n")); + return -1; + } + + return pci->read_io_32(config->base + ES1370_REG_CODEC); +} + +void +es1370_codec_write(device_config *config, int regno, uint16 value) +{ + ASSERT(regno >= 0); + if(es1370_codec_wait(config)!=B_OK) { + PRINT(("codec busy (4)\n")); + return; + } + pci->write_io_32(config->base + ES1370_REG_CODEC, (regno << 8) | value); +} diff --git a/src/add-ons/kernel/drivers/audio/ac97/es1370/io.h b/src/add-ons/kernel/drivers/audio/ac97/es1370/io.h new file mode 100644 index 0000000000..ceb11a1e0c --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/io.h @@ -0,0 +1,27 @@ +/* + * ES1370 Haiku Driver for ES1370 audio + * + * Copyright 2002-2007, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Marcus Overhagen, marcus@overhagen.de + * Jerome Duval, jerome.duval@free.fr + */ +#ifndef _IO_H_ +#define _IO_H_ + +#include "config.h" + +uint8 es1370_reg_read_8(device_config *config, int regno); +uint16 es1370_reg_read_16(device_config *config, int regno); +uint32 es1370_reg_read_32(device_config *config, int regno); + +void es1370_reg_write_8(device_config *config, int regno, uint8 value); +void es1370_reg_write_16(device_config *config, int regno, uint16 value); +void es1370_reg_write_32(device_config *config, int regno, uint32 value); + +uint16 es1370_codec_read(device_config *config, int regno); +void es1370_codec_write(device_config *config, int regno, uint16 value); + +#endif diff --git a/src/add-ons/kernel/drivers/audio/ac97/es1370/multi.c b/src/add-ons/kernel/drivers/audio/ac97/es1370/multi.c new file mode 100644 index 0000000000..9e1a03ff66 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/multi.c @@ -0,0 +1,881 @@ +/* + * ES1370 Haiku Driver for ES1370 audio + * + * Copyright 2002-2007, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jerome Duval, jerome.duval@free.fr + */ + +#include +#include +#include +#include "hmulti_audio.h" +#include "multi.h" +#include "ac97.h" + +//#define DEBUG 1 + +#include "debug.h" +#include "es1370.h" +#include "util.h" +#include "io.h" + +static void +es1370_ac97_get_mix(void *card, const void *cookie, int32 type, float *values) { + es1370_dev *dev = (es1370_dev*)card; + ac97_source_info *info = (ac97_source_info *)cookie; + uint16 value, mask; + float gain; + + switch(type) { + case B_MIX_GAIN: + value = es1370_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 = es1370_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 = es1370_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 = es1370_codec_read(&dev->config, AC97_RECORD_SELECT); + value &= mask; + //PRINT(("B_MIX_MUX value : %u\n", value)); + values[0] = (float)value; + break; + } +} + +static void +es1370_ac97_set_mix(void *card, const void *cookie, int32 type, float *values) { + es1370_dev *dev = (es1370_dev*)card; + ac97_source_info *info = (ac97_source_info *)cookie; + uint16 value, mask; + float gain; + + switch(type) { + case B_MIX_GAIN: + value = es1370_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)); + es1370_codec_write(&dev->config, info->reg, value); + break; + case B_MIX_MUTE: + mask = ((1 << 1) - 1) << 15; + value = es1370_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)); + es1370_codec_write(&dev->config, info->reg, value); + break; + case B_MIX_MICBOOST: + mask = ((1 << 1) - 1) << 6; + value = es1370_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)); + es1370_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)); + es1370_codec_write(&dev->config, AC97_RECORD_SELECT, value); + break; + } + +} + +static int32 +es1370_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 +es1370_create_controls_list(multi_dev *multi) +{ + multi->control_count = 0; + PRINT(("multi->control_count %lu\n", multi->control_count)); + return B_OK; +} + +static status_t +es1370_get_mix(es1370_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(("es1370_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 +es1370_set_mix(es1370_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(("es1370_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(("es1370_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 +es1370_list_mix_controls(es1370_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(es1370_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 +es1370_list_mix_connections(es1370_dev *card, multi_mix_connection_info * data) +{ + return B_ERROR; +} + +static status_t +es1370_list_mix_channels(es1370_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 +es1370_create_channels_list(multi_dev *multi) +{ + es1370_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=ES1370_USE_PLAY; mode!=-1; + mode = (mode == ES1370_USE_PLAY) ? ES1370_USE_RECORD : -1) { + LIST_FOREACH(stream, &((es1370_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 == ES1370_USE_PLAY) ? B_MULTI_OUTPUT_CHANNEL : B_MULTI_INPUT_CHANNEL; + chans[index].designations = designations | chan_designations[i]; + chans[index].connectors = 0; + index++; + } + } + + if(mode==ES1370_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 +es1370_get_description(es1370_dev *card, multi_description *data) +{ + uint32 size; + + data->interface_version = B_CURRENT_INTERFACE_VERSION; + data->interface_minimum = B_CURRENT_INTERFACE_VERSION; + + strncpy(data->friendly_name, FRIENDLY_NAME, 32); + strcpy(data->vendor_info, AUTHOR); + + 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_44100;// | B_SR_48000 | B_SR_CVSR; + data->input_rates = B_SR_44100;// | B_SR_48000 | B_SR_CVSR; + data->output_rates = B_SR_44100; + data->input_rates = B_SR_44100; + data->min_cvsr_rate = 0; + 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 +es1370_get_enabled_channels(es1370_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 +es1370_set_enabled_channels(es1370_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 +es1370_get_global_format(es1370_dev *card, multi_format_info *data) +{ + data->output_latency = 0; + data->input_latency = 0; + data->timecode_kind = 0; + 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 +es1370_get_buffers(es1370_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 < current_settings.buffer_count || + data->request_playback_channels < (pchannels) || + data->request_record_buffers < current_settings.buffer_count || + data->request_record_channels < (rchannels)) { + LOG(("not enough channels/buffers\n")); + } + + data->flags = B_MULTI_BUFFER_PLAYBACK | B_MULTI_BUFFER_RECORD; // XXX ??? +// data->flags = 0; + + data->return_playback_buffers = current_settings.buffer_count; /* playback_buffers[b][] */ + data->return_playback_channels = pchannels; /* playback_buffers[][c] */ + data->return_playback_buffer_size = current_settings.buffer_frames; /* frames */ + + bufcount = current_settings.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 = current_settings.buffer_count; + data->return_record_channels = rchannels; + data->return_record_buffer_size = current_settings.buffer_frames; /* frames */ + + bufcount = current_settings.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 +es1370_play_inth(void* inthparams) +{ + es1370_stream *stream = (es1370_stream *)inthparams; + //int32 count; + + acquire_spinlock(&slock); + stream->real_time = system_time(); + stream->frames_count += current_settings.buffer_frames; + stream->buffer_cycle = stream->trigblk; + stream->update_needed = true; + release_spinlock(&slock); + + //TRACE(("es1370_play_inth : cycle : %d\n", stream->buffer_cycle)); + + release_sem_etc(stream->card->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE); +} + +static void +es1370_record_inth(void* inthparams) +{ + es1370_stream *stream = (es1370_stream *)inthparams; + //int32 count; + + acquire_spinlock(&slock); + stream->real_time = system_time(); + stream->frames_count += current_settings.buffer_frames; + stream->buffer_cycle = (stream->trigblk + + stream->blkmod - 1) % stream->blkmod; + stream->update_needed = true; + release_spinlock(&slock); + + //TRACE(("es1370_record_inth : cycle : %d\n", stream->buffer_cycle)); + + release_sem_etc(stream->card->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE); +} + +static status_t +es1370_buffer_exchange(es1370_dev *card, multi_buffer_info *data) +{ + cpu_status status; + es1370_stream *pstream, *rstream; + + data->flags = B_MULTI_BUFFER_PLAYBACK | B_MULTI_BUFFER_RECORD; + + if (!(card->pstream->state & ES1370_STATE_STARTED)) + es1370_stream_start(card->pstream, es1370_play_inth, card->pstream); + + if (!(card->rstream->state & ES1370_STATE_STARTED)) + es1370_stream_start(card->rstream, es1370_record_inth, card->rstream); + + if (acquire_sem_etc(card->buffer_ready_sem, 1, B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, 100000) + == B_TIMED_OUT) { + LOG(("buffer_exchange timeout ff\n")); + } + + status = lock(); + + LIST_FOREACH(pstream, &card->streams, next) { + if ((pstream->use & ES1370_USE_PLAY) == 0 || + (pstream->state & ES1370_STATE_STARTED) == 0) + continue; + if(pstream->update_needed) + break; + } + + LIST_FOREACH(rstream, &card->streams, next) { + if ((rstream->use & ES1370_USE_RECORD) == 0 || + (rstream->state & ES1370_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 +es1370_buffer_force_stop(es1370_dev *card) +{ + //es1370_voice_halt(card->pvoice); + return B_OK; +} + +static status_t +es1370_multi_control(void *cookie, uint32 op, void *data, size_t length) +{ + es1370_dev *card = (es1370_dev *)cookie; + + switch (op) { + case B_MULTI_GET_DESCRIPTION: + LOG(("B_MULTI_GET_DESCRIPTION\n")); + return es1370_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 es1370_get_enabled_channels(card, (multi_channel_enable *)data); + case B_MULTI_SET_ENABLED_CHANNELS: + LOG(("B_MULTI_SET_ENABLED_CHANNELS\n")); + return es1370_set_enabled_channels(card, (multi_channel_enable *)data); + case B_MULTI_GET_GLOBAL_FORMAT: + LOG(("B_MULTI_GET_GLOBAL_FORMAT\n")); + return es1370_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 es1370_get_mix(card, (multi_mix_value_info *)data); + case B_MULTI_SET_MIX: + LOG(("B_MULTI_SET_MIX\n")); + return es1370_set_mix(card, (multi_mix_value_info *)data); + case B_MULTI_LIST_MIX_CHANNELS: + LOG(("B_MULTI_LIST_MIX_CHANNELS\n")); + return es1370_list_mix_channels(card, (multi_mix_channel_info *)data); + case B_MULTI_LIST_MIX_CONTROLS: + LOG(("B_MULTI_LIST_MIX_CONTROLS\n")); + return es1370_list_mix_controls(card, (multi_mix_control_info *)data); + case B_MULTI_LIST_MIX_CONNECTIONS: + LOG(("B_MULTI_LIST_MIX_CONNECTIONS\n")); + return es1370_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 es1370_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 es1370_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 es1370_buffer_force_stop(card); + } + LOG(("ERROR: unknown multi_control %#x\n",op)); + return B_ERROR; +} + +static status_t es1370_open(const char *name, uint32 flags, void** cookie); +static status_t es1370_close(void* cookie); +static status_t es1370_free(void* cookie); +static status_t es1370_control(void* cookie, uint32 op, void* arg, size_t len); +static status_t es1370_read(void* cookie, off_t position, void *buf, size_t* num_bytes); +static status_t es1370_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes); + +device_hooks multi_hooks = { + es1370_open, /* -> open entry point */ + es1370_close, /* -> close entry point */ + es1370_free, /* -> free cookie */ + es1370_control, /* -> control entry point */ + es1370_read, /* -> read entry point */ + es1370_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 +es1370_open(const char *name, uint32 flags, void** cookie) +{ + es1370_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 = es1370_stream_new(card, ES1370_USE_RECORD, current_settings.buffer_frames, current_settings.buffer_count); + card->pstream = es1370_stream_new(card, ES1370_USE_PLAY, current_settings.buffer_frames, current_settings.buffer_count); + + card->buffer_ready_sem = create_sem(0, "pbuffer ready"); + + LOG(("stream_setaudio\n")); + + es1370_stream_set_audioparms(card->pstream, 2, true, current_settings.sample_rate); + es1370_stream_set_audioparms(card->rstream, 2, true, current_settings.sample_rate); + + card->pstream->first_channel = 0; + card->rstream->first_channel = 2; + + es1370_stream_commit_parms(card->pstream); + es1370_stream_commit_parms(card->rstream); + + es1370_create_channels_list(&card->multi); + + return B_OK; +} + +static status_t +es1370_close(void* cookie) +{ + //es1370_dev *card = cookie; + LOG(("close()\n")); + + return B_OK; +} + +static status_t +es1370_free(void* cookie) +{ + es1370_dev *card = cookie; + es1370_stream *stream; + LOG(("free()\n")); + + if (card->buffer_ready_sem > B_OK) + delete_sem(card->buffer_ready_sem); + + LIST_FOREACH(stream, &card->streams, next) { + es1370_stream_halt(stream); + } + + while (!LIST_EMPTY(&card->streams)) { + es1370_stream_delete(LIST_FIRST(&card->streams)); + } + + card->pstream = NULL; + card->rstream = NULL; + + return B_OK; +} + +static status_t +es1370_control(void* cookie, uint32 op, void* arg, size_t len) +{ + return es1370_multi_control(cookie, op, arg, len); +} + +static status_t +es1370_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 +es1370_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/es1370/multi.h b/src/add-ons/kernel/drivers/audio/ac97/es1370/multi.h new file mode 100644 index 0000000000..1c228fc6c2 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/multi.h @@ -0,0 +1,41 @@ +/* + * ES1370 Haiku Driver for ES1370 audio + * + * Copyright 2002-2007, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jerome Duval, jerome.duval@free.fr + */ + +#ifndef _MULTI_H_ +#define _MULTI_H_ + +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/es1370/queue.h b/src/add-ons/kernel/drivers/audio/ac97/es1370/queue.h new file mode 100644 index 0000000000..396c70fb2e --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/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/es1370/util.c b/src/add-ons/kernel/drivers/audio/ac97/es1370/util.c new file mode 100644 index 0000000000..59fd55d6f8 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/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/es1370/util.h b/src/add-ons/kernel/drivers/audio/ac97/es1370/util.h new file mode 100644 index 0000000000..79a2c69809 --- /dev/null +++ b/src/add-ons/kernel/drivers/audio/ac97/es1370/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