17979b1cfSJérôme Duval /*
27979b1cfSJérôme Duval * AC97 interface
37979b1cfSJérôme Duval *
47979b1cfSJérôme Duval * Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
5eb864c53SJérôme Duval * Copyright (c) 2008-2013, Jérôme Duval
67979b1cfSJérôme Duval *
77979b1cfSJérôme Duval * All rights reserved.
87979b1cfSJérôme Duval * Redistribution and use in source and binary forms, with or without modification,
97979b1cfSJérôme Duval * are permitted provided that the following conditions are met:
107979b1cfSJérôme Duval *
117979b1cfSJérôme Duval * - Redistributions of source code must retain the above copyright notice,
127979b1cfSJérôme Duval * this list of conditions and the following disclaimer.
137979b1cfSJérôme Duval * - Redistributions in binary form must reproduce the above copyright notice,
147979b1cfSJérôme Duval * this list of conditions and the following disclaimer in the documentation
157979b1cfSJérôme Duval * and/or other materials provided with the distribution.
167979b1cfSJérôme Duval *
177979b1cfSJérôme Duval * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
187979b1cfSJérôme Duval * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
197979b1cfSJérôme Duval * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
207979b1cfSJérôme Duval * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
217979b1cfSJérôme Duval * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
227979b1cfSJérôme Duval * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
237979b1cfSJérôme Duval * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
247979b1cfSJérôme Duval * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
257979b1cfSJérôme Duval * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
267979b1cfSJérôme Duval * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
277979b1cfSJérôme Duval *
287979b1cfSJérôme Duval */
29eb864c53SJérôme Duval
30eb864c53SJérôme Duval
317979b1cfSJérôme Duval #include <KernelExport.h>
327979b1cfSJérôme Duval #include <OS.h>
337979b1cfSJérôme Duval #include <stdio.h>
347979b1cfSJérôme Duval #include <stdlib.h>
35f559e518SJérôme Duval #include <string.h>
367979b1cfSJérôme Duval #include <MediaDefs.h>
377979b1cfSJérôme Duval #include "ac97.h"
387979b1cfSJérôme Duval
397979b1cfSJérôme Duval #define LOG(x) dprintf x
407979b1cfSJérôme Duval
417979b1cfSJérôme Duval #define B_UTF8_REGISTERED "\xC2\xAE"
427979b1cfSJérôme Duval
437979b1cfSJérôme Duval bool ac97_reg_is_valid(ac97_dev *dev, uint8 reg);
447979b1cfSJérôme Duval void ac97_amp_enable(ac97_dev *dev, bool onoff);
457979b1cfSJérôme Duval void ac97_dump_capabilities(ac97_dev *dev);
467979b1cfSJérôme Duval void ac97_detect_capabilities(ac97_dev *dev);
477979b1cfSJérôme Duval void ac97_detect_rates(ac97_dev *dev);
487979b1cfSJérôme Duval void ac97_update_register_cache(ac97_dev *dev);
497979b1cfSJérôme Duval
50eb864c53SJérôme Duval
517979b1cfSJérôme Duval const char * stereo_enhancement_technique[] =
527979b1cfSJérôme Duval {
537979b1cfSJérôme Duval "No 3D Stereo Enhancement",
547979b1cfSJérôme Duval "Analog Devices",
557979b1cfSJérôme Duval "Creative Technology",
567979b1cfSJérôme Duval "National Semiconductor",
577979b1cfSJérôme Duval "Yamaha",
587979b1cfSJérôme Duval "BBE Sound",
597979b1cfSJérôme Duval "Crystal Semiconductor",
607979b1cfSJérôme Duval "Qsound Labs",
617979b1cfSJérôme Duval "Spatializer Audio Laboratories",
627979b1cfSJérôme Duval "SRS Labs",
637979b1cfSJérôme Duval "Platform Tech",
647979b1cfSJérôme Duval "AKM Semiconductor",
657979b1cfSJérôme Duval "Aureal",
667979b1cfSJérôme Duval "Aztech Labs",
677979b1cfSJérôme Duval "Binaura",
687979b1cfSJérôme Duval "ESS Technology",
697979b1cfSJérôme Duval "Harman International",
707979b1cfSJérôme Duval "Nvidea",
717979b1cfSJérôme Duval "Philips",
727979b1cfSJérôme Duval "Texas Instruments",
737979b1cfSJérôme Duval "VLSI Technology",
747979b1cfSJérôme Duval "TriTech",
757979b1cfSJérôme Duval "Realtek",
767979b1cfSJérôme Duval "Samsung",
777979b1cfSJérôme Duval "Wolfson Microelectronics",
787979b1cfSJérôme Duval "Delta Integration",
797979b1cfSJérôme Duval "SigmaTel",
807979b1cfSJérôme Duval "KS Waves",
817979b1cfSJérôme Duval "Rockwell",
827979b1cfSJérôme Duval "Unknown (29)",
837979b1cfSJérôme Duval "Unknown (30)",
847979b1cfSJérôme Duval "Unknown (31)"
857979b1cfSJérôme Duval };
867979b1cfSJérôme Duval
87eb864c53SJérôme Duval
88eb864c53SJérôme Duval static void default_init(ac97_dev *dev);
89eb864c53SJérôme Duval static void ad1819_init(ac97_dev *dev);
90eb864c53SJérôme Duval static void ad1881_init(ac97_dev *dev);
91eb864c53SJérôme Duval static void ad1885_init(ac97_dev *dev);
92eb864c53SJérôme Duval static void ad1886_init(ac97_dev *dev);
93eb864c53SJérôme Duval static void ad1980_init(ac97_dev *dev);
94eb864c53SJérôme Duval static void ad1981b_init(ac97_dev *dev);
95eb864c53SJérôme Duval static void alc203_init(ac97_dev *dev);
96eb864c53SJérôme Duval static void alc650_init(ac97_dev *dev);
97eb864c53SJérôme Duval static void alc655_init(ac97_dev *dev);
98eb864c53SJérôme Duval static void alc850_init(ac97_dev *dev);
99eb864c53SJérôme Duval static void stac9708_init(ac97_dev *dev);
100eb864c53SJérôme Duval static void stac9721_init(ac97_dev *dev);
101eb864c53SJérôme Duval static void stac9744_init(ac97_dev *dev);
102eb864c53SJérôme Duval static void stac9756_init(ac97_dev *dev);
103fe2d4a0fSJérôme Duval static void stac9758_init(ac97_dev *dev);
104eb864c53SJérôme Duval static void tr28028_init(ac97_dev *dev);
105eb864c53SJérôme Duval static void wm9701_init(ac97_dev *dev);
106eb864c53SJérôme Duval static void wm9703_init(ac97_dev *dev);
107eb864c53SJérôme Duval static void wm9704_init(ac97_dev *dev);
1087979b1cfSJérôme Duval
1097979b1cfSJérôme Duval bool ad1819_set_rate(ac97_dev *dev, uint8 reg, uint32 rate);
1107979b1cfSJérôme Duval bool ad1819_get_rate(ac97_dev *dev, uint8 reg, uint32 *rate);
1117979b1cfSJérôme Duval
112eb864c53SJérôme Duval
1137979b1cfSJérôme Duval typedef struct
1147979b1cfSJérôme Duval {
1157979b1cfSJérôme Duval uint32 id;
1167979b1cfSJérôme Duval uint32 mask;
1177979b1cfSJérôme Duval codec_init init;
1187979b1cfSJérôme Duval const char *info;
1197979b1cfSJérôme Duval } codec_table;
1207979b1cfSJérôme Duval
121eb864c53SJérôme Duval
1227979b1cfSJérôme Duval codec_table codecs[] =
1237979b1cfSJérôme Duval {
1247979b1cfSJérôme Duval { CODEC_ID_AD1819, 0xffffffff, ad1819_init, "Analog Devices AD1819A, AD1819B SoundPort" B_UTF8_REGISTERED },
1257979b1cfSJérôme Duval { CODEC_ID_AD1881, 0xffffffff, ad1881_init, "Analog Devices AD1881 SoundMAX" B_UTF8_REGISTERED },
1267979b1cfSJérôme Duval { CODEC_ID_AD1881A, 0xffffffff, ad1881_init, "Analog Devices AD1881A SoundMAX" B_UTF8_REGISTERED },
1277979b1cfSJérôme Duval { CODEC_ID_AD1885, 0xffffffff, ad1885_init, "Analog Devices AD1885 SoundMAX" B_UTF8_REGISTERED },
1287979b1cfSJérôme Duval { CODEC_ID_AD1886, 0xffffffff, ad1886_init, "Analog Devices AD1886 SoundMAX" B_UTF8_REGISTERED },
1297979b1cfSJérôme Duval { CODEC_ID_AD1886A, 0xffffffff, ad1881_init, "Analog Devices AD1886A SoundMAX" B_UTF8_REGISTERED },
1307979b1cfSJérôme Duval { CODEC_ID_AD1887, 0xffffffff, ad1881_init, "Analog Devices AD1887 SoundMAX" B_UTF8_REGISTERED },
1317979b1cfSJérôme Duval { CODEC_ID_AD1888, 0xffffffff, ad1881_init, "Analog Devices AD1888 SoundMAX" B_UTF8_REGISTERED },
1327979b1cfSJérôme Duval { CODEC_ID_AD1980, 0xffffffff, ad1980_init, "Analog Devices AD1980 SoundMAX" B_UTF8_REGISTERED },
133*26a211b0SMáximo Castañeda { 0x41445371, 0xffffffff, default_init, "Analog Devices 0x41445371 (\?\?\?)" },
1347979b1cfSJérôme Duval { 0x41445372, 0xffffffff, default_init, "Analog Devices AD1981A SoundMAX" B_UTF8_REGISTERED },
1357979b1cfSJérôme Duval { CODEC_ID_AD1981B, 0xffffffff, ad1981b_init, "Analog Devices AD1981B SoundMAX" B_UTF8_REGISTERED },
1367979b1cfSJérôme Duval { CODEC_ID_AD1985, 0xffffffff, default_init, "Analog Devices AD1985 SoundMAX" B_UTF8_REGISTERED },
1377979b1cfSJérôme Duval { CODEC_ID_AD1986, 0xffffffff, default_init, "Analog Devices AD1986 SoundMAX" B_UTF8_REGISTERED },
1387979b1cfSJérôme Duval { CODEC_ID_AK4540, 0xffffffff, default_init, "Asahi Kasei AK4540" },
1397979b1cfSJérôme Duval { CODEC_ID_AK4542, 0xffffffff, default_init, "Asahi Kasei AK4542" },
1407979b1cfSJérôme Duval { CODEC_ID_AK4543, 0xffffffff, default_init, "Asahi Kasei AK4543" },
141fe2d4a0fSJérôme Duval { 0x414b4d06, 0xffffffff, default_init, "Asahi Kasei AK4544A" },
142fe2d4a0fSJérôme Duval { 0x414b4d07, 0xffffffff, default_init, "Asahi Kasei AK4545" },
143eb864c53SJérôme Duval { 0x414c4300, 0xffffff00, default_init, "Avance Logic (Realtek) ALC100" }, /* 0x4300 = ALC100 */
1447979b1cfSJérôme Duval { 0x414c4320, 0xfffffff0, default_init, "Avance Logic (Realtek) ALC100/ALC100P, RL5383/RL5522" },
145eb864c53SJérôme Duval { CODEC_ID_ALC201A, 0xfffffff0, default_init, "Avance Logic (Realtek) ALC200/ALC200A, ALC201/ALC201A" }, /* 0x4710 = ALC201A */
1467979b1cfSJérôme Duval { 0x414c4720, 0xffffffff, alc650_init, "Avance Logic (Realtek) ALC650" }, /* 0x4720 = ALC650 */
147eb864c53SJérôme Duval { 0x414c4730, 0xffffffff, default_init, "Avance Logic (Realtek) ALC101" },
148eb864c53SJérôme Duval { 0x414c4740, 0xfffffff0, default_init, "Avance Logic (Realtek) ALC202/ALC202A" },
149eb864c53SJérôme Duval { 0x414c4750, 0xfffffff0, default_init, "Avance Logic (Realtek) ALC250" },
150eb864c53SJérôme Duval { 0x414c4760, 0xfffffff0, alc655_init, "Avance Logic (Realtek) ALC655" }, /* 0x4760 = ALC655 */
151eb864c53SJérôme Duval { 0x414c4770, 0xfffffff0, alc203_init, "Avance Logic (Realtek) ALC203" },
152eb864c53SJérôme Duval { 0x414c4780, 0xffffffff, alc655_init, "Avance Logic (Realtek) ALC658" },
153eb864c53SJérôme Duval { 0x414c4790, 0xfffffff0, alc850_init, "Avance Logic (Realtek) ALC850" },
1547979b1cfSJérôme Duval { 0x434d4941, 0xffffffff, default_init, "C-Media CMI9738" },
1557979b1cfSJérôme Duval { 0x434d4961, 0xffffffff, default_init, "C-Media CMI9739" },
1567979b1cfSJérôme Duval { 0x43525900, 0xffffffff, default_init, "Cirrus Logic CS4297" },
1577979b1cfSJérôme Duval { 0x43525903, 0xffffffff, default_init, "Cirrus Logic CS4297" },
1587979b1cfSJérôme Duval { 0x43525913, 0xffffffff, default_init, "Cirrus Logic CS4297A" },
1597979b1cfSJérôme Duval { 0x43525914, 0xffffffff, default_init, "Cirrus Logic CS4297B" },
1607979b1cfSJérôme Duval { 0x43525923, 0xffffffff, default_init, "Cirrus Logic CS4294C" },
1617979b1cfSJérôme Duval { 0x4352592b, 0xffffffff, default_init, "Cirrus Logic CS4298C" },
1627979b1cfSJérôme Duval { CODEC_ID_CS4299A, 0xffffffff, default_init, "Cirrus Logic CS4299A" },
1637979b1cfSJérôme Duval { CODEC_ID_CS4299C, 0xffffffff, default_init, "Cirrus Logic CS4299C" },
1647979b1cfSJérôme Duval { CODEC_ID_CS4299D, 0xffffffff, default_init, "Cirrus Logic CS4299D" },
1657979b1cfSJérôme Duval { 0x43525941, 0xffffffff, default_init, "Cirrus Logic CS4201A" },
1667979b1cfSJérôme Duval { 0x43525951, 0xffffffff, default_init, "Cirrus Logic CS4205A" },
1677979b1cfSJérôme Duval { 0x43525961, 0xffffffff, default_init, "Cirrus Logic CS4291A" },
168fe2d4a0fSJérôme Duval { 0x43585421, 0xffffffff, default_init, "HSD11246" },
169fe2d4a0fSJérôme Duval { 0x44543031, 0xffffffff, default_init, "DT0398" },
170fe2d4a0fSJérôme Duval { 0x454d4328, 0xffffffff, default_init, "EM28028" },
1717979b1cfSJérôme Duval { 0x45838308, 0xffffffff, default_init, "ESS Technology ES1921" },
1720db19308SJérôme Duval { 0x49434501, 0xffffffff, default_init, "ICEnsemble ICE1230" },
1737979b1cfSJérôme Duval { 0x49434511, 0xffffffff, default_init, "ICEnsemble ICE1232" },
1740db19308SJérôme Duval { 0x49434514, 0xffffffff, default_init, "ICEnsemble ICE1232A" },
1750db19308SJérôme Duval { 0x49434551, 0xffffffff, default_init, "Via Technologies VT1616" }, /* rebranded from ICEnsemble */
1760db19308SJérôme Duval { 0x49544520, 0xffffffff, default_init, "Integrated Technology Express ITE2226E" },
1770db19308SJérôme Duval { 0x49544560, 0xffffffff, default_init, "Integrated Technology Express ITE2646E" },
1787979b1cfSJérôme Duval { 0x4e534331, 0xffffffff, default_init, "National Semiconductor LM4549" },
1797979b1cfSJérôme Duval { CODEC_ID_STAC9700,0xffffffff, default_init, "SigmaTel STAC9700/9783/9784" },
180*26a211b0SMáximo Castañeda { CODEC_ID_STAC9704,0xffffffff, default_init, "SigmaTel STAC9701/03, STAC9704/07, STAC9705 (\?\?\?)" },
181*26a211b0SMáximo Castañeda { CODEC_ID_STAC9705,0xffffffff, default_init, "SigmaTel STAC9704 (\?\?\?)" },
1827979b1cfSJérôme Duval { CODEC_ID_STAC9708,0xffffffff, stac9708_init, "SigmaTel STAC9708/9711" },
1837979b1cfSJérôme Duval { CODEC_ID_STAC9721,0xffffffff, stac9721_init, "SigmaTel STAC9721/9723" },
1847979b1cfSJérôme Duval { CODEC_ID_STAC9744,0xffffffff, stac9744_init, "SigmaTel STAC9744" },
185fe2d4a0fSJérôme Duval { CODEC_ID_STAC9750,0xffffffff, default_init, "SigmaTel STAC9750/51" },
1867979b1cfSJérôme Duval { CODEC_ID_STAC9752,0xffffffff, default_init, "SigmaTel STAC9752/53" },
1877979b1cfSJérôme Duval { CODEC_ID_STAC9756,0xffffffff, stac9756_init, "SigmaTel STAC9756/9757" },
188fe2d4a0fSJérôme Duval { CODEC_ID_STAC9758,0xffffffff, stac9758_init, "SigmaTel STAC9758/59" },
1897979b1cfSJérôme Duval { CODEC_ID_STAC9766,0xffffffff, default_init, "SigmaTel STAC9766/67" },
1907979b1cfSJérôme Duval { 0x53494c22, 0xffffffff, default_init, "Silicon Laboratory Si3036" },
1917979b1cfSJérôme Duval { 0x53494c23, 0xffffffff, default_init, "Silicon Laboratory Si3038" },
192fe2d4a0fSJérôme Duval { 0x53544d02, 0xffffffff, default_init, "ST7597" },
1930db19308SJérôme Duval { 0x54524103, 0xffffffff, default_init, "TriTech TR28023" },
1947979b1cfSJérôme Duval { 0x54524106, 0xffffffff, default_init, "TriTech TR28026" },
1957979b1cfSJérôme Duval { 0x54524108, 0xffffffff, tr28028_init, "TriTech TR28028" },
1967979b1cfSJérôme Duval { 0x54524123, 0xffffffff, default_init, "TriTech TR28602" },
1970db19308SJérôme Duval { 0x56494161, 0xffffffff, default_init, "Via Technologies VIA1612A" },
1980db19308SJérôme Duval { 0x56494170, 0xffffffff, default_init, "Via Technologies VIA1617A" },
1997979b1cfSJérôme Duval { 0x574d4c00, 0xffffffff, wm9701_init, "Wolfson WM9701A" },
2007979b1cfSJérôme Duval { 0x574d4c03, 0xffffffff, wm9703_init, "Wolfson WM9703/9704" },
2017979b1cfSJérôme Duval { 0x574d4c04, 0xffffffff, wm9704_init, "Wolfson WM9704 (quad)" },
2020db19308SJérôme Duval { 0x574d4c05, 0xffffffff, wm9703_init, "Wolfson WM9705/WM9710" },
2030db19308SJérôme Duval { 0x574d4d09, 0xffffffff, default_init, "Wolfson WM9709" },
2040db19308SJérôme Duval { 0x574d4c12, 0xffffffff, default_init, "Wolfson WM9711/12" },
2050db19308SJérôme Duval { 0x574d4c13, 0xffffffff, default_init, "Wolfson WM9713/14" },
2060db19308SJérôme Duval { 0x57454301, 0xffffffff, default_init, "Wolfson W83971D" },
2077979b1cfSJérôme Duval /* Vendors only: */
2087979b1cfSJérôme Duval { 0x41445300, 0xffffff00, default_init, "Analog Devices" },
2097979b1cfSJérôme Duval { 0x414b4d00, 0xffffff00, default_init, "Asahi Kasei" },
2107979b1cfSJérôme Duval { 0x414c4700, 0xffffff00, default_init, "Avance Logic (Realtek)" },
2117979b1cfSJérôme Duval { 0x434d4900, 0xffffff00, default_init, "C-Media" },
2127979b1cfSJérôme Duval { 0x43525900, 0xffffff00, default_init, "Cirrus Logic" },
2137979b1cfSJérôme Duval { 0x45838300, 0xffffff00, default_init, "ESS Technology" },
2147979b1cfSJérôme Duval { 0x49434500, 0xffffff00, default_init, "ICEnsemble" },
2150db19308SJérôme Duval { 0x49544500, 0xffffff00, default_init, "ITE, Inc." },
2167979b1cfSJérôme Duval { 0x4e534300, 0xffffff00, default_init, "National Semiconductor" },
2177979b1cfSJérôme Duval { 0x83847600, 0xffffff00, default_init, "SigmaTel" },
2187979b1cfSJérôme Duval { 0x53494c00, 0xffffff00, default_init, "Silicon Laboratory" },
2197979b1cfSJérôme Duval { 0x54524100, 0xffffff00, default_init, "TriTech" },
220fe2d4a0fSJérôme Duval { 0x54584e00, 0xffffff00, default_init, "Texas Instruments" },
2210db19308SJérôme Duval { 0x56494100, 0xffffff00, default_init, "VIA Technologies" },
2227979b1cfSJérôme Duval { 0x574d4c00, 0xffffff00, default_init, "Wolfson" },
2230db19308SJérôme Duval { 0x594d4800, 0xffffff00, default_init, "Yamaha" },
2247979b1cfSJérôme Duval { 0x00000000, 0x00000000, default_init, "Unknown" } /* must be last one, matches every codec */
2257979b1cfSJérôme Duval };
2267979b1cfSJérôme Duval
2277979b1cfSJérôme Duval
228eb864c53SJérôme Duval static codec_table *
find_codec_table(uint32 codecid)2297979b1cfSJérôme Duval find_codec_table(uint32 codecid)
2307979b1cfSJérôme Duval {
2317979b1cfSJérôme Duval codec_table *codec;
2327979b1cfSJérôme Duval for (codec = codecs; codec->id; codec++)
2337979b1cfSJérôme Duval if ((codec->id & codec->mask) == (codecid & codec->mask))
2347979b1cfSJérôme Duval break;
2357979b1cfSJérôme Duval return codec;
2367979b1cfSJérôme Duval }
2377979b1cfSJérôme Duval
238eb864c53SJérôme Duval
2397979b1cfSJérôme Duval void
ac97_attach(ac97_dev ** _dev,codec_reg_read reg_read,codec_reg_write reg_write,void * cookie,ushort subvendor_id,ushort subsystem_id)2407979b1cfSJérôme Duval ac97_attach(ac97_dev **_dev, codec_reg_read reg_read, codec_reg_write reg_write, void *cookie,
2417979b1cfSJérôme Duval ushort subvendor_id, ushort subsystem_id)
2427979b1cfSJérôme Duval {
2437979b1cfSJérôme Duval ac97_dev *dev;
2447979b1cfSJérôme Duval codec_table *codec;
2457979b1cfSJérôme Duval int i;
2467979b1cfSJérôme Duval
2477979b1cfSJérôme Duval *_dev = dev = (ac97_dev *) malloc(sizeof(ac97_dev));
248f559e518SJérôme Duval memset(dev->reg_cache, 0, sizeof(dev->reg_cache));
2497979b1cfSJérôme Duval dev->cookie = cookie;
2507979b1cfSJérôme Duval dev->reg_read = reg_read;
2517979b1cfSJérôme Duval dev->reg_write = reg_write;
2527979b1cfSJérôme Duval dev->set_rate = 0;
2537979b1cfSJérôme Duval dev->get_rate = 0;
2547979b1cfSJérôme Duval dev->clock = 48000; /* default clock on non-broken motherboards */
2557979b1cfSJérôme Duval dev->min_vsr = 0x0001;
2567979b1cfSJérôme Duval dev->max_vsr = 0xffff;
2577979b1cfSJérôme Duval dev->reversed_eamp_polarity = false;
2587979b1cfSJérôme Duval dev->capabilities = 0;
2597979b1cfSJérôme Duval
2607979b1cfSJérôme Duval dev->subsystem = (subvendor_id << 16) | subsystem_id;
2617979b1cfSJérôme Duval
2627979b1cfSJérôme Duval if (dev->subsystem == 0x161f202f
2637979b1cfSJérôme Duval || dev->subsystem == 0x161f203a
2648ddc0909SJérôme Duval || dev->subsystem == 0x161f203e
2657979b1cfSJérôme Duval || dev->subsystem == 0x161f204c
2667979b1cfSJérôme Duval || dev->subsystem == 0x104d8144
2677979b1cfSJérôme Duval || dev->subsystem == 0x104d8197
2687979b1cfSJérôme Duval || dev->subsystem == 0x104d81c0
2697979b1cfSJérôme Duval || dev->subsystem == 0x104d81c5
2707979b1cfSJérôme Duval || dev->subsystem == 0x103c3089
2717979b1cfSJérôme Duval || dev->subsystem == 0x103c309a
2727979b1cfSJérôme Duval || dev->subsystem == 0x10338213
2737979b1cfSJérôme Duval || dev->subsystem == 0x103382be) {
2747979b1cfSJérôme Duval dev->reversed_eamp_polarity = true;
2757979b1cfSJérôme Duval }
2767979b1cfSJérôme Duval
2777979b1cfSJérôme Duval /* reset the codec */
2787979b1cfSJérôme Duval LOG(("codec reset\n"));
2797979b1cfSJérôme Duval ac97_reg_uncached_write(dev, AC97_RESET, 0x0000);
2807979b1cfSJérôme Duval for (i = 0; i < 500; i++) {
2817979b1cfSJérôme Duval if ((ac97_reg_uncached_read(dev, AC97_POWERDOWN) & 0xf) == 0xf)
2827979b1cfSJérôme Duval break;
2837979b1cfSJérôme Duval snooze(1000);
2847979b1cfSJérôme Duval }
2857979b1cfSJérôme Duval
2867979b1cfSJérôme Duval dev->codec_id = ((uint32)reg_read(cookie, AC97_VENDOR_ID1) << 16) | reg_read(cookie, AC97_VENDOR_ID2);
2877979b1cfSJérôme Duval codec = find_codec_table(dev->codec_id);
2887979b1cfSJérôme Duval dev->codec_info = codec->info;
2897979b1cfSJérôme Duval dev->init = codec->init;
2907979b1cfSJérôme Duval
2917979b1cfSJérôme Duval dev->codec_3d_stereo_enhancement = stereo_enhancement_technique[(ac97_reg_cached_read(dev, AC97_RESET) >> 10) & 31];
2927979b1cfSJérôme Duval
2937979b1cfSJérôme Duval /* setup register cache */
2947979b1cfSJérôme Duval ac97_update_register_cache(dev);
2957979b1cfSJérôme Duval
2967979b1cfSJérôme Duval ac97_reg_update_bits(dev, AC97_EXTENDED_STAT_CTRL, 1, 1); // enable variable rate audio
2977979b1cfSJérôme Duval
2987979b1cfSJérôme Duval ac97_detect_capabilities(dev);
2997979b1cfSJérôme Duval
3007979b1cfSJérôme Duval dev->init(dev);
3017979b1cfSJérôme Duval ac97_amp_enable(dev, true);
3027979b1cfSJérôme Duval
3037979b1cfSJérôme Duval /* set mixer defaults, enabled Line-out sources are PCM-out, CD-in, Line-in */
3047979b1cfSJérôme Duval ac97_reg_update(dev, AC97_CENTER_LFE_VOLUME, 0x0000); /* set LFE & center volume 0dB */
3057979b1cfSJérôme Duval ac97_reg_update(dev, AC97_SURR_VOLUME, 0x0000); /* set surround volume 0dB */
3067979b1cfSJérôme Duval ac97_reg_update(dev, AC97_MASTER_VOLUME, 0x0000); /* set master output 0dB */
3077979b1cfSJérôme Duval ac97_reg_update(dev, AC97_AUX_OUT_VOLUME, 0x0000); /* set aux output 0dB */
3087979b1cfSJérôme Duval ac97_reg_update(dev, AC97_MONO_VOLUME, 0x0000); /* set mono output 0dB */
3097979b1cfSJérôme Duval ac97_reg_update(dev, AC97_PCM_OUT_VOLUME, 0x0808); /* enable pcm-out */
3107979b1cfSJérôme Duval ac97_reg_update(dev, AC97_CD_VOLUME, 0x0808); /* enable cd-in */
3117979b1cfSJérôme Duval ac97_reg_update(dev, AC97_LINE_IN_VOLUME, 0x0808); /* enable line-in */
3127979b1cfSJérôme Duval
3137979b1cfSJérôme Duval /* set record line in */
3147979b1cfSJérôme Duval ac97_reg_update(dev, AC97_RECORD_SELECT, 0x0404);
3157979b1cfSJérôme Duval
316fe5e83a6SMurai Takashi LOG(("codec vendor id = %#08" B_PRIx32 "\n", dev->codec_id));
3170db19308SJérôme Duval LOG(("codec description = %s\n", dev->codec_info));
3180db19308SJérôme Duval LOG(("codec 3d enhancement = %s\n", dev->codec_3d_stereo_enhancement));
3190db19308SJérôme Duval
3207979b1cfSJérôme Duval ac97_dump_capabilities(dev);
3217979b1cfSJérôme Duval }
3227979b1cfSJérôme Duval
323eb864c53SJérôme Duval
3247979b1cfSJérôme Duval void
ac97_detach(ac97_dev * dev)3257979b1cfSJérôme Duval ac97_detach(ac97_dev *dev)
3267979b1cfSJérôme Duval {
3277979b1cfSJérôme Duval /* Mute everything */
3287979b1cfSJérôme Duval ac97_reg_update_bits(dev, AC97_CENTER_LFE_VOLUME, 0x8000, 0x8000);
3297979b1cfSJérôme Duval ac97_reg_update_bits(dev, AC97_SURR_VOLUME, 0x8000, 0x8000);
3307979b1cfSJérôme Duval ac97_reg_update_bits(dev, AC97_MASTER_VOLUME, 0x8000, 0x8000);
3317979b1cfSJérôme Duval ac97_reg_update_bits(dev, AC97_AUX_OUT_VOLUME, 0x8000, 0x8000);
3327979b1cfSJérôme Duval ac97_reg_update_bits(dev, AC97_MONO_VOLUME, 0x8000, 0x8000);
3337979b1cfSJérôme Duval ac97_reg_update_bits(dev, AC97_PCM_OUT_VOLUME, 0x8000, 0x8000);
3347979b1cfSJérôme Duval ac97_reg_update_bits(dev, AC97_CD_VOLUME, 0x8000, 0x8000);
3357979b1cfSJérôme Duval ac97_reg_update_bits(dev, AC97_LINE_IN_VOLUME, 0x8000, 0x8000);
3367979b1cfSJérôme Duval
3377979b1cfSJérôme Duval ac97_amp_enable(dev, false);
3387979b1cfSJérôme Duval
3397979b1cfSJérôme Duval free(dev);
3407979b1cfSJérôme Duval }
3417979b1cfSJérôme Duval
342eb864c53SJérôme Duval
3437979b1cfSJérôme Duval void
ac97_suspend(ac97_dev * dev)3447979b1cfSJérôme Duval ac97_suspend(ac97_dev *dev)
3457979b1cfSJérôme Duval {
3467979b1cfSJérôme Duval ac97_amp_enable(dev, false);
3477979b1cfSJérôme Duval }
3487979b1cfSJérôme Duval
349eb864c53SJérôme Duval
3507979b1cfSJérôme Duval void
ac97_resume(ac97_dev * dev)3517979b1cfSJérôme Duval ac97_resume(ac97_dev *dev)
3527979b1cfSJérôme Duval {
3537979b1cfSJérôme Duval ac97_amp_enable(dev, true);
3547979b1cfSJérôme Duval }
3557979b1cfSJérôme Duval
356eb864c53SJérôme Duval
3577979b1cfSJérôme Duval void
ac97_reg_cached_write(ac97_dev * dev,uint8 reg,uint16 value)3587979b1cfSJérôme Duval ac97_reg_cached_write(ac97_dev *dev, uint8 reg, uint16 value)
3597979b1cfSJérôme Duval {
3607979b1cfSJérôme Duval if (!ac97_reg_is_valid(dev, reg))
3617979b1cfSJérôme Duval return;
3627979b1cfSJérôme Duval dev->reg_write(dev->cookie, reg, value);
3637979b1cfSJérôme Duval dev->reg_cache[reg] = value;
3647979b1cfSJérôme Duval }
3657979b1cfSJérôme Duval
366eb864c53SJérôme Duval
3677979b1cfSJérôme Duval uint16
ac97_reg_cached_read(ac97_dev * dev,uint8 reg)3687979b1cfSJérôme Duval ac97_reg_cached_read(ac97_dev *dev, uint8 reg)
3697979b1cfSJérôme Duval {
3707979b1cfSJérôme Duval if (!ac97_reg_is_valid(dev, reg))
3717979b1cfSJérôme Duval return 0;
3727979b1cfSJérôme Duval return dev->reg_cache[reg];
3737979b1cfSJérôme Duval }
3747979b1cfSJérôme Duval
3757979b1cfSJérôme Duval void
ac97_reg_uncached_write(ac97_dev * dev,uint8 reg,uint16 value)3767979b1cfSJérôme Duval ac97_reg_uncached_write(ac97_dev *dev, uint8 reg, uint16 value)
3777979b1cfSJérôme Duval {
3787979b1cfSJérôme Duval if (!ac97_reg_is_valid(dev, reg))
3797979b1cfSJérôme Duval return;
3807979b1cfSJérôme Duval dev->reg_write(dev->cookie, reg, value);
3817979b1cfSJérôme Duval }
3827979b1cfSJérôme Duval
383eb864c53SJérôme Duval
3847979b1cfSJérôme Duval uint16
ac97_reg_uncached_read(ac97_dev * dev,uint8 reg)3857979b1cfSJérôme Duval ac97_reg_uncached_read(ac97_dev *dev, uint8 reg)
3867979b1cfSJérôme Duval {
3877979b1cfSJérôme Duval if (!ac97_reg_is_valid(dev, reg))
3887979b1cfSJérôme Duval return 0;
3897979b1cfSJérôme Duval return dev->reg_read(dev->cookie, reg);
3907979b1cfSJérôme Duval }
3917979b1cfSJérôme Duval
392eb864c53SJérôme Duval
3937979b1cfSJérôme Duval bool
ac97_reg_update(ac97_dev * dev,uint8 reg,uint16 value)3947979b1cfSJérôme Duval ac97_reg_update(ac97_dev *dev, uint8 reg, uint16 value)
3957979b1cfSJérôme Duval {
3967979b1cfSJérôme Duval if (!ac97_reg_is_valid(dev, reg))
3977979b1cfSJérôme Duval return false;
3987979b1cfSJérôme Duval if (ac97_reg_cached_read(dev, reg) == value)
3997979b1cfSJérôme Duval return false;
4007979b1cfSJérôme Duval ac97_reg_cached_write(dev, reg, value);
4017979b1cfSJérôme Duval return true;
4027979b1cfSJérôme Duval }
4037979b1cfSJérôme Duval
404eb864c53SJérôme Duval
4057979b1cfSJérôme Duval bool
ac97_reg_update_bits(ac97_dev * dev,uint8 reg,uint16 mask,uint16 value)4067979b1cfSJérôme Duval ac97_reg_update_bits(ac97_dev *dev, uint8 reg, uint16 mask, uint16 value)
4077979b1cfSJérôme Duval {
4087979b1cfSJérôme Duval uint16 old;
4097979b1cfSJérôme Duval if (!ac97_reg_is_valid(dev, reg))
4107979b1cfSJérôme Duval return false;
4117979b1cfSJérôme Duval old = ac97_reg_cached_read(dev, reg);
4127979b1cfSJérôme Duval value &= mask;
4137979b1cfSJérôme Duval value |= (old & ~mask);
4147979b1cfSJérôme Duval if (old == value)
4157979b1cfSJérôme Duval return false;
4167979b1cfSJérôme Duval ac97_reg_cached_write(dev, reg, value);
4177979b1cfSJérôme Duval return true;
4187979b1cfSJérôme Duval }
4197979b1cfSJérôme Duval
420eb864c53SJérôme Duval
4217979b1cfSJérôme Duval void
ac97_update_register_cache(ac97_dev * dev)4227979b1cfSJérôme Duval ac97_update_register_cache(ac97_dev *dev)
4237979b1cfSJérôme Duval {
4247979b1cfSJérôme Duval int reg;
4257979b1cfSJérôme Duval for (reg = 0; reg <= 0x7e; reg += 2)
4267979b1cfSJérôme Duval dev->reg_cache[reg] = ac97_reg_uncached_read(dev, reg);
4277979b1cfSJérôme Duval }
4287979b1cfSJérôme Duval
429eb864c53SJérôme Duval
4307979b1cfSJérôme Duval bool
ac97_set_rate(ac97_dev * dev,uint8 reg,uint32 rate)4317979b1cfSJérôme Duval ac97_set_rate(ac97_dev *dev, uint8 reg, uint32 rate)
4327979b1cfSJérôme Duval {
4337979b1cfSJérôme Duval uint32 value;
4347979b1cfSJérôme Duval uint32 old;
4357979b1cfSJérôme Duval
4367979b1cfSJérôme Duval if (dev->set_rate)
4377979b1cfSJérôme Duval return dev->set_rate(dev, reg, rate);
4387979b1cfSJérôme Duval
4397979b1cfSJérôme Duval value = (uint32)((rate * 48000ULL) / dev->clock); /* need 64 bit calculation for rates 96000 or higher */
4407979b1cfSJérôme Duval
441fe5e83a6SMurai Takashi LOG(("ac97_set_rate: clock = %" B_PRIu32 ", "
442fe5e83a6SMurai Takashi "rate = %" B_PRIu32 ", "
443fe5e83a6SMurai Takashi "value = %" B_PRIu32 "\n",
444fe5e83a6SMurai Takashi dev->clock, rate, value));
4457979b1cfSJérôme Duval
4467979b1cfSJérôme Duval /* if double rate audio is currently enabled, divide value by 2 */
4477979b1cfSJérôme Duval if (ac97_reg_cached_read(dev, AC97_EXTENDED_STAT_CTRL) & 0x0002)
4487979b1cfSJérôme Duval value /= 2;
4497979b1cfSJérôme Duval
4507979b1cfSJérôme Duval if (value < dev->min_vsr || value > dev->max_vsr)
4517979b1cfSJérôme Duval return false;
4527979b1cfSJérôme Duval
4537979b1cfSJérôme Duval old = ac97_reg_cached_read(dev, reg);
4547979b1cfSJérôme Duval ac97_reg_cached_write(dev, reg, value);
4557979b1cfSJérôme Duval if (value != ac97_reg_uncached_read(dev, reg)) {
4567979b1cfSJérôme Duval LOG(("ac97_set_rate failed, new rate %d\n", ac97_reg_uncached_read(dev, reg)));
4577979b1cfSJérôme Duval ac97_reg_cached_write(dev, reg, old);
4587979b1cfSJérôme Duval return false;
4597979b1cfSJérôme Duval }
4607979b1cfSJérôme Duval LOG(("ac97_set_rate done\n"));
4617979b1cfSJérôme Duval return true;
4627979b1cfSJérôme Duval }
4637979b1cfSJérôme Duval
464eb864c53SJérôme Duval
4657979b1cfSJérôme Duval bool
ac97_get_rate(ac97_dev * dev,uint8 reg,uint32 * rate)4667979b1cfSJérôme Duval ac97_get_rate(ac97_dev *dev, uint8 reg, uint32 *rate)
4677979b1cfSJérôme Duval {
4687979b1cfSJérôme Duval uint32 value;
4697979b1cfSJérôme Duval
4707979b1cfSJérôme Duval if (dev->get_rate)
4717979b1cfSJérôme Duval return dev->get_rate(dev, reg, rate);
4727979b1cfSJérôme Duval
4737979b1cfSJérôme Duval value = ac97_reg_cached_read(dev, reg);
4747979b1cfSJérôme Duval if (value == 0)
4757979b1cfSJérôme Duval return false;
4767979b1cfSJérôme Duval
4777979b1cfSJérôme Duval /* if double rate audio is currently enabled, multiply value by 2 */
4787979b1cfSJérôme Duval if (ac97_reg_cached_read(dev, AC97_EXTENDED_STAT_CTRL) & 0x0002)
4797979b1cfSJérôme Duval value *= 2;
4807979b1cfSJérôme Duval
4817979b1cfSJérôme Duval *rate = (uint32)((value * (uint64)dev->clock) / 48000); /* need 64 bit calculation to avoid overflow*/
4827979b1cfSJérôme Duval return true;
4837979b1cfSJérôme Duval }
4847979b1cfSJérôme Duval
485eb864c53SJérôme Duval
4867979b1cfSJérôme Duval void
ac97_set_clock(ac97_dev * dev,uint32 clock)4877979b1cfSJérôme Duval ac97_set_clock(ac97_dev *dev, uint32 clock)
4887979b1cfSJérôme Duval {
489fe5e83a6SMurai Takashi LOG(("ac97_set_clock: clock = %" B_PRIu32 "\n", clock));
4907979b1cfSJérôme Duval dev->clock = clock;
4917979b1cfSJérôme Duval ac97_detect_rates(dev);
4927979b1cfSJérôme Duval ac97_dump_capabilities(dev);
4937979b1cfSJérôme Duval }
4947979b1cfSJérôme Duval
495eb864c53SJérôme Duval
4967979b1cfSJérôme Duval void
ac97_detect_capabilities(ac97_dev * dev)4977979b1cfSJérôme Duval ac97_detect_capabilities(ac97_dev *dev)
4987979b1cfSJérôme Duval {
4997979b1cfSJérôme Duval uint16 val;
5007979b1cfSJérôme Duval
5017979b1cfSJérôme Duval val = ac97_reg_cached_read(dev, AC97_RESET);
5027979b1cfSJérôme Duval if (val & 0x0001)
5037979b1cfSJérôme Duval dev->capabilities |= CAP_PCM_MIC;
5047979b1cfSJérôme Duval if (val & 0x0004)
5057979b1cfSJérôme Duval dev->capabilities |= CAP_BASS_TREBLE_CTRL;
5067979b1cfSJérôme Duval if (val & 0x0008)
5077979b1cfSJérôme Duval dev->capabilities |= CAP_SIMULATED_STEREO;
5087979b1cfSJérôme Duval if (val & 0x0010)
5097979b1cfSJérôme Duval dev->capabilities |= CAP_HEADPHONE_OUT;
5107979b1cfSJérôme Duval if (val & 0x0020)
5117979b1cfSJérôme Duval dev->capabilities |= CAP_LAUDNESS;
5127979b1cfSJérôme Duval if (val & 0x0040)
5137979b1cfSJérôme Duval dev->capabilities |= CAP_DAC_18BIT;
5147979b1cfSJérôme Duval if (val & 0x0080)
5157979b1cfSJérôme Duval dev->capabilities |= CAP_DAC_20BIT;
5167979b1cfSJérôme Duval if (val & 0x0100)
5177979b1cfSJérôme Duval dev->capabilities |= CAP_ADC_18BIT;
5187979b1cfSJérôme Duval if (val & 0x0200)
5197979b1cfSJérôme Duval dev->capabilities |= CAP_ADC_20BIT;
5207979b1cfSJérôme Duval if (val & 0x7C00)
5217979b1cfSJérôme Duval dev->capabilities |= CAP_3D_ENHANCEMENT;
5227979b1cfSJérôme Duval
5237979b1cfSJérôme Duval val = ac97_reg_cached_read(dev, AC97_EXTENDED_ID);
5247979b1cfSJérôme Duval if (val & EXID_VRA)
5257979b1cfSJérôme Duval dev->capabilities |= CAP_VARIABLE_PCM;
5267979b1cfSJérôme Duval if (val & EXID_DRA)
5277979b1cfSJérôme Duval dev->capabilities |= CAP_DOUBLE_PCM;
5287979b1cfSJérôme Duval if (val & EXID_SPDIF)
5297979b1cfSJérôme Duval dev->capabilities |= CAP_SPDIF;
5307979b1cfSJérôme Duval if (val & EXID_VRM)
5317979b1cfSJérôme Duval dev->capabilities |= CAP_VARIABLE_MIC;
5327979b1cfSJérôme Duval if (val & EXID_CDAC)
5337979b1cfSJérôme Duval dev->capabilities |= CAP_CENTER_DAC;
5347979b1cfSJérôme Duval if (val & EXID_SDAC)
5357979b1cfSJérôme Duval dev->capabilities |= CAP_SURR_DAC;
5367979b1cfSJérôme Duval if (val & EXID_LDAC)
5377979b1cfSJérôme Duval dev->capabilities |= CAP_LFE_DAC;
5387979b1cfSJérôme Duval if (val & EXID_AMAP)
5397979b1cfSJérôme Duval dev->capabilities |= CAP_AMAP;
5407979b1cfSJérôme Duval if ((val & (EXID_REV0 | EXID_REV1)) == 0)
5417979b1cfSJérôme Duval dev->capabilities |= CAP_REV21;
5427979b1cfSJérôme Duval if ((val & (EXID_REV0 | EXID_REV1)) == EXID_REV0)
5437979b1cfSJérôme Duval dev->capabilities |= CAP_REV22;
5447979b1cfSJérôme Duval if ((val & (EXID_REV0 | EXID_REV1)) == EXID_REV1)
5457979b1cfSJérôme Duval dev->capabilities |= CAP_REV23;
5467979b1cfSJérôme Duval
5477979b1cfSJérôme Duval ac97_detect_rates(dev);
5487979b1cfSJérôme Duval }
5497979b1cfSJérôme Duval
5507979b1cfSJérôme Duval void
ac97_detect_rates(ac97_dev * dev)5517979b1cfSJérôme Duval ac97_detect_rates(ac97_dev *dev)
5527979b1cfSJérôme Duval {
5537979b1cfSJérôme Duval uint32 oldrate;
5547979b1cfSJérôme Duval
5557979b1cfSJérôme Duval dev->capabilities &= ~CAP_PCM_RATE_MASK;
5567979b1cfSJérôme Duval
5577979b1cfSJérôme Duval if (!ac97_get_rate(dev, AC97_PCM_FRONT_DAC_RATE, &oldrate))
5587979b1cfSJérôme Duval oldrate = 48000;
5597979b1cfSJérôme Duval
5607979b1cfSJérôme Duval if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 20000))
5617979b1cfSJérôme Duval dev->capabilities |= CAP_PCM_RATE_CONTINUOUS;
5627979b1cfSJérôme Duval if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 8000))
5637979b1cfSJérôme Duval dev->capabilities |= CAP_PCM_RATE_8000;
5647979b1cfSJérôme Duval if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 11025))
5657979b1cfSJérôme Duval dev->capabilities |= CAP_PCM_RATE_11025;
5667979b1cfSJérôme Duval if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 12000))
5677979b1cfSJérôme Duval dev->capabilities |= CAP_PCM_RATE_12000;
5687979b1cfSJérôme Duval if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 16000))
5697979b1cfSJérôme Duval dev->capabilities |= CAP_PCM_RATE_16000;
5707979b1cfSJérôme Duval if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 22050))
5717979b1cfSJérôme Duval dev->capabilities |= CAP_PCM_RATE_22050;
5727979b1cfSJérôme Duval if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 24000))
5737979b1cfSJérôme Duval dev->capabilities |= CAP_PCM_RATE_24000;
5747979b1cfSJérôme Duval if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 32000))
5757979b1cfSJérôme Duval dev->capabilities |= CAP_PCM_RATE_32000;
5767979b1cfSJérôme Duval if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 44100))
5777979b1cfSJérôme Duval dev->capabilities |= CAP_PCM_RATE_44100;
5787979b1cfSJérôme Duval if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 48000))
5797979b1cfSJérôme Duval dev->capabilities |= CAP_PCM_RATE_48000;
5807979b1cfSJérôme Duval
5817979b1cfSJérôme Duval if (dev->capabilities & CAP_DOUBLE_PCM) {
5827979b1cfSJérôme Duval // enable double rate mode
5837979b1cfSJérôme Duval if (ac97_reg_update_bits(dev, AC97_EXTENDED_STAT_CTRL, 0x0002, 0x0002)) {
5847979b1cfSJérôme Duval if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 88200))
5857979b1cfSJérôme Duval dev->capabilities |= CAP_PCM_RATE_88200;
5867979b1cfSJérôme Duval if (ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 96000))
5877979b1cfSJérôme Duval dev->capabilities |= CAP_PCM_RATE_96000;
5887979b1cfSJérôme Duval // disable double rate mode
5897979b1cfSJérôme Duval ac97_reg_update_bits(dev, AC97_EXTENDED_STAT_CTRL, 0x0002, 0x0000);
5907979b1cfSJérôme Duval }
5917979b1cfSJérôme Duval }
5927979b1cfSJérôme Duval
5937979b1cfSJérôme Duval ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, oldrate);
5947979b1cfSJérôme Duval }
5957979b1cfSJérôme Duval
596eb864c53SJérôme Duval
5977979b1cfSJérôme Duval void
ac97_dump_capabilities(ac97_dev * dev)5987979b1cfSJérôme Duval ac97_dump_capabilities(ac97_dev *dev)
5997979b1cfSJérôme Duval {
6007979b1cfSJérôme Duval LOG(("AC97 capabilities:\n"));
6017979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_PCM_MIC))
6027979b1cfSJérôme Duval LOG(("CAP_PCM_MIC\n"));
6037979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_BASS_TREBLE_CTRL))
6047979b1cfSJérôme Duval LOG(("CAP_BASS_TREBLE_CTRL\n"));
6057979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_SIMULATED_STEREO))
6067979b1cfSJérôme Duval LOG(("CAP_SIMULATED_STEREO\n"));
6077979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_HEADPHONE_OUT))
6087979b1cfSJérôme Duval LOG(("CAP_HEADPHONE_OUT\n"));
6097979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_LAUDNESS))
6107979b1cfSJérôme Duval LOG(("CAP_LAUDNESS\n"));
6117979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_DAC_18BIT))
6127979b1cfSJérôme Duval LOG(("CAP_DAC_18BIT\n"));
6137979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_DAC_20BIT))
6147979b1cfSJérôme Duval LOG(("CAP_DAC_20BIT\n"));
6157979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_ADC_18BIT))
6167979b1cfSJérôme Duval LOG(("CAP_ADC_18BIT\n"));
6177979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_ADC_20BIT))
6187979b1cfSJérôme Duval LOG(("CAP_ADC_20BIT\n"));
6197979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_3D_ENHANCEMENT))
6207979b1cfSJérôme Duval LOG(("CAP_3D_ENHANCEMENT\n"));
6217979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_VARIABLE_PCM))
6227979b1cfSJérôme Duval LOG(("CAP_VARIABLE_PCM\n"));
6237979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_DOUBLE_PCM))
6247979b1cfSJérôme Duval LOG(("CAP_DOUBLE_PCM\n"));
6257979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_VARIABLE_MIC))
6267979b1cfSJérôme Duval LOG(("CAP_VARIABLE_MIC\n"));
6277979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_CENTER_DAC))
6287979b1cfSJérôme Duval LOG(("CAP_CENTER_DAC\n"));
6297979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_SURR_DAC))
6307979b1cfSJérôme Duval LOG(("CAP_SURR_DAC\n"));
6317979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_LFE_DAC))
6327979b1cfSJérôme Duval LOG(("CAP_LFE_DAC\n"));
6337979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_AMAP))
6347979b1cfSJérôme Duval LOG(("CAP_AMAP\n"));
6357979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_REV21))
6367979b1cfSJérôme Duval LOG(("CAP_REV21\n"));
6377979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_REV22))
6387979b1cfSJérôme Duval LOG(("CAP_REV22\n"));
6397979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_REV23))
6407979b1cfSJérôme Duval LOG(("CAP_REV23\n"));
6417979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_PCM_RATE_CONTINUOUS))
6427979b1cfSJérôme Duval LOG(("CAP_PCM_RATE_CONTINUOUS\n"));
6437979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_PCM_RATE_8000))
6447979b1cfSJérôme Duval LOG(("CAP_PCM_RATE_8000\n"));
6457979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_PCM_RATE_11025))
6467979b1cfSJérôme Duval LOG(("CAP_PCM_RATE_11025\n"));
6477979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_PCM_RATE_12000))
6487979b1cfSJérôme Duval LOG(("CAP_PCM_RATE_12000\n"));
6497979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_PCM_RATE_16000))
6507979b1cfSJérôme Duval LOG(("CAP_PCM_RATE_16000\n"));
6517979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_PCM_RATE_22050))
6527979b1cfSJérôme Duval LOG(("CAP_PCM_RATE_22050\n"));
6537979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_PCM_RATE_24000))
6547979b1cfSJérôme Duval LOG(("CAP_PCM_RATE_24000\n"));
6557979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_PCM_RATE_32000))
6567979b1cfSJérôme Duval LOG(("CAP_PCM_RATE_32000\n"));
6577979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_PCM_RATE_44100))
6587979b1cfSJérôme Duval LOG(("CAP_PCM_RATE_44100\n"));
6597979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_PCM_RATE_48000))
6607979b1cfSJérôme Duval LOG(("CAP_PCM_RATE_48000\n"));
6617979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_PCM_RATE_88200))
6627979b1cfSJérôme Duval LOG(("CAP_PCM_RATE_88200\n"));
6637979b1cfSJérôme Duval if (ac97_has_capability(dev, CAP_PCM_RATE_96000))
6647979b1cfSJérôme Duval LOG(("CAP_PCM_RATE_96000\n"));
6657979b1cfSJérôme Duval }
6667979b1cfSJérôme Duval
667eb864c53SJérôme Duval
6687979b1cfSJérôme Duval bool
ac97_has_capability(ac97_dev * dev,uint64 cap)6697979b1cfSJérôme Duval ac97_has_capability(ac97_dev *dev, uint64 cap)
6707979b1cfSJérôme Duval {
6717979b1cfSJérôme Duval // return (dev->capabilities & cap); // does not work because of 64 bit to integer trucation
6727979b1cfSJérôme Duval return (dev->capabilities & cap) != 0;
6737979b1cfSJérôme Duval }
6747979b1cfSJérôme Duval
6757979b1cfSJérôme Duval /*************************************************
6767979b1cfSJérôme Duval * Codec specific initialization, etc.
6777979b1cfSJérôme Duval */
6787979b1cfSJérôme Duval
6797979b1cfSJérôme Duval bool
ac97_reg_is_valid(ac97_dev * dev,uint8 reg)6807979b1cfSJérôme Duval ac97_reg_is_valid(ac97_dev *dev, uint8 reg)
6817979b1cfSJérôme Duval {
6827979b1cfSJérôme Duval if (reg & 1)
6837979b1cfSJérôme Duval return false;
6847979b1cfSJérôme Duval if (reg > 0x7e)
6857979b1cfSJérôme Duval return false;
6867979b1cfSJérôme Duval
6877979b1cfSJérôme Duval switch (dev->codec_id) {
6887979b1cfSJérôme Duval case CODEC_ID_AK4540:
6897979b1cfSJérôme Duval case CODEC_ID_AK4542:
6907979b1cfSJérôme Duval if (reg < 0x1e || reg == 0x20 || reg == 0x26 || reg > 0x7a)
6917979b1cfSJérôme Duval return true;
6927979b1cfSJérôme Duval return false;
6937979b1cfSJérôme Duval
6947979b1cfSJérôme Duval case CODEC_ID_AD1819:
6957979b1cfSJérôme Duval case CODEC_ID_AD1881:
6967979b1cfSJérôme Duval case CODEC_ID_AD1881A:
6977979b1cfSJérôme Duval if (reg < 0x3a || reg > 0x6e)
6987979b1cfSJérôme Duval return true;
6997979b1cfSJérôme Duval return false;
7007979b1cfSJérôme Duval
7017979b1cfSJérôme Duval case CODEC_ID_AD1885:
7027979b1cfSJérôme Duval case CODEC_ID_AD1886:
7037979b1cfSJérôme Duval case CODEC_ID_AD1886A:
7047979b1cfSJérôme Duval case CODEC_ID_AD1887:
7057979b1cfSJérôme Duval if (reg < 0x3c || reg == 0x5a || reg > 0x6e)
7067979b1cfSJérôme Duval return true;
7077979b1cfSJérôme Duval return false;
7087979b1cfSJérôme Duval
7097979b1cfSJérôme Duval case CODEC_ID_STAC9700:
7107979b1cfSJérôme Duval case CODEC_ID_STAC9704:
7117979b1cfSJérôme Duval case CODEC_ID_STAC9705:
7127979b1cfSJérôme Duval case CODEC_ID_STAC9708:
7137979b1cfSJérôme Duval case CODEC_ID_STAC9721:
7147979b1cfSJérôme Duval case CODEC_ID_STAC9744:
7157979b1cfSJérôme Duval case CODEC_ID_STAC9756:
7167979b1cfSJérôme Duval if (reg < 0x3c || reg > 0x58)
7177979b1cfSJérôme Duval return true;
7187979b1cfSJérôme Duval return false;
7197979b1cfSJérôme Duval
7207979b1cfSJérôme Duval default:
7217979b1cfSJérôme Duval return true;
7227979b1cfSJérôme Duval }
7237979b1cfSJérôme Duval }
7247979b1cfSJérôme Duval
725eb864c53SJérôme Duval
726eb864c53SJérôme Duval void
ac97_amp_enable(ac97_dev * dev,bool yesno)727eb864c53SJérôme Duval ac97_amp_enable(ac97_dev *dev, bool yesno)
7287979b1cfSJérôme Duval {
7297979b1cfSJérôme Duval switch (dev->codec_id) {
7307979b1cfSJérôme Duval case CODEC_ID_CS4299A:
7317979b1cfSJérôme Duval case CODEC_ID_CS4299C:
7327979b1cfSJérôme Duval case CODEC_ID_CS4299D:
7337979b1cfSJérôme Duval LOG(("cs4299_amp_enable\n"));
7347979b1cfSJérôme Duval if (yesno)
7357979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x68, 0x8004);
7367979b1cfSJérôme Duval else
7377979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x68, 0);
7387979b1cfSJérôme Duval break;
7397979b1cfSJérôme Duval
7407979b1cfSJérôme Duval default:
7417979b1cfSJérôme Duval LOG(("ac97_amp_enable, reverse eamp = %d\n", dev->reversed_eamp_polarity));
7427979b1cfSJérôme Duval LOG(("powerdown register was = %#04x\n", ac97_reg_uncached_read(dev, AC97_POWERDOWN)));
7437979b1cfSJérôme Duval if (dev->reversed_eamp_polarity)
7447979b1cfSJérôme Duval yesno = !yesno;
7457979b1cfSJérôme Duval if (yesno)
7467979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_POWERDOWN, ac97_reg_uncached_read(dev, AC97_POWERDOWN) & ~0x8000); /* switch on (low active) */
7477979b1cfSJérôme Duval else
7487979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_POWERDOWN, ac97_reg_uncached_read(dev, AC97_POWERDOWN) | 0x8000); /* switch off */
7497979b1cfSJérôme Duval LOG(("powerdown register is = %#04x\n", ac97_reg_uncached_read(dev, AC97_POWERDOWN)));
7507979b1cfSJérôme Duval break;
7517979b1cfSJérôme Duval }
7527979b1cfSJérôme Duval }
7537979b1cfSJérôme Duval
754eb864c53SJérôme Duval
7557979b1cfSJérôme Duval bool
ad1819_set_rate(ac97_dev * dev,uint8 reg,uint32 rate)7567979b1cfSJérôme Duval ad1819_set_rate(ac97_dev *dev, uint8 reg, uint32 rate)
7577979b1cfSJérôme Duval {
7587979b1cfSJérôme Duval uint32 value;
7597979b1cfSJérôme Duval
7607979b1cfSJérôme Duval value = (uint32)((rate * 48000ULL) / dev->clock); /* need 64 bit calculation for rates 96000 or higher */
7617979b1cfSJérôme Duval
762fe5e83a6SMurai Takashi LOG(("ad1819_set_rate: clock = %" B_PRIu32 ", "
763fe5e83a6SMurai Takashi "rate = %" B_PRIu32 ", "
764fe5e83a6SMurai Takashi "value = %" B_PRIu32 "\n",
765fe5e83a6SMurai Takashi dev->clock, rate, value));
7667979b1cfSJérôme Duval
7677979b1cfSJérôme Duval if (value < 0x1B58 || value > 0xBB80)
7687979b1cfSJérôme Duval return false;
7697979b1cfSJérôme Duval
7707979b1cfSJérôme Duval switch (reg) {
7717979b1cfSJérôme Duval case AC97_PCM_FRONT_DAC_RATE:
7727979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_AD_SAMPLE_RATE_0, value);
7737979b1cfSJérôme Duval return true;
7747979b1cfSJérôme Duval
7757979b1cfSJérôme Duval case AC97_PCM_L_R_ADC_RATE:
7767979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_AD_SAMPLE_RATE_1, value);
7777979b1cfSJérôme Duval return true;
7787979b1cfSJérôme Duval
7797979b1cfSJérôme Duval default:
7807979b1cfSJérôme Duval return false;
7817979b1cfSJérôme Duval }
7827979b1cfSJérôme Duval }
7837979b1cfSJérôme Duval
784eb864c53SJérôme Duval
7857979b1cfSJérôme Duval bool
ad1819_get_rate(ac97_dev * dev,uint8 reg,uint32 * rate)7867979b1cfSJérôme Duval ad1819_get_rate(ac97_dev *dev, uint8 reg, uint32 *rate)
7877979b1cfSJérôme Duval {
7887979b1cfSJérôme Duval uint32 value;
7897979b1cfSJérôme Duval
7907979b1cfSJérôme Duval switch (reg) {
7917979b1cfSJérôme Duval case AC97_PCM_FRONT_DAC_RATE:
7927979b1cfSJérôme Duval value = ac97_reg_cached_read(dev, AC97_AD_SAMPLE_RATE_0);
7937979b1cfSJérôme Duval break;
7947979b1cfSJérôme Duval
7957979b1cfSJérôme Duval case AC97_PCM_L_R_ADC_RATE:
7967979b1cfSJérôme Duval value = ac97_reg_cached_read(dev, AC97_AD_SAMPLE_RATE_1);
7977979b1cfSJérôme Duval break;
7987979b1cfSJérôme Duval
7997979b1cfSJérôme Duval default:
8007979b1cfSJérôme Duval return false;
8017979b1cfSJérôme Duval }
8027979b1cfSJérôme Duval
8037979b1cfSJérôme Duval *rate = (uint32)((value * (uint64)dev->clock) / 48000); /* need 64 bit calculation to avoid overflow*/
8047979b1cfSJérôme Duval return true;
8057979b1cfSJérôme Duval }
8067979b1cfSJérôme Duval
8077979b1cfSJérôme Duval
808eb864c53SJérôme Duval void
default_init(ac97_dev * dev)809eb864c53SJérôme Duval default_init(ac97_dev *dev)
8107979b1cfSJérôme Duval {
8117979b1cfSJérôme Duval LOG(("default_init\n"));
8127979b1cfSJérôme Duval }
8137979b1cfSJérôme Duval
814eb864c53SJérôme Duval
815eb864c53SJérôme Duval void
ad1819_init(ac97_dev * dev)816eb864c53SJérôme Duval ad1819_init(ac97_dev *dev)
8177979b1cfSJérôme Duval {
8187979b1cfSJérôme Duval LOG(("ad1819_init\n"));
8197979b1cfSJérôme Duval
8207979b1cfSJérôme Duval /* Default config for system with single AD1819 codec */
8217979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_AD_SERIAL_CONFIG, 0x7000);
8227979b1cfSJérôme Duval ac97_update_register_cache(dev);
8237979b1cfSJérôme Duval
8247979b1cfSJérôme Duval /* The AD1819 chip has proprietary sample rate controls
8257979b1cfSJérôme Duval * Setup sample rate 0 generator for DAC,
8267979b1cfSJérôme Duval * Setup sample rate 1 generator for ADC,
8277979b1cfSJérôme Duval * ARSR=1, DRSR=0, ALSR=1, DLSR=0
8287979b1cfSJérôme Duval */
8297979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_AD_MISC_CONTROL, 0x0101);
8307979b1cfSJérôme Duval /* connect special rate set/get functions */
8317979b1cfSJérôme Duval dev->set_rate = ad1819_set_rate;
8327979b1cfSJérôme Duval dev->get_rate = ad1819_get_rate;
8337979b1cfSJérôme Duval ac97_detect_rates(dev);
8347979b1cfSJérôme Duval ac97_set_rate(dev, AC97_PCM_FRONT_DAC_RATE, 48000);
8357979b1cfSJérôme Duval ac97_set_rate(dev, AC97_PCM_L_R_ADC_RATE, 48000);
8367979b1cfSJérôme Duval }
8377979b1cfSJérôme Duval
838eb864c53SJérôme Duval
839eb864c53SJérôme Duval void
ad1881_init(ac97_dev * dev)840eb864c53SJérôme Duval ad1881_init(ac97_dev *dev)
8417979b1cfSJérôme Duval {
8427979b1cfSJérôme Duval LOG(("ad1881_init\n"));
8437979b1cfSJérôme Duval
8447979b1cfSJérôme Duval /* Default config for system with single AD1819 codec,
8457979b1cfSJérôme Duval * BROKEN on systems with master & slave codecs */
8467979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_AD_SERIAL_CONFIG, 0x7000);
8477979b1cfSJérôme Duval ac97_update_register_cache(dev);
8487979b1cfSJérôme Duval
8497979b1cfSJérôme Duval /* Setup DAC and ADC rate generator assignments compatible with AC97 */
8507979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_AD_MISC_CONTROL, 0x0404);
8517979b1cfSJérôme Duval
8527979b1cfSJérôme Duval /* Setup variable frame rate limits */
8537979b1cfSJérôme Duval dev->min_vsr = 0x1B58; /* 7kHz */
8547979b1cfSJérôme Duval dev->max_vsr = 0xBB80; /* 48kHz */
8557979b1cfSJérôme Duval }
8567979b1cfSJérôme Duval
857eb864c53SJérôme Duval
858eb864c53SJérôme Duval void
ad1885_init(ac97_dev * dev)859eb864c53SJérôme Duval ad1885_init(ac97_dev *dev)
8607979b1cfSJérôme Duval {
8617979b1cfSJérôme Duval LOG(("ad1885_init\n"));
8627979b1cfSJérôme Duval ad1881_init(dev);
8637979b1cfSJérôme Duval
8647979b1cfSJérôme Duval /* disable jack sense 0 and 1 (JS0, JS1) to turn off automatic mute */
8657979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_AD_JACK_SENSE, ac97_reg_cached_read(dev, AC97_AD_JACK_SENSE) | 0x0300);
8667979b1cfSJérôme Duval }
8677979b1cfSJérôme Duval
868eb864c53SJérôme Duval
869eb864c53SJérôme Duval void
ad1886_init(ac97_dev * dev)870eb864c53SJérôme Duval ad1886_init(ac97_dev *dev)
8717979b1cfSJérôme Duval {
8727979b1cfSJérôme Duval LOG(("ad1886_init\n"));
8737979b1cfSJérôme Duval ad1881_init(dev);
8747979b1cfSJérôme Duval
8757979b1cfSJérôme Duval /* change jack sense to always activate outputs*/
8767979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_AD_JACK_SENSE, 0x0010);
8777979b1cfSJérôme Duval /* change SPDIF to a valid value */
8787979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_SPDIF_CONTROL, 0x2a20);
8797979b1cfSJérôme Duval }
8807979b1cfSJérôme Duval
881eb864c53SJérôme Duval
882eb864c53SJérôme Duval void
ad1980_init(ac97_dev * dev)883eb864c53SJérôme Duval ad1980_init(ac97_dev *dev)
8847979b1cfSJérôme Duval {
8857979b1cfSJérôme Duval LOG(("ad1980_init\n"));
8867979b1cfSJérôme Duval
8877979b1cfSJérôme Duval /* Select only master codec,
8887979b1cfSJérôme Duval * SPDIF and DAC are linked
8897979b1cfSJérôme Duval */
8907979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_AD_SERIAL_CONFIG, 0x1001);
8917979b1cfSJérôme Duval ac97_update_register_cache(dev);
8927979b1cfSJérôme Duval
8937979b1cfSJérôme Duval /* Select Line-out driven with mixer data (not surround data)
8947979b1cfSJérôme Duval * Select Headphone-out driven with mixer data (not surround data),
8957979b1cfSJérôme Duval * LOSEL = 0, HPSEL = 1
8967979b1cfSJérôme Duval * XXX this one needs to be changed to support surround out
8977979b1cfSJérôme Duval */
8987979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_AD_MISC_CONTROL, 0x0400);
8997979b1cfSJérôme Duval }
9007979b1cfSJérôme Duval
901eb864c53SJérôme Duval
902eb864c53SJérôme Duval void
ad1981b_init(ac97_dev * dev)903eb864c53SJérôme Duval ad1981b_init(ac97_dev *dev)
9047979b1cfSJérôme Duval {
9057979b1cfSJérôme Duval LOG(("ad1981b_init\n"));
9066a2a59e1SJérôme Duval if (dev->subsystem == 0x0e11005a
9077979b1cfSJérôme Duval || dev->subsystem == 0x103c006d
9087979b1cfSJérôme Duval || dev->subsystem == 0x103c088c
9097979b1cfSJérôme Duval || dev->subsystem == 0x103c0890
9107979b1cfSJérôme Duval || dev->subsystem == 0x103c0934
9117979b1cfSJérôme Duval || dev->subsystem == 0x103c0938
9127979b1cfSJérôme Duval || dev->subsystem == 0x103c0944
9137979b1cfSJérôme Duval || dev->subsystem == 0x103c099c
9147979b1cfSJérôme Duval || dev->subsystem == 0x101402d9) {
9157979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_AD_JACK_SENSE,
9167979b1cfSJérôme Duval ac97_reg_cached_read(dev, AC97_AD_JACK_SENSE) | 0x0800);
9177979b1cfSJérôme Duval }
9187979b1cfSJérôme Duval }
9197979b1cfSJérôme Duval
920eb864c53SJérôme Duval
921eb864c53SJérôme Duval void
alc203_init(ac97_dev * dev)922eb864c53SJérôme Duval alc203_init(ac97_dev *dev)
923eb864c53SJérôme Duval {
924eb864c53SJérôme Duval LOG(("alc203_init\n"));
925eb864c53SJérôme Duval
926eb864c53SJérôme Duval ac97_reg_update_bits(dev, AC97_ALC650_CLOCK_SOURCE, 0x400, 0x400);
927eb864c53SJérôme Duval }
928eb864c53SJérôme Duval
929eb864c53SJérôme Duval
930eb864c53SJérôme Duval void
alc650_init(ac97_dev * dev)931eb864c53SJérôme Duval alc650_init(ac97_dev *dev)
9327979b1cfSJérôme Duval {
9337979b1cfSJérôme Duval LOG(("alc650_init\n"));
9347979b1cfSJérôme Duval
9357979b1cfSJérôme Duval /* Enable Surround, LFE and Center downmix into Line-out,
9367979b1cfSJérôme Duval * Set Surround-out as duplicated Line-out.
9377979b1cfSJérôme Duval */
9387979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_ALC650_MULTI_CHAN_CTRL, 0x0007);
9397979b1cfSJérôme Duval
9407979b1cfSJérôme Duval /* Set Surround DAC Volume to 0dB
9417979b1cfSJérôme Duval * Set Center/LFE DAC Volume to 0dB
9427979b1cfSJérôme Duval * (but both should already be set, as these are hardware reset defaults)
9437979b1cfSJérôme Duval */
9447979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_ALC650_SURR_VOLUME, 0x0808);
9457979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_ALC650_CEN_LFE_VOLUME, 0x0808);
9467979b1cfSJérôme Duval }
9477979b1cfSJérôme Duval
948eb864c53SJérôme Duval
949eb864c53SJérôme Duval void
alc655_init(ac97_dev * dev)950eb864c53SJérôme Duval alc655_init(ac97_dev *dev)
951eb864c53SJérôme Duval {
952eb864c53SJérôme Duval uint16 val;
953eb864c53SJérôme Duval LOG(("alc655_init\n"));
954eb864c53SJérôme Duval
955eb864c53SJérôme Duval ac97_reg_update_bits(dev, AC97_PAGING, 0xf, 0);
956eb864c53SJérôme Duval
957eb864c53SJérôme Duval val = ac97_reg_cached_read(dev, AC97_ALC650_CLOCK_SOURCE);
958eb864c53SJérôme Duval // TODO update bits for specific devices
959eb864c53SJérôme Duval val &= ~0x1000;
960eb864c53SJérôme Duval ac97_reg_cached_write(dev, AC97_ALC650_CLOCK_SOURCE, val);
961eb864c53SJérôme Duval
962eb864c53SJérôme Duval ac97_reg_cached_write(dev, AC97_ALC650_MULTI_CHAN_CTRL, 0x8000);
963eb864c53SJérôme Duval ac97_reg_cached_write(dev, AC97_ALC650_SURR_VOLUME, 0x808);
964eb864c53SJérôme Duval ac97_reg_cached_write(dev, AC97_ALC650_CEN_LFE_VOLUME, 0x808);
965eb864c53SJérôme Duval
966eb864c53SJérôme Duval if (dev->codec_id == 0x414c4781)
967eb864c53SJérôme Duval ac97_reg_update_bits(dev, AC97_ALC650_MISC_CONTROL, 0x800, 0x800);
968eb864c53SJérôme Duval }
969eb864c53SJérôme Duval
970eb864c53SJérôme Duval
971eb864c53SJérôme Duval void
alc850_init(ac97_dev * dev)972eb864c53SJérôme Duval alc850_init(ac97_dev *dev)
973eb864c53SJérôme Duval {
974eb864c53SJérôme Duval LOG(("alc850_init\n"));
975eb864c53SJérôme Duval
976eb864c53SJérôme Duval ac97_reg_update_bits(dev, AC97_PAGING, 0xf, 0);
977eb864c53SJérôme Duval
978eb864c53SJérôme Duval ac97_reg_cached_write(dev, AC97_ALC650_MULTI_CHAN_CTRL, 0x8000);
979eb864c53SJérôme Duval ac97_reg_cached_write(dev, AC97_ALC650_CLOCK_SOURCE, 0x20d2);
980eb864c53SJérôme Duval ac97_reg_cached_write(dev, AC97_ALC650_GPIO_SETUP, 0x8a90);
981eb864c53SJérôme Duval ac97_reg_cached_write(dev, AC97_ALC650_SURR_VOLUME, 0x808);
982eb864c53SJérôme Duval ac97_reg_cached_write(dev, AC97_ALC650_CEN_LFE_VOLUME, 0x808);
983eb864c53SJérôme Duval }
984eb864c53SJérôme Duval
985eb864c53SJérôme Duval
986eb864c53SJérôme Duval void
stac9708_init(ac97_dev * dev)987eb864c53SJérôme Duval stac9708_init(ac97_dev *dev)
9887979b1cfSJérôme Duval {
9897979b1cfSJérôme Duval LOG(("stac9708_init\n"));
9907979b1cfSJérôme Duval /* ALSA initializes some registers that according to the
9917979b1cfSJérôme Duval * documentation for this codec do not exist. If the
9927979b1cfSJérôme Duval * following doesn't work, we may need to do that, too.
9937979b1cfSJérôme Duval */
9947979b1cfSJérôme Duval /* The Analog Special reg is at 0x6C, other codecs have it at 0x6E */
9957979b1cfSJérôme Duval /* Set Analog Special to default (DAC/ADC -6dB disabled) */
9967979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x6C, 0x0000);
9977979b1cfSJérôme Duval /* Set Multi Channel to default */
9987979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x74, 0x0000);
9997979b1cfSJérôme Duval }
10007979b1cfSJérôme Duval
1001eb864c53SJérôme Duval
1002eb864c53SJérôme Duval void
stac9721_init(ac97_dev * dev)1003eb864c53SJérôme Duval stac9721_init(ac97_dev *dev)
10047979b1cfSJérôme Duval {
10057979b1cfSJérôme Duval LOG(("stac9721_init\n"));
10067979b1cfSJérôme Duval /* Set Analog Special to default (DAC/ADC -6dB disabled) */
10077979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x6E, 0x0000);
10087979b1cfSJérôme Duval /* Enable register 0x72 */
10097979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x70, 0xabba);
10107979b1cfSJérôme Duval /* Set Analog Current to -50% */
10117979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x72, 0x0002);
10127979b1cfSJérôme Duval /* Set Multi Channel to default */
10137979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x74, 0x0000);
10147979b1cfSJérôme Duval /* Enable register 0x78 */
10157979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x76, 0xabba);
10167979b1cfSJérôme Duval /* Set Clock Access to default */
10177979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x78, 0x0000);
10187979b1cfSJérôme Duval }
10197979b1cfSJérôme Duval
1020eb864c53SJérôme Duval
1021eb864c53SJérôme Duval void
stac9744_init(ac97_dev * dev)1022eb864c53SJérôme Duval stac9744_init(ac97_dev *dev)
10237979b1cfSJérôme Duval {
10247979b1cfSJérôme Duval LOG(("stac9744_init\n"));
10257979b1cfSJérôme Duval /* Set Analog Special to default (DAC/ADC -6dB disabled) */
10267979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x6E, 0x0000);
10277979b1cfSJérôme Duval /* Enable register 0x72 */
10287979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x70, 0xabba);
10297979b1cfSJérôme Duval /* Set Analog Current to -50% */
10307979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x72, 0x0002);
10317979b1cfSJérôme Duval /* Set Multi Channel to default */
10327979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x74, 0x0000);
10337979b1cfSJérôme Duval /* Enable register 0x78 */
10347979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x76, 0xabba);
10357979b1cfSJérôme Duval /* Set Clock Access to default */
10367979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x78, 0x0000);
10377979b1cfSJérôme Duval }
10387979b1cfSJérôme Duval
1039eb864c53SJérôme Duval
1040eb864c53SJérôme Duval void
stac9756_init(ac97_dev * dev)1041eb864c53SJérôme Duval stac9756_init(ac97_dev *dev)
10427979b1cfSJérôme Duval {
10437979b1cfSJérôme Duval LOG(("stac9756_init\n"));
10447979b1cfSJérôme Duval /* Set Analog Special to default (AC97 all-mix, DAC/ADC -6dB disabled) */
10457979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x6E, 0x1000);
10467979b1cfSJérôme Duval /* Enable register 0x72 */
10477979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x70, 0xabba);
10487979b1cfSJérôme Duval /* Set Analog Current to -50% */
10497979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x72, 0x0002);
10507979b1cfSJérôme Duval /* Set Multi Channel to default */
10517979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x74, 0x0000);
10527979b1cfSJérôme Duval /* Enable register 0x78 */
10537979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x76, 0xabba);
10547979b1cfSJérôme Duval /* Set Clock Access to default */
10557979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x78, 0x0000);
10567979b1cfSJérôme Duval }
10577979b1cfSJérôme Duval
1058eb864c53SJérôme Duval
1059fe2d4a0fSJérôme Duval
1060fe2d4a0fSJérôme Duval void
stac9758_init(ac97_dev * dev)1061fe2d4a0fSJérôme Duval stac9758_init(ac97_dev *dev)
1062fe2d4a0fSJérôme Duval {
1063fe2d4a0fSJérôme Duval LOG(("stac9758_init\n"));
1064fe2d4a0fSJérôme Duval
1065fe2d4a0fSJérôme Duval ac97_reg_update_bits(dev, AC97_PAGING, 0xf, 0);
1066fe2d4a0fSJérôme Duval
1067fe2d4a0fSJérôme Duval if (dev->subsystem == 0x107b0601) {
1068fe2d4a0fSJérôme Duval ac97_reg_cached_write(dev, 0x64, 0xfc70);
1069fe2d4a0fSJérôme Duval ac97_reg_cached_write(dev, 0x68, 0x2102);
1070fe2d4a0fSJérôme Duval ac97_reg_cached_write(dev, 0x66, 0x0203);
1071fe2d4a0fSJérôme Duval ac97_reg_cached_write(dev, 0x72, 0x0041);
1072fe2d4a0fSJérôme Duval } else {
1073fe2d4a0fSJérôme Duval ac97_reg_cached_write(dev, 0x64, 0xd794);
1074fe2d4a0fSJérôme Duval ac97_reg_cached_write(dev, 0x68, 0x2001);
1075fe2d4a0fSJérôme Duval ac97_reg_cached_write(dev, 0x66, 0x0201);
1076fe2d4a0fSJérôme Duval ac97_reg_cached_write(dev, 0x72, 0x0040);
1077fe2d4a0fSJérôme Duval }
1078fe2d4a0fSJérôme Duval }
1079fe2d4a0fSJérôme Duval
1080fe2d4a0fSJérôme Duval
1081eb864c53SJérôme Duval void
tr28028_init(ac97_dev * dev)1082eb864c53SJérôme Duval tr28028_init(ac97_dev *dev)
10837979b1cfSJérôme Duval {
10847979b1cfSJérôme Duval LOG(("tr28028_init\n"));
10857979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_POWERDOWN, 0x0300);
10867979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_POWERDOWN, 0x0000);
10877979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_SURR_VOLUME, 0x0000);
10887979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_SPDIF_CONTROL, 0x0000);
10897979b1cfSJérôme Duval }
10907979b1cfSJérôme Duval
1091eb864c53SJérôme Duval
1092eb864c53SJérôme Duval void
wm9701_init(ac97_dev * dev)1093eb864c53SJérôme Duval wm9701_init(ac97_dev *dev)
10947979b1cfSJérôme Duval {
10957979b1cfSJérôme Duval LOG(("wm9701_init\n"));
10967979b1cfSJérôme Duval /* ALSA writes some of these registers, but the codec
10977979b1cfSJérôme Duval * documentation states explicitly that 0x38 and 0x70 to 0x74
10987979b1cfSJérôme Duval * are not used in the WM9701A
10997979b1cfSJérôme Duval */
11007979b1cfSJérôme Duval
11017979b1cfSJérôme Duval /* DVD noise patch (?) */
11027979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x5a, 0x0200);
11037979b1cfSJérôme Duval }
11047979b1cfSJérôme Duval
1105eb864c53SJérôme Duval
1106eb864c53SJérôme Duval void
wm9703_init(ac97_dev * dev)1107eb864c53SJérôme Duval wm9703_init(ac97_dev *dev)
11087979b1cfSJérôme Duval {
11097979b1cfSJérôme Duval LOG(("wm9703_init\n"));
11107979b1cfSJérôme Duval /* Set front mixer value to unmuted */
11117979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x72, 0x0808);
11127979b1cfSJérôme Duval /* Disable loopback, etc */
11137979b1cfSJérôme Duval ac97_reg_cached_write(dev, AC97_GENERAL_PURPOSE, 0x8000);
11147979b1cfSJérôme Duval }
11157979b1cfSJérôme Duval
1116eb864c53SJérôme Duval
1117eb864c53SJérôme Duval void
wm9704_init(ac97_dev * dev)1118eb864c53SJérôme Duval wm9704_init(ac97_dev *dev)
11197979b1cfSJérôme Duval {
11207979b1cfSJérôme Duval LOG(("wm9704_init\n"));
11217979b1cfSJérôme Duval /* Set read DAC value to unmuted */
11227979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x70, 0x0808);
11237979b1cfSJérôme Duval /* Set front mixer value to unmuted */
11247979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x72, 0x0808);
11257979b1cfSJérôme Duval /* Set rear mixer value to unmuted */
11267979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x74, 0x0808);
11277979b1cfSJérôme Duval /* DVD noise patch (?) */
11287979b1cfSJérôme Duval ac97_reg_cached_write(dev, 0x5a, 0x0200);
11297979b1cfSJérôme Duval }
11307979b1cfSJérôme Duval
1131eb864c53SJérôme Duval
11327979b1cfSJérôme Duval const ac97_source_info source_info[] = {
11337979b1cfSJérôme Duval { "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 },
11347979b1cfSJérôme Duval { "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 },
11357979b1cfSJérôme Duval //{ "Bass/Treble", B_MIX_GAIN|B_MIX_STEREO, 102, AC97_MASTER_TONE, 0x0f0f, 4, 0, 1, 1,-12.0, 10.5, 1.5 },
1136ab496a4bSStephan Aßmus //{ "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 },
1137ab496a4bSStephan Aßmus { "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 },
11387979b1cfSJérôme Duval { "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 },
11397979b1cfSJérôme Duval { "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 },
11407979b1cfSJérôme Duval { "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 },
11417979b1cfSJérôme Duval { "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 },
1142ab496a4bSStephan Aßmus { "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 },
11437979b1cfSJérôme Duval //{ "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 },
11447979b1cfSJérôme Duval { "Center/Lfe" /* should be "Surround" but no */, B_MIX_GAIN|B_MIX_MUTE|B_MIX_STEREO, 110, AC97_SURR_VOLUME, 0x8080, 5, 0, 1, 1,-46.5, 0.0, 1.5 }
11457979b1cfSJérôme Duval };
11467979b1cfSJérôme Duval
11477979b1cfSJérôme Duval const int32 source_info_size = (sizeof(source_info)/sizeof(source_info[0]));
1148