1*0f9891f1Sjsg /* $OpenBSD: envy.c,v 1.88 2024/05/24 06:02:53 jsg Exp $ */
23c27163dSratchov /*
33c27163dSratchov * Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org>
43c27163dSratchov *
53c27163dSratchov * Permission to use, copy, modify, and distribute this software for any
63c27163dSratchov * purpose with or without fee is hereby granted, provided that the above
73c27163dSratchov * copyright notice and this permission notice appear in all copies.
83c27163dSratchov *
93c27163dSratchov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
103c27163dSratchov * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
113c27163dSratchov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
123c27163dSratchov * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
133c27163dSratchov * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
143c27163dSratchov * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
153c27163dSratchov * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
163c27163dSratchov */
173c27163dSratchov
189ad31cc3Sratchov /*
199ad31cc3Sratchov * TODO:
209ad31cc3Sratchov *
212c4be9d3Sratchov * - add nspdin, nspdout, to struct envy_card
229ad31cc3Sratchov *
239ad31cc3Sratchov * - use eeprom version rather isht flag
249ad31cc3Sratchov *
2510a0d23aSratchov * - implement HT mixer, midi uart, spdif, init ADC/DACs for >48kHz modes
2610a0d23aSratchov *
279ad31cc3Sratchov */
289ad31cc3Sratchov
29c737f4d2Sratchov #include "midi.h"
303c27163dSratchov #include <sys/param.h>
313c27163dSratchov #include <sys/systm.h>
323c27163dSratchov #include <sys/device.h>
333c27163dSratchov #include <sys/audioio.h>
343c27163dSratchov #include <sys/malloc.h>
353a87d643Sratchov #include <dev/audio_if.h>
36c737f4d2Sratchov #include <dev/midi_if.h>
373a87d643Sratchov #include <dev/ic/ac97.h>
383c27163dSratchov #include <dev/pci/pcivar.h>
393c27163dSratchov #include <dev/pci/pcidevs.h>
403c27163dSratchov #include <dev/pci/envyvar.h>
413c27163dSratchov #include <dev/pci/envyreg.h>
423c27163dSratchov #include <machine/bus.h>
433609b215Sratchov #include <uvm/uvm.h>
443c27163dSratchov
453c27163dSratchov #ifdef ENVY_DEBUG
463c27163dSratchov #define DPRINTF(...) do { if (envydebug) printf(__VA_ARGS__); } while(0)
473c27163dSratchov #define DPRINTFN(n, ...) do { if (envydebug > (n)) printf(__VA_ARGS__); } while(0)
483c27163dSratchov int envydebug = 1;
493c27163dSratchov #else
503c27163dSratchov #define DPRINTF(...) do {} while(0)
513c27163dSratchov #define DPRINTFN(n, ...) do {} while(0)
523c27163dSratchov #endif
533c27163dSratchov #define DEVNAME(sc) ((sc)->dev.dv_xname)
543c27163dSratchov
553c27163dSratchov int envymatch(struct device *, void *, void *);
563c27163dSratchov void envyattach(struct device *, struct device *, void *);
573c27163dSratchov int envydetach(struct device *, int);
582af838c1Sratchov int envyactivate(struct device *, int);
593c27163dSratchov
603c27163dSratchov int envy_ccs_read(struct envy_softc *, int);
613c27163dSratchov void envy_ccs_write(struct envy_softc *, int, int);
629383d68eSratchov int envy_mt_read_1(struct envy_softc *, int);
639383d68eSratchov void envy_mt_write_1(struct envy_softc *, int, int);
649383d68eSratchov int envy_mt_read_2(struct envy_softc *, int);
659383d68eSratchov void envy_mt_write_2(struct envy_softc *, int, int);
669383d68eSratchov int envy_mt_read_4(struct envy_softc *, int);
679383d68eSratchov void envy_mt_write_4(struct envy_softc *, int, int);
683c27163dSratchov int envy_cci_read(struct envy_softc *, int);
693c27163dSratchov void envy_cci_write(struct envy_softc *, int, int);
703c27163dSratchov void envy_i2c_wait(struct envy_softc *);
713c27163dSratchov int envy_i2c_read(struct envy_softc *, int, int);
723c27163dSratchov void envy_i2c_write(struct envy_softc *, int, int, int);
739ad31cc3Sratchov int envy_gpio_getstate(struct envy_softc *);
749ad31cc3Sratchov void envy_gpio_setstate(struct envy_softc *, int);
759ad31cc3Sratchov int envy_gpio_getmask(struct envy_softc *);
769ad31cc3Sratchov void envy_gpio_setmask(struct envy_softc *, int);
779ad31cc3Sratchov int envy_gpio_getdir(struct envy_softc *);
789ad31cc3Sratchov void envy_gpio_setdir(struct envy_softc *, int);
79d3da81d3Sratchov void envy_gpio_i2c_start_bit(struct envy_softc *, int, int);
80d3da81d3Sratchov void envy_gpio_i2c_stop_bit(struct envy_softc *, int, int);
81d3da81d3Sratchov void envy_gpio_i2c_byte_out(struct envy_softc *, int, int, int);
829ad31cc3Sratchov int envy_eeprom_gpioxxx(struct envy_softc *, int);
83c737f4d2Sratchov void envy_midi_wait(struct envy_softc *);
843c27163dSratchov void envy_reset(struct envy_softc *);
856ce34ffeSratchov int envy_codec_read(struct envy_softc *, int, int);
866ce34ffeSratchov void envy_codec_write(struct envy_softc *, int, int, int);
8742ddd7d9Sratchov void envy_pintr(struct envy_softc *);
883c27163dSratchov int envy_intr(void *);
893c27163dSratchov
903c27163dSratchov int envy_lineout_getsrc(struct envy_softc *, int);
913c27163dSratchov void envy_lineout_setsrc(struct envy_softc *, int, int);
923c27163dSratchov int envy_spdout_getsrc(struct envy_softc *, int);
933c27163dSratchov void envy_spdout_setsrc(struct envy_softc *, int, int);
94eb5d355fSratchov void envy_mon_getvol(struct envy_softc *, int, int, int *);
953c27163dSratchov void envy_mon_setvol(struct envy_softc *, int, int, int);
963c27163dSratchov
973c27163dSratchov int envy_open(void *, int);
983c27163dSratchov void envy_close(void *);
993c27163dSratchov void *envy_allocm(void *, int, size_t, int, int);
1003c27163dSratchov void envy_freem(void *, void *, int);
1013c27163dSratchov int envy_set_params(void *, int, int, struct audio_params *,
1023c27163dSratchov struct audio_params *);
1033c27163dSratchov int envy_round_blocksize(void *, int);
1043c27163dSratchov int envy_trigger_output(void *, void *, void *, int,
1053c27163dSratchov void (*)(void *), void *, struct audio_params *);
1063c27163dSratchov int envy_trigger_input(void *, void *, void *, int,
1073c27163dSratchov void (*)(void *), void *, struct audio_params *);
1083c27163dSratchov int envy_halt_output(void *);
1093c27163dSratchov int envy_halt_input(void *);
1103c27163dSratchov int envy_query_devinfo(void *, struct mixer_devinfo *);
1113c27163dSratchov int envy_get_port(void *, struct mixer_ctrl *);
1123c27163dSratchov int envy_set_port(void *, struct mixer_ctrl *);
113c737f4d2Sratchov #if NMIDI > 0
114c737f4d2Sratchov int envy_midi_open(void *, int, void (*)(void *, int),
115c737f4d2Sratchov void (*)(void *), void *);
116c737f4d2Sratchov void envy_midi_close(void *);
117c737f4d2Sratchov int envy_midi_output(void *, int);
118c737f4d2Sratchov void envy_midi_getinfo(void *, struct midi_info *);
119c737f4d2Sratchov #endif
1203c27163dSratchov
1213a87d643Sratchov int envy_ac97_wait(struct envy_softc *);
1223a87d643Sratchov int envy_ac97_attach_codec(void *, struct ac97_codec_if *);
1233a87d643Sratchov int envy_ac97_read_codec(void *, u_int8_t, u_int16_t *);
1243a87d643Sratchov int envy_ac97_write_codec(void *, u_int8_t, u_int16_t);
1253a87d643Sratchov void envy_ac97_reset_codec(void *);
1263a87d643Sratchov enum ac97_host_flags envy_ac97_flags_codec(void *);
1273a87d643Sratchov
1289ad31cc3Sratchov void delta_init(struct envy_softc *);
1296ce34ffeSratchov void delta_codec_write(struct envy_softc *, int, int, int);
1309ad31cc3Sratchov
131e7015270Sratchov void ap192k_init(struct envy_softc *);
132e7015270Sratchov void ap192k_codec_write(struct envy_softc *, int, int, int);
1332b965e79Sratchov void ap192k_set_rate(struct envy_softc *, int);
134e7015270Sratchov
13523d7a21aSstsp void ewx_codec_write(struct envy_softc *, int, int, int);
13623d7a21aSstsp
137d3da81d3Sratchov void revo51_init(struct envy_softc *);
138d3da81d3Sratchov void revo51_codec_write(struct envy_softc *, int, int, int);
139d3da81d3Sratchov
1405f8101f3Sjakemsr void envy_ac97_init(struct envy_softc *);
1415f8101f3Sjakemsr void dynex_sc51_init(struct envy_softc *);
1423a87d643Sratchov
14310a0d23aSratchov void julia_init(struct envy_softc *);
1446ce34ffeSratchov void julia_codec_write(struct envy_softc *, int, int, int);
145fef9958fSratchov void julia_set_rate(struct envy_softc *, int);
14610a0d23aSratchov
1479ad31cc3Sratchov void unkenvy_init(struct envy_softc *);
1486ce34ffeSratchov void unkenvy_codec_write(struct envy_softc *, int, int, int);
1498565898fSratchov int unkenvy_codec_ndev(struct envy_softc *);
1509ad31cc3Sratchov
15181bfbe5bSratchov int ak4524_dac_ndev(struct envy_softc *);
1527399aea9Sratchov void ak4524_dac_devinfo(struct envy_softc *, struct mixer_devinfo *, int);
1537399aea9Sratchov void ak4524_dac_get(struct envy_softc *, struct mixer_ctrl *, int);
1547399aea9Sratchov int ak4524_dac_set(struct envy_softc *, struct mixer_ctrl *, int);
15581bfbe5bSratchov int ak4524_adc_ndev(struct envy_softc *);
1567399aea9Sratchov void ak4524_adc_devinfo(struct envy_softc *, struct mixer_devinfo *, int);
1577399aea9Sratchov void ak4524_adc_get(struct envy_softc *, struct mixer_ctrl *, int);
1587399aea9Sratchov int ak4524_adc_set(struct envy_softc *, struct mixer_ctrl *, int);
1597399aea9Sratchov
16081bff893Sratchov int ak4358_dac_ndev(struct envy_softc *);
16181bff893Sratchov void ak4358_dac_devinfo(struct envy_softc *, struct mixer_devinfo *, int);
16281bff893Sratchov void ak4358_dac_get(struct envy_softc *, struct mixer_ctrl *, int);
16381bff893Sratchov int ak4358_dac_set(struct envy_softc *, struct mixer_ctrl *, int);
164fef9958fSratchov void ak4358_set_rate(struct envy_softc *, int);
16581bff893Sratchov
166d3da81d3Sratchov int ak5365_adc_ndev(struct envy_softc *);
167d3da81d3Sratchov void ak5365_adc_devinfo(struct envy_softc *, struct mixer_devinfo *, int);
168d3da81d3Sratchov void ak5365_adc_get(struct envy_softc *, struct mixer_ctrl *, int);
169d3da81d3Sratchov int ak5365_adc_set(struct envy_softc *, struct mixer_ctrl *, int);
170d3da81d3Sratchov
1718d2c75e4Smpi const struct cfattach envy_ca = {
1722af838c1Sratchov sizeof(struct envy_softc), envymatch, envyattach, envydetach,
1732af838c1Sratchov envyactivate
1743c27163dSratchov };
1753c27163dSratchov
1763c27163dSratchov struct cfdriver envy_cd = {
1773c27163dSratchov NULL, "envy", DV_DULL
1783c27163dSratchov };
1793c27163dSratchov
1800d6a2fdeSmiod const struct audio_hw_if envy_hw_if = {
1815a8d990aSkn .open = envy_open,
1825a8d990aSkn .close = envy_close,
1835a8d990aSkn .set_params = envy_set_params,
1845a8d990aSkn .round_blocksize = envy_round_blocksize,
1855a8d990aSkn .halt_output = envy_halt_output,
1865a8d990aSkn .halt_input = envy_halt_input,
1875a8d990aSkn .set_port = envy_set_port,
1885a8d990aSkn .get_port = envy_get_port,
1895a8d990aSkn .query_devinfo = envy_query_devinfo,
1905a8d990aSkn .allocm = envy_allocm,
1915a8d990aSkn .freem = envy_freem,
1925a8d990aSkn .trigger_output = envy_trigger_output,
1935a8d990aSkn .trigger_input = envy_trigger_input,
1943c27163dSratchov };
1953c27163dSratchov
196c737f4d2Sratchov #if NMIDI > 0
1970d6a2fdeSmiod const struct midi_hw_if envy_midi_hw_if = {
198c737f4d2Sratchov envy_midi_open,
199c737f4d2Sratchov envy_midi_close,
200c737f4d2Sratchov envy_midi_output,
201c737f4d2Sratchov NULL, /* flush */
202c737f4d2Sratchov envy_midi_getinfo,
203c737f4d2Sratchov NULL /* ioctl */
204c737f4d2Sratchov };
205c737f4d2Sratchov #endif
206c737f4d2Sratchov
2079ad31cc3Sratchov struct pci_matchid envy_matchids[] = {
2089ad31cc3Sratchov { PCI_VENDOR_ICENSEMBLE, PCI_PRODUCT_ICENSEMBLE_ICE1712 },
20946564b25Sjsg { PCI_VENDOR_ICENSEMBLE, PCI_PRODUCT_ICENSEMBLE_VT172X }
2109ad31cc3Sratchov };
2119ad31cc3Sratchov
2123c27163dSratchov /*
2133c27163dSratchov * correspondence between rates (in frames per second)
2143c27163dSratchov * and values of rate register
2153c27163dSratchov */
2163c27163dSratchov struct {
2173c27163dSratchov int rate, reg;
2183c27163dSratchov } envy_rates[] = {
2193c27163dSratchov { 8000, 0x6}, { 9600, 0x3}, {11025, 0xa}, {12000, 2}, {16000, 5},
2203c27163dSratchov {22050, 0x9}, {24000, 0x1}, {32000, 0x4}, {44100, 8}, {48000, 0},
221b2611ef7Sratchov {64000, 0xf}, {88200, 0xb}, {96000, 0x7},
222b2611ef7Sratchov {176400, 0xc}, {192000, 0xe},
223b2611ef7Sratchov {-1, -1}
2243c27163dSratchov };
2253c27163dSratchov
2269ad31cc3Sratchov /*
22703f99936Sratchov * ESI Julia cards don't have EEPROM, use this copy
22810a0d23aSratchov */
22910a0d23aSratchov static unsigned char julia_eeprom[ENVY_EEPROM_MAXSZ] = {
23010a0d23aSratchov /* gpio mask/dir/state is from linux */
23110a0d23aSratchov 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
23210a0d23aSratchov 0x20, 0x80, 0xf8, 0xc3,
23310a0d23aSratchov 0x9f, 0xff, 0x7f,
2340a4071e5Sratchov 0x60, 0x00, 0x7f,
2350a4071e5Sratchov 0x0a, 0x00, 0x00
23610a0d23aSratchov };
23710a0d23aSratchov
2387399aea9Sratchov struct envy_codec ak4524_dac = {
23981bfbe5bSratchov "ak4524 dac", ak4524_dac_ndev, ak4524_dac_devinfo, ak4524_dac_get, ak4524_dac_set
2407399aea9Sratchov }, ak4524_adc = {
24181bfbe5bSratchov "ak4524 adc", ak4524_adc_ndev, ak4524_adc_devinfo, ak4524_adc_get, ak4524_adc_set
24281bff893Sratchov }, ak4358_dac = {
24381bff893Sratchov "ak4358 dac", ak4358_dac_ndev, ak4358_dac_devinfo, ak4358_dac_get, ak4358_dac_set
244d3da81d3Sratchov }, ak5365_adc = {
245d3da81d3Sratchov "ak5365 adc", ak5365_adc_ndev, ak5365_adc_devinfo, ak5365_adc_get, ak5365_adc_set
2467399aea9Sratchov }, unkenvy_codec = {
2478565898fSratchov "unknown codec", unkenvy_codec_ndev, NULL, NULL, NULL
2487399aea9Sratchov };
2497399aea9Sratchov
25010a0d23aSratchov /*
2519ad31cc3Sratchov * array with vendor/product sub-IDs to card info
2529ad31cc3Sratchov */
2539ad31cc3Sratchov struct envy_card envy_cards[] = {
2549ad31cc3Sratchov {
25545ebdb29Sratchov PCI_ID_CODE(0x1412, 0xd630),
25645ebdb29Sratchov "M-Audio Delta 1010",
257e836b2ceSratchov 8, &ak4524_adc, 8, &ak4524_dac, 1,
25845ebdb29Sratchov delta_init,
25945ebdb29Sratchov delta_codec_write,
26045ebdb29Sratchov }, {
26145ebdb29Sratchov PCI_ID_CODE(0x1412, 0xd632),
26245ebdb29Sratchov "M-Audio Delta 66",
263e836b2ceSratchov 4, &ak4524_adc, 4, &ak4524_dac, 0,
26445ebdb29Sratchov delta_init,
26545ebdb29Sratchov delta_codec_write,
26645ebdb29Sratchov }, {
267d8adf3a4Sratchov #define ENVY_SUBID_DELTA44 (PCI_ID_CODE(0x1412, 0xd633))
26845ebdb29Sratchov PCI_ID_CODE(0x1412, 0xd633),
26945ebdb29Sratchov "M-Audio Delta 44",
270e836b2ceSratchov 4, &ak4524_adc, 4, &ak4524_dac, 0,
27145ebdb29Sratchov delta_init,
27245ebdb29Sratchov delta_codec_write,
27345ebdb29Sratchov }, {
2749ad31cc3Sratchov PCI_ID_CODE(0x1412, 0xd63b),
27545ebdb29Sratchov "M-Audio Delta 1010LT",
276e836b2ceSratchov 8, &ak4524_adc, 8, &ak4524_dac, 1,
2779ad31cc3Sratchov delta_init,
2786ce34ffeSratchov delta_codec_write,
2799ad31cc3Sratchov }, {
2805b36a676Sratchov PCI_ID_CODE(0x1412, 0xd634),
2815b36a676Sratchov "M-Audio Audiophile 2496",
282e836b2ceSratchov 2, &ak4524_adc, 2, &ak4524_dac, 1,
2835b36a676Sratchov delta_init,
2845b36a676Sratchov delta_codec_write,
2855b36a676Sratchov }, {
28623d7a21aSstsp PCI_ID_CODE(0x153b, 0x1130),
28723d7a21aSstsp "Terratec EWX 24/96",
288e836b2ceSratchov 2, &ak4524_adc, 2, &ak4524_dac, 1,
28923d7a21aSstsp delta_init,
29023d7a21aSstsp ewx_codec_write,
29123d7a21aSstsp }, {
2929ad31cc3Sratchov 0,
29345ebdb29Sratchov "unknown 1712-based card",
294e836b2ceSratchov 8, &unkenvy_codec, 8, &unkenvy_codec, 1,
2959ad31cc3Sratchov unkenvy_init,
2966ce34ffeSratchov unkenvy_codec_write
2979ad31cc3Sratchov }
2987399aea9Sratchov }, envy_cards_ht[] = {
29910a0d23aSratchov {
30010a0d23aSratchov PCI_ID_CODE(0x3031, 0x4553),
30145ebdb29Sratchov "ESI Julia",
302e836b2ceSratchov 2, &unkenvy_codec, 2, &ak4358_dac, 1,
30310a0d23aSratchov julia_init,
3046ce34ffeSratchov julia_codec_write,
305fef9958fSratchov julia_set_rate,
30610a0d23aSratchov julia_eeprom
30710a0d23aSratchov }, {
308e7015270Sratchov PCI_ID_CODE(0x1412, 0x3632),
309e7015270Sratchov "M-Audio Audiophile 192k",
310e836b2ceSratchov 2, &unkenvy_codec, 2, &ak4358_dac, 1,
311e7015270Sratchov ap192k_init,
3122b965e79Sratchov ap192k_codec_write,
3132b965e79Sratchov ap192k_set_rate
314e7015270Sratchov }, {
315d3da81d3Sratchov PCI_ID_CODE(0x1412, 0x3631),
316d3da81d3Sratchov "M-Audio Revolution 5.1",
317e836b2ceSratchov 2, &ak5365_adc, 6, &ak4358_dac, 1,
318d3da81d3Sratchov revo51_init,
319d3da81d3Sratchov revo51_codec_write
320d3da81d3Sratchov }, {
3213a87d643Sratchov PCI_ID_CODE(0x1412, 0x2403),
3223a87d643Sratchov "VIA Tremor 5.1",
323e836b2ceSratchov 2, &unkenvy_codec, 6, &unkenvy_codec, 1,
3245f8101f3Sjakemsr envy_ac97_init,
3255f8101f3Sjakemsr unkenvy_codec_write
3265f8101f3Sjakemsr }, {
3275f8101f3Sjakemsr PCI_ID_CODE(0x14c3, 0x1705),
3285f8101f3Sjakemsr "Dynex DX-SC51",
329e836b2ceSratchov 2, &unkenvy_codec, 6, &unkenvy_codec, 0,
3305f8101f3Sjakemsr dynex_sc51_init,
3313a87d643Sratchov unkenvy_codec_write
3323a87d643Sratchov }, {
33310a0d23aSratchov 0,
33445ebdb29Sratchov "unknown 1724-based card",
335e836b2ceSratchov 2, &unkenvy_codec, 8, &unkenvy_codec, 1,
33610a0d23aSratchov unkenvy_init,
3376ce34ffeSratchov unkenvy_codec_write
33810a0d23aSratchov }
33910a0d23aSratchov };
34010a0d23aSratchov
3419ad31cc3Sratchov
3429ad31cc3Sratchov /*
34303f99936Sratchov * M-Audio Delta specific code
3449ad31cc3Sratchov */
3459ad31cc3Sratchov
3469ad31cc3Sratchov void
delta_init(struct envy_softc * sc)3479ad31cc3Sratchov delta_init(struct envy_softc *sc)
3489ad31cc3Sratchov {
3499ad31cc3Sratchov int dev;
3509ad31cc3Sratchov
35145ebdb29Sratchov for (dev = 0; dev < sc->card->noch / 2; dev++) {
3526ce34ffeSratchov envy_codec_write(sc, dev, AK4524_RST, 0x0);
3539ad31cc3Sratchov delay(300);
3546ce34ffeSratchov envy_codec_write(sc, dev, AK4524_RST,
3556ce34ffeSratchov AK4524_RST_AD | AK4524_RST_DA);
3566ce34ffeSratchov envy_codec_write(sc, dev, AK4524_FMT,
3576ce34ffeSratchov AK4524_FMT_IIS24);
35881bff893Sratchov sc->shadow[dev][AK4524_DEEMVOL] = AK4524_DEEM_OFF;
35981bff893Sratchov sc->shadow[dev][AK4524_ADC_GAIN0] = 0x7f;
36081bff893Sratchov sc->shadow[dev][AK4524_ADC_GAIN1] = 0x7f;
36181bff893Sratchov sc->shadow[dev][AK4524_DAC_GAIN0] = 0x7f;
36281bff893Sratchov sc->shadow[dev][AK4524_DAC_GAIN1] = 0x7f;
3639ad31cc3Sratchov }
3649ad31cc3Sratchov }
3659ad31cc3Sratchov
3669ad31cc3Sratchov void
delta_codec_write(struct envy_softc * sc,int dev,int addr,int data)3676ce34ffeSratchov delta_codec_write(struct envy_softc *sc, int dev, int addr, int data)
3689ad31cc3Sratchov {
3699ad31cc3Sratchov int bits, i, reg;
370d8adf3a4Sratchov int clk, dout, csmask, cs;
371d8adf3a4Sratchov
372d8adf3a4Sratchov /*
373d8adf3a4Sratchov * GPIO pin numbers
374d8adf3a4Sratchov */
375d8adf3a4Sratchov if (sc->card->subid == ENVY_SUBID_DELTA44) {
376d8adf3a4Sratchov clk = 0x20;
377d8adf3a4Sratchov dout = 0x10;
378d8adf3a4Sratchov csmask = 0xc0;
379d8adf3a4Sratchov cs = dev ? 0x40 : 0x80;
380d8adf3a4Sratchov } else {
381d8adf3a4Sratchov clk = 0x2;
382d8adf3a4Sratchov dout = 0x8;
383d8adf3a4Sratchov csmask = 0x70;
384d8adf3a4Sratchov cs = dev << 4;
385d8adf3a4Sratchov }
3869ad31cc3Sratchov
3879ad31cc3Sratchov reg = envy_gpio_getstate(sc);
388d8adf3a4Sratchov reg &= ~csmask;
389d8adf3a4Sratchov reg |= cs;
3909ad31cc3Sratchov envy_gpio_setstate(sc, reg);
3919ad31cc3Sratchov delay(1);
3929ad31cc3Sratchov
3939ad31cc3Sratchov bits = 0xa000 | (addr << 8) | data;
3949ad31cc3Sratchov for (i = 0; i < 16; i++) {
395d8adf3a4Sratchov reg &= ~(clk | dout);
396d8adf3a4Sratchov reg |= (bits & 0x8000) ? dout : 0;
3979ad31cc3Sratchov envy_gpio_setstate(sc, reg);
3989ad31cc3Sratchov delay(1);
3999ad31cc3Sratchov
400d8adf3a4Sratchov reg |= clk;
4019ad31cc3Sratchov envy_gpio_setstate(sc, reg);
4029ad31cc3Sratchov delay(1);
4039ad31cc3Sratchov bits <<= 1;
4049ad31cc3Sratchov }
4059ad31cc3Sratchov
406d8adf3a4Sratchov reg |= csmask;
407e7015270Sratchov envy_gpio_setstate(sc, reg);
408e7015270Sratchov delay(1);
409e7015270Sratchov }
410e7015270Sratchov
411e7015270Sratchov /*
412e7015270Sratchov * M-Audio Audiophile 192 specific code
413e7015270Sratchov */
414e7015270Sratchov
415e7015270Sratchov /*
416e7015270Sratchov * GPIO pin numbers
417e7015270Sratchov */
418e7015270Sratchov #define AP192K_GPIO_CLK 0x2
419e7015270Sratchov #define AP192K_GPIO_DOUT 0x8
42003334a99Sratchov #define AP192K_GPIO_CSMASK 0x30
421e7015270Sratchov #define AP192K_GPIO_CS(dev) ((dev) << 4)
422904a4f57Sratchov
423904a4f57Sratchov #define AP192K_AK5385_CKS0 (1 << 8)
424904a4f57Sratchov #define AP192K_AK5385_DFS0 (1 << 9)
425904a4f57Sratchov #define AP192K_AK5385_DFS1 (1 << 10)
426904a4f57Sratchov #define AP192K_AK5385_PWR (1 << 11)
427904a4f57Sratchov #define AP192K_AK5385_SPD_MASK 0x700
4285d16c64dSratchov
429e7015270Sratchov void
ap192k_init(struct envy_softc * sc)430e7015270Sratchov ap192k_init(struct envy_softc *sc)
431e7015270Sratchov {
43203334a99Sratchov int i, reg;
433e7015270Sratchov
43403334a99Sratchov /* AK4358 */
435e7015270Sratchov envy_codec_write(sc, 0, 0, 0); /* reset */
436e7015270Sratchov delay(300);
437e7015270Sratchov envy_codec_write(sc, 0, 0, 0x87); /* i2s mode */
43803334a99Sratchov delay(1);
439e7015270Sratchov for (i = 0; i < sc->card->noch; i++) {
440e7015270Sratchov sc->shadow[0][AK4358_ATT(i)] = 0xff;
441e7015270Sratchov }
44203334a99Sratchov
4435d16c64dSratchov /* AK5385 */
44403334a99Sratchov delay(1);
44503334a99Sratchov reg = envy_gpio_getstate(sc);
446904a4f57Sratchov reg &= ~(AP192K_AK5385_PWR | AP192K_AK5385_SPD_MASK);
44703334a99Sratchov envy_gpio_setstate(sc, reg);
448904a4f57Sratchov reg |= AP192K_AK5385_PWR;
44903334a99Sratchov envy_gpio_setstate(sc, reg);
450e7015270Sratchov }
451e7015270Sratchov
452e7015270Sratchov void
ap192k_codec_write(struct envy_softc * sc,int dev,int addr,int data)453e7015270Sratchov ap192k_codec_write(struct envy_softc *sc, int dev, int addr, int data)
454e7015270Sratchov {
455e7015270Sratchov int bits, i, reg;
456e7015270Sratchov
457e7015270Sratchov reg = envy_gpio_getstate(sc);
458e7015270Sratchov reg &= ~AP192K_GPIO_CSMASK;
459e7015270Sratchov reg |= AP192K_GPIO_CS(dev);
460e7015270Sratchov envy_gpio_setstate(sc, reg);
461e7015270Sratchov delay(1);
462e7015270Sratchov
463e7015270Sratchov bits = 0xa000 | (addr << 8) | data;
464e7015270Sratchov for (i = 0; i < 16; i++) {
465e7015270Sratchov reg &= ~(AP192K_GPIO_CLK | AP192K_GPIO_DOUT);
466e7015270Sratchov reg |= (bits & 0x8000) ? AP192K_GPIO_DOUT : 0;
467e7015270Sratchov envy_gpio_setstate(sc, reg);
468e7015270Sratchov delay(1);
469e7015270Sratchov
470e7015270Sratchov reg |= AP192K_GPIO_CLK;
471e7015270Sratchov envy_gpio_setstate(sc, reg);
472e7015270Sratchov delay(1);
473e7015270Sratchov bits <<= 1;
474e7015270Sratchov }
475e7015270Sratchov
476e7015270Sratchov reg |= AP192K_GPIO_CSMASK;
4779ad31cc3Sratchov envy_gpio_setstate(sc, reg);
4789ad31cc3Sratchov delay(1);
4799ad31cc3Sratchov }
4809ad31cc3Sratchov
4812b965e79Sratchov void
ap192k_set_rate(struct envy_softc * sc,int rate)4822b965e79Sratchov ap192k_set_rate(struct envy_softc *sc, int rate)
4832b965e79Sratchov {
4842b965e79Sratchov int reg;
4852b965e79Sratchov
4862b965e79Sratchov /* set AK5385 clock params */
4872b965e79Sratchov reg = envy_gpio_getstate(sc) & ~(AP192K_AK5385_SPD_MASK);
4882b965e79Sratchov if (rate > 96000)
4892b965e79Sratchov reg |= AP192K_AK5385_CKS0 | AP192K_AK5385_DFS1;
4902b965e79Sratchov else if (rate > 48000)
4912b965e79Sratchov reg |= AP192K_AK5385_DFS0;
4922b965e79Sratchov envy_gpio_setstate(sc, reg);
4932b965e79Sratchov
4942b965e79Sratchov ak4358_set_rate(sc, rate);
4952b965e79Sratchov }
4962b965e79Sratchov
497d3da81d3Sratchov /*
49823d7a21aSstsp * Terratec EWX specific code
49923d7a21aSstsp */
50023d7a21aSstsp
50123d7a21aSstsp /*
50223d7a21aSstsp * GPIO pin numbers
50323d7a21aSstsp */
50423d7a21aSstsp #define EWX_GPIO_CSMASK 0x01
50523d7a21aSstsp #define EWX_GPIO_DOUT 0x10
50623d7a21aSstsp #define EWX_GPIO_CLK 0x20
50723d7a21aSstsp
50823d7a21aSstsp void
ewx_codec_write(struct envy_softc * sc,int dev,int addr,int data)50923d7a21aSstsp ewx_codec_write(struct envy_softc *sc, int dev, int addr, int data)
51023d7a21aSstsp {
51123d7a21aSstsp int bits, i, reg;
51223d7a21aSstsp
51323d7a21aSstsp reg = envy_gpio_getstate(sc);
51423d7a21aSstsp reg |= (EWX_GPIO_CSMASK | EWX_GPIO_CLK);
51523d7a21aSstsp envy_gpio_setstate(sc, reg);
51623d7a21aSstsp delay(1);
51723d7a21aSstsp
51823d7a21aSstsp bits = 0xa000 | (addr << 8) | data;
51923d7a21aSstsp for (i = 0; i < 16; i++) {
52023d7a21aSstsp reg &= ~(EWX_GPIO_CLK | EWX_GPIO_DOUT);
52123d7a21aSstsp reg |= (bits & 0x8000) ? EWX_GPIO_DOUT : 0;
52223d7a21aSstsp envy_gpio_setstate(sc, reg);
52323d7a21aSstsp delay(1);
52423d7a21aSstsp
52523d7a21aSstsp reg |= EWX_GPIO_CLK;
52623d7a21aSstsp envy_gpio_setstate(sc, reg);
52723d7a21aSstsp delay(1);
52823d7a21aSstsp bits <<= 1;
52923d7a21aSstsp }
53023d7a21aSstsp
53123d7a21aSstsp reg &= ~EWX_GPIO_CSMASK;
53223d7a21aSstsp envy_gpio_setstate(sc, reg);
53323d7a21aSstsp delay(1);
53423d7a21aSstsp
53523d7a21aSstsp reg |= EWX_GPIO_CSMASK;
53623d7a21aSstsp envy_gpio_setstate(sc, reg);
53723d7a21aSstsp delay(1);
53823d7a21aSstsp }
53923d7a21aSstsp
54023d7a21aSstsp
54123d7a21aSstsp /*
54203f99936Sratchov * M-Audio Revolution 5.1 specific code
543d3da81d3Sratchov */
544d3da81d3Sratchov
545e7015270Sratchov #define REVO51_GPIO_CLK 0x2
546e7015270Sratchov #define REVO51_GPIO_DOUT 0x8
547d3da81d3Sratchov #define REVO51_GPIO_CSMASK 0x30
548d3da81d3Sratchov #define REVO51_GPIO_CS(dev) ((dev) ? 0x10 : 0x20)
549d3da81d3Sratchov #define REVO51_MUTE 0x400000
550d3da81d3Sratchov #define REVO51_PT2258S_SDA 0x40
551d3da81d3Sratchov #define REVO51_PT2258S_SCL 0x80
552d3da81d3Sratchov #define REVO51_PT2258S_ADDR 0x80
553d3da81d3Sratchov #define REVO51_PT2258S_MUTE 6
554d3da81d3Sratchov
555d3da81d3Sratchov void
revo51_init(struct envy_softc * sc)556d3da81d3Sratchov revo51_init(struct envy_softc *sc)
557d3da81d3Sratchov {
558d3da81d3Sratchov int i, reg;
559d3da81d3Sratchov
560d3da81d3Sratchov /* AK4358 */
561d3da81d3Sratchov envy_codec_write(sc, 0, 0, 0); /* reset */
562d3da81d3Sratchov delay(300);
563d3da81d3Sratchov envy_codec_write(sc, 0, 0, 0x87); /* i2s mode */
564d3da81d3Sratchov for (i = 0; i < sc->card->noch; i++) {
565d3da81d3Sratchov sc->shadow[0][AK4358_ATT(i)] = 0xff;
566d3da81d3Sratchov }
567d3da81d3Sratchov
568d3da81d3Sratchov /* AK5365 */
569d3da81d3Sratchov envy_codec_write(sc, 1, AK5365_RST, 0); /* reset */
570d3da81d3Sratchov delay(300);
571d3da81d3Sratchov envy_codec_write(sc, 1, AK5365_CTRL, AK5365_CTRL_I2S); /* i2s mode */
572d3da81d3Sratchov envy_codec_write(sc, 1, AK5365_RST , AK5365_RST_NORM);
573d3da81d3Sratchov sc->shadow[1][AK5365_ATT(0)] = 0x7f;
574d3da81d3Sratchov sc->shadow[1][AK5365_ATT(1)] = 0x7f;
575d3da81d3Sratchov
576d3da81d3Sratchov /* PT2258S */
577d3da81d3Sratchov envy_codec_write(sc, 2, REVO51_PT2258S_MUTE, 0xc0); /* reset */
578d3da81d3Sratchov envy_codec_write(sc, 2, REVO51_PT2258S_MUTE, 0xf9); /* mute */
579d3da81d3Sratchov
580d3da81d3Sratchov reg = envy_gpio_getstate(sc);
581d3da81d3Sratchov reg |= REVO51_MUTE;
582d3da81d3Sratchov envy_gpio_setstate(sc, reg);
583d3da81d3Sratchov }
584d3da81d3Sratchov
585d3da81d3Sratchov void
revo51_codec_write(struct envy_softc * sc,int dev,int addr,int data)586d3da81d3Sratchov revo51_codec_write(struct envy_softc *sc, int dev, int addr, int data)
587d3da81d3Sratchov {
588d3da81d3Sratchov int attn, bits, mask, reg;
589d3da81d3Sratchov int xlat[6] = {0x90, 0x50, 0x10, 0x30, 0x70, 0xb0};
590d3da81d3Sratchov
591d3da81d3Sratchov /* AK4358 & AK5365 */
592d3da81d3Sratchov if (dev < 2) {
593d3da81d3Sratchov reg = envy_gpio_getstate(sc);
594d3da81d3Sratchov reg &= ~REVO51_GPIO_CSMASK;
595d3da81d3Sratchov reg |= REVO51_GPIO_CS(dev);
596d3da81d3Sratchov envy_gpio_setstate(sc, reg);
597d3da81d3Sratchov delay(1);
598d3da81d3Sratchov
599d3da81d3Sratchov bits = 0xa000 | (addr << 8) | data;
600d3da81d3Sratchov for (mask = 0x8000; mask != 0; mask >>= 1) {
601e7015270Sratchov reg &= ~(REVO51_GPIO_CLK | REVO51_GPIO_DOUT);
602e7015270Sratchov reg |= (bits & mask) ? REVO51_GPIO_DOUT : 0;
603d3da81d3Sratchov envy_gpio_setstate(sc, reg);
604d3da81d3Sratchov delay(1);
605d3da81d3Sratchov
606e7015270Sratchov reg |= REVO51_GPIO_CLK;
607d3da81d3Sratchov envy_gpio_setstate(sc, reg);
608d3da81d3Sratchov delay(1);
609d3da81d3Sratchov }
610d3da81d3Sratchov
611d3da81d3Sratchov reg |= REVO51_GPIO_CSMASK;
612d3da81d3Sratchov envy_gpio_setstate(sc, reg);
613d3da81d3Sratchov delay(1);
614d3da81d3Sratchov return;
615d3da81d3Sratchov }
616d3da81d3Sratchov
617d3da81d3Sratchov /* PT2258S */
618d3da81d3Sratchov envy_gpio_i2c_start_bit(sc, REVO51_PT2258S_SDA, REVO51_PT2258S_SCL);
619d3da81d3Sratchov envy_gpio_i2c_byte_out(sc, REVO51_PT2258S_SDA, REVO51_PT2258S_SCL,
620d3da81d3Sratchov REVO51_PT2258S_ADDR);
621d3da81d3Sratchov
622d3da81d3Sratchov if (addr == REVO51_PT2258S_MUTE) {
623d3da81d3Sratchov envy_gpio_i2c_byte_out(sc, REVO51_PT2258S_SDA,
624d3da81d3Sratchov REVO51_PT2258S_SCL, data);
625d3da81d3Sratchov } else {
626d3da81d3Sratchov /* 1's digit */
627d3da81d3Sratchov attn = data % 10;
628d3da81d3Sratchov attn += xlat[addr];
629d3da81d3Sratchov envy_gpio_i2c_byte_out(sc, REVO51_PT2258S_SDA,
630d3da81d3Sratchov REVO51_PT2258S_SCL, attn);
631d3da81d3Sratchov
632d3da81d3Sratchov /* 10's digit */
633d3da81d3Sratchov attn = data / 10;
634d3da81d3Sratchov attn += xlat[addr] - 0x10;
635d3da81d3Sratchov envy_gpio_i2c_byte_out(sc, REVO51_PT2258S_SDA,
636d3da81d3Sratchov REVO51_PT2258S_SCL, attn);
637d3da81d3Sratchov }
638d3da81d3Sratchov
639d3da81d3Sratchov envy_gpio_i2c_stop_bit(sc, REVO51_PT2258S_SDA, REVO51_PT2258S_SCL);
640d3da81d3Sratchov }
64110a0d23aSratchov
64210a0d23aSratchov /*
6435f8101f3Sjakemsr * Generic AC'97 initialization
6443a87d643Sratchov */
6453a87d643Sratchov
6463a87d643Sratchov void
envy_ac97_init(struct envy_softc * sc)6475f8101f3Sjakemsr envy_ac97_init(struct envy_softc *sc)
6483a87d643Sratchov {
6493a87d643Sratchov sc->isac97 = 1;
6503a87d643Sratchov sc->host_if.arg = sc;
6513a87d643Sratchov sc->host_if.attach = envy_ac97_attach_codec;
6523a87d643Sratchov sc->host_if.read = envy_ac97_read_codec;
6533a87d643Sratchov sc->host_if.write = envy_ac97_write_codec;
6543a87d643Sratchov sc->host_if.reset = envy_ac97_reset_codec;
6553a87d643Sratchov sc->host_if.flags = envy_ac97_flags_codec;
6563a87d643Sratchov
6573a87d643Sratchov if (ac97_attach(&sc->host_if) != 0)
6583a87d643Sratchov printf("%s: can't attach ac97\n", DEVNAME(sc));
6593a87d643Sratchov }
6603a87d643Sratchov
6613a87d643Sratchov /*
6625f8101f3Sjakemsr * Dynex
6635f8101f3Sjakemsr */
6645f8101f3Sjakemsr
6655f8101f3Sjakemsr void
dynex_sc51_init(struct envy_softc * sc)6665f8101f3Sjakemsr dynex_sc51_init(struct envy_softc *sc)
6675f8101f3Sjakemsr {
6685f8101f3Sjakemsr sc->codec_flags |= AC97_HOST_VT1616_DYNEX;
6695f8101f3Sjakemsr envy_ac97_init(sc);
6705f8101f3Sjakemsr }
6715f8101f3Sjakemsr
6725f8101f3Sjakemsr /*
67303f99936Sratchov * ESI Julia specific code
67410a0d23aSratchov */
67510a0d23aSratchov
676fef9958fSratchov #define JULIA_AK5385_CKS0 (1 << 8)
677fef9958fSratchov #define JULIA_AK5385_DFS1 (1 << 9)
678fef9958fSratchov #define JULIA_AK5385_DFS0 (1 << 10)
679fef9958fSratchov #define JULIA_AK5385_CKS1 (1 << 14)
680fef9958fSratchov #define JULIA_AK5385_MASK 0x4700
681fef9958fSratchov
68210a0d23aSratchov void
julia_init(struct envy_softc * sc)68310a0d23aSratchov julia_init(struct envy_softc *sc)
68410a0d23aSratchov {
68581bff893Sratchov int i;
68681bff893Sratchov
6876ce34ffeSratchov envy_codec_write(sc, 0, 0, 0); /* reset */
68810a0d23aSratchov delay(300);
6896ce34ffeSratchov envy_codec_write(sc, 0, 0, 0x87); /* i2s mode */
69081bff893Sratchov for (i = 0; i < sc->card->noch; i++) {
69181bff893Sratchov sc->shadow[0][AK4358_ATT(i)] = 0xff;
69281bff893Sratchov }
69310a0d23aSratchov }
69410a0d23aSratchov
69510a0d23aSratchov void
julia_codec_write(struct envy_softc * sc,int dev,int addr,int data)6966ce34ffeSratchov julia_codec_write(struct envy_softc *sc, int dev, int addr, int data)
69710a0d23aSratchov {
69810a0d23aSratchov #define JULIA_AK4358_ADDR 0x11
69910a0d23aSratchov envy_i2c_write(sc, JULIA_AK4358_ADDR, addr, data);
70010a0d23aSratchov }
70110a0d23aSratchov
702fef9958fSratchov void
julia_set_rate(struct envy_softc * sc,int rate)703fef9958fSratchov julia_set_rate(struct envy_softc *sc, int rate)
704fef9958fSratchov {
705fef9958fSratchov int reg;
706fef9958fSratchov
707fef9958fSratchov /* set AK5385 clock params */
708fef9958fSratchov reg = envy_gpio_getstate(sc) & ~(JULIA_AK5385_MASK);
709fef9958fSratchov if (rate > 96000)
710fef9958fSratchov reg |= JULIA_AK5385_CKS0 | JULIA_AK5385_DFS1;
711fef9958fSratchov else if (rate > 48000)
712fef9958fSratchov reg |= JULIA_AK5385_DFS0;
713fef9958fSratchov envy_gpio_setstate(sc, reg);
714fef9958fSratchov
715fef9958fSratchov ak4358_set_rate(sc, rate);
716fef9958fSratchov }
717fef9958fSratchov
7189ad31cc3Sratchov /*
7199ad31cc3Sratchov * unknown card, ignore codecs setup and hope it works with the power on
7209ad31cc3Sratchov * settings
7219ad31cc3Sratchov */
7229ad31cc3Sratchov
7239ad31cc3Sratchov void
unkenvy_init(struct envy_softc * sc)7249ad31cc3Sratchov unkenvy_init(struct envy_softc *sc)
7259ad31cc3Sratchov {
7269ad31cc3Sratchov }
7279ad31cc3Sratchov
7289ad31cc3Sratchov void
unkenvy_codec_write(struct envy_softc * sc,int dev,int addr,int data)7296ce34ffeSratchov unkenvy_codec_write(struct envy_softc *sc, int dev, int addr, int data)
7309ad31cc3Sratchov {
7319ad31cc3Sratchov }
7329ad31cc3Sratchov
7338565898fSratchov int
unkenvy_codec_ndev(struct envy_softc * sc)7348565898fSratchov unkenvy_codec_ndev(struct envy_softc *sc)
7358565898fSratchov {
7368565898fSratchov return 0;
7378565898fSratchov }
7388565898fSratchov
7399ad31cc3Sratchov /*
74081bff893Sratchov * AK 4358 DAC specific code
74181bff893Sratchov */
74281bff893Sratchov int
ak4358_dac_ndev(struct envy_softc * sc)74381bff893Sratchov ak4358_dac_ndev(struct envy_softc *sc)
74481bff893Sratchov {
74581bff893Sratchov /* 1 volume knob per channel */
74681bff893Sratchov return sc->card->noch;
74781bff893Sratchov }
74881bff893Sratchov
74981bff893Sratchov void
ak4358_dac_devinfo(struct envy_softc * sc,struct mixer_devinfo * dev,int idx)75081bff893Sratchov ak4358_dac_devinfo(struct envy_softc *sc, struct mixer_devinfo *dev, int idx)
75181bff893Sratchov {
75281bff893Sratchov dev->type = AUDIO_MIXER_VALUE;
75381bff893Sratchov dev->mixer_class = ENVY_MIX_CLASSOUT;
75481bff893Sratchov dev->un.v.delta = 2;
75581bff893Sratchov dev->un.v.num_channels = 1;
75681bff893Sratchov snprintf(dev->label.name, MAX_AUDIO_DEV_LEN,
757fbdda81eSratchov AudioNline "-%d", idx);
75881bff893Sratchov strlcpy(dev->un.v.units.name, AudioNvolume,
75981bff893Sratchov MAX_AUDIO_DEV_LEN);
76081bff893Sratchov }
76181bff893Sratchov
76281bff893Sratchov void
ak4358_dac_get(struct envy_softc * sc,struct mixer_ctrl * ctl,int idx)76381bff893Sratchov ak4358_dac_get(struct envy_softc *sc, struct mixer_ctrl *ctl, int idx)
76481bff893Sratchov {
76581bff893Sratchov int val;
76681bff893Sratchov
76781bff893Sratchov val = envy_codec_read(sc, 0, AK4358_ATT(idx)) & ~AK4358_ATT_EN;
76881bff893Sratchov ctl->un.value.num_channels = 1;
76981bff893Sratchov ctl->un.value.level[0] = 2 * val;
77081bff893Sratchov }
77181bff893Sratchov
77281bff893Sratchov int
ak4358_dac_set(struct envy_softc * sc,struct mixer_ctrl * ctl,int idx)77381bff893Sratchov ak4358_dac_set(struct envy_softc *sc, struct mixer_ctrl *ctl, int idx)
77481bff893Sratchov {
77581bff893Sratchov int val;
77681bff893Sratchov
77781bff893Sratchov if (ctl->un.value.num_channels != 1)
77881bff893Sratchov return EINVAL;
77981bff893Sratchov val = ctl->un.value.level[0] / 2;
78081bff893Sratchov envy_codec_write(sc, 0, AK4358_ATT(idx), val | AK4358_ATT_EN);
78181bff893Sratchov return 0;
78281bff893Sratchov }
78381bff893Sratchov
784fef9958fSratchov void
ak4358_set_rate(struct envy_softc * sc,int rate)785fef9958fSratchov ak4358_set_rate(struct envy_softc *sc, int rate)
786fef9958fSratchov {
787fef9958fSratchov int reg;
788fef9958fSratchov
789fef9958fSratchov reg = AK4358_SPEED_DEFAULT & ~(AK4358_SPEED_DFS0 | AK4358_SPEED_DFS1);
790fef9958fSratchov if (rate > 96000)
791fef9958fSratchov reg |= AK4358_SPEED_DFS1;
792fef9958fSratchov else if (rate > 48000)
793fef9958fSratchov reg |= AK4358_SPEED_DFS0;
794fef9958fSratchov
795fef9958fSratchov /* put in reset state */
796fef9958fSratchov reg &= ~AK4358_SPEED_RSTN;
797fef9958fSratchov envy_codec_write(sc, 0, AK4358_SPEED, reg);
798fef9958fSratchov
799fef9958fSratchov /* back in normal state */
800fef9958fSratchov reg |= AK4358_SPEED_RSTN;
801fef9958fSratchov envy_codec_write(sc, 0, AK4358_SPEED, reg);
802fef9958fSratchov }
803fef9958fSratchov
80481bff893Sratchov /*
8057399aea9Sratchov * AK 4524 DAC specific code
8067399aea9Sratchov */
80781bfbe5bSratchov int
ak4524_dac_ndev(struct envy_softc * sc)80881bfbe5bSratchov ak4524_dac_ndev(struct envy_softc *sc)
80981bfbe5bSratchov {
81081bfbe5bSratchov /* 1 mute + 2 volume knobs per channel pair */
81181bfbe5bSratchov return 3 * (sc->card->noch / 2);
81281bfbe5bSratchov }
81381bfbe5bSratchov
8147399aea9Sratchov void
ak4524_dac_devinfo(struct envy_softc * sc,struct mixer_devinfo * dev,int idx)8157399aea9Sratchov ak4524_dac_devinfo(struct envy_softc *sc, struct mixer_devinfo *dev, int idx)
8167399aea9Sratchov {
8177399aea9Sratchov int ndev;
8187399aea9Sratchov
81981bfbe5bSratchov ndev = sc->card->noch;
8207399aea9Sratchov if (idx < ndev) {
8217399aea9Sratchov dev->type = AUDIO_MIXER_VALUE;
8227399aea9Sratchov dev->mixer_class = ENVY_MIX_CLASSOUT;
8237399aea9Sratchov dev->un.v.delta = 2;
8247399aea9Sratchov dev->un.v.num_channels = 1;
8257399aea9Sratchov snprintf(dev->label.name, MAX_AUDIO_DEV_LEN,
826fbdda81eSratchov AudioNline "-%d", idx);
8277399aea9Sratchov strlcpy(dev->un.v.units.name, AudioNvolume,
8287399aea9Sratchov MAX_AUDIO_DEV_LEN);
8297399aea9Sratchov } else {
8307399aea9Sratchov idx -= ndev;
8317399aea9Sratchov dev->type = AUDIO_MIXER_ENUM;
8327399aea9Sratchov dev->mixer_class = ENVY_MIX_CLASSOUT;
8337399aea9Sratchov dev->un.e.member[0].ord = 0;
8347399aea9Sratchov strlcpy(dev->un.e.member[0].label.name, AudioNoff,
8357399aea9Sratchov MAX_AUDIO_DEV_LEN);
8367399aea9Sratchov dev->un.e.member[1].ord = 1;
8377399aea9Sratchov strlcpy(dev->un.e.member[1].label.name, AudioNon,
8387399aea9Sratchov MAX_AUDIO_DEV_LEN);
83942346b82Sratchov dev->un.e.num_mem = 2;
8407399aea9Sratchov snprintf(dev->label.name, MAX_AUDIO_DEV_LEN,
841fbdda81eSratchov AudioNline "-%d:%d_" AudioNmute, 2 * idx, 2 * idx + 1);
8427399aea9Sratchov }
8437399aea9Sratchov }
8447399aea9Sratchov
8457399aea9Sratchov void
ak4524_dac_get(struct envy_softc * sc,struct mixer_ctrl * ctl,int idx)8467399aea9Sratchov ak4524_dac_get(struct envy_softc *sc, struct mixer_ctrl *ctl, int idx)
8477399aea9Sratchov {
8487399aea9Sratchov int val, ndev;
8497399aea9Sratchov
85081bfbe5bSratchov ndev = sc->card->noch;
8517399aea9Sratchov if (idx < ndev) {
8526ce34ffeSratchov val = envy_codec_read(sc, idx / 2,
8536ce34ffeSratchov (idx % 2) + AK4524_DAC_GAIN0);
8547399aea9Sratchov ctl->un.value.num_channels = 1;
8557399aea9Sratchov ctl->un.value.level[0] = 2 * val;
8567399aea9Sratchov } else {
8577399aea9Sratchov idx -= ndev;
8586ce34ffeSratchov val = envy_codec_read(sc, idx, AK4524_DEEMVOL);
8596ce34ffeSratchov ctl->un.ord = (val & AK4524_MUTE) ? 1 : 0;
8607399aea9Sratchov }
8617399aea9Sratchov }
8627399aea9Sratchov
8637399aea9Sratchov int
ak4524_dac_set(struct envy_softc * sc,struct mixer_ctrl * ctl,int idx)8647399aea9Sratchov ak4524_dac_set(struct envy_softc *sc, struct mixer_ctrl *ctl, int idx)
8657399aea9Sratchov {
8667399aea9Sratchov int val, ndev;
8677399aea9Sratchov
86881bfbe5bSratchov ndev = sc->card->noch;
8697399aea9Sratchov if (idx < ndev) {
8707399aea9Sratchov if (ctl->un.value.num_channels != 1)
8717399aea9Sratchov return EINVAL;
8727399aea9Sratchov val = ctl->un.value.level[0] / 2;
8736ce34ffeSratchov envy_codec_write(sc, idx / 2,
8746ce34ffeSratchov (idx % 2) + AK4524_DAC_GAIN0, val);
8757399aea9Sratchov } else {
8767399aea9Sratchov idx -= ndev;
8777399aea9Sratchov if (ctl->un.ord >= 2)
8787399aea9Sratchov return EINVAL;
8796ce34ffeSratchov val = AK4524_DEEM_OFF | (ctl->un.ord ? AK4524_MUTE : 0);
8806ce34ffeSratchov envy_codec_write(sc, idx, AK4524_DEEMVOL, val);
8817399aea9Sratchov }
8827399aea9Sratchov return 0;
8837399aea9Sratchov }
8847399aea9Sratchov
8857399aea9Sratchov /*
8867399aea9Sratchov * AK 4524 ADC specific code
8877399aea9Sratchov */
88881bfbe5bSratchov int
ak4524_adc_ndev(struct envy_softc * sc)88981bfbe5bSratchov ak4524_adc_ndev(struct envy_softc *sc)
89081bfbe5bSratchov {
89181bfbe5bSratchov /* one volume per channel */
89281bfbe5bSratchov return sc->card->nich;
89381bfbe5bSratchov }
89481bfbe5bSratchov
8957399aea9Sratchov void
ak4524_adc_devinfo(struct envy_softc * sc,struct mixer_devinfo * dev,int idx)8967399aea9Sratchov ak4524_adc_devinfo(struct envy_softc *sc, struct mixer_devinfo *dev, int idx)
8977399aea9Sratchov {
8987399aea9Sratchov dev->type = AUDIO_MIXER_VALUE;
8997399aea9Sratchov dev->mixer_class = ENVY_MIX_CLASSIN;
9007399aea9Sratchov dev->un.v.delta = 2;
9017399aea9Sratchov dev->un.v.num_channels = 1;
902fbdda81eSratchov snprintf(dev->label.name, MAX_AUDIO_DEV_LEN, AudioNline "-%d", idx);
9037399aea9Sratchov strlcpy(dev->un.v.units.name, AudioNvolume, MAX_AUDIO_DEV_LEN);
9047399aea9Sratchov }
9057399aea9Sratchov
9067399aea9Sratchov void
ak4524_adc_get(struct envy_softc * sc,struct mixer_ctrl * ctl,int idx)9077399aea9Sratchov ak4524_adc_get(struct envy_softc *sc, struct mixer_ctrl *ctl, int idx)
9087399aea9Sratchov {
9097399aea9Sratchov int val;
9107399aea9Sratchov
9116ce34ffeSratchov val = envy_codec_read(sc, idx / 2, (idx % 2) + AK4524_ADC_GAIN0);
9127399aea9Sratchov ctl->un.value.num_channels = 1;
9137399aea9Sratchov ctl->un.value.level[0] = 2 * val;
9147399aea9Sratchov }
9157399aea9Sratchov
9167399aea9Sratchov int
ak4524_adc_set(struct envy_softc * sc,struct mixer_ctrl * ctl,int idx)9177399aea9Sratchov ak4524_adc_set(struct envy_softc *sc, struct mixer_ctrl *ctl, int idx)
9187399aea9Sratchov {
9197399aea9Sratchov int val;
9207399aea9Sratchov
9217399aea9Sratchov if (ctl->un.value.num_channels != 1)
9227399aea9Sratchov return EINVAL;
9237399aea9Sratchov val = ctl->un.value.level[0] / 2;
9246ce34ffeSratchov envy_codec_write(sc, idx / 2, (idx % 2) + AK4524_ADC_GAIN0, val);
9257399aea9Sratchov return 0;
9267399aea9Sratchov }
9277399aea9Sratchov
9287399aea9Sratchov /*
929d3da81d3Sratchov * AK 5365 ADC specific code
930d3da81d3Sratchov */
931d3da81d3Sratchov int
ak5365_adc_ndev(struct envy_softc * sc)932d3da81d3Sratchov ak5365_adc_ndev(struct envy_softc *sc)
933d3da81d3Sratchov {
934d3da81d3Sratchov /* 1 source + 2 volume knobs per channel pair */
935d3da81d3Sratchov return (sc->card->nich + 1);
936d3da81d3Sratchov }
937d3da81d3Sratchov
938d3da81d3Sratchov void
ak5365_adc_devinfo(struct envy_softc * sc,struct mixer_devinfo * dev,int idx)939d3da81d3Sratchov ak5365_adc_devinfo(struct envy_softc *sc, struct mixer_devinfo *dev, int idx)
940d3da81d3Sratchov {
941d3da81d3Sratchov int ndev, i;
942d3da81d3Sratchov
943d3da81d3Sratchov ndev = sc->card->nich;
944d3da81d3Sratchov if (idx < ndev) {
945d3da81d3Sratchov dev->type = AUDIO_MIXER_VALUE;
946d3da81d3Sratchov dev->mixer_class = ENVY_MIX_CLASSIN;
947d3da81d3Sratchov dev->un.v.delta = 2;
948d3da81d3Sratchov dev->un.v.num_channels = 1;
949d3da81d3Sratchov snprintf(dev->label.name, MAX_AUDIO_DEV_LEN,
950fbdda81eSratchov AudioNline "-%d", idx);
951d3da81d3Sratchov strlcpy(dev->un.v.units.name, AudioNvolume,
952d3da81d3Sratchov MAX_AUDIO_DEV_LEN);
953d3da81d3Sratchov } else {
954d3da81d3Sratchov dev->type = AUDIO_MIXER_ENUM;
955d3da81d3Sratchov dev->mixer_class = ENVY_MIX_CLASSIN;
956d3da81d3Sratchov for (i = 0; i < 5; i++) {
957d3da81d3Sratchov dev->un.e.member[i].ord = i;
958d3da81d3Sratchov snprintf(dev->un.e.member[i].label.name,
959fbdda81eSratchov MAX_AUDIO_DEV_LEN, AudioNline "-%d", i);
960d3da81d3Sratchov }
961d3da81d3Sratchov dev->un.e.num_mem = 5;
962d3da81d3Sratchov strlcpy(dev->label.name, AudioNsource,
963d3da81d3Sratchov MAX_AUDIO_DEV_LEN);
964d3da81d3Sratchov }
965d3da81d3Sratchov }
966d3da81d3Sratchov
967d3da81d3Sratchov void
ak5365_adc_get(struct envy_softc * sc,struct mixer_ctrl * ctl,int idx)968d3da81d3Sratchov ak5365_adc_get(struct envy_softc *sc, struct mixer_ctrl *ctl, int idx)
969d3da81d3Sratchov {
970d3da81d3Sratchov int val, ndev;
971d3da81d3Sratchov
972d3da81d3Sratchov ndev = sc->card->nich;
973d3da81d3Sratchov if (idx < ndev) {
974d3da81d3Sratchov val = envy_codec_read(sc, 1, AK5365_ATT(idx));
975d3da81d3Sratchov ctl->un.value.num_channels = 1;
976d3da81d3Sratchov ctl->un.value.level[0] = 2 * val;
977d3da81d3Sratchov } else {
978d3da81d3Sratchov ctl->un.ord = envy_codec_read(sc, 1, AK5365_SRC);
979d3da81d3Sratchov }
980d3da81d3Sratchov }
981d3da81d3Sratchov
982d3da81d3Sratchov int
ak5365_adc_set(struct envy_softc * sc,struct mixer_ctrl * ctl,int idx)983d3da81d3Sratchov ak5365_adc_set(struct envy_softc *sc, struct mixer_ctrl *ctl, int idx)
984d3da81d3Sratchov {
985d3da81d3Sratchov int val, ndev;
986d3da81d3Sratchov
987d3da81d3Sratchov ndev = sc->card->nich;
988d3da81d3Sratchov if (idx < ndev) {
989d3da81d3Sratchov if (ctl->un.value.num_channels != 1)
990d3da81d3Sratchov return EINVAL;
991d3da81d3Sratchov val = ctl->un.value.level[0] / 2;
992d3da81d3Sratchov envy_codec_write(sc, 1, AK5365_ATT(idx), val);
993d3da81d3Sratchov } else {
994d3da81d3Sratchov if (ctl->un.ord >= 5)
995d3da81d3Sratchov return EINVAL;
996d3da81d3Sratchov val = ctl->un.ord & AK5365_SRC_MASK;
997d3da81d3Sratchov envy_codec_write(sc, 1, AK5365_SRC, val);
998d3da81d3Sratchov }
999d3da81d3Sratchov return 0;
1000d3da81d3Sratchov }
1001d3da81d3Sratchov
1002d3da81d3Sratchov /*
10039ad31cc3Sratchov * generic Envy24 and Envy24HT code, common to all cards
10049ad31cc3Sratchov */
10059ad31cc3Sratchov
10063c27163dSratchov int
envy_ccs_read(struct envy_softc * sc,int reg)10073c27163dSratchov envy_ccs_read(struct envy_softc *sc, int reg)
10083c27163dSratchov {
1009dba4939bSratchov int val;
1010dba4939bSratchov
1011dba4939bSratchov val = bus_space_read_1(sc->ccs_iot, sc->ccs_ioh, reg);
1012dba4939bSratchov bus_space_barrier(sc->ccs_iot, sc->ccs_ioh, 0, sc->ccs_iosz,
1013dba4939bSratchov BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
1014dba4939bSratchov return val;
10153c27163dSratchov }
10163c27163dSratchov
10173c27163dSratchov void
envy_ccs_write(struct envy_softc * sc,int reg,int val)10183c27163dSratchov envy_ccs_write(struct envy_softc *sc, int reg, int val)
10193c27163dSratchov {
10203c27163dSratchov bus_space_write_1(sc->ccs_iot, sc->ccs_ioh, reg, val);
1021dba4939bSratchov bus_space_barrier(sc->ccs_iot, sc->ccs_ioh, 0, sc->ccs_iosz,
1022dba4939bSratchov BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
10233c27163dSratchov }
10243c27163dSratchov
10253c27163dSratchov int
envy_mt_read_1(struct envy_softc * sc,int reg)10269383d68eSratchov envy_mt_read_1(struct envy_softc *sc, int reg)
10279383d68eSratchov {
1028dba4939bSratchov int val;
1029dba4939bSratchov
1030dba4939bSratchov val = bus_space_read_1(sc->mt_iot, sc->mt_ioh, reg);
1031dba4939bSratchov bus_space_barrier(sc->mt_iot, sc->mt_ioh, 0, sc->mt_iosz,
1032dba4939bSratchov BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
1033dba4939bSratchov return val;
10349383d68eSratchov }
10359383d68eSratchov
10369383d68eSratchov void
envy_mt_write_1(struct envy_softc * sc,int reg,int val)10379383d68eSratchov envy_mt_write_1(struct envy_softc *sc, int reg, int val)
10389383d68eSratchov {
10399383d68eSratchov bus_space_write_1(sc->mt_iot, sc->mt_ioh, reg, val);
1040dba4939bSratchov bus_space_barrier(sc->mt_iot, sc->mt_ioh, 0, sc->mt_iosz,
1041dba4939bSratchov BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
10429383d68eSratchov }
10439383d68eSratchov
10449383d68eSratchov int
envy_mt_read_2(struct envy_softc * sc,int reg)10459383d68eSratchov envy_mt_read_2(struct envy_softc *sc, int reg)
10469383d68eSratchov {
1047dba4939bSratchov int val;
1048dba4939bSratchov
1049dba4939bSratchov val = bus_space_read_2(sc->mt_iot, sc->mt_ioh, reg);
1050dba4939bSratchov bus_space_barrier(sc->mt_iot, sc->mt_ioh, 0, sc->mt_iosz,
1051dba4939bSratchov BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
1052dba4939bSratchov return val;
10539383d68eSratchov }
10549383d68eSratchov
10559383d68eSratchov void
envy_mt_write_2(struct envy_softc * sc,int reg,int val)10569383d68eSratchov envy_mt_write_2(struct envy_softc *sc, int reg, int val)
10579383d68eSratchov {
10589383d68eSratchov bus_space_write_2(sc->mt_iot, sc->mt_ioh, reg, val);
1059dba4939bSratchov bus_space_barrier(sc->mt_iot, sc->mt_ioh, 0, sc->mt_iosz,
1060dba4939bSratchov BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
10619383d68eSratchov }
10629383d68eSratchov
10639383d68eSratchov int
envy_mt_read_4(struct envy_softc * sc,int reg)10649383d68eSratchov envy_mt_read_4(struct envy_softc *sc, int reg)
10659383d68eSratchov {
1066dba4939bSratchov int val;
1067dba4939bSratchov
1068dba4939bSratchov val = bus_space_read_4(sc->mt_iot, sc->mt_ioh, reg);
1069dba4939bSratchov bus_space_barrier(sc->mt_iot, sc->mt_ioh, 0, sc->mt_iosz,
1070dba4939bSratchov BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
1071dba4939bSratchov return val;
10729383d68eSratchov }
10739383d68eSratchov
10749383d68eSratchov void
envy_mt_write_4(struct envy_softc * sc,int reg,int val)10759383d68eSratchov envy_mt_write_4(struct envy_softc *sc, int reg, int val)
10769383d68eSratchov {
10779383d68eSratchov bus_space_write_4(sc->mt_iot, sc->mt_ioh, reg, val);
1078dba4939bSratchov bus_space_barrier(sc->mt_iot, sc->mt_ioh, 0, sc->mt_iosz,
1079dba4939bSratchov BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
10809383d68eSratchov }
10819383d68eSratchov
10829383d68eSratchov int
envy_cci_read(struct envy_softc * sc,int index)10833c27163dSratchov envy_cci_read(struct envy_softc *sc, int index)
10843c27163dSratchov {
10853c27163dSratchov envy_ccs_write(sc, ENVY_CCI_INDEX, index);
10868375db9bSratchov return (envy_ccs_read(sc, ENVY_CCI_DATA));
10873c27163dSratchov }
10883c27163dSratchov
10893c27163dSratchov void
envy_cci_write(struct envy_softc * sc,int index,int data)10903c27163dSratchov envy_cci_write(struct envy_softc *sc, int index, int data)
10913c27163dSratchov {
10923c27163dSratchov envy_ccs_write(sc, ENVY_CCI_INDEX, index);
10933c27163dSratchov envy_ccs_write(sc, ENVY_CCI_DATA, data);
10943c27163dSratchov }
10953c27163dSratchov
10969ad31cc3Sratchov int
envy_gpio_getstate(struct envy_softc * sc)10979ad31cc3Sratchov envy_gpio_getstate(struct envy_softc *sc)
10989ad31cc3Sratchov {
109910a0d23aSratchov if (sc->isht) {
110010a0d23aSratchov return envy_ccs_read(sc, ENVY_CCS_GPIODATA0) |
110110a0d23aSratchov (envy_ccs_read(sc, ENVY_CCS_GPIODATA1) << 8) |
110210a0d23aSratchov (envy_ccs_read(sc, ENVY_CCS_GPIODATA2) << 16);
110310a0d23aSratchov } else
11049ad31cc3Sratchov return envy_cci_read(sc, ENVY_CCI_GPIODATA);
11059ad31cc3Sratchov }
11069ad31cc3Sratchov
11079ad31cc3Sratchov void
envy_gpio_setstate(struct envy_softc * sc,int reg)11089ad31cc3Sratchov envy_gpio_setstate(struct envy_softc *sc, int reg)
11099ad31cc3Sratchov {
111010a0d23aSratchov if (sc->isht) {
111110a0d23aSratchov envy_ccs_write(sc, ENVY_CCS_GPIODATA0, reg & 0xff);
111210a0d23aSratchov envy_ccs_write(sc, ENVY_CCS_GPIODATA1, (reg >> 8) & 0xff);
111310a0d23aSratchov envy_ccs_write(sc, ENVY_CCS_GPIODATA2, (reg >> 16) & 0xff);
111410a0d23aSratchov } else
11159ad31cc3Sratchov envy_cci_write(sc, ENVY_CCI_GPIODATA, reg);
11169ad31cc3Sratchov }
11179ad31cc3Sratchov
11189ad31cc3Sratchov int
envy_gpio_getmask(struct envy_softc * sc)11199ad31cc3Sratchov envy_gpio_getmask(struct envy_softc *sc)
11209ad31cc3Sratchov {
112110a0d23aSratchov if (sc->isht) {
112210a0d23aSratchov return envy_ccs_read(sc, ENVY_CCS_GPIOMASK0) |
112310a0d23aSratchov (envy_ccs_read(sc, ENVY_CCS_GPIOMASK1) << 8) |
112410a0d23aSratchov (envy_ccs_read(sc, ENVY_CCS_GPIOMASK2) << 16);
112510a0d23aSratchov } else
11269ad31cc3Sratchov return envy_cci_read(sc, ENVY_CCI_GPIOMASK);
11279ad31cc3Sratchov }
11289ad31cc3Sratchov
11299ad31cc3Sratchov void
envy_gpio_setmask(struct envy_softc * sc,int mask)11309ad31cc3Sratchov envy_gpio_setmask(struct envy_softc *sc, int mask)
11319ad31cc3Sratchov {
113210a0d23aSratchov if (sc->isht) {
113310a0d23aSratchov envy_ccs_write(sc, ENVY_CCS_GPIOMASK0, mask & 0xff);
113410a0d23aSratchov envy_ccs_write(sc, ENVY_CCS_GPIOMASK1, (mask >> 8) & 0xff);
113510a0d23aSratchov envy_ccs_write(sc, ENVY_CCS_GPIOMASK2, (mask >> 16) & 0xff);
113610a0d23aSratchov } else
11379ad31cc3Sratchov envy_cci_write(sc, ENVY_CCI_GPIOMASK, mask);
11389ad31cc3Sratchov }
11399ad31cc3Sratchov
11409ad31cc3Sratchov int
envy_gpio_getdir(struct envy_softc * sc)11419ad31cc3Sratchov envy_gpio_getdir(struct envy_softc *sc)
11429ad31cc3Sratchov {
114310a0d23aSratchov if (sc->isht) {
114410a0d23aSratchov return envy_ccs_read(sc, ENVY_CCS_GPIODIR0) |
114510a0d23aSratchov (envy_ccs_read(sc, ENVY_CCS_GPIODIR1) << 8) |
114610a0d23aSratchov (envy_ccs_read(sc, ENVY_CCS_GPIODIR2) << 16);
114710a0d23aSratchov } else
11489ad31cc3Sratchov return envy_cci_read(sc, ENVY_CCI_GPIODIR);
11499ad31cc3Sratchov }
11509ad31cc3Sratchov
11519ad31cc3Sratchov void
envy_gpio_setdir(struct envy_softc * sc,int dir)11529ad31cc3Sratchov envy_gpio_setdir(struct envy_softc *sc, int dir)
11539ad31cc3Sratchov {
115410a0d23aSratchov if (sc->isht) {
115510a0d23aSratchov envy_ccs_write(sc, ENVY_CCS_GPIODIR0, dir & 0xff);
115610a0d23aSratchov envy_ccs_write(sc, ENVY_CCS_GPIODIR1, (dir >> 8) & 0xff);
115710a0d23aSratchov envy_ccs_write(sc, ENVY_CCS_GPIODIR2, (dir >> 16) & 0xff);
115810a0d23aSratchov } else
11599ad31cc3Sratchov envy_cci_write(sc, ENVY_CCI_GPIODIR, dir);
11609ad31cc3Sratchov }
11619ad31cc3Sratchov
11623c27163dSratchov void
envy_gpio_i2c_start_bit(struct envy_softc * sc,int sda,int scl)1163d3da81d3Sratchov envy_gpio_i2c_start_bit(struct envy_softc *sc, int sda, int scl)
1164d3da81d3Sratchov {
1165d3da81d3Sratchov int reg;
1166d3da81d3Sratchov
1167d3da81d3Sratchov reg = envy_gpio_getstate(sc);
1168d3da81d3Sratchov reg |= (sda | scl);
1169d3da81d3Sratchov envy_gpio_setstate(sc, reg);
1170d3da81d3Sratchov delay(5);
1171d3da81d3Sratchov reg &= ~sda;
1172d3da81d3Sratchov envy_gpio_setstate(sc, reg);
1173d3da81d3Sratchov delay(4);
1174d3da81d3Sratchov reg &= ~scl;
1175d3da81d3Sratchov envy_gpio_setstate(sc, reg);
1176d3da81d3Sratchov delay(5);
1177d3da81d3Sratchov }
1178d3da81d3Sratchov
1179d3da81d3Sratchov void
envy_gpio_i2c_stop_bit(struct envy_softc * sc,int sda,int scl)1180d3da81d3Sratchov envy_gpio_i2c_stop_bit(struct envy_softc *sc, int sda, int scl)
1181d3da81d3Sratchov {
1182d3da81d3Sratchov int reg;
1183d3da81d3Sratchov
1184d3da81d3Sratchov reg = envy_gpio_getstate(sc);
1185d3da81d3Sratchov reg &= ~sda;
1186d3da81d3Sratchov reg |= scl;
1187d3da81d3Sratchov envy_gpio_setstate(sc, reg);
1188d3da81d3Sratchov delay(4);
1189d3da81d3Sratchov reg |= sda;
1190d3da81d3Sratchov envy_gpio_setstate(sc, reg);
1191d3da81d3Sratchov }
1192d3da81d3Sratchov
1193d3da81d3Sratchov void
envy_gpio_i2c_byte_out(struct envy_softc * sc,int sda,int scl,int val)1194d3da81d3Sratchov envy_gpio_i2c_byte_out(struct envy_softc *sc, int sda, int scl, int val)
1195d3da81d3Sratchov {
1196d3da81d3Sratchov int mask, reg;
1197d3da81d3Sratchov
1198d3da81d3Sratchov reg = envy_gpio_getstate(sc);
1199d3da81d3Sratchov
1200d3da81d3Sratchov for (mask = 0x80; mask != 0; mask >>= 1) {
1201d3da81d3Sratchov reg &= ~sda;
1202d3da81d3Sratchov reg |= (val & mask) ? sda : 0;
1203d3da81d3Sratchov envy_gpio_setstate(sc, reg);
1204d3da81d3Sratchov delay(1);
1205d3da81d3Sratchov reg |= scl;
1206d3da81d3Sratchov envy_gpio_setstate(sc, reg);
1207d3da81d3Sratchov delay(4);
1208d3da81d3Sratchov reg &= ~scl;
1209d3da81d3Sratchov envy_gpio_setstate(sc, reg);
1210d3da81d3Sratchov delay(5);
1211d3da81d3Sratchov }
1212d3da81d3Sratchov
1213d3da81d3Sratchov reg |= scl;
1214d3da81d3Sratchov envy_gpio_setstate(sc, reg);
1215d3da81d3Sratchov delay(4);
1216d3da81d3Sratchov reg &= ~scl;
1217d3da81d3Sratchov envy_gpio_setstate(sc, reg);
1218d3da81d3Sratchov delay(5);
1219d3da81d3Sratchov }
1220d3da81d3Sratchov
1221d3da81d3Sratchov void
envy_i2c_wait(struct envy_softc * sc)12223c27163dSratchov envy_i2c_wait(struct envy_softc *sc)
12233c27163dSratchov {
12243c27163dSratchov int timeout = 50, st;
12253c27163dSratchov
12263c27163dSratchov for (;;) {
12273c27163dSratchov st = envy_ccs_read(sc, ENVY_I2C_CTL);
12283c27163dSratchov if (!(st & ENVY_I2C_CTL_BUSY))
12293c27163dSratchov break;
12303c27163dSratchov if (timeout == 0) {
12313c27163dSratchov printf("%s: i2c busy timeout\n", DEVNAME(sc));
12323c27163dSratchov break;
12333c27163dSratchov }
12343c27163dSratchov delay(50);
12353c27163dSratchov timeout--;
12363c27163dSratchov }
12373c27163dSratchov }
12383c27163dSratchov
12393c27163dSratchov int
envy_i2c_read(struct envy_softc * sc,int dev,int addr)12403c27163dSratchov envy_i2c_read(struct envy_softc *sc, int dev, int addr)
12413c27163dSratchov {
12423c27163dSratchov envy_i2c_wait(sc);
12433c27163dSratchov envy_ccs_write(sc, ENVY_I2C_ADDR, addr);
12443c27163dSratchov envy_i2c_wait(sc);
12453c27163dSratchov envy_ccs_write(sc, ENVY_I2C_DEV, dev << 1);
12463c27163dSratchov envy_i2c_wait(sc);
12473c27163dSratchov return envy_ccs_read(sc, ENVY_I2C_DATA);
12483c27163dSratchov }
12493c27163dSratchov
12503c27163dSratchov void
envy_i2c_write(struct envy_softc * sc,int dev,int addr,int data)12513c27163dSratchov envy_i2c_write(struct envy_softc *sc, int dev, int addr, int data)
12523c27163dSratchov {
12533c27163dSratchov if (dev == 0x50) {
12543c27163dSratchov printf("%s: writing on eeprom is evil...\n", DEVNAME(sc));
12553c27163dSratchov return;
12563c27163dSratchov }
12573c27163dSratchov envy_i2c_wait(sc);
12583c27163dSratchov envy_ccs_write(sc, ENVY_I2C_ADDR, addr);
12593c27163dSratchov envy_i2c_wait(sc);
12603c27163dSratchov envy_ccs_write(sc, ENVY_I2C_DATA, data);
12613c27163dSratchov envy_i2c_wait(sc);
12623c27163dSratchov envy_ccs_write(sc, ENVY_I2C_DEV, (dev << 1) | 1);
12633c27163dSratchov }
12643c27163dSratchov
12653c27163dSratchov int
envy_codec_read(struct envy_softc * sc,int dev,int addr)12666ce34ffeSratchov envy_codec_read(struct envy_softc *sc, int dev, int addr) {
126781bff893Sratchov return sc->shadow[dev][addr];
12683c27163dSratchov }
12693c27163dSratchov
12703c27163dSratchov void
envy_codec_write(struct envy_softc * sc,int dev,int addr,int data)12716ce34ffeSratchov envy_codec_write(struct envy_softc *sc, int dev, int addr, int data)
12723c27163dSratchov {
12736ce34ffeSratchov DPRINTFN(2, "envy_codec_write: %d, %d, 0x%x\n", dev, addr, data);
127481bff893Sratchov sc->shadow[dev][addr] = data;
12756ce34ffeSratchov sc->card->codec_write(sc, dev, addr, data);
12763c27163dSratchov }
12773c27163dSratchov
127810a0d23aSratchov int
envy_eeprom_gpioxxx(struct envy_softc * sc,int addr)127910a0d23aSratchov envy_eeprom_gpioxxx(struct envy_softc *sc, int addr)
128010a0d23aSratchov {
128110a0d23aSratchov int val;
128210a0d23aSratchov
128310a0d23aSratchov val = sc->eeprom[addr];
128410a0d23aSratchov if (sc->isht) {
128510a0d23aSratchov val |= sc->eeprom[++addr] << 8;
128610a0d23aSratchov val |= sc->eeprom[++addr] << 16;
128710a0d23aSratchov }
128810a0d23aSratchov return val;
128910a0d23aSratchov }
129010a0d23aSratchov
12913a87d643Sratchov int
envy_ac97_wait(struct envy_softc * sc)12923a87d643Sratchov envy_ac97_wait(struct envy_softc *sc)
12933a87d643Sratchov {
12943a87d643Sratchov int timeout = 50, st;
12953a87d643Sratchov
12963a87d643Sratchov for (;;) {
12979383d68eSratchov st = envy_mt_read_1(sc, ENVY_MT_AC97_CMD);
12983a87d643Sratchov if ((st & ENVY_MT_AC97_READY) && !(st & ENVY_MT_AC97_CMD_MASK)) {
12993a87d643Sratchov st = 0;
13003a87d643Sratchov break;
13013a87d643Sratchov }
13023a87d643Sratchov if (timeout == 0) {
13033a87d643Sratchov st = -1;
13043a87d643Sratchov break;
13053a87d643Sratchov }
13063a87d643Sratchov delay(50);
13073a87d643Sratchov timeout--;
13083a87d643Sratchov }
13093a87d643Sratchov
13103a87d643Sratchov return (st);
13113a87d643Sratchov }
13123a87d643Sratchov
13133a87d643Sratchov int
envy_ac97_attach_codec(void * hdl,struct ac97_codec_if * codec_if)13143a87d643Sratchov envy_ac97_attach_codec(void *hdl, struct ac97_codec_if *codec_if)
13153a87d643Sratchov {
13163a87d643Sratchov struct envy_softc *sc = hdl;
13173a87d643Sratchov
13183a87d643Sratchov sc->codec_if = codec_if;
13193a87d643Sratchov
13203a87d643Sratchov return (0);
13213a87d643Sratchov }
13223a87d643Sratchov
13233a87d643Sratchov int
envy_ac97_read_codec(void * hdl,u_int8_t reg,u_int16_t * result)13243a87d643Sratchov envy_ac97_read_codec(void *hdl, u_int8_t reg, u_int16_t *result)
13253a87d643Sratchov {
13263a87d643Sratchov struct envy_softc *sc = hdl;
13273a87d643Sratchov
13283a87d643Sratchov if (envy_ac97_wait(sc)) {
13293a87d643Sratchov printf("%s: envy_ac97_read_codec: timed out\n", DEVNAME(sc));
13303a87d643Sratchov return (-1);
13313a87d643Sratchov }
13323a87d643Sratchov
13339383d68eSratchov envy_mt_write_1(sc, ENVY_MT_AC97_IDX, reg & 0x7f);
13349383d68eSratchov envy_mt_write_1(sc, ENVY_MT_AC97_CMD,
13353a87d643Sratchov ENVY_MT_AC97_CMD_RD);
13363a87d643Sratchov delay(50);
13373a87d643Sratchov
13383a87d643Sratchov if (envy_ac97_wait(sc)) {
13393a87d643Sratchov printf("%s: envy_ac97_read_codec: timed out\n", DEVNAME(sc));
13403a87d643Sratchov return (-1);
13413a87d643Sratchov }
13423a87d643Sratchov
13439383d68eSratchov *result = envy_mt_read_2(sc, ENVY_MT_AC97_DATA);
13443a87d643Sratchov
13453a87d643Sratchov return (0);
13463a87d643Sratchov }
13473a87d643Sratchov
13483a87d643Sratchov int
envy_ac97_write_codec(void * hdl,u_int8_t reg,u_int16_t data)13493a87d643Sratchov envy_ac97_write_codec(void *hdl, u_int8_t reg, u_int16_t data)
13503a87d643Sratchov {
13513a87d643Sratchov struct envy_softc *sc = hdl;
13523a87d643Sratchov
13533a87d643Sratchov if (envy_ac97_wait(sc)) {
13543a87d643Sratchov printf("%s: envy_ac97_write_codec: timed out\n", DEVNAME(sc));
13553a87d643Sratchov return (-1);
13563a87d643Sratchov }
13573a87d643Sratchov
13589383d68eSratchov envy_mt_write_1(sc, ENVY_MT_AC97_IDX, reg & 0x7f);
13599383d68eSratchov envy_mt_write_2(sc, ENVY_MT_AC97_DATA, data);
13609383d68eSratchov envy_mt_write_1(sc, ENVY_MT_AC97_CMD,
13613a87d643Sratchov ENVY_MT_AC97_CMD_WR);
13623a87d643Sratchov delay(50);
13633a87d643Sratchov
13643a87d643Sratchov return (0);
13653a87d643Sratchov }
13663a87d643Sratchov
13673a87d643Sratchov void
envy_ac97_reset_codec(void * hdl)13683a87d643Sratchov envy_ac97_reset_codec(void *hdl)
13693a87d643Sratchov {
13703a87d643Sratchov struct envy_softc *sc = hdl;
13713a87d643Sratchov
13729383d68eSratchov envy_mt_write_1(sc, ENVY_MT_AC97_CMD, ENVY_MT_AC97_CMD_RST);
13733a87d643Sratchov delay(50);
13749383d68eSratchov envy_mt_write_1(sc, ENVY_MT_AC97_CMD, 0);
13753a87d643Sratchov delay(50);
13763a87d643Sratchov
13773a87d643Sratchov if (envy_ac97_wait(sc)) {
13783a87d643Sratchov printf("%s: envy_ac97_reset_codec: timed out\n", DEVNAME(sc));
13793a87d643Sratchov }
13803a87d643Sratchov
13813a87d643Sratchov return;
13823a87d643Sratchov }
13833a87d643Sratchov
13843a87d643Sratchov enum ac97_host_flags
envy_ac97_flags_codec(void * hdl)13853a87d643Sratchov envy_ac97_flags_codec(void *hdl)
13863a87d643Sratchov {
13873a87d643Sratchov struct envy_softc *sc = hdl;
13883a87d643Sratchov
13893a87d643Sratchov return (sc->codec_flags);
13903a87d643Sratchov }
13913a87d643Sratchov
13923c27163dSratchov void
envy_midi_wait(struct envy_softc * sc)1393c737f4d2Sratchov envy_midi_wait(struct envy_softc *sc)
1394c737f4d2Sratchov {
1395c737f4d2Sratchov int i, st;
1396c737f4d2Sratchov
1397c737f4d2Sratchov for (i = 100;; i--) {
1398c737f4d2Sratchov st = envy_ccs_read(sc, ENVY_CCS_MIDISTAT0);
1399c737f4d2Sratchov if (!(st & ENVY_MIDISTAT_OBUSY(sc)))
1400c737f4d2Sratchov break;
1401c737f4d2Sratchov if (i == 0) {
1402c737f4d2Sratchov printf("%s: midi wait timeout\n", DEVNAME(sc));
1403c737f4d2Sratchov break;
1404c737f4d2Sratchov }
1405c737f4d2Sratchov delay(10);
1406c737f4d2Sratchov }
1407c737f4d2Sratchov }
1408c737f4d2Sratchov
1409c737f4d2Sratchov void
envy_reset(struct envy_softc * sc)14103c27163dSratchov envy_reset(struct envy_softc *sc)
14113c27163dSratchov {
14126f1c5514Sratchov int i, reg;
14133c27163dSratchov
14143c27163dSratchov /*
14153c27163dSratchov * full reset
14163c27163dSratchov */
14173c27163dSratchov envy_ccs_write(sc, ENVY_CTL, ENVY_CTL_RESET | ENVY_CTL_NATIVE);
14183c27163dSratchov delay(200);
14193c27163dSratchov envy_ccs_write(sc, ENVY_CTL, ENVY_CTL_NATIVE);
14203c27163dSratchov delay(200);
14213c27163dSratchov
14223c27163dSratchov /*
142303f99936Sratchov * read EEPROM using i2c device or from a static array
14243c27163dSratchov */
14259ad31cc3Sratchov if (sc->card->eeprom == NULL) {
14269ad31cc3Sratchov for (i = 0; i < ENVY_EEPROM_MAXSZ; i++) {
14279ad31cc3Sratchov sc->eeprom[i] = envy_i2c_read(sc, ENVY_I2C_DEV_EEPROM, i);
14289ad31cc3Sratchov }
14299ad31cc3Sratchov #ifdef ENVY_DEBUG
14309ad31cc3Sratchov printf("%s: eeprom: ", DEVNAME(sc));
14319ad31cc3Sratchov for (i = 0; i < ENVY_EEPROM_MAXSZ; i++) {
14329ad31cc3Sratchov printf(" %02x", (unsigned)sc->eeprom[i]);
14339ad31cc3Sratchov }
14349ad31cc3Sratchov printf("\n");
14359ad31cc3Sratchov #endif
14369ad31cc3Sratchov } else
14379ad31cc3Sratchov memcpy(sc->eeprom, sc->card->eeprom, ENVY_EEPROM_MAXSZ);
14383c27163dSratchov
14393c27163dSratchov /*
144003f99936Sratchov * write EEPROM values to corresponding registers
14413c27163dSratchov */
144242346b82Sratchov if (sc->isht) {
144342346b82Sratchov envy_ccs_write(sc, ENVY_CCS_CONF,
144442346b82Sratchov sc->eeprom[ENVY_EEPROM_CONF]);
144542346b82Sratchov envy_ccs_write(sc, ENVY_CCS_ACLINK,
144642346b82Sratchov sc->eeprom[ENVY_EEPROM_ACLINK]);
144742346b82Sratchov envy_ccs_write(sc, ENVY_CCS_I2S,
144842346b82Sratchov sc->eeprom[ENVY_EEPROM_I2S]);
144942346b82Sratchov envy_ccs_write(sc, ENVY_CCS_SPDIF,
145042346b82Sratchov sc->eeprom[ENVY_EEPROM_SPDIF]);
145142346b82Sratchov } else {
14529ad31cc3Sratchov pci_conf_write(sc->pci_pc, sc->pci_tag, ENVY_CONF,
14539ad31cc3Sratchov sc->eeprom[ENVY_EEPROM_CONF] |
14549ad31cc3Sratchov (sc->eeprom[ENVY_EEPROM_ACLINK] << 8) |
14559ad31cc3Sratchov (sc->eeprom[ENVY_EEPROM_I2S] << 16) |
14569ad31cc3Sratchov (sc->eeprom[ENVY_EEPROM_SPDIF] << 24));
145742346b82Sratchov }
14589ad31cc3Sratchov
145942346b82Sratchov envy_gpio_setmask(sc, envy_eeprom_gpioxxx(sc, ENVY_EEPROM_GPIOMASK(sc)));
146010a0d23aSratchov envy_gpio_setdir(sc, envy_eeprom_gpioxxx(sc, ENVY_EEPROM_GPIODIR(sc)));
146110a0d23aSratchov envy_gpio_setstate(sc, envy_eeprom_gpioxxx(sc, ENVY_EEPROM_GPIOST(sc)));
14629ad31cc3Sratchov
14639ad31cc3Sratchov DPRINTF("%s: gpio_mask = %02x\n", DEVNAME(sc),
14649ad31cc3Sratchov envy_gpio_getmask(sc));
14659ad31cc3Sratchov DPRINTF("%s: gpio_dir = %02x\n", DEVNAME(sc),
14669ad31cc3Sratchov envy_gpio_getdir(sc));
14679ad31cc3Sratchov DPRINTF("%s: gpio_state = %02x\n", DEVNAME(sc),
14689ad31cc3Sratchov envy_gpio_getstate(sc));
14693c27163dSratchov
1470c737f4d2Sratchov if (sc->isht) {
1471c737f4d2Sratchov /*
1472c737f4d2Sratchov * set water marks so we get an interrupt for each byte
1473c737f4d2Sratchov */
1474c737f4d2Sratchov envy_ccs_write(sc, ENVY_CCS_MIDIWAT, 1);
1475c737f4d2Sratchov envy_ccs_write(sc, ENVY_CCS_MIDIWAT, 1 | ENVY_CCS_MIDIWAT_RX);
1476c737f4d2Sratchov }
1477c737f4d2Sratchov
1478c737f4d2Sratchov /*
1479c737f4d2Sratchov * switch to UART mode
1480c737f4d2Sratchov */
1481c737f4d2Sratchov envy_ccs_write(sc, ENVY_CCS_MIDISTAT0, 0xff);
1482c737f4d2Sratchov envy_midi_wait(sc);
1483c737f4d2Sratchov envy_ccs_write(sc, ENVY_CCS_MIDISTAT0, ENVY_MIDISTAT_UART);
1484c737f4d2Sratchov envy_midi_wait(sc);
1485c737f4d2Sratchov if (!sc->isht)
1486c737f4d2Sratchov (void)envy_ccs_read(sc, ENVY_CCS_MIDIDATA0);
1487c737f4d2Sratchov
14883c27163dSratchov /*
14893c27163dSratchov * clear all interrupts and unmask used ones
14903c27163dSratchov */
14913c27163dSratchov envy_ccs_write(sc, ENVY_CCS_INTSTAT, 0xff);
14926f1c5514Sratchov reg = ~ENVY_CCS_INT_MT;
14936f1c5514Sratchov if (sc->midi_isopen)
14946f1c5514Sratchov reg &= ~ENVY_CCS_INT_MIDI0;
14956f1c5514Sratchov envy_ccs_write(sc, ENVY_CCS_INTMASK, ~ENVY_CCS_INT_MT);
149610a0d23aSratchov if (sc->isht) {
14979383d68eSratchov envy_mt_write_1(sc, ENVY_MT_NSTREAM, 4 - sc->card->noch / 2);
14989383d68eSratchov envy_mt_write_1(sc, ENVY_MT_IMASK, ~(ENVY_MT_IMASK_PDMA0 |
149942ddd7d9Sratchov ENVY_MT_IMASK_RDMA0 | ENVY_MT_IMASK_ERR));
150010a0d23aSratchov }
150142ddd7d9Sratchov sc->iactive = 0;
150242ddd7d9Sratchov sc->oactive = 0;
15039ad31cc3Sratchov sc->card->init(sc);
15043c27163dSratchov }
15053c27163dSratchov
15063c27163dSratchov int
envy_lineout_getsrc(struct envy_softc * sc,int out)15076ce34ffeSratchov envy_lineout_getsrc(struct envy_softc *sc, int out)
15086ce34ffeSratchov {
15093c27163dSratchov int reg, shift, src;
15103c27163dSratchov
151107f96c57Sratchov if (sc->isht) {
15129383d68eSratchov reg = envy_mt_read_4(sc, ENVY_MT_HTSRC);
151307f96c57Sratchov DPRINTF("%s: outsrc=%x\n", DEVNAME(sc), reg);
151407f96c57Sratchov shift = 3 * (out / 2) + ((out & 1) ? 20 : 8);
151507f96c57Sratchov src = (reg >> shift) & ENVY_MT_HTSRC_MASK;
151607f96c57Sratchov if (src == ENVY_MT_HTSRC_DMA) {
151707f96c57Sratchov return ENVY_MIX_OUTSRC_DMA;
151807f96c57Sratchov } else {
151907f96c57Sratchov src -= ENVY_MT_HTSRC_LINE;
152007f96c57Sratchov return ENVY_MIX_OUTSRC_LINEIN + src;
152107f96c57Sratchov }
152207f96c57Sratchov }
152307f96c57Sratchov
15249383d68eSratchov reg = envy_mt_read_2(sc, ENVY_MT_OUTSRC);
15253c27163dSratchov DPRINTF("%s: outsrc=%x\n", DEVNAME(sc), reg);
15263c27163dSratchov shift = (out & 1) ? (out & ~1) + 8 : out;
15273c27163dSratchov src = (reg >> shift) & 3;
15283c27163dSratchov if (src == ENVY_MT_OUTSRC_DMA) {
15293c27163dSratchov return ENVY_MIX_OUTSRC_DMA;
15303c27163dSratchov } else if (src == ENVY_MT_OUTSRC_MON) {
15313c27163dSratchov return ENVY_MIX_OUTSRC_MON;
15323c27163dSratchov }
15339383d68eSratchov reg = envy_mt_read_4(sc, ENVY_MT_INSEL);
15343c27163dSratchov DPRINTF("%s: insel=%x\n", DEVNAME(sc), reg);
15353c27163dSratchov reg = (reg >> (out * 4)) & 0xf;
15363c27163dSratchov if (src == ENVY_MT_OUTSRC_LINE)
15373c27163dSratchov return ENVY_MIX_OUTSRC_LINEIN + (reg & 7);
15383c27163dSratchov else
15393c27163dSratchov return ENVY_MIX_OUTSRC_SPDIN + (reg >> 3);
15403c27163dSratchov }
15413c27163dSratchov
15423c27163dSratchov void
envy_lineout_setsrc(struct envy_softc * sc,int out,int src)15436ce34ffeSratchov envy_lineout_setsrc(struct envy_softc *sc, int out, int src)
15446ce34ffeSratchov {
15453c27163dSratchov int reg, shift, mask, sel;
15463c27163dSratchov
154707f96c57Sratchov if (sc->isht) {
154807f96c57Sratchov if (src < ENVY_MIX_OUTSRC_SPDIN) {
154907f96c57Sratchov sel = ENVY_MT_HTSRC_LINE;
155007f96c57Sratchov sel += src;
155107f96c57Sratchov } else if (src < ENVY_MIX_OUTSRC_DMA) {
155207f96c57Sratchov sel = ENVY_MT_HTSRC_SPD;
155307f96c57Sratchov sel += src - ENVY_MIX_OUTSRC_SPDIN;
155407f96c57Sratchov } else {
155507f96c57Sratchov sel = ENVY_MT_HTSRC_DMA;
155607f96c57Sratchov }
155707f96c57Sratchov shift = 3 * (out / 2) + ((out & 1) ? 20 : 8);
155807f96c57Sratchov mask = ENVY_MT_HTSRC_MASK << shift;
15599383d68eSratchov reg = envy_mt_read_4(sc, ENVY_MT_HTSRC);
156007f96c57Sratchov reg = (reg & ~mask) | (sel << shift);
15619383d68eSratchov envy_mt_write_4(sc, ENVY_MT_HTSRC, reg);
156207f96c57Sratchov DPRINTF("%s: outsrc <- %x\n", DEVNAME(sc), reg);
156307f96c57Sratchov return;
156407f96c57Sratchov }
156507f96c57Sratchov
15663c27163dSratchov if (src < ENVY_MIX_OUTSRC_DMA) {
15673c27163dSratchov /*
15683c27163dSratchov * linein and spdin are used as output source so we
15693c27163dSratchov * must select the input source channel number
15703c27163dSratchov */
15713c27163dSratchov if (src < ENVY_MIX_OUTSRC_SPDIN)
15723c27163dSratchov sel = src - ENVY_MIX_OUTSRC_LINEIN;
15733c27163dSratchov else
15743c27163dSratchov sel = (src - ENVY_MIX_OUTSRC_SPDIN) << 3;
15753c27163dSratchov
15763c27163dSratchov shift = out * ENVY_MT_INSEL_BITS;
15773c27163dSratchov mask = ENVY_MT_INSEL_MASK << shift;
15789383d68eSratchov reg = envy_mt_read_4(sc, ENVY_MT_INSEL);
15793c27163dSratchov reg = (reg & ~mask) | (sel << shift);
15809383d68eSratchov envy_mt_write_4(sc, ENVY_MT_INSEL, reg);
15813c27163dSratchov DPRINTF("%s: insel <- %x\n", DEVNAME(sc), reg);
15823c27163dSratchov }
15833c27163dSratchov
15843c27163dSratchov /*
15853c27163dSratchov * set the lineout route register
15863c27163dSratchov */
15873c27163dSratchov if (src < ENVY_MIX_OUTSRC_SPDIN) {
15883c27163dSratchov sel = ENVY_MT_OUTSRC_LINE;
15893c27163dSratchov } else if (src < ENVY_MIX_OUTSRC_DMA) {
15903c27163dSratchov sel = ENVY_MT_OUTSRC_SPD;
15913c27163dSratchov } else if (src == ENVY_MIX_OUTSRC_DMA) {
15923c27163dSratchov sel = ENVY_MT_OUTSRC_DMA;
15933c27163dSratchov } else {
15943c27163dSratchov sel = ENVY_MT_OUTSRC_MON;
15953c27163dSratchov }
15963c27163dSratchov shift = (out & 1) ? (out & ~1) + 8 : out;
1597d20977e2Sratchov mask = ENVY_MT_OUTSRC_MASK << shift;
15989383d68eSratchov reg = envy_mt_read_2(sc, ENVY_MT_OUTSRC);
15993c27163dSratchov reg = (reg & ~mask) | (sel << shift);
16009383d68eSratchov envy_mt_write_2(sc, ENVY_MT_OUTSRC, reg);
16013c27163dSratchov DPRINTF("%s: outsrc <- %x\n", DEVNAME(sc), reg);
16023c27163dSratchov }
16033c27163dSratchov
16043c27163dSratchov
16053c27163dSratchov int
envy_spdout_getsrc(struct envy_softc * sc,int out)16066ce34ffeSratchov envy_spdout_getsrc(struct envy_softc *sc, int out)
16076ce34ffeSratchov {
16083c27163dSratchov int reg, src, sel;
16093c27163dSratchov
16109383d68eSratchov reg = envy_mt_read_2(sc, ENVY_MT_SPDROUTE);
16113c27163dSratchov DPRINTF("%s: spdroute=%x\n", DEVNAME(sc), reg);
16123c27163dSratchov src = (out == 0) ? reg : reg >> 2;
16133c27163dSratchov src &= ENVY_MT_SPDSRC_MASK;
16143c27163dSratchov if (src == ENVY_MT_SPDSRC_DMA) {
16153c27163dSratchov return ENVY_MIX_OUTSRC_DMA;
16163c27163dSratchov } else if (src == ENVY_MT_SPDSRC_MON) {
16173c27163dSratchov return ENVY_MIX_OUTSRC_MON;
16183c27163dSratchov }
16193c27163dSratchov
16203c27163dSratchov sel = (out == 0) ? reg >> 8 : reg >> 12;
16213c27163dSratchov sel &= ENVY_MT_SPDSEL_MASK;
16223c27163dSratchov if (src == ENVY_MT_SPDSRC_LINE)
16233c27163dSratchov return ENVY_MIX_OUTSRC_LINEIN + (sel & 7);
16243c27163dSratchov else
16253c27163dSratchov return ENVY_MIX_OUTSRC_SPDIN + (sel >> 3);
16263c27163dSratchov }
16273c27163dSratchov
16283c27163dSratchov void
envy_spdout_setsrc(struct envy_softc * sc,int out,int src)16296ce34ffeSratchov envy_spdout_setsrc(struct envy_softc *sc, int out, int src)
16306ce34ffeSratchov {
16313c27163dSratchov int reg, shift, mask, sel;
16323c27163dSratchov
16339383d68eSratchov reg = envy_mt_read_2(sc, ENVY_MT_SPDROUTE);
16343c27163dSratchov if (src < ENVY_MIX_OUTSRC_DMA) {
16353c27163dSratchov /*
16363c27163dSratchov * linein and spdin are used as output source so we
16373c27163dSratchov * must select the input source channel number
16383c27163dSratchov */
16393c27163dSratchov if (src < ENVY_MIX_OUTSRC_SPDIN)
16403c27163dSratchov sel = src - ENVY_MIX_OUTSRC_LINEIN;
16413c27163dSratchov else
16423c27163dSratchov sel = (src - ENVY_MIX_OUTSRC_SPDIN) << 3;
16433c27163dSratchov
16443c27163dSratchov shift = 8 + out * ENVY_MT_SPDSEL_BITS;
16453c27163dSratchov mask = ENVY_MT_SPDSEL_MASK << shift;
16463c27163dSratchov reg = (reg & ~mask) | (sel << shift);
16473c27163dSratchov }
16483c27163dSratchov
16493c27163dSratchov /*
16503c27163dSratchov * set the lineout route register
16513c27163dSratchov */
16523c27163dSratchov if (src < ENVY_MIX_OUTSRC_SPDIN) {
16533c27163dSratchov sel = ENVY_MT_OUTSRC_LINE;
16543c27163dSratchov } else if (src < ENVY_MIX_OUTSRC_DMA) {
16553c27163dSratchov sel = ENVY_MT_OUTSRC_SPD;
16563c27163dSratchov } else if (src == ENVY_MIX_OUTSRC_DMA) {
16573c27163dSratchov sel = ENVY_MT_OUTSRC_DMA;
16583c27163dSratchov } else {
16593c27163dSratchov sel = ENVY_MT_OUTSRC_MON;
16603c27163dSratchov }
16613c27163dSratchov shift = out * 2;
16623c27163dSratchov mask = ENVY_MT_SPDSRC_MASK << shift;
16633c27163dSratchov reg = (reg & ~mask) | (sel << shift);
16649383d68eSratchov envy_mt_write_2(sc, ENVY_MT_SPDROUTE, reg);
16653c27163dSratchov DPRINTF("%s: spdroute <- %x\n", DEVNAME(sc), reg);
16663c27163dSratchov }
16673c27163dSratchov
16683c27163dSratchov void
envy_mon_getvol(struct envy_softc * sc,int idx,int ch,int * val)16696ce34ffeSratchov envy_mon_getvol(struct envy_softc *sc, int idx, int ch, int *val)
16706ce34ffeSratchov {
16713c27163dSratchov int reg;
16723c27163dSratchov
16739383d68eSratchov envy_mt_write_2(sc, ENVY_MT_MONIDX, idx);
16749383d68eSratchov reg = envy_mt_read_1(sc, ENVY_MT_MONDATA + ch);
1675141aee41Sratchov *val = 0x7f - (reg & 0x7f);
16763c27163dSratchov }
16773c27163dSratchov
16783c27163dSratchov void
envy_mon_setvol(struct envy_softc * sc,int idx,int ch,int val)16796ce34ffeSratchov envy_mon_setvol(struct envy_softc *sc, int idx, int ch, int val)
16806ce34ffeSratchov {
16813c27163dSratchov int reg;
16823c27163dSratchov
16839383d68eSratchov envy_mt_write_2(sc, ENVY_MT_MONIDX, idx);
1684eb5d355fSratchov reg = 0x7f - val;
1685eb5d355fSratchov DPRINTF("%s: mon=%d/%d <- %d\n", DEVNAME(sc), reg, ch, val);
16869383d68eSratchov envy_mt_write_1(sc, ENVY_MT_MONDATA + ch, reg);
16873c27163dSratchov }
16883c27163dSratchov
16893c27163dSratchov int
envymatch(struct device * parent,void * match,void * aux)16906ce34ffeSratchov envymatch(struct device *parent, void *match, void *aux)
16916ce34ffeSratchov {
16929ad31cc3Sratchov return pci_matchbyid((struct pci_attach_args *)aux, envy_matchids,
16939ad31cc3Sratchov sizeof(envy_matchids) / sizeof(envy_matchids[0]));
16943c27163dSratchov }
16953c27163dSratchov
16963c27163dSratchov void
envyattach(struct device * parent,struct device * self,void * aux)16973c27163dSratchov envyattach(struct device *parent, struct device *self, void *aux)
16983c27163dSratchov {
16993c27163dSratchov struct envy_softc *sc = (struct envy_softc *)self;
17003c27163dSratchov struct pci_attach_args *pa = (struct pci_attach_args *)aux;
17013c27163dSratchov pci_intr_handle_t ih;
17023c27163dSratchov const char *intrstr;
17039ad31cc3Sratchov int subid;
17043c27163dSratchov
17055d318f87Sratchov #if NMIDI > 0
17065d318f87Sratchov sc->midi_isopen = 0;
17075d318f87Sratchov #endif
17083c27163dSratchov sc->pci_tag = pa->pa_tag;
17093c27163dSratchov sc->pci_pc = pa->pa_pc;
17103c27163dSratchov sc->pci_dmat = pa->pa_dmat;
17113c27163dSratchov sc->pci_ih = NULL;
17123c27163dSratchov sc->ibuf.addr = sc->obuf.addr = NULL;
17133c27163dSratchov sc->ccs_iosz = 0;
17143c27163dSratchov sc->mt_iosz = 0;
171546564b25Sjsg sc->isht = (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ICENSEMBLE_VT172X);
17163c27163dSratchov
17173c27163dSratchov if (pci_mapreg_map(pa, ENVY_CTL_BAR, PCI_MAPREG_TYPE_IO, 0,
17183c27163dSratchov &sc->ccs_iot, &sc->ccs_ioh, NULL, &sc->ccs_iosz, 0)) {
1719e10c952fSsthen printf(": can't map ctl i/o space\n");
17203c27163dSratchov sc->ccs_iosz = 0;
17213c27163dSratchov return;
17223c27163dSratchov }
172310a0d23aSratchov if (pci_mapreg_map(pa, ENVY_MT_BAR(sc->isht), PCI_MAPREG_TYPE_IO, 0,
17243c27163dSratchov &sc->mt_iot, &sc->mt_ioh, NULL, &sc->mt_iosz, 0)) {
1725e10c952fSsthen printf(": can't map mt i/o space\n");
17263c27163dSratchov sc->mt_iosz = 0;
17273c27163dSratchov return;
17283c27163dSratchov }
17293c27163dSratchov if (pci_intr_map(pa, &ih)) {
17303c27163dSratchov printf(": can't map interrupt\n");
17313c27163dSratchov }
17323c27163dSratchov intrstr = pci_intr_string(sc->pci_pc, ih);
173361932e14Sratchov sc->pci_ih = pci_intr_establish(sc->pci_pc, ih, IPL_AUDIO | IPL_MPSAFE,
17343c27163dSratchov envy_intr, sc, sc->dev.dv_xname);
17353c27163dSratchov if (sc->pci_ih == NULL) {
17363c27163dSratchov printf(": can't establish interrupt");
17373c27163dSratchov if (intrstr)
17383c27163dSratchov printf(" at %s", intrstr);
17393c27163dSratchov printf("\n");
17403c27163dSratchov return;
17413c27163dSratchov }
17423c27163dSratchov printf(": %s\n", intrstr);
17439ad31cc3Sratchov subid = pci_conf_read(sc->pci_pc, sc->pci_tag, PCI_SUBVEND_0);
174410a0d23aSratchov sc->card = sc->isht ? envy_cards_ht : envy_cards;
17459ad31cc3Sratchov while (sc->card->subid != subid) {
1746985f45c4Sratchov if (sc->card->subid == 0)
17479ad31cc3Sratchov break;
17489ad31cc3Sratchov sc->card++;
17499ad31cc3Sratchov }
1750985f45c4Sratchov printf("%s: %s, %u inputs, %u outputs\n", DEVNAME(sc),
1751985f45c4Sratchov sc->card->name, sc->card->nich, sc->card->noch);
17523c27163dSratchov envy_reset(sc);
17532baa08e2Santon sc->audio = audio_attach_mi(&envy_hw_if, sc, NULL, &sc->dev);
1754c737f4d2Sratchov #if NMIDI > 0
1755e836b2ceSratchov if (sc->card->nmidi > 0 && (!sc->isht ||
1756e836b2ceSratchov sc->eeprom[ENVY_EEPROM_CONF] & ENVY_CONF_MIDI)) {
1757c737f4d2Sratchov sc->midi = midi_attach_mi(&envy_midi_hw_if, sc, &sc->dev);
17586f1c5514Sratchov }
1759c737f4d2Sratchov #endif
17603c27163dSratchov }
17613c27163dSratchov
17623c27163dSratchov int
envydetach(struct device * self,int flags)17633c27163dSratchov envydetach(struct device *self, int flags)
17643c27163dSratchov {
17653c27163dSratchov struct envy_softc *sc = (struct envy_softc *)self;
17663c27163dSratchov
17673c27163dSratchov if (sc->pci_ih != NULL) {
17683c27163dSratchov pci_intr_disestablish(sc->pci_pc, sc->pci_ih);
17693c27163dSratchov sc->pci_ih = NULL;
17703c27163dSratchov }
17713c27163dSratchov if (sc->ccs_iosz) {
17723c27163dSratchov bus_space_unmap(sc->ccs_iot, sc->ccs_ioh, sc->ccs_iosz);
17733c27163dSratchov }
17743c27163dSratchov if (sc->mt_iosz) {
1775702e7eabSratchov bus_space_unmap(sc->mt_iot, sc->mt_ioh, sc->mt_iosz);
17763c27163dSratchov }
17773c27163dSratchov return 0;
17783c27163dSratchov }
17793c27163dSratchov
17803c27163dSratchov int
envyactivate(struct device * self,int act)17812af838c1Sratchov envyactivate(struct device *self, int act)
17822af838c1Sratchov {
17832af838c1Sratchov struct envy_softc *sc = (struct envy_softc *)self;
17842af838c1Sratchov
17852af838c1Sratchov if (act == DVACT_RESUME) {
17862af838c1Sratchov /*
17872af838c1Sratchov * The audio(4) layer will restore parameters and, if
17882af838c1Sratchov * needed, start DMA. So we only need to reach the
17892af838c1Sratchov * same device state as after the audio_attach() call.
17902af838c1Sratchov */
17912af838c1Sratchov envy_reset(sc);
17922af838c1Sratchov }
17932af838c1Sratchov return config_activate_children(self, act);
17942af838c1Sratchov }
17952af838c1Sratchov
17962af838c1Sratchov int
envy_open(void * self,int flags)17973c27163dSratchov envy_open(void *self, int flags)
17983c27163dSratchov {
17993c27163dSratchov return 0;
18003c27163dSratchov }
18013c27163dSratchov
18023c27163dSratchov void
envy_close(void * self)18033c27163dSratchov envy_close(void *self)
18043c27163dSratchov {
18053c27163dSratchov }
18063c27163dSratchov
18073c27163dSratchov void *
envy_allocm(void * self,int dir,size_t size,int type,int flags)18083c27163dSratchov envy_allocm(void *self, int dir, size_t size, int type, int flags)
18093c27163dSratchov {
18103c27163dSratchov struct envy_softc *sc = (struct envy_softc *)self;
18118295e4dbSratchov int err, wait;
18123c27163dSratchov struct envy_buf *buf;
1813d992c219Sratchov bus_addr_t dma_addr;
18143c27163dSratchov
18158295e4dbSratchov buf = (dir == AUMODE_RECORD) ? &sc->ibuf : &sc->obuf;
18163c27163dSratchov if (buf->addr != NULL) {
18173c27163dSratchov DPRINTF("%s: multiple alloc, dir = %d\n", DEVNAME(sc), dir);
18183c27163dSratchov return NULL;
18193c27163dSratchov }
18203c27163dSratchov buf->size = size;
18213c27163dSratchov wait = (flags & M_NOWAIT) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK;
18223c27163dSratchov
18233c27163dSratchov #define ENVY_ALIGN 4
18243609b215Sratchov #define ENVY_MAXADDR ((1 << 28) - 1)
18253c27163dSratchov
18263609b215Sratchov buf->addr = (caddr_t)uvm_km_kmemalloc_pla(kernel_map,
18273609b215Sratchov uvm.kernel_object, buf->size, 0, UVM_KMF_NOWAIT, 0,
18283609b215Sratchov (paddr_t)ENVY_MAXADDR, 0, 0, 1);
18293609b215Sratchov if (buf->addr == NULL) {
18303609b215Sratchov DPRINTF("%s: unable to alloc dma segment\n", DEVNAME(sc));
18313c27163dSratchov goto err_ret;
18323c27163dSratchov }
18333c27163dSratchov err = bus_dmamap_create(sc->pci_dmat, buf->size, 1, buf->size, 0,
18343c27163dSratchov wait, &buf->map);
18353c27163dSratchov if (err) {
18363c27163dSratchov DPRINTF("%s: dmamap_create: failed %d\n", DEVNAME(sc), err);
18373c27163dSratchov goto err_unmap;
18383c27163dSratchov }
18393c27163dSratchov err = bus_dmamap_load(sc->pci_dmat, buf->map, buf->addr,
18403c27163dSratchov buf->size, NULL, wait);
18413c27163dSratchov if (err) {
18423c27163dSratchov DPRINTF("%s: dmamap_load: failed %d\n", DEVNAME(sc), err);
18433c27163dSratchov goto err_destroy;
18443c27163dSratchov }
1845d992c219Sratchov dma_addr = buf->map->dm_segs[0].ds_addr;
1846c4d66003Sratchov DPRINTF("%s: allocated %zd bytes dir=%d, ka=%p, da=%lx\n", DEVNAME(sc),
1847d992c219Sratchov buf->size, dir, buf->addr, dma_addr);
18483609b215Sratchov if (!sc->isht && (dma_addr & ~ENVY_MAXADDR)) {
1849d992c219Sratchov printf("%s: DMA address beyond 0x10000000\n", DEVNAME(sc));
1850d992c219Sratchov goto err_unload;
1851d992c219Sratchov }
18523c27163dSratchov return buf->addr;
1853d992c219Sratchov err_unload:
1854d992c219Sratchov bus_dmamap_unload(sc->pci_dmat, buf->map);
18553c27163dSratchov err_destroy:
18563c27163dSratchov bus_dmamap_destroy(sc->pci_dmat, buf->map);
18573c27163dSratchov err_unmap:
18583609b215Sratchov uvm_km_free(kernel_map, (vaddr_t)buf->addr, buf->size);
18593c27163dSratchov err_ret:
18603c27163dSratchov return NULL;
18613c27163dSratchov }
18623c27163dSratchov
18633c27163dSratchov void
envy_freem(void * self,void * addr,int type)18643c27163dSratchov envy_freem(void *self, void *addr, int type)
18653c27163dSratchov {
18663c27163dSratchov struct envy_buf *buf;
18673c27163dSratchov struct envy_softc *sc = (struct envy_softc *)self;
18683c27163dSratchov int dir;
18693c27163dSratchov
18703c27163dSratchov if (sc->ibuf.addr == addr) {
18713c27163dSratchov buf = &sc->ibuf;
18723c27163dSratchov dir = AUMODE_RECORD;
18733c27163dSratchov } else if (sc->obuf.addr == addr) {
18743c27163dSratchov buf = &sc->obuf;
18753c27163dSratchov dir = AUMODE_PLAY;
18763c27163dSratchov } else {
18773c27163dSratchov DPRINTF("%s: no buf to free\n", DEVNAME(sc));
18783c27163dSratchov return;
18793c27163dSratchov }
1880d992c219Sratchov bus_dmamap_unload(sc->pci_dmat, buf->map);
18813c27163dSratchov bus_dmamap_destroy(sc->pci_dmat, buf->map);
18823609b215Sratchov uvm_km_free(kernel_map, (vaddr_t)&buf->addr, buf->size);
18833c27163dSratchov buf->addr = NULL;
18843c27163dSratchov DPRINTF("%s: freed buffer (mode=%d)\n", DEVNAME(sc), dir);
18853c27163dSratchov }
18863c27163dSratchov
18873c27163dSratchov int
envy_set_params(void * self,int setmode,int usemode,struct audio_params * p,struct audio_params * r)18883c27163dSratchov envy_set_params(void *self, int setmode, int usemode,
18893c27163dSratchov struct audio_params *p, struct audio_params *r)
18903c27163dSratchov {
18913c27163dSratchov struct envy_softc *sc = (struct envy_softc *)self;
18923c27163dSratchov int i, rate, reg;
18933c27163dSratchov
189442ddd7d9Sratchov if (setmode == 0)
18953c27163dSratchov return 0;
18963c27163dSratchov if (setmode == (AUMODE_PLAY | AUMODE_RECORD) &&
18973c27163dSratchov p->sample_rate != r->sample_rate) {
18983c27163dSratchov DPRINTF("%s: play/rec rates mismatch\n", DEVNAME(sc));
1899981a8affSjakemsr r->sample_rate = p->sample_rate;
19003c27163dSratchov }
1901b2611ef7Sratchov
19023c27163dSratchov rate = (setmode & AUMODE_PLAY) ? p->sample_rate : r->sample_rate;
1903b2611ef7Sratchov
1904b2611ef7Sratchov /* only HT model supports rates above 96kHz */
1905b2611ef7Sratchov if (!sc->isht && rate > 96000)
1906b2611ef7Sratchov rate = 96000;
1907b2611ef7Sratchov
19083c27163dSratchov for (i = 0; envy_rates[i].rate < rate; i++) {
19093c27163dSratchov if (envy_rates[i].rate == -1) {
19103c27163dSratchov i--;
19113c27163dSratchov DPRINTF("%s: rate: %d -> %d\n", DEVNAME(sc), rate, i);
19123c27163dSratchov break;
19133c27163dSratchov }
19143c27163dSratchov }
1915b2611ef7Sratchov
1916b2611ef7Sratchov if (sc->isht) {
1917b2611ef7Sratchov reg = envy_mt_read_1(sc, ENVY_MT_FMT);
1918b2611ef7Sratchov if (rate > 96000)
1919b2611ef7Sratchov reg |= ENVY_MT_FMT_128X;
1920b2611ef7Sratchov else
1921b2611ef7Sratchov reg &= ~ENVY_MT_FMT_128X;
1922b2611ef7Sratchov envy_mt_write_1(sc, ENVY_MT_FMT, reg);
1923b2611ef7Sratchov }
1924b2611ef7Sratchov
1925ec22dfc9Sratchov if (sc->card->set_rate)
1926ec22dfc9Sratchov sc->card->set_rate(sc, rate);
1927ec22dfc9Sratchov
19289383d68eSratchov reg = envy_mt_read_1(sc, ENVY_MT_RATE);
19293c27163dSratchov reg &= ~ENVY_MT_RATEMASK;
19303c27163dSratchov reg |= envy_rates[i].reg;
19319383d68eSratchov envy_mt_write_1(sc, ENVY_MT_RATE, reg);
1932b2611ef7Sratchov
19333c27163dSratchov if (setmode & AUMODE_PLAY) {
193494c25ab8Sratchov p->sample_rate = envy_rates[i].rate;
1935b662ad46Sratchov p->encoding = AUDIO_ENCODING_SLINEAR_LE;
19364e4f5a8eSratchov p->precision = 24;
193716579317Sjakemsr p->bps = 4;
193816579317Sjakemsr p->msb = 1;
193981bfbe5bSratchov p->channels = sc->isht ? sc->card->noch : ENVY_PCHANS;
19403c27163dSratchov }
19413c27163dSratchov if (setmode & AUMODE_RECORD) {
194294c25ab8Sratchov r->sample_rate = envy_rates[i].rate;
1943b662ad46Sratchov r->encoding = AUDIO_ENCODING_SLINEAR_LE;
19444e4f5a8eSratchov r->precision = 24;
194516579317Sjakemsr r->bps = 4;
194616579317Sjakemsr r->msb = 1;
194781bfbe5bSratchov r->channels = sc->isht ? sc->card->nich : ENVY_RCHANS;
19483c27163dSratchov }
19493c27163dSratchov return 0;
19503c27163dSratchov }
19513c27163dSratchov
19523c27163dSratchov int
envy_round_blocksize(void * self,int blksz)19533c27163dSratchov envy_round_blocksize(void *self, int blksz)
19543c27163dSratchov {
19551cf28608Sratchov return (blksz + 0x1f) & ~0x1f;
19563c27163dSratchov }
19573c27163dSratchov
195842ddd7d9Sratchov #ifdef ENVY_DEBUG
195942ddd7d9Sratchov void
envy_pintr(struct envy_softc * sc)196042ddd7d9Sratchov envy_pintr(struct envy_softc *sc)
196142ddd7d9Sratchov {
196242ddd7d9Sratchov int i;
196342ddd7d9Sratchov
196442ddd7d9Sratchov if (sc->spurious > 0 || envydebug >= 2) {
1965f276f21fSderaadt printf("%s: spurious = %u, start = %lld.%ld\n",
196642ddd7d9Sratchov DEVNAME(sc), sc->spurious,
1967f276f21fSderaadt (long long)sc->start_ts.tv_sec, sc->start_ts.tv_nsec);
196842ddd7d9Sratchov for (i = 0; i < sc->nintr; i++) {
1969f276f21fSderaadt printf("%lld.%09ld: "
197042ddd7d9Sratchov "active=%d/%d pos=%d/%d st=%x/%x, ctl=%x\n",
1971f276f21fSderaadt (long long)sc->intrs[i].ts.tv_sec,
197242ddd7d9Sratchov sc->intrs[i].ts.tv_nsec,
197342ddd7d9Sratchov sc->intrs[i].iactive,
197442ddd7d9Sratchov sc->intrs[i].oactive,
197542ddd7d9Sratchov sc->intrs[i].ipos,
197642ddd7d9Sratchov sc->intrs[i].opos,
197742ddd7d9Sratchov sc->intrs[i].st,
197842ddd7d9Sratchov sc->intrs[i].mask,
197942ddd7d9Sratchov sc->intrs[i].ctl);
198042ddd7d9Sratchov }
198142ddd7d9Sratchov }
198242ddd7d9Sratchov }
198342ddd7d9Sratchov #endif
198442ddd7d9Sratchov
198542ddd7d9Sratchov int
envy_intr(void * self)198642ddd7d9Sratchov envy_intr(void *self)
198742ddd7d9Sratchov {
198842ddd7d9Sratchov struct envy_softc *sc = (struct envy_softc *)self;
19890d2f6f9aSratchov unsigned int reg, hwpos, cnt;
1990c737f4d2Sratchov int mintr, mstat, mdata;
199142ddd7d9Sratchov int st, err, ctl;
1992c737f4d2Sratchov int max;
199342ddd7d9Sratchov
1994886882aaSratchov mtx_enter(&audio_lock);
19959383d68eSratchov st = envy_mt_read_1(sc, ENVY_MT_INTR);
1996c737f4d2Sratchov mintr = envy_ccs_read(sc, ENVY_CCS_INTSTAT);
1997886882aaSratchov if (!(st & ENVY_MT_INTR_ALL) && !(mintr & ENVY_CCS_INT_MIDI0)) {
1998886882aaSratchov mtx_leave(&audio_lock);
199942ddd7d9Sratchov return 0;
2000886882aaSratchov }
2001e5ecfe50Sratchov if (st & ENVY_MT_INTR_ERR) {
2002e5ecfe50Sratchov err = envy_mt_read_1(sc, ENVY_MT_ERR);
2003e5ecfe50Sratchov envy_mt_write_1(sc, ENVY_MT_ERR, err);
2004e5ecfe50Sratchov }
2005e5ecfe50Sratchov envy_mt_write_1(sc, ENVY_MT_INTR, st);
2006e5ecfe50Sratchov envy_ccs_write(sc, ENVY_CCS_INTSTAT, mintr);
2007c737f4d2Sratchov
200842ddd7d9Sratchov #ifdef ENVY_DEBUG
200942ddd7d9Sratchov if (sc->nintr < ENVY_NINTR) {
201042ddd7d9Sratchov sc->intrs[sc->nintr].iactive = sc->iactive;
201142ddd7d9Sratchov sc->intrs[sc->nintr].oactive = sc->oactive;
201242ddd7d9Sratchov sc->intrs[sc->nintr].st = st;
20139383d68eSratchov sc->intrs[sc->nintr].ipos = envy_mt_read_2(sc, ENVY_MT_RBUFSZ);
20149383d68eSratchov sc->intrs[sc->nintr].opos = envy_mt_read_2(sc, ENVY_MT_PBUFSZ);
20159383d68eSratchov sc->intrs[sc->nintr].ctl = envy_mt_read_1(sc, ENVY_MT_CTL);
20169383d68eSratchov sc->intrs[sc->nintr].mask = envy_mt_read_1(sc, ENVY_MT_IMASK);
201742ddd7d9Sratchov nanouptime(&sc->intrs[sc->nintr].ts);
201842ddd7d9Sratchov sc->nintr++;
201942ddd7d9Sratchov }
202042ddd7d9Sratchov #endif
2021c737f4d2Sratchov if (mintr & ENVY_CCS_INT_MIDI0) {
2022c737f4d2Sratchov for (max = 128; max > 0; max--) {
2023c737f4d2Sratchov mstat = envy_ccs_read(sc, ENVY_CCS_MIDISTAT0);
2024c737f4d2Sratchov if (mstat & ENVY_MIDISTAT_IEMPTY(sc))
2025c737f4d2Sratchov break;
2026c737f4d2Sratchov mdata = envy_ccs_read(sc, ENVY_CCS_MIDIDATA0);
2027c737f4d2Sratchov #if NMIDI > 0
2028c737f4d2Sratchov if (sc->midi_in)
2029c737f4d2Sratchov sc->midi_in(sc->midi_arg, mdata);
2030c737f4d2Sratchov #endif
2031c737f4d2Sratchov }
2032c737f4d2Sratchov }
203342ddd7d9Sratchov if (st & ENVY_MT_INTR_PACK) {
20340d2f6f9aSratchov if (sc->oactive) {
20350d2f6f9aSratchov reg = envy_mt_read_2(sc, ENVY_MT_PBUFSZ);
20360d2f6f9aSratchov hwpos = sc->obuf.bufsz - 4 * (reg + 1);
20370d2f6f9aSratchov if (hwpos >= sc->obuf.bufsz)
20380d2f6f9aSratchov hwpos -= sc->obuf.bufsz;
20390d2f6f9aSratchov DPRINTFN(2, "%s: play: reg = %u, pos: %u -> %u\n",
20400d2f6f9aSratchov DEVNAME(sc), reg, sc->obuf.swpos, hwpos);
20410d2f6f9aSratchov cnt = 0;
20420d2f6f9aSratchov while (hwpos - sc->obuf.swpos >= sc->obuf.blksz) {
204342ddd7d9Sratchov sc->ointr(sc->oarg);
20440d2f6f9aSratchov sc->obuf.swpos += sc->obuf.blksz;
20450d2f6f9aSratchov if (sc->obuf.swpos == sc->obuf.bufsz)
20460d2f6f9aSratchov sc->obuf.swpos = 0;
20470d2f6f9aSratchov cnt++;
20480d2f6f9aSratchov }
20490d2f6f9aSratchov if (cnt != 1) {
20500d2f6f9aSratchov DPRINTFN(2, "%s: play: %u intrs\n",
20510d2f6f9aSratchov DEVNAME(sc), cnt);
20520d2f6f9aSratchov }
20530d2f6f9aSratchov } else {
20549383d68eSratchov ctl = envy_mt_read_1(sc, ENVY_MT_CTL);
205542ddd7d9Sratchov if (ctl & ENVY_MT_CTL_PSTART) {
20569383d68eSratchov envy_mt_write_1(sc,
205742ddd7d9Sratchov ENVY_MT_CTL, ctl & ~ENVY_MT_CTL_PSTART);
205842ddd7d9Sratchov st &= ~ENVY_MT_INTR_PACK;
205942ddd7d9Sratchov sc->obusy = 0;
206042ddd7d9Sratchov wakeup(&sc->obusy);
206142ddd7d9Sratchov }
206242ddd7d9Sratchov #ifdef ENVY_DEBUG
206342ddd7d9Sratchov else
206442ddd7d9Sratchov sc->spurious++;
206542ddd7d9Sratchov #endif
206642ddd7d9Sratchov }
206742ddd7d9Sratchov }
206842ddd7d9Sratchov if (st & ENVY_MT_INTR_RACK) {
20690d2f6f9aSratchov if (sc->iactive) {
20700d2f6f9aSratchov reg = envy_mt_read_2(sc, ENVY_MT_RBUFSZ);
20710d2f6f9aSratchov hwpos = sc->ibuf.bufsz - 4 * (reg + 1);
20720d2f6f9aSratchov if (hwpos >= sc->ibuf.bufsz)
20730d2f6f9aSratchov hwpos -= sc->ibuf.bufsz;
20740d2f6f9aSratchov DPRINTFN(2, "%s: rec: reg = %u, pos: %u -> %u\n",
20750d2f6f9aSratchov DEVNAME(sc), reg, sc->ibuf.swpos, hwpos);
20760d2f6f9aSratchov cnt = 0;
20770d2f6f9aSratchov while (hwpos - sc->ibuf.swpos >= sc->ibuf.blksz) {
207842ddd7d9Sratchov sc->iintr(sc->iarg);
20790d2f6f9aSratchov sc->ibuf.swpos += sc->ibuf.blksz;
20800d2f6f9aSratchov if (sc->ibuf.swpos == sc->ibuf.bufsz)
20810d2f6f9aSratchov sc->ibuf.swpos = 0;
20820d2f6f9aSratchov cnt++;
20830d2f6f9aSratchov }
20840d2f6f9aSratchov if (cnt != 1) {
20850d2f6f9aSratchov DPRINTFN(2, "%s: rec: %u intrs\n",
20860d2f6f9aSratchov DEVNAME(sc), cnt);
20870d2f6f9aSratchov }
20880d2f6f9aSratchov } else {
20899383d68eSratchov ctl = envy_mt_read_1(sc, ENVY_MT_CTL);
209042ddd7d9Sratchov if (ctl & ENVY_MT_CTL_RSTART(sc)) {
20919383d68eSratchov envy_mt_write_1(sc,
209242ddd7d9Sratchov ENVY_MT_CTL, ctl & ~ENVY_MT_CTL_RSTART(sc));
209342ddd7d9Sratchov st &= ~ENVY_MT_INTR_RACK;
209442ddd7d9Sratchov sc->ibusy = 0;
209542ddd7d9Sratchov wakeup(&sc->ibusy);
209642ddd7d9Sratchov }
209742ddd7d9Sratchov #ifdef ENVY_DEBUG
209842ddd7d9Sratchov else
209942ddd7d9Sratchov sc->spurious++;
210042ddd7d9Sratchov #endif
210142ddd7d9Sratchov }
210242ddd7d9Sratchov }
2103886882aaSratchov mtx_leave(&audio_lock);
210442ddd7d9Sratchov return 1;
210542ddd7d9Sratchov }
210642ddd7d9Sratchov
21073c27163dSratchov int
envy_trigger_output(void * self,void * start,void * end,int blksz,void (* intr)(void *),void * arg,struct audio_params * param)21083c27163dSratchov envy_trigger_output(void *self, void *start, void *end, int blksz,
21093c27163dSratchov void (*intr)(void *), void *arg, struct audio_params *param)
21103c27163dSratchov {
21113c27163dSratchov struct envy_softc *sc = (struct envy_softc *)self;
21123c27163dSratchov size_t bufsz;
2113886882aaSratchov int st;
21143c27163dSratchov
2115bea7b4e8Sratchov bufsz = (char *)end - (char *)start;
21169ad31cc3Sratchov #ifdef ENVY_DEBUG
211781bfbe5bSratchov if (blksz % (sc->isht ? sc->card->noch * 4 : ENVY_PFRAME_SIZE) != 0) {
21189ad31cc3Sratchov printf("%s: %d: bad output blksz\n", DEVNAME(sc), blksz);
21193c27163dSratchov return EINVAL;
21203c27163dSratchov }
21219ad31cc3Sratchov if (bufsz % blksz) {
21229ad31cc3Sratchov printf("%s: %ld: bad output bufsz\n", DEVNAME(sc), bufsz);
21233c27163dSratchov return EINVAL;
21243c27163dSratchov }
21259ad31cc3Sratchov #endif
2126886882aaSratchov mtx_enter(&audio_lock);
21278295e4dbSratchov envy_mt_write_4(sc, ENVY_MT_PADDR, sc->obuf.map->dm_segs[0].ds_addr);
21289383d68eSratchov envy_mt_write_2(sc, ENVY_MT_PBUFSZ, bufsz / 4 - 1);
21299383d68eSratchov envy_mt_write_2(sc, ENVY_MT_PBLKSZ(sc), blksz / 4 - 1);
21303c27163dSratchov
213142ddd7d9Sratchov #ifdef ENVY_DEBUG
213242ddd7d9Sratchov if (!sc->iactive) {
213342ddd7d9Sratchov sc->nintr = 0;
213442ddd7d9Sratchov sc->spurious = 0;
213542ddd7d9Sratchov nanouptime(&sc->start_ts);
213642ddd7d9Sratchov }
213742ddd7d9Sratchov #endif
21380d2f6f9aSratchov sc->obuf.bufsz = bufsz;
21390d2f6f9aSratchov sc->obuf.blksz = blksz;
21400d2f6f9aSratchov sc->obuf.swpos = 0;
21413c27163dSratchov sc->ointr = intr;
21423c27163dSratchov sc->oarg = arg;
214342ddd7d9Sratchov sc->oactive = 1;
214442ddd7d9Sratchov sc->obusy = 1;
21453c27163dSratchov st = ENVY_MT_INTR_PACK;
21469383d68eSratchov envy_mt_write_1(sc, ENVY_MT_INTR, st);
21479383d68eSratchov st = envy_mt_read_1(sc, ENVY_MT_CTL);
21483c27163dSratchov st |= ENVY_MT_CTL_PSTART;
21499383d68eSratchov envy_mt_write_1(sc, ENVY_MT_CTL, st);
2150886882aaSratchov mtx_leave(&audio_lock);
21513c27163dSratchov return 0;
21523c27163dSratchov }
21533c27163dSratchov
21543c27163dSratchov int
envy_trigger_input(void * self,void * start,void * end,int blksz,void (* intr)(void *),void * arg,struct audio_params * param)21553c27163dSratchov envy_trigger_input(void *self, void *start, void *end, int blksz,
21563c27163dSratchov void (*intr)(void *), void *arg, struct audio_params *param)
21573c27163dSratchov {
21583c27163dSratchov struct envy_softc *sc = (struct envy_softc *)self;
21593c27163dSratchov size_t bufsz;
2160886882aaSratchov int st;
21613c27163dSratchov
2162bea7b4e8Sratchov bufsz = (char *)end - (char *)start;
21639ad31cc3Sratchov #ifdef ENVY_DEBUG
216481bfbe5bSratchov if (blksz % (sc->isht ? sc->card->nich * 4 : ENVY_RFRAME_SIZE) != 0) {
21659ad31cc3Sratchov printf("%s: %d: bad input blksz\n", DEVNAME(sc), blksz);
21663c27163dSratchov return EINVAL;
21673c27163dSratchov }
21689ad31cc3Sratchov if (bufsz % blksz != 0) {
21699ad31cc3Sratchov printf("%s: %ld: bad input bufsz\n", DEVNAME(sc), bufsz);
21703c27163dSratchov return EINVAL;
21713c27163dSratchov }
21729ad31cc3Sratchov #endif
2173886882aaSratchov mtx_enter(&audio_lock);
21748295e4dbSratchov envy_mt_write_4(sc, ENVY_MT_RADDR, sc->ibuf.map->dm_segs[0].ds_addr);
21759383d68eSratchov envy_mt_write_2(sc, ENVY_MT_RBUFSZ, bufsz / 4 - 1);
21769383d68eSratchov envy_mt_write_2(sc, ENVY_MT_RBLKSZ, blksz / 4 - 1);
21773c27163dSratchov
217842ddd7d9Sratchov #ifdef ENVY_DEBUG
217942ddd7d9Sratchov if (!sc->oactive) {
218042ddd7d9Sratchov sc->nintr = 0;
218142ddd7d9Sratchov sc->spurious = 0;
218242ddd7d9Sratchov nanouptime(&sc->start_ts);
218342ddd7d9Sratchov }
218442ddd7d9Sratchov #endif
21850d2f6f9aSratchov sc->ibuf.bufsz = bufsz;
21860d2f6f9aSratchov sc->ibuf.blksz = blksz;
21870d2f6f9aSratchov sc->ibuf.swpos = 0;
21883c27163dSratchov sc->iintr = intr;
21893c27163dSratchov sc->iarg = arg;
219042ddd7d9Sratchov sc->iactive = 1;
219142ddd7d9Sratchov sc->ibusy = 1;
21923c27163dSratchov st = ENVY_MT_INTR_RACK;
21939383d68eSratchov envy_mt_write_1(sc, ENVY_MT_INTR, st);
21949383d68eSratchov st = envy_mt_read_1(sc, ENVY_MT_CTL);
219510a0d23aSratchov st |= ENVY_MT_CTL_RSTART(sc);
21969383d68eSratchov envy_mt_write_1(sc, ENVY_MT_CTL, st);
2197886882aaSratchov mtx_leave(&audio_lock);
21983c27163dSratchov return 0;
21993c27163dSratchov }
22003c27163dSratchov
22013c27163dSratchov int
envy_halt_output(void * self)22023c27163dSratchov envy_halt_output(void *self)
22033c27163dSratchov {
22043c27163dSratchov struct envy_softc *sc = (struct envy_softc *)self;
2205886882aaSratchov int err;
22063c27163dSratchov
2207886882aaSratchov mtx_enter(&audio_lock);
220842ddd7d9Sratchov sc->oactive = 0;
220942ddd7d9Sratchov if (sc->obusy) {
2210254679ffScheloha err = msleep_nsec(&sc->obusy, &audio_lock, PWAIT, "envyobus",
2211254679ffScheloha SEC_TO_NSEC(4));
221242ddd7d9Sratchov if (err)
221342ddd7d9Sratchov printf("%s: output DMA halt timeout\n", DEVNAME(sc));
221442ddd7d9Sratchov }
221542ddd7d9Sratchov #ifdef ENVY_DEBUG
221642ddd7d9Sratchov if (!sc->iactive)
221742ddd7d9Sratchov envy_pintr(sc);
221842ddd7d9Sratchov #endif
2219886882aaSratchov mtx_leave(&audio_lock);
22203c27163dSratchov return 0;
22213c27163dSratchov }
22223c27163dSratchov
22233c27163dSratchov int
envy_halt_input(void * self)22243c27163dSratchov envy_halt_input(void *self)
22253c27163dSratchov {
22263c27163dSratchov struct envy_softc *sc = (struct envy_softc *)self;
2227886882aaSratchov int err;
22283c27163dSratchov
2229886882aaSratchov mtx_enter(&audio_lock);
223042ddd7d9Sratchov sc->iactive = 0;
223142ddd7d9Sratchov if (sc->ibusy) {
2232254679ffScheloha err = msleep_nsec(&sc->ibusy, &audio_lock, PWAIT, "envyibus",
2233254679ffScheloha SEC_TO_NSEC(4));
223442ddd7d9Sratchov if (err)
223542ddd7d9Sratchov printf("%s: input DMA halt timeout\n", DEVNAME(sc));
223642ddd7d9Sratchov }
223742ddd7d9Sratchov #ifdef ENVY_DEBUG
223842ddd7d9Sratchov if (!sc->oactive)
223942ddd7d9Sratchov envy_pintr(sc);
224042ddd7d9Sratchov #endif
2241886882aaSratchov mtx_leave(&audio_lock);
22423c27163dSratchov return 0;
22433c27163dSratchov }
22443c27163dSratchov
22453c27163dSratchov int
envy_query_devinfo(void * self,struct mixer_devinfo * dev)22463c27163dSratchov envy_query_devinfo(void *self, struct mixer_devinfo *dev)
22473c27163dSratchov {
224810a0d23aSratchov struct envy_softc *sc = (struct envy_softc *)self;
22497aefcb42Sratchov int i, n, idx, ndev;
22503c27163dSratchov char *classes[] = {
2251b7a60a70Sratchov AudioCinputs, AudioCoutputs, AudioCmonitor
22523c27163dSratchov };
22533c27163dSratchov
22543a87d643Sratchov if (sc->isac97)
22553a87d643Sratchov return (sc->codec_if->vtbl->query_devinfo(sc->codec_if, dev));
22563a87d643Sratchov
22571f784b02Sjakemsr if (dev->index < 0)
22581f784b02Sjakemsr return ENXIO;
22591f784b02Sjakemsr
22607aefcb42Sratchov idx = dev->index;
22617aefcb42Sratchov ndev = ENVY_MIX_NCLASS;
22623c27163dSratchov dev->prev = dev->next = AUDIO_MIXER_LAST;
22637aefcb42Sratchov
22647aefcb42Sratchov /*
22657aefcb42Sratchov * classes
22667aefcb42Sratchov */
22677aefcb42Sratchov if (idx < ndev) {
22688d326a21Sratchov dev->type = AUDIO_MIXER_CLASS;
22697aefcb42Sratchov dev->mixer_class = idx;
22707aefcb42Sratchov strlcpy(dev->label.name, classes[idx], MAX_AUDIO_DEV_LEN);
22713c27163dSratchov return 0;
22723c27163dSratchov }
22737aefcb42Sratchov idx -= ndev;
22747aefcb42Sratchov
22757aefcb42Sratchov /*
22762c4be9d3Sratchov * output.lineX_source
22777aefcb42Sratchov */
227807f96c57Sratchov ndev = sc->card->noch;
22797aefcb42Sratchov if (idx < ndev) {
22803c27163dSratchov n = 0;
22813c27163dSratchov dev->type = AUDIO_MIXER_ENUM;
2282b7a60a70Sratchov dev->mixer_class = ENVY_MIX_CLASSOUT;
228381bfbe5bSratchov for (i = 0; i < sc->card->nich; i++) {
22843c27163dSratchov dev->un.e.member[n].ord = n;
22853c27163dSratchov snprintf(dev->un.e.member[n++].label.name,
2286fbdda81eSratchov MAX_AUDIO_DEV_LEN, AudioNline "-%d", i);
22873c27163dSratchov }
22883c27163dSratchov dev->un.e.member[n].ord = n;
22893c27163dSratchov snprintf(dev->un.e.member[n++].label.name,
2290fbdda81eSratchov MAX_AUDIO_DEV_LEN, "play-%d", idx);
229107f96c57Sratchov if (!sc->isht && idx < 2) {
22923c27163dSratchov dev->un.e.member[n].ord = n;
22933c27163dSratchov snprintf(dev->un.e.member[n++].label.name,
2294fbdda81eSratchov MAX_AUDIO_DEV_LEN, "mon-%d", idx);
22953c27163dSratchov }
2296b7a60a70Sratchov snprintf(dev->label.name, MAX_AUDIO_DEV_LEN,
2297fbdda81eSratchov AudioNline "-%d_" AudioNsource, idx);
22983c27163dSratchov dev->un.s.num_mem = n;
22993c27163dSratchov return 0;
23003c27163dSratchov }
23017aefcb42Sratchov idx -= ndev;
23027aefcb42Sratchov
23037aefcb42Sratchov /*
23047aefcb42Sratchov * envy monitor level
23057aefcb42Sratchov */
23068565898fSratchov ndev = sc->isht ? 0 : ENVY_MIX_NMONITOR;
23077aefcb42Sratchov if (idx < ndev) {
23083c27163dSratchov dev->type = AUDIO_MIXER_VALUE;
23093c27163dSratchov dev->mixer_class = ENVY_MIX_CLASSMON;
23103c27163dSratchov dev->un.v.delta = 2;
2311eb5d355fSratchov dev->un.v.num_channels = 1;
23123c27163dSratchov snprintf(dev->label.name, MAX_AUDIO_DEV_LEN,
2313fbdda81eSratchov "%s-%d", idx < 10 ? "play" : "rec", idx % 10);
23143c27163dSratchov strlcpy(dev->un.v.units.name, AudioNvolume, MAX_AUDIO_DEV_LEN);
23153c27163dSratchov return 0;
23163c27163dSratchov }
23177aefcb42Sratchov idx -= ndev;
23187aefcb42Sratchov
23197aefcb42Sratchov /*
23207399aea9Sratchov * inputs.xxx
23217aefcb42Sratchov */
232281bfbe5bSratchov ndev = sc->card->adc->ndev(sc);
23237aefcb42Sratchov if (idx < ndev) {
23247399aea9Sratchov sc->card->adc->devinfo(sc, dev, idx);
2325141aee41Sratchov return 0;
2326141aee41Sratchov }
23277aefcb42Sratchov idx -= ndev;
23287aefcb42Sratchov
23297aefcb42Sratchov /*
23307399aea9Sratchov * outputs.xxx
23317aefcb42Sratchov */
233281bfbe5bSratchov ndev = sc->card->dac->ndev(sc);
23337aefcb42Sratchov if (idx < ndev) {
23347399aea9Sratchov sc->card->dac->devinfo(sc, dev, idx);
2335141aee41Sratchov return 0;
2336141aee41Sratchov }
23373c27163dSratchov return ENXIO;
23383c27163dSratchov }
23393c27163dSratchov
23403c27163dSratchov int
envy_get_port(void * self,struct mixer_ctrl * ctl)23413c27163dSratchov envy_get_port(void *self, struct mixer_ctrl *ctl)
23423c27163dSratchov {
23433c27163dSratchov struct envy_softc *sc = (struct envy_softc *)self;
23447aefcb42Sratchov int val, idx, ndev;
23453c27163dSratchov
23463a87d643Sratchov if (sc->isac97)
23473a87d643Sratchov return (sc->codec_if->vtbl->mixer_get_port(sc->codec_if, ctl));
23483a87d643Sratchov
23497aefcb42Sratchov if (ctl->dev < ENVY_MIX_NCLASS) {
23503c27163dSratchov return EINVAL;
23513c27163dSratchov }
23527aefcb42Sratchov
23537aefcb42Sratchov idx = ctl->dev - ENVY_MIX_NCLASS;
235407f96c57Sratchov ndev = sc->card->noch;
23557aefcb42Sratchov if (idx < ndev) {
23567aefcb42Sratchov ctl->un.ord = envy_lineout_getsrc(sc, idx);
23572c4be9d3Sratchov if (ctl->un.ord >= ENVY_MIX_NOUTSRC)
235881bfbe5bSratchov ctl->un.ord -= ENVY_MIX_NOUTSRC - sc->card->nich;
23593c27163dSratchov return 0;
23603c27163dSratchov }
23617aefcb42Sratchov idx -= ndev;
23628565898fSratchov ndev = sc->isht ? 0 : ENVY_MIX_NMONITOR;
23637aefcb42Sratchov if (idx < ndev) {
23647aefcb42Sratchov envy_mon_getvol(sc, idx / 2, idx % 2, &val);
2365eb5d355fSratchov ctl->un.value.num_channels = 1;
2366eb5d355fSratchov ctl->un.value.level[0] = 2 * val;
23673c27163dSratchov return 0;
23683c27163dSratchov }
23697aefcb42Sratchov idx -= ndev;
237081bfbe5bSratchov ndev = sc->card->adc->ndev(sc);
23717aefcb42Sratchov if (idx < ndev) {
23727399aea9Sratchov sc->card->adc->get(sc, ctl, idx);
2373141aee41Sratchov return 0;
2374141aee41Sratchov }
23757aefcb42Sratchov idx -= ndev;
237681bfbe5bSratchov ndev = sc->card->dac->ndev(sc);
23777aefcb42Sratchov if (idx < ndev) {
23787399aea9Sratchov sc->card->dac->get(sc, ctl, idx);
2379141aee41Sratchov return 0;
2380141aee41Sratchov }
23813c27163dSratchov return ENXIO;
23823c27163dSratchov }
23833c27163dSratchov
23843c27163dSratchov int
envy_set_port(void * self,struct mixer_ctrl * ctl)23853c27163dSratchov envy_set_port(void *self, struct mixer_ctrl *ctl)
23863c27163dSratchov {
23873c27163dSratchov struct envy_softc *sc = (struct envy_softc *)self;
23887aefcb42Sratchov int maxsrc, val, idx, ndev;
23893c27163dSratchov
23903a87d643Sratchov if (sc->isac97)
23913a87d643Sratchov return (sc->codec_if->vtbl->mixer_set_port(sc->codec_if, ctl));
23923a87d643Sratchov
23937aefcb42Sratchov if (ctl->dev < ENVY_MIX_NCLASS) {
23943c27163dSratchov return EINVAL;
23953c27163dSratchov }
23967aefcb42Sratchov
23977aefcb42Sratchov idx = ctl->dev - ENVY_MIX_NCLASS;
239807f96c57Sratchov ndev = sc->card->noch;
23997aefcb42Sratchov if (idx < ndev) {
240081bfbe5bSratchov maxsrc = sc->card->nich + 1;
24012c4be9d3Sratchov if (idx < 2)
24022c4be9d3Sratchov maxsrc++;
24033c27163dSratchov if (ctl->un.ord < 0 || ctl->un.ord >= maxsrc)
24043c27163dSratchov return EINVAL;
240581bfbe5bSratchov if (ctl->un.ord >= sc->card->nich)
240681bfbe5bSratchov ctl->un.ord += ENVY_MIX_NOUTSRC - sc->card->nich;
24077aefcb42Sratchov envy_lineout_setsrc(sc, idx, ctl->un.ord);
24083c27163dSratchov return 0;
24093c27163dSratchov }
24107aefcb42Sratchov idx -= ndev;
24118565898fSratchov ndev = sc->isht ? 0 : ENVY_MIX_NMONITOR;
24127aefcb42Sratchov if (idx < ndev) {
2413eb5d355fSratchov if (ctl->un.value.num_channels != 1) {
24143c27163dSratchov return EINVAL;
24153c27163dSratchov }
2416eb5d355fSratchov val = ctl->un.value.level[0] / 2;
24177aefcb42Sratchov envy_mon_setvol(sc, idx / 2, idx % 2, val);
24183c27163dSratchov return 0;
24193c27163dSratchov }
24207aefcb42Sratchov idx -= ndev;
242181bfbe5bSratchov ndev = sc->card->adc->ndev(sc);
24227399aea9Sratchov if (idx < ndev)
24237399aea9Sratchov return sc->card->adc->set(sc, ctl, idx);
24247aefcb42Sratchov idx -= ndev;
242581bfbe5bSratchov ndev = sc->card->dac->ndev(sc);
24267399aea9Sratchov if (idx < ndev)
24277399aea9Sratchov return sc->card->dac->set(sc, ctl, idx);
24283c27163dSratchov return ENXIO;
24293c27163dSratchov }
24303c27163dSratchov
2431c737f4d2Sratchov #if NMIDI > 0
2432c737f4d2Sratchov int
envy_midi_open(void * self,int flags,void (* in)(void *,int),void (* out)(void *),void * arg)2433c737f4d2Sratchov envy_midi_open(void *self, int flags,
2434c737f4d2Sratchov void (*in)(void *, int),
2435c737f4d2Sratchov void (*out)(void *),
2436c737f4d2Sratchov void *arg)
2437c737f4d2Sratchov {
2438c737f4d2Sratchov struct envy_softc *sc = (struct envy_softc *)self;
24396f1c5514Sratchov unsigned int i, reg;
2440c737f4d2Sratchov
24416f1c5514Sratchov /* discard pending data */
24426f1c5514Sratchov for (i = 0; i < 128; i++) {
24436f1c5514Sratchov reg = envy_ccs_read(sc, ENVY_CCS_MIDISTAT0);
24446f1c5514Sratchov if (reg & ENVY_MIDISTAT_IEMPTY(sc))
24456f1c5514Sratchov break;
24466f1c5514Sratchov (void)envy_ccs_read(sc, ENVY_CCS_MIDIDATA0);
24476f1c5514Sratchov }
24486f1c5514Sratchov #ifdef ENVY_DEBUG
24496f1c5514Sratchov if (i > 0)
24506f1c5514Sratchov DPRINTF("%s: midi: discarded %u bytes\n", DEVNAME(sc), i);
24516f1c5514Sratchov #endif
24526f1c5514Sratchov
24536f1c5514Sratchov /* clear pending midi interrupt */
24546f1c5514Sratchov envy_ccs_write(sc, ENVY_CCS_INTSTAT, ENVY_CCS_INT_MIDI0);
24556f1c5514Sratchov
24566f1c5514Sratchov /* interrupts are disabled, it safe to manipulate these */
2457c737f4d2Sratchov sc->midi_in = in;
2458c737f4d2Sratchov sc->midi_out = out;
2459c737f4d2Sratchov sc->midi_arg = arg;
24606f1c5514Sratchov sc->midi_isopen = 1;
24616f1c5514Sratchov
24626f1c5514Sratchov /* enable interrupts */
24636f1c5514Sratchov reg = envy_ccs_read(sc, ENVY_CCS_INTMASK);
24646f1c5514Sratchov reg &= ~ENVY_CCS_INT_MIDI0;
24656f1c5514Sratchov envy_ccs_write(sc, ENVY_CCS_INTMASK, reg);
2466c737f4d2Sratchov return 0;
2467c737f4d2Sratchov }
2468c737f4d2Sratchov
2469c737f4d2Sratchov void
envy_midi_close(void * self)2470c737f4d2Sratchov envy_midi_close(void *self)
2471c737f4d2Sratchov {
2472c737f4d2Sratchov struct envy_softc *sc = (struct envy_softc *)self;
24736f1c5514Sratchov unsigned int reg;
2474c737f4d2Sratchov
24756f1c5514Sratchov /* wait for output fifo to drain */
2476254679ffScheloha tsleep_nsec(sc, PWAIT, "envymid", MSEC_TO_NSEC(100));
24776f1c5514Sratchov
24786f1c5514Sratchov /* disable interrupts */
24796f1c5514Sratchov reg = envy_ccs_read(sc, ENVY_CCS_INTMASK);
24806f1c5514Sratchov reg |= ENVY_CCS_INT_MIDI0;
24816f1c5514Sratchov envy_ccs_write(sc, ENVY_CCS_INTMASK, reg);
24826f1c5514Sratchov
24836f1c5514Sratchov /* interrupts are disabled, it safe to manipulate these */
2484c737f4d2Sratchov sc->midi_in = NULL;
2485c737f4d2Sratchov sc->midi_out = NULL;
24866f1c5514Sratchov sc->midi_isopen = 0;
2487c737f4d2Sratchov }
2488c737f4d2Sratchov
2489c737f4d2Sratchov int
envy_midi_output(void * self,int data)2490c737f4d2Sratchov envy_midi_output(void *self, int data)
2491c737f4d2Sratchov {
2492c737f4d2Sratchov struct envy_softc *sc = (struct envy_softc *)self;
2493f3db5e0dSratchov int st;
2494c737f4d2Sratchov
2495f3db5e0dSratchov st = envy_ccs_read(sc, ENVY_CCS_MIDISTAT0);
2496f3db5e0dSratchov if (st & ENVY_MIDISTAT_OBUSY(sc))
2497c737f4d2Sratchov return 0;
2498f3db5e0dSratchov envy_ccs_write(sc, ENVY_CCS_MIDIDATA0, data);
2499f3db5e0dSratchov return 1;
2500c737f4d2Sratchov }
2501c737f4d2Sratchov
2502c737f4d2Sratchov void
envy_midi_getinfo(void * self,struct midi_info * mi)2503c737f4d2Sratchov envy_midi_getinfo(void *self, struct midi_info *mi)
2504c737f4d2Sratchov {
2505c737f4d2Sratchov mi->props = MIDI_PROP_CAN_INPUT;
2506c737f4d2Sratchov mi->name = "Envy24 MIDI UART";
2507c737f4d2Sratchov }
2508c737f4d2Sratchov #endif
2509