From 4886ec588e04d37f1cd302d2cdc5969c366db030 Mon Sep 17 00:00:00 2001 From: Hasso Tepper Date: Sat, 16 Jun 2007 19:48:05 +0000 Subject: [PATCH] Bring in the latest sound changes from RELENG_6. Obtained-from: FreeBSD --- sys/conf/files | 5 +- sys/dev/sound/driver.c | 7 +- sys/dev/sound/driver/Makefile | 14 +- sys/dev/sound/driver/envy24/Makefile | 11 + sys/dev/sound/driver/envy24ht/Makefile | 11 + sys/dev/sound/driver/spicds/Makefile | 11 + sys/dev/sound/pci/atiixp.c | 24 +- sys/dev/sound/pci/envy24.c | 2657 ++++++++++++++++++++++++ sys/dev/sound/pci/envy24.h | 496 +++++ sys/dev/sound/pci/envy24ht.c | 2604 +++++++++++++++++++++++ sys/dev/sound/pci/envy24ht.h | 189 ++ sys/dev/sound/pci/hda/hda_reg.h | 52 +- sys/dev/sound/pci/hda/hdac.c | 2298 ++++++++++++++++---- sys/dev/sound/pci/hda/hdac_private.h | 30 +- sys/dev/sound/pci/ich.c | 478 +++-- sys/dev/sound/pci/maestro3.c | 182 +- sys/dev/sound/pci/spicds.c | 361 ++++ sys/dev/sound/pci/spicds.h | 122 ++ sys/dev/sound/pci/via8233.c | 8 +- sys/dev/sound/pci/via82c686.c | 8 +- sys/dev/sound/pcm/ac97.c | 49 +- sys/dev/sound/pcm/ac97.h | 7 +- sys/dev/sound/pcm/ac97_patch.c | 63 +- sys/dev/sound/pcm/ac97_patch.h | 6 +- sys/dev/sound/pcm/buffer.c | 38 +- sys/dev/sound/pcm/buffer.h | 7 +- sys/dev/sound/pcm/channel.c | 38 +- sys/dev/sound/pcm/mixer.c | 191 +- sys/dev/sound/pcm/mixer.h | 8 +- sys/dev/sound/pcm/sound.c | 11 +- sys/dev/sound/pcm/sound.h | 6 +- sys/dev/sound/pcm/vchan.c | 7 +- sys/dev/sound/usb/uaudio_pcm.c | 24 +- 33 files changed, 9252 insertions(+), 771 deletions(-) create mode 100644 sys/dev/sound/driver/envy24/Makefile create mode 100644 sys/dev/sound/driver/envy24ht/Makefile create mode 100644 sys/dev/sound/driver/spicds/Makefile create mode 100644 sys/dev/sound/pci/envy24.c create mode 100644 sys/dev/sound/pci/envy24.h create mode 100644 sys/dev/sound/pci/envy24ht.c create mode 100644 sys/dev/sound/pci/envy24ht.h create mode 100644 sys/dev/sound/pci/spicds.c create mode 100644 sys/dev/sound/pci/spicds.h diff --git a/sys/conf/files b/sys/conf/files index b4e95bac48..455d244ff4 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,5 +1,5 @@ # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/sys/conf/files,v 1.161 2007/06/16 18:55:28 dillon Exp $ +# $DragonFly: src/sys/conf/files,v 1.162 2007/06/16 19:48:04 hasso Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and @@ -1224,6 +1224,8 @@ dev/sound/pci/csapcm.c optional snd_csa pci dev/sound/pci/ds1.c optional snd_ds1 pci dev/sound/pci/emu10k1.c optional snd_emu10k1 pci \ dependency "emu10k1-alsa%diked.h" +dev/sound/pci/envy24.c optional snd_envy24 pci +dev/sound/pci/envy24ht.c optional snd_envy24ht pci dev/sound/pci/es137x.c optional snd_es137x pci dev/sound/pci/fm801.c optional snd_fm801 pci dev/sound/pci/hda/hdac.c optional snd_hda pci @@ -1232,6 +1234,7 @@ dev/sound/pci/maestro.c optional snd_maestro pci dev/sound/pci/maestro3.c optional snd_maestro3 pci dev/sound/pci/neomagic.c optional snd_neomagic pci dev/sound/pci/solo.c optional snd_solo pci +dev/sound/pci/spicds.c optional snd_spicds pci dev/sound/pci/t4dwave.c optional snd_t4dwave pci dev/sound/pci/via8233.c optional snd_via8233 pci dev/sound/pci/via82c686.c optional snd_via82c686 pci diff --git a/sys/dev/sound/driver.c b/sys/dev/sound/driver.c index c49858566a..6284b10ac8 100644 --- a/sys/dev/sound/driver.c +++ b/sys/dev/sound/driver.c @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/driver.c,v 1.13.2.2 2006/01/24 18:56:11 joel Exp $ - * $DragonFly: src/sys/dev/sound/driver.c,v 1.3 2007/01/04 21:47:00 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/driver.c,v 1.13.2.3 2007/05/13 21:11:40 ariff Exp $ + * $DragonFly: src/sys/dev/sound/driver.c,v 1.4 2007/06/16 19:48:04 hasso Exp $ */ #include @@ -63,6 +63,8 @@ MODULE_DEPEND(snd_driver, snd_csa, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_csapcm, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_ds1, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_emu10k1, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_envy24, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_envy24ht, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_es137x, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_es1888, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_ess, 1, 1, 1); @@ -78,6 +80,7 @@ MODULE_DEPEND(snd_driver, snd_sb16, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_sb8, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_sbc, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_solo, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_spicds, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_t4dwave, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_via8233, 1, 1, 1); MODULE_DEPEND(snd_driver, snd_via82c686, 1, 1, 1); diff --git a/sys/dev/sound/driver/Makefile b/sys/dev/sound/driver/Makefile index af2333cc53..ce4c2ddd23 100644 --- a/sys/dev/sound/driver/Makefile +++ b/sys/dev/sound/driver/Makefile @@ -1,15 +1,13 @@ -# $FreeBSD: src/sys/modules/sound/driver/Makefile,v 1.16.2.1 2005/12/30 19:55:54 netchild Exp $ -# $DragonFly: src/sys/dev/sound/driver/Makefile,v 1.3 2007/01/04 21:47:00 corecode Exp $ +# $FreeBSD: src/sys/modules/sound/driver/Makefile,v 1.16.2.2 2007/05/13 21:11:40 ariff Exp $ +# $DragonFly: src/sys/dev/sound/driver/Makefile,v 1.4 2007/06/16 19:48:04 hasso Exp $ .if ${MACHINE_ARCH} == "sparc64" SUBDIR = audiocs es137x .else -SUBDIR = als4000 ad1816 atiixp cmi cs4281 csa ds1 emu10k1 es137x ess -SUBDIR += fm801 ich maestro maestro3 mss neomagic sb16 sb8 sbc solo -SUBDIR += t4dwave via8233 via82c686 vibes -SUBDIR += hda -SUBDIR += driver -SUBDIR += uaudio +SUBDIR = als4000 ad1816 atiixp cmi cs4281 csa ds1 emu10k1 envy24 +SUBDIR += envy24ht es137x ess fm801 hda ich maestro maestro3 mss neomagic +SUBDIR += sb16 sb8 sbc solo spicds t4dwave via8233 via82c686 vibes +SUBDIR += driver uaudio .endif .include diff --git a/sys/dev/sound/driver/envy24/Makefile b/sys/dev/sound/driver/envy24/Makefile new file mode 100644 index 0000000000..c777971a32 --- /dev/null +++ b/sys/dev/sound/driver/envy24/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD: src/sys/modules/sound/driver/envy24/Makefile,v 1.3.2.1 2007/05/13 21:03:46 ariff Exp $ +# $DragonFly: src/sys/dev/sound/driver/envy24/Makefile,v 1.1 2007/06/16 19:48:05 hasso Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_envy24 +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= envy24.c + +KMODDEPS= sound +.include diff --git a/sys/dev/sound/driver/envy24ht/Makefile b/sys/dev/sound/driver/envy24ht/Makefile new file mode 100644 index 0000000000..8dab4cfa8d --- /dev/null +++ b/sys/dev/sound/driver/envy24ht/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD: src/sys/modules/sound/driver/envy24ht/Makefile,v 1.2.2.1 2007/05/13 21:03:46 ariff Exp $ +# $DragonFly: src/sys/dev/sound/driver/envy24ht/Makefile,v 1.1 2007/06/16 19:48:05 hasso Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_envy24ht +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= envy24ht.c + +KMODDEPS= sound +.include diff --git a/sys/dev/sound/driver/spicds/Makefile b/sys/dev/sound/driver/spicds/Makefile new file mode 100644 index 0000000000..37f0c14c77 --- /dev/null +++ b/sys/dev/sound/driver/spicds/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD: src/sys/modules/sound/driver/spicds/Makefile,v 1.2.2.1 2007/05/13 21:03:46 ariff Exp $ +# $DragonFly: src/sys/dev/sound/driver/spicds/Makefile,v 1.1 2007/06/16 19:48:05 hasso Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_spicds +SRCS= device_if.h bus_if.h isa_if.h pci_if.h +SRCS+= spicds.c + +KMODDEPS= sound +.include diff --git a/sys/dev/sound/pci/atiixp.c b/sys/dev/sound/pci/atiixp.c index a0e1e6626a..be387ef896 100644 --- a/sys/dev/sound/pci/atiixp.c +++ b/sys/dev/sound/pci/atiixp.c @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/atiixp.c,v 1.2.2.5 2006/11/14 13:08:11 ariff Exp $ - * $DragonFly: src/sys/dev/sound/pci/atiixp.c,v 1.3 2007/05/13 18:33:58 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/atiixp.c,v 1.2.2.7 2007/04/26 08:21:44 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/atiixp.c,v 1.4 2007/06/16 19:48:05 hasso Exp $ */ /* @@ -66,7 +66,7 @@ #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/atiixp.c,v 1.3 2007/05/13 18:33:58 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/atiixp.c,v 1.4 2007/06/16 19:48:05 hasso Exp $"); struct atiixp_dma_op { volatile uint32_t addr; @@ -800,6 +800,7 @@ atiixp_chip_post_init(void *arg) subdev = (pci_get_subdevice(sc->dev) << 16) | pci_get_subvendor(sc->dev); switch (subdev) { + case 0x11831043: /* ASUS A6R */ case 0x2043161f: /* Maxselect x710s - http://maxselect.ru/ */ ac97_setflags(sc->codec, ac97_getflags(sc->codec) | AC97_F_EAPD_INV); break; @@ -844,27 +845,30 @@ atiixp_release_resource(struct atiixp_info *sc) } if (sc->ih) { bus_teardown_intr(sc->dev, sc->irq, sc->ih); - sc->ih = 0; + sc->ih = NULL; } if (sc->reg) { bus_release_resource(sc->dev, sc->regtype, sc->regid, sc->reg); - sc->reg = 0; + sc->reg = NULL; } if (sc->irq) { bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irqid, sc->irq); - sc->irq = 0; + sc->irq = NULL; } if (sc->parent_dmat) { bus_dma_tag_destroy(sc->parent_dmat); - sc->parent_dmat = 0; + sc->parent_dmat = NULL; } - if (sc->sgd_dmamap) { + if (sc->sgd_dmamap) bus_dmamap_unload(sc->sgd_dmat, sc->sgd_dmamap); - sc->sgd_dmamap = 0; + if (sc->sgd_table) { + bus_dmamem_free(sc->sgd_dmat, sc->sgd_table, sc->sgd_dmamap); + sc->sgd_table = NULL; } + sc->sgd_dmamap = NULL; if (sc->sgd_dmat) { bus_dma_tag_destroy(sc->sgd_dmat); - sc->sgd_dmat = 0; + sc->sgd_dmat = NULL; } if (sc->lock) { snd_mtxfree(sc->lock); diff --git a/sys/dev/sound/pci/envy24.c b/sys/dev/sound/pci/envy24.c new file mode 100644 index 0000000000..3ede549bd3 --- /dev/null +++ b/sys/dev/sound/pci/envy24.c @@ -0,0 +1,2657 @@ +/* + * Copyright (c) 2001 Katsurajima Naoto + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/sound/pci/envy24.c,v 1.11.2.2 2007/06/11 19:33:27 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/envy24.c,v 1.1 2007/06/16 19:48:05 hasso Exp $ + */ + +#include +#include +#include +#include + +#include +#include + +#include "mixer_if.h" + +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/envy24.c,v 1.1 2007/06/16 19:48:05 hasso Exp $"); + +MALLOC_DEFINE(M_ENVY24, "envy24", "envy24 audio"); + +/* -------------------------------------------------------------------- */ + +struct sc_info; + +#define ENVY24_PLAY_CHNUM 10 +#define ENVY24_REC_CHNUM 12 +#define ENVY24_PLAY_BUFUNIT (4 /* byte/sample */ * 10 /* channel */) +#define ENVY24_REC_BUFUNIT (4 /* byte/sample */ * 12 /* channel */) +#define ENVY24_SAMPLE_NUM 4096 + +#define ENVY24_TIMEOUT 1000 + +#define ENVY24_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE) + +#define ENVY24_NAMELEN 32 + +#define SDA_GPIO 0x10 +#define SCL_GPIO 0x20 + +#define abs(i) (i < 0 ? -i : i) + +struct envy24_sample { + volatile u_int32_t buffer; +}; + +typedef struct envy24_sample sample32_t; + +/* channel registers */ +struct sc_chinfo { + struct snd_dbuf *buffer; + struct pcm_channel *channel; + struct sc_info *parent; + int dir; + unsigned num; /* hw channel number */ + + /* channel information */ + u_int32_t format; + u_int32_t speed; + u_int32_t blk; /* hw block size(dword) */ + + /* format conversion structure */ + u_int8_t *data; + unsigned int size; /* data buffer size(byte) */ + int unit; /* sample size(byte) */ + unsigned int offset; /* samples number offset */ + void (*emldma)(struct sc_chinfo *); + + /* flags */ + int run; +}; + +/* codec interface entrys */ +struct codec_entry { + void *(*create)(device_t dev, void *devinfo, int dir, int num); + void (*destroy)(void *codec); + void (*init)(void *codec); + void (*reinit)(void *codec); + void (*setvolume)(void *codec, int dir, unsigned int left, unsigned int right); + void (*setrate)(void *codec, int which, int rate); +}; + +/* system configuration information */ +struct cfg_info { + char *name; + u_int16_t subvendor, subdevice; + u_int8_t scfg, acl, i2s, spdif; + u_int8_t gpiomask, gpiostate, gpiodir; + u_int8_t cdti, cclk, cs, cif, type; + u_int8_t free; + struct codec_entry *codec; +}; + +/* device private data */ +struct sc_info { + device_t dev; + struct spinlock *lock; + + /* Control/Status registor */ + struct resource *cs; + int csid; + bus_space_tag_t cst; + bus_space_handle_t csh; + /* DDMA registor */ + struct resource *ddma; + int ddmaid; + bus_space_tag_t ddmat; + bus_space_handle_t ddmah; + /* Consumer Section DMA Channel Registers */ + struct resource *ds; + int dsid; + bus_space_tag_t dst; + bus_space_handle_t dsh; + /* MultiTrack registor */ + struct resource *mt; + int mtid; + bus_space_tag_t mtt; + bus_space_handle_t mth; + /* DMA tag */ + bus_dma_tag_t dmat; + /* IRQ resource */ + struct resource *irq; + int irqid; + void *ih; + + /* system configuration data */ + struct cfg_info *cfg; + + /* ADC/DAC number and info */ + int adcn, dacn; + void *adc[4], *dac[4]; + + /* mixer control data */ + u_int32_t src; + u_int8_t left[ENVY24_CHAN_NUM]; + u_int8_t right[ENVY24_CHAN_NUM]; + + /* Play/Record DMA fifo */ + sample32_t *pbuf; + sample32_t *rbuf; + u_int32_t psize, rsize; /* DMA buffer size(byte) */ + u_int16_t blk[2]; /* transfer check blocksize(dword) */ + bus_dmamap_t pmap, rmap; + + /* current status */ + u_int32_t speed; + int run[2]; + u_int16_t intr[2]; + struct pcmchan_caps caps[2]; + + /* channel info table */ + unsigned chnum; + struct sc_chinfo chan[11]; +}; + +/* -------------------------------------------------------------------- */ + +/* + * prototypes + */ + +/* DMA emulator */ +static void envy24_p8u(struct sc_chinfo *); +static void envy24_p16sl(struct sc_chinfo *); +static void envy24_p32sl(struct sc_chinfo *); +static void envy24_r16sl(struct sc_chinfo *); +static void envy24_r32sl(struct sc_chinfo *); + +/* channel interface */ +static void *envy24chan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int); +static int envy24chan_setformat(kobj_t, void *, u_int32_t); +static int envy24chan_setspeed(kobj_t, void *, u_int32_t); +static int envy24chan_setblocksize(kobj_t, void *, u_int32_t); +static int envy24chan_trigger(kobj_t, void *, int); +static int envy24chan_getptr(kobj_t, void *); +static struct pcmchan_caps *envy24chan_getcaps(kobj_t, void *); + +/* mixer interface */ +static int envy24mixer_init(struct snd_mixer *); +static int envy24mixer_reinit(struct snd_mixer *); +static int envy24mixer_uninit(struct snd_mixer *); +static int envy24mixer_set(struct snd_mixer *, unsigned, unsigned, unsigned); +static u_int32_t envy24mixer_setrecsrc(struct snd_mixer *, u_int32_t); + +/* M-Audio Delta series AK4524 access interface */ +static void *envy24_delta_ak4524_create(device_t, void *, int, int); +static void envy24_delta_ak4524_destroy(void *); +static void envy24_delta_ak4524_init(void *); +static void envy24_delta_ak4524_reinit(void *); +static void envy24_delta_ak4524_setvolume(void *, int, unsigned int, unsigned int); + +/* -------------------------------------------------------------------- */ + +/* + system constant tables +*/ + +/* API -> hardware channel map */ +static unsigned envy24_chanmap[ENVY24_CHAN_NUM] = { + ENVY24_CHAN_PLAY_SPDIF, /* 0 */ + ENVY24_CHAN_PLAY_DAC1, /* 1 */ + ENVY24_CHAN_PLAY_DAC2, /* 2 */ + ENVY24_CHAN_PLAY_DAC3, /* 3 */ + ENVY24_CHAN_PLAY_DAC4, /* 4 */ + ENVY24_CHAN_REC_MIX, /* 5 */ + ENVY24_CHAN_REC_SPDIF, /* 6 */ + ENVY24_CHAN_REC_ADC1, /* 7 */ + ENVY24_CHAN_REC_ADC2, /* 8 */ + ENVY24_CHAN_REC_ADC3, /* 9 */ + ENVY24_CHAN_REC_ADC4, /* 10 */ +}; + +/* mixer -> API channel map. see above */ +static int envy24_mixmap[] = { + -1, /* Master output level. It is depend on codec support */ + -1, /* Treble level of all output channels */ + -1, /* Bass level of all output channels */ + -1, /* Volume of synthesier input */ + 0, /* Output level for the audio device */ + -1, /* Output level for the PC speaker */ + 7, /* line in jack */ + -1, /* microphone jack */ + -1, /* CD audio input */ + -1, /* Recording monitor */ + 1, /* alternative codec */ + -1, /* global recording level */ + -1, /* Input gain */ + -1, /* Output gain */ + 8, /* Input source 1 */ + 9, /* Input source 2 */ + 10, /* Input source 3 */ + 6, /* Digital (input) 1 */ + -1, /* Digital (input) 2 */ + -1, /* Digital (input) 3 */ + -1, /* Phone input */ + -1, /* Phone output */ + -1, /* Video/TV (audio) in */ + -1, /* Radio in */ + -1, /* Monitor volume */ +}; + +/* variable rate audio */ +static u_int32_t envy24_speed[] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, + 12000, 11025, 9600, 8000, 0 +}; + +/* known boards configuration */ +static struct codec_entry delta_codec = { + envy24_delta_ak4524_create, + envy24_delta_ak4524_destroy, + envy24_delta_ak4524_init, + envy24_delta_ak4524_reinit, + envy24_delta_ak4524_setvolume, + NULL, /* setrate */ +}; + +static struct cfg_info cfg_table[] = { + { + "Envy24 audio (M Audio Delta Dio 2496)", + 0x1412, 0xd631, + 0x10, 0x80, 0xf0, 0x03, + 0xff, 0x00, 0x00, + 0x10, 0x20, 0x40, 0x00, 0x00, + 0x00, + &delta_codec, + }, + { + "Envy24 audio (Terratec DMX 6fire)", + 0x153b, 0x1138, + 0x2f, 0x80, 0xf0, 0x03, + 0xc0, 0xff, 0x7f, + 0x10, 0x20, 0x01, 0x01, 0x00, + 0x00, + &delta_codec, + }, + { + "Envy24 audio (M Audio Audiophile 2496)", + 0x1412, 0xd634, + 0x10, 0x80, 0x72, 0x03, + 0x04, 0xfe, 0xfb, + 0x08, 0x02, 0x20, 0x00, 0x01, + 0x00, + &delta_codec, + }, + { + "Envy24 audio (Generic)", + 0, 0, + 0x0f, 0x00, 0x01, 0x03, + 0xff, 0x00, 0x00, + 0x10, 0x20, 0x40, 0x00, 0x00, + 0x00, + &delta_codec, /* default codec routines */ + } +}; + +static u_int32_t envy24_recfmt[] = { + AFMT_STEREO | AFMT_S16_LE, + AFMT_STEREO | AFMT_S32_LE, + 0 +}; +static struct pcmchan_caps envy24_reccaps = {8000, 96000, envy24_recfmt, 0}; + +static u_int32_t envy24_playfmt[] = { + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_S16_LE, + AFMT_STEREO | AFMT_S32_LE, + 0 +}; + +static struct pcmchan_caps envy24_playcaps = {8000, 96000, envy24_playfmt, 0}; + +struct envy24_emldma { + u_int32_t format; + void (*emldma)(struct sc_chinfo *); + int unit; +}; + +static struct envy24_emldma envy24_pemltab[] = { + {AFMT_STEREO | AFMT_U8, envy24_p8u, 2}, + {AFMT_STEREO | AFMT_S16_LE, envy24_p16sl, 4}, + {AFMT_STEREO | AFMT_S32_LE, envy24_p32sl, 8}, + {0, NULL, 0} +}; + +static struct envy24_emldma envy24_remltab[] = { + {AFMT_STEREO | AFMT_S16_LE, envy24_r16sl, 4}, + {AFMT_STEREO | AFMT_S32_LE, envy24_r32sl, 8}, + {0, NULL, 0} +}; + +/* -------------------------------------------------------------------- */ + +/* common routines */ +static u_int32_t +envy24_rdcs(struct sc_info *sc, int regno, int size) +{ + switch (size) { + case 1: + return bus_space_read_1(sc->cst, sc->csh, regno); + case 2: + return bus_space_read_2(sc->cst, sc->csh, regno); + case 4: + return bus_space_read_4(sc->cst, sc->csh, regno); + default: + return 0xffffffff; + } +} + +static void +envy24_wrcs(struct sc_info *sc, int regno, u_int32_t data, int size) +{ + switch (size) { + case 1: + bus_space_write_1(sc->cst, sc->csh, regno, data); + break; + case 2: + bus_space_write_2(sc->cst, sc->csh, regno, data); + break; + case 4: + bus_space_write_4(sc->cst, sc->csh, regno, data); + break; + } +} + +static u_int32_t +envy24_rdmt(struct sc_info *sc, int regno, int size) +{ + switch (size) { + case 1: + return bus_space_read_1(sc->mtt, sc->mth, regno); + case 2: + return bus_space_read_2(sc->mtt, sc->mth, regno); + case 4: + return bus_space_read_4(sc->mtt, sc->mth, regno); + default: + return 0xffffffff; + } +} + +static void +envy24_wrmt(struct sc_info *sc, int regno, u_int32_t data, int size) +{ + switch (size) { + case 1: + bus_space_write_1(sc->mtt, sc->mth, regno, data); + break; + case 2: + bus_space_write_2(sc->mtt, sc->mth, regno, data); + break; + case 4: + bus_space_write_4(sc->mtt, sc->mth, regno, data); + break; + } +} + +static u_int32_t +envy24_rdci(struct sc_info *sc, int regno) +{ + envy24_wrcs(sc, ENVY24_CCS_INDEX, regno, 1); + return envy24_rdcs(sc, ENVY24_CCS_DATA, 1); +} + +static void +envy24_wrci(struct sc_info *sc, int regno, u_int32_t data) +{ + envy24_wrcs(sc, ENVY24_CCS_INDEX, regno, 1); + envy24_wrcs(sc, ENVY24_CCS_DATA, data, 1); +} + +/* -------------------------------------------------------------------- */ + +/* I2C port/E2PROM access routines */ + +static int +envy24_rdi2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr) +{ + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr); +#endif + for (i = 0; i < ENVY24_TIMEOUT; i++) { + data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1); + if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24_TIMEOUT) { + return -1; + } + envy24_wrcs(sc, ENVY24_CCS_I2CADDR, addr, 1); + envy24_wrcs(sc, ENVY24_CCS_I2CDEV, + (dev & ENVY24_CCS_I2CDEV_ADDR) | ENVY24_CCS_I2CDEV_RD, 1); + for (i = 0; i < ENVY24_TIMEOUT; i++) { + data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1); + if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24_TIMEOUT) { + return -1; + } + data = envy24_rdcs(sc, ENVY24_CCS_I2CDATA, 1); + +#if(0) + device_printf(sc->dev, "envy24_rdi2c(): return 0x%x\n", data); +#endif + return (int)data; +} + +#if 0 +static int +envy24_wri2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr, u_int32_t data) +{ + u_int32_t tmp; + int i; + +#if(0) + device_printf(sc->dev, "envy24_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr); +#endif + for (i = 0; i < ENVY24_TIMEOUT; i++) { + tmp = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1); + if ((tmp & ENVY24_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24_TIMEOUT) { + return -1; + } + envy24_wrcs(sc, ENVY24_CCS_I2CADDR, addr, 1); + envy24_wrcs(sc, ENVY24_CCS_I2CDATA, data, 1); + envy24_wrcs(sc, ENVY24_CCS_I2CDEV, + (dev & ENVY24_CCS_I2CDEV_ADDR) | ENVY24_CCS_I2CDEV_WR, 1); + for (i = 0; i < ENVY24_TIMEOUT; i++) { + data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1); + if ((data & ENVY24_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24_TIMEOUT) { + return -1; + } + + return 0; +} +#endif + +static int +envy24_rdrom(struct sc_info *sc, u_int32_t addr) +{ + u_int32_t data; + +#if(0) + device_printf(sc->dev, "envy24_rdrom(sc, 0x%02x)\n", addr); +#endif + data = envy24_rdcs(sc, ENVY24_CCS_I2CSTAT, 1); + if ((data & ENVY24_CCS_I2CSTAT_ROM) == 0) { +#if(0) + device_printf(sc->dev, "envy24_rdrom(): E2PROM not presented\n"); +#endif + return -1; + } + + return envy24_rdi2c(sc, ENVY24_CCS_I2CDEV_ROM, addr); +} + +static struct cfg_info * +envy24_rom2cfg(struct sc_info *sc) +{ + struct cfg_info *buff; + int size; + int i; + +#if(0) + device_printf(sc->dev, "envy24_rom2cfg(sc)\n"); +#endif + size = envy24_rdrom(sc, ENVY24_E2PROM_SIZE); + if (size < ENVY24_E2PROM_GPIODIR + 1) { +#if(0) + device_printf(sc->dev, "envy24_rom2cfg(): ENVY24_E2PROM_SIZE-->%d\n", size); +#endif + return NULL; + } + buff = kmalloc(sizeof(*buff), M_ENVY24, M_NOWAIT); + if (buff == NULL) { +#if(0) + device_printf(sc->dev, "envy24_rom2cfg(): malloc()\n"); +#endif + return NULL; + } + buff->free = 1; + + buff->subvendor = envy24_rdrom(sc, ENVY24_E2PROM_SUBVENDOR) << 8; + buff->subvendor += envy24_rdrom(sc, ENVY24_E2PROM_SUBVENDOR + 1); + buff->subdevice = envy24_rdrom(sc, ENVY24_E2PROM_SUBDEVICE) << 8; + buff->subdevice += envy24_rdrom(sc, ENVY24_E2PROM_SUBDEVICE + 1); + buff->scfg = envy24_rdrom(sc, ENVY24_E2PROM_SCFG); + buff->acl = envy24_rdrom(sc, ENVY24_E2PROM_ACL); + buff->i2s = envy24_rdrom(sc, ENVY24_E2PROM_I2S); + buff->spdif = envy24_rdrom(sc, ENVY24_E2PROM_SPDIF); + buff->gpiomask = envy24_rdrom(sc, ENVY24_E2PROM_GPIOMASK); + buff->gpiostate = envy24_rdrom(sc, ENVY24_E2PROM_GPIOSTATE); + buff->gpiodir = envy24_rdrom(sc, ENVY24_E2PROM_GPIODIR); + + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) + if (cfg_table[i].subvendor == buff->subvendor && + cfg_table[i].subdevice == buff->subdevice) + break; + buff->name = cfg_table[i].name; + buff->codec = cfg_table[i].codec; + + return buff; +} + +static void +envy24_cfgfree(struct cfg_info *cfg) { + if (cfg == NULL) + return; + if (cfg->free) + kfree(cfg, M_ENVY24); + return; +} + +/* -------------------------------------------------------------------- */ + +/* AC'97 codec access routines */ + +#if 0 +static int +envy24_coldcd(struct sc_info *sc) +{ + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24_coldcd()\n"); +#endif + envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_CLD, 1); + DELAY(10); + envy24_wrmt(sc, ENVY24_MT_AC97CMD, 0, 1); + DELAY(1000); + for (i = 0; i < ENVY24_TIMEOUT; i++) { + data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1); + if (data & ENVY24_MT_AC97CMD_RDY) { + return 0; + } + } + + return -1; +} +#endif + +static int +envy24_slavecd(struct sc_info *sc) +{ + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24_slavecd()\n"); +#endif + envy24_wrmt(sc, ENVY24_MT_AC97CMD, + ENVY24_MT_AC97CMD_CLD | ENVY24_MT_AC97CMD_WRM, 1); + DELAY(10); + envy24_wrmt(sc, ENVY24_MT_AC97CMD, 0, 1); + DELAY(1000); + for (i = 0; i < ENVY24_TIMEOUT; i++) { + data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1); + if (data & ENVY24_MT_AC97CMD_RDY) { + return 0; + } + } + + return -1; +} + +#if 0 +static int +envy24_rdcd(kobj_t obj, void *devinfo, int regno) +{ + struct sc_info *sc = (struct sc_info *)devinfo; + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24_rdcd(obj, sc, 0x%02x)\n", regno); +#endif + envy24_wrmt(sc, ENVY24_MT_AC97IDX, (u_int32_t)regno, 1); + envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_RD, 1); + for (i = 0; i < ENVY24_TIMEOUT; i++) { + data = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1); + if ((data & ENVY24_MT_AC97CMD_RD) == 0) + break; + } + data = envy24_rdmt(sc, ENVY24_MT_AC97DLO, 2); + +#if(0) + device_printf(sc->dev, "envy24_rdcd(): return 0x%x\n", data); +#endif + return (int)data; +} + +static int +envy24_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data) +{ + struct sc_info *sc = (struct sc_info *)devinfo; + u_int32_t cmd; + int i; + +#if(0) + device_printf(sc->dev, "envy24_wrcd(obj, sc, 0x%02x, 0x%04x)\n", regno, data); +#endif + envy24_wrmt(sc, ENVY24_MT_AC97IDX, (u_int32_t)regno, 1); + envy24_wrmt(sc, ENVY24_MT_AC97DLO, (u_int32_t)data, 2); + envy24_wrmt(sc, ENVY24_MT_AC97CMD, ENVY24_MT_AC97CMD_WR, 1); + for (i = 0; i < ENVY24_TIMEOUT; i++) { + cmd = envy24_rdmt(sc, ENVY24_MT_AC97CMD, 1); + if ((cmd & ENVY24_MT_AC97CMD_WR) == 0) + break; + } + + return 0; +} + +static kobj_method_t envy24_ac97_methods[] = { + KOBJMETHOD(ac97_read, envy24_rdcd), + KOBJMETHOD(ac97_write, envy24_wrcd), + {0, 0} +}; +AC97_DECLARE(envy24_ac97); +#endif + +/* -------------------------------------------------------------------- */ + +/* GPIO access routines */ + +static u_int32_t +envy24_gpiord(struct sc_info *sc) +{ + return envy24_rdci(sc, ENVY24_CCI_GPIODAT); +} + +static void +envy24_gpiowr(struct sc_info *sc, u_int32_t data) +{ +#if(0) + device_printf(sc->dev, "envy24_gpiowr(sc, 0x%02x)\n", data & 0xff); + return; +#endif + envy24_wrci(sc, ENVY24_CCI_GPIODAT, data); + return; +} + +#if 0 +static u_int32_t +envy24_gpiogetmask(struct sc_info *sc) +{ + return envy24_rdci(sc, ENVY24_CCI_GPIOMASK); +} +#endif + +static void +envy24_gpiosetmask(struct sc_info *sc, u_int32_t mask) +{ + envy24_wrci(sc, ENVY24_CCI_GPIOMASK, mask); + return; +} + +#if 0 +static u_int32_t +envy24_gpiogetdir(struct sc_info *sc) +{ + return envy24_rdci(sc, ENVY24_CCI_GPIOCTL); +} +#endif + +static void +envy24_gpiosetdir(struct sc_info *sc, u_int32_t dir) +{ + envy24_wrci(sc, ENVY24_CCI_GPIOCTL, dir); + return; +} + +/* -------------------------------------------------------------------- */ + +/* Envy24 I2C through GPIO bit-banging */ + +struct envy24_delta_ak4524_codec { + struct spicds_info *info; + struct sc_info *parent; + int dir; + int num; + int cs, cclk, cdti; +}; + +static void +envy24_gpio_i2c_ctl(void *codec, unsigned int scl, unsigned int sda) +{ + u_int32_t data = 0; + struct envy24_delta_ak4524_codec *ptr = codec; +#if(0) + device_printf(ptr->parent->dev, "--> %d, %d\n", scl, sda); +#endif + data = envy24_gpiord(ptr->parent); + data &= ~(SDA_GPIO | SCL_GPIO); + if (scl) data += SCL_GPIO; + if (sda) data += SDA_GPIO; + envy24_gpiowr(ptr->parent, data); + return; +} + +static void +i2c_wrbit(void *codec, void (*ctrl)(void*, unsigned int, unsigned int), int bit) +{ + struct envy24_delta_ak4524_codec *ptr = codec; + unsigned int sda; + + if (bit) + sda = 1; + else + sda = 0; + + ctrl(ptr, 0, sda); + DELAY(I2C_DELAY); + ctrl(ptr, 1, sda); + DELAY(I2C_DELAY); + ctrl(ptr, 0, sda); + DELAY(I2C_DELAY); +} + +static void +i2c_start(void *codec, void (*ctrl)(void*, unsigned int, unsigned int)) +{ + struct envy24_delta_ak4524_codec *ptr = codec; + + ctrl(ptr, 1, 1); + DELAY(I2C_DELAY); + ctrl(ptr, 1, 0); + DELAY(I2C_DELAY); + ctrl(ptr, 0, 0); + DELAY(I2C_DELAY); +} + +static void +i2c_stop(void *codec, void (*ctrl)(void*, unsigned int, unsigned int)) +{ + struct envy24_delta_ak4524_codec *ptr = codec; + + ctrl(ptr, 0, 0); + DELAY(I2C_DELAY); + ctrl(ptr, 1, 0); + DELAY(I2C_DELAY); + ctrl(ptr, 1, 1); + DELAY(I2C_DELAY); +} + +static void +i2c_ack(void *codec, void (*ctrl)(void*, unsigned int, unsigned int)) +{ + struct envy24_delta_ak4524_codec *ptr = codec; + + ctrl(ptr, 0, 1); + DELAY(I2C_DELAY); + ctrl(ptr, 1, 1); + DELAY(I2C_DELAY); + /* dummy, need routine to change gpio direction */ + ctrl(ptr, 0, 1); + DELAY(I2C_DELAY); +} + +static void +i2c_wr(void *codec, void (*ctrl)(void*, unsigned int, unsigned int), u_int32_t dev, int reg, u_int8_t val) +{ + struct envy24_delta_ak4524_codec *ptr = codec; + int mask; + + i2c_start(ptr, ctrl); + + for (mask = 0x80; mask != 0; mask >>= 1) + i2c_wrbit(ptr, ctrl, dev & mask); + i2c_ack(ptr, ctrl); + + if (reg != 0xff) { + for (mask = 0x80; mask != 0; mask >>= 1) + i2c_wrbit(ptr, ctrl, reg & mask); + i2c_ack(ptr, ctrl); + } + + for (mask = 0x80; mask != 0; mask >>= 1) + i2c_wrbit(ptr, ctrl, val & mask); + i2c_ack(ptr, ctrl); + + i2c_stop(ptr, ctrl); +} + +/* -------------------------------------------------------------------- */ + +/* M-Audio Delta series AK4524 access interface routine */ + +static void +envy24_delta_ak4524_ctl(void *codec, unsigned int cs, unsigned int cclk, unsigned int cdti) +{ + u_int32_t data = 0; + struct envy24_delta_ak4524_codec *ptr = codec; + +#if(0) + device_printf(ptr->parent->dev, "--> %d, %d, %d\n", cs, cclk, cdti); +#endif + data = envy24_gpiord(ptr->parent); + data &= ~(ptr->cs | ptr->cclk | ptr->cdti); + if (cs) data += ptr->cs; + if (cclk) data += ptr->cclk; + if (cdti) data += ptr->cdti; + envy24_gpiowr(ptr->parent, data); + return; +} + +static void * +envy24_delta_ak4524_create(device_t dev, void *info, int dir, int num) +{ + struct sc_info *sc = info; + struct envy24_delta_ak4524_codec *buff = NULL; + +#if(0) + device_printf(sc->dev, "envy24_delta_ak4524_create(dev, sc, %d, %d)\n", dir, num); +#endif + + buff = kmalloc(sizeof(*buff), M_ENVY24, M_NOWAIT); + if (buff == NULL) + return NULL; + + if (dir == PCMDIR_REC && sc->adc[num] != NULL) + buff->info = ((struct envy24_delta_ak4524_codec *)sc->adc[num])->info; + else if (dir == PCMDIR_PLAY && sc->dac[num] != NULL) + buff->info = ((struct envy24_delta_ak4524_codec *)sc->dac[num])->info; + else + buff->info = spicds_create(dev, buff, num, envy24_delta_ak4524_ctl); + if (buff->info == NULL) { + kfree(buff, M_ENVY24); + return NULL; + } + + buff->parent = sc; + buff->dir = dir; + buff->num = num; + + return (void *)buff; +} + +static void +envy24_delta_ak4524_destroy(void *codec) +{ + struct envy24_delta_ak4524_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24_delta_ak4524_destroy()\n"); +#endif + + if (ptr->dir == PCMDIR_PLAY) { + if (ptr->parent->dac[ptr->num] != NULL) + spicds_destroy(ptr->info); + } + else { + if (ptr->parent->adc[ptr->num] != NULL) + spicds_destroy(ptr->info); + } + + kfree(codec, M_ENVY24); +} + +static void +envy24_delta_ak4524_init(void *codec) +{ +#if 0 + u_int32_t gpiomask, gpiodir; +#endif + struct envy24_delta_ak4524_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24_delta_ak4524_init()\n"); +#endif + + /* + gpiomask = envy24_gpiogetmask(ptr->parent); + gpiomask &= ~(ENVY24_GPIO_AK4524_CDTI | ENVY24_GPIO_AK4524_CCLK | ENVY24_GPIO_AK4524_CS0 | ENVY24_GPIO_AK4524_CS1); + envy24_gpiosetmask(ptr->parent, gpiomask); + gpiodir = envy24_gpiogetdir(ptr->parent); + gpiodir |= ENVY24_GPIO_AK4524_CDTI | ENVY24_GPIO_AK4524_CCLK | ENVY24_GPIO_AK4524_CS0 | ENVY24_GPIO_AK4524_CS1; + envy24_gpiosetdir(ptr->parent, gpiodir); + */ + ptr->cs = ptr->parent->cfg->cs; +#if 0 + envy24_gpiosetmask(ptr->parent, ENVY24_GPIO_CS8414_STATUS); + envy24_gpiosetdir(ptr->parent, ~ENVY24_GPIO_CS8414_STATUS); + if (ptr->num == 0) + ptr->cs = ENVY24_GPIO_AK4524_CS0; + else + ptr->cs = ENVY24_GPIO_AK4524_CS1; + ptr->cclk = ENVY24_GPIO_AK4524_CCLK; +#endif + ptr->cclk = ptr->parent->cfg->cclk; + ptr->cdti = ptr->parent->cfg->cdti; + spicds_settype(ptr->info, ptr->parent->cfg->type); + spicds_setcif(ptr->info, ptr->parent->cfg->cif); + spicds_setformat(ptr->info, + AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X); + spicds_setdvc(ptr->info, AK452X_DVC_DEMOFF); + /* for the time being, init only first codec */ + if (ptr->num == 0) + spicds_init(ptr->info); + + /* 6fire rear input init test, set ptr->num to 1 for test */ + if (ptr->parent->cfg->subvendor == 0x153b && \ + ptr->parent->cfg->subdevice == 0x1138 && ptr->num == 100) { + ptr->cs = 0x02; + spicds_init(ptr->info); + device_printf(ptr->parent->dev, "6fire rear input init\n"); + i2c_wr(ptr, envy24_gpio_i2c_ctl, \ + PCA9554_I2CDEV, PCA9554_DIR, 0x80); + i2c_wr(ptr, envy24_gpio_i2c_ctl, \ + PCA9554_I2CDEV, PCA9554_OUT, 0x02); + } +} + +static void +envy24_delta_ak4524_reinit(void *codec) +{ + struct envy24_delta_ak4524_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24_delta_ak4524_reinit()\n"); +#endif + + spicds_reinit(ptr->info); +} + +static void +envy24_delta_ak4524_setvolume(void *codec, int dir, unsigned int left, unsigned int right) +{ + struct envy24_delta_ak4524_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24_delta_ak4524_set()\n"); +#endif + + spicds_set(ptr->info, dir, left, right); +} + +/* + There is no need for AK452[48] codec to set sample rate + static void + envy24_delta_ak4524_setrate(struct envy24_delta_ak4524_codec *codec, int which, int rate) + { + } +*/ + +/* -------------------------------------------------------------------- */ + +/* hardware access routeines */ + +static struct { + u_int32_t speed; + u_int32_t code; +} envy24_speedtab[] = { + {48000, ENVY24_MT_RATE_48000}, + {24000, ENVY24_MT_RATE_24000}, + {12000, ENVY24_MT_RATE_12000}, + {9600, ENVY24_MT_RATE_9600}, + {32000, ENVY24_MT_RATE_32000}, + {16000, ENVY24_MT_RATE_16000}, + {8000, ENVY24_MT_RATE_8000}, + {96000, ENVY24_MT_RATE_96000}, + {64000, ENVY24_MT_RATE_64000}, + {44100, ENVY24_MT_RATE_44100}, + {22050, ENVY24_MT_RATE_22050}, + {11025, ENVY24_MT_RATE_11025}, + {88200, ENVY24_MT_RATE_88200}, + {0, 0x10} +}; + +static int +envy24_setspeed(struct sc_info *sc, u_int32_t speed) { + u_int32_t code; + int i = 0; + +#if(0) + device_printf(sc->dev, "envy24_setspeed(sc, %d)\n", speed); +#endif + if (speed == 0) { + code = ENVY24_MT_RATE_SPDIF; /* external master clock */ + envy24_slavecd(sc); + } + else { + for (i = 0; envy24_speedtab[i].speed != 0; i++) { + if (envy24_speedtab[i].speed == speed) + break; + } + code = envy24_speedtab[i].code; + } +#if(0) + device_printf(sc->dev, "envy24_setspeed(): speed %d/code 0x%04x\n", envy24_speedtab[i].speed, code); +#endif + if (code < 0x10) { + envy24_wrmt(sc, ENVY24_MT_RATE, code, 1); + code = envy24_rdmt(sc, ENVY24_MT_RATE, 1); + code &= ENVY24_MT_RATE_MASK; + for (i = 0; envy24_speedtab[i].code < 0x10; i++) { + if (envy24_speedtab[i].code == code) + break; + } + speed = envy24_speedtab[i].speed; + } + else + speed = 0; + +#if(0) + device_printf(sc->dev, "envy24_setspeed(): return %d\n", speed); +#endif + return speed; +} + +static void +envy24_setvolume(struct sc_info *sc, unsigned ch) +{ +#if(0) + device_printf(sc->dev, "envy24_setvolume(sc, %d)\n", ch); +#endif +if (sc->cfg->subvendor==0x153b && sc->cfg->subdevice==0x1138 ) { + envy24_wrmt(sc, ENVY24_MT_VOLIDX, 16, 1); + envy24_wrmt(sc, ENVY24_MT_VOLUME, 0x7f7f, 2); + envy24_wrmt(sc, ENVY24_MT_VOLIDX, 17, 1); + envy24_wrmt(sc, ENVY24_MT_VOLUME, 0x7f7f, 2); + } + + envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2, 1); + envy24_wrmt(sc, ENVY24_MT_VOLUME, 0x7f00 | sc->left[ch], 2); + envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2 + 1, 1); + envy24_wrmt(sc, ENVY24_MT_VOLUME, (sc->right[ch] << 8) | 0x7f, 2); +} + +static void +envy24_mutevolume(struct sc_info *sc, unsigned ch) +{ + u_int32_t vol; + +#if(0) + device_printf(sc->dev, "envy24_mutevolume(sc, %d)\n", ch); +#endif + vol = ENVY24_VOL_MUTE << 8 | ENVY24_VOL_MUTE; + envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2, 1); + envy24_wrmt(sc, ENVY24_MT_VOLUME, vol, 2); + envy24_wrmt(sc, ENVY24_MT_VOLIDX, ch * 2 + 1, 1); + envy24_wrmt(sc, ENVY24_MT_VOLUME, vol, 2); +} + +static u_int32_t +envy24_gethwptr(struct sc_info *sc, int dir) +{ + int unit, regno; + u_int32_t ptr, rtn; + +#if(0) + device_printf(sc->dev, "envy24_gethwptr(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) { + rtn = sc->psize / 4; + unit = ENVY24_PLAY_BUFUNIT / 4; + regno = ENVY24_MT_PCNT; + } + else { + rtn = sc->rsize / 4; + unit = ENVY24_REC_BUFUNIT / 4; + regno = ENVY24_MT_RCNT; + } + + ptr = envy24_rdmt(sc, regno, 2); + rtn -= (ptr + 1); + rtn /= unit; + +#if(0) + device_printf(sc->dev, "envy24_gethwptr(): return %d\n", rtn); +#endif + return rtn; +} + +static void +envy24_updintr(struct sc_info *sc, int dir) +{ + int regptr, regintr; + u_int32_t mask, intr; + u_int32_t ptr, size, cnt; + u_int16_t blk; + +#if(0) + device_printf(sc->dev, "envy24_updintr(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) { + blk = sc->blk[0]; + size = sc->psize / 4; + regptr = ENVY24_MT_PCNT; + regintr = ENVY24_MT_PTERM; + mask = ~ENVY24_MT_INT_PMASK; + } + else { + blk = sc->blk[1]; + size = sc->rsize / 4; + regptr = ENVY24_MT_RCNT; + regintr = ENVY24_MT_RTERM; + mask = ~ENVY24_MT_INT_RMASK; + } + + ptr = size - envy24_rdmt(sc, regptr, 2) - 1; + /* + cnt = blk - ptr % blk - 1; + if (cnt == 0) + cnt = blk - 1; + */ + cnt = blk - 1; +#if(0) + device_printf(sc->dev, "envy24_updintr():ptr = %d, blk = %d, cnt = %d\n", ptr, blk, cnt); +#endif + envy24_wrmt(sc, regintr, cnt, 2); + intr = envy24_rdmt(sc, ENVY24_MT_INT, 1); +#if(0) + device_printf(sc->dev, "envy24_updintr():intr = 0x%02x, mask = 0x%02x\n", intr, mask); +#endif + envy24_wrmt(sc, ENVY24_MT_INT, intr & mask, 1); +#if(0) + device_printf(sc->dev, "envy24_updintr():INT-->0x%02x\n", + envy24_rdmt(sc, ENVY24_MT_INT, 1)); +#endif + + return; +} + +#if 0 +static void +envy24_maskintr(struct sc_info *sc, int dir) +{ + u_int32_t mask, intr; + +#if(0) + device_printf(sc->dev, "envy24_maskintr(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) + mask = ENVY24_MT_INT_PMASK; + else + mask = ENVY24_MT_INT_RMASK; + intr = envy24_rdmt(sc, ENVY24_MT_INT, 1); + envy24_wrmt(sc, ENVY24_MT_INT, intr | mask, 1); + + return; +} +#endif + +static int +envy24_checkintr(struct sc_info *sc, int dir) +{ + u_int32_t mask, stat, intr, rtn; + +#if(0) + device_printf(sc->dev, "envy24_checkintr(sc, %d)\n", dir); +#endif + intr = envy24_rdmt(sc, ENVY24_MT_INT, 1); + if (dir == PCMDIR_PLAY) { + if ((rtn = intr & ENVY24_MT_INT_PSTAT) != 0) { + mask = ~ENVY24_MT_INT_RSTAT; + stat = ENVY24_MT_INT_PSTAT | ENVY24_MT_INT_PMASK; + envy24_wrmt(sc, ENVY24_MT_INT, (intr & mask) | stat, 1); + } + } + else { + if ((rtn = intr & ENVY24_MT_INT_RSTAT) != 0) { + mask = ~ENVY24_MT_INT_PSTAT; + stat = ENVY24_MT_INT_RSTAT | ENVY24_MT_INT_RMASK; + envy24_wrmt(sc, ENVY24_MT_INT, (intr & mask) | stat, 1); + } + } + + return rtn; +} + +static void +envy24_start(struct sc_info *sc, int dir) +{ + u_int32_t stat, sw; + +#if(0) + device_printf(sc->dev, "envy24_start(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) + sw = ENVY24_MT_PCTL_PSTART; + else + sw = ENVY24_MT_PCTL_RSTART; + + stat = envy24_rdmt(sc, ENVY24_MT_PCTL, 1); + envy24_wrmt(sc, ENVY24_MT_PCTL, stat | sw, 1); +#if(0) + DELAY(100); + device_printf(sc->dev, "PADDR:0x%08x\n", envy24_rdmt(sc, ENVY24_MT_PADDR, 4)); + device_printf(sc->dev, "PCNT:%ld\n", envy24_rdmt(sc, ENVY24_MT_PCNT, 2)); +#endif + + return; +} + +static void +envy24_stop(struct sc_info *sc, int dir) +{ + u_int32_t stat, sw; + +#if(0) + device_printf(sc->dev, "envy24_stop(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) + sw = ~ENVY24_MT_PCTL_PSTART; + else + sw = ~ENVY24_MT_PCTL_RSTART; + + stat = envy24_rdmt(sc, ENVY24_MT_PCTL, 1); + envy24_wrmt(sc, ENVY24_MT_PCTL, stat & sw, 1); + + return; +} + +static int +envy24_route(struct sc_info *sc, int dac, int class, int adc, int rev) +{ + u_int32_t reg, mask; + u_int32_t left, right; + +#if(0) + device_printf(sc->dev, "envy24_route(sc, %d, %d, %d, %d)\n", + dac, class, adc, rev); +#endif + /* parameter pattern check */ + if (dac < 0 || ENVY24_ROUTE_DAC_SPDIF < dac) + return -1; + if (class == ENVY24_ROUTE_CLASS_MIX && + (dac != ENVY24_ROUTE_DAC_1 && dac != ENVY24_ROUTE_DAC_SPDIF)) + return -1; + if (rev) { + left = ENVY24_ROUTE_RIGHT; + right = ENVY24_ROUTE_LEFT; + } + else { + left = ENVY24_ROUTE_LEFT; + right = ENVY24_ROUTE_RIGHT; + } + + if (dac == ENVY24_ROUTE_DAC_SPDIF) { + reg = class | class << 2 | + ((adc << 1 | left) | left << 3) << 8 | + ((adc << 1 | right) | right << 3) << 12; +#if(0) + device_printf(sc->dev, "envy24_route(): MT_SPDOUT-->0x%04x\n", reg); +#endif + envy24_wrmt(sc, ENVY24_MT_SPDOUT, reg, 2); + } + else { + mask = ~(0x0303 << dac * 2); + reg = envy24_rdmt(sc, ENVY24_MT_PSDOUT, 2); + reg = (reg & mask) | ((class | class << 8) << dac * 2); +#if(0) + device_printf(sc->dev, "envy24_route(): MT_PSDOUT-->0x%04x\n", reg); +#endif + envy24_wrmt(sc, ENVY24_MT_PSDOUT, reg, 2); + mask = ~(0xff << dac * 8); + reg = envy24_rdmt(sc, ENVY24_MT_RECORD, 4); + reg = (reg & mask) | + (((adc << 1 | left) | left << 3) | + ((adc << 1 | right) | right << 3) << 4) << dac * 8; +#if(0) + device_printf(sc->dev, "envy24_route(): MT_RECORD-->0x%08x\n", reg); +#endif + envy24_wrmt(sc, ENVY24_MT_RECORD, reg, 4); + + /* 6fire rear input init test */ + envy24_wrmt(sc, ENVY24_MT_RECORD, 0x00, 4); + } + + return 0; +} + +/* -------------------------------------------------------------------- */ + +/* buffer copy routines */ +static void +envy24_p32sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int32_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getready(ch->buffer) / 8; + dmabuf = ch->parent->pbuf; + data = (u_int32_t *)ch->data; + src = sndbuf_getreadyptr(ch->buffer) / 4; + dst = src / 2 + ch->offset; + ssize = ch->size / 4; + dsize = ch->size / 8; + slot = ch->num * 2; + + for (i = 0; i < length; i++) { + dmabuf[dst * ENVY24_PLAY_CHNUM + slot].buffer = data[src]; + dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1].buffer = data[src + 1]; + dst++; + dst %= dsize; + src += 2; + src %= ssize; + } + + return; +} + +static void +envy24_p16sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int16_t *data; + int src, dst, ssize, dsize, slot; + int i; + +#if(0) + device_printf(ch->parent->dev, "envy24_p16sl()\n"); +#endif + length = sndbuf_getready(ch->buffer) / 4; + dmabuf = ch->parent->pbuf; + data = (u_int16_t *)ch->data; + src = sndbuf_getreadyptr(ch->buffer) / 2; + dst = src / 2 + ch->offset; + ssize = ch->size / 2; + dsize = ch->size / 4; + slot = ch->num * 2; +#if(0) + device_printf(ch->parent->dev, "envy24_p16sl():%lu-->%lu(%lu)\n", src, dst, length); +#endif + + for (i = 0; i < length; i++) { + dmabuf[dst * ENVY24_PLAY_CHNUM + slot].buffer = (u_int32_t)data[src] << 16; + dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1].buffer = (u_int32_t)data[src + 1] << 16; +#if(0) + if (i < 16) { + printf("%08x", dmabuf[dst * ENVY24_PLAY_CHNUM + slot]); + printf("%08x", dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1]); + } +#endif + dst++; + dst %= dsize; + src += 2; + src %= ssize; + } +#if(0) + printf("\n"); +#endif + + return; +} + +static void +envy24_p8u(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int8_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getready(ch->buffer) / 2; + dmabuf = ch->parent->pbuf; + data = (u_int8_t *)ch->data; + src = sndbuf_getreadyptr(ch->buffer); + dst = src / 2 + ch->offset; + ssize = ch->size; + dsize = ch->size / 4; + slot = ch->num * 2; + + for (i = 0; i < length; i++) { + dmabuf[dst * ENVY24_PLAY_CHNUM + slot].buffer = ((u_int32_t)data[src] ^ 0x80) << 24; + dmabuf[dst * ENVY24_PLAY_CHNUM + slot + 1].buffer = ((u_int32_t)data[src + 1] ^ 0x80) << 24; + dst++; + dst %= dsize; + src += 2; + src %= ssize; + } + + return; +} + +static void +envy24_r32sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int32_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getfree(ch->buffer) / 8; + dmabuf = ch->parent->rbuf; + data = (u_int32_t *)ch->data; + dst = sndbuf_getfreeptr(ch->buffer) / 4; + src = dst / 2 + ch->offset; + dsize = ch->size / 4; + ssize = ch->size / 8; + slot = (ch->num - ENVY24_CHAN_REC_ADC1) * 2; + + for (i = 0; i < length; i++) { + data[dst] = dmabuf[src * ENVY24_REC_CHNUM + slot].buffer; + data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1].buffer; + dst += 2; + dst %= dsize; + src++; + src %= ssize; + } + + return; +} + +static void +envy24_r16sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int16_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getfree(ch->buffer) / 4; + dmabuf = ch->parent->rbuf; + data = (u_int16_t *)ch->data; + dst = sndbuf_getfreeptr(ch->buffer) / 2; + src = dst / 2 + ch->offset; + dsize = ch->size / 2; + ssize = ch->size / 8; + slot = (ch->num - ENVY24_CHAN_REC_ADC1) * 2; + + for (i = 0; i < length; i++) { + data[dst] = dmabuf[src * ENVY24_REC_CHNUM + slot].buffer; + data[dst + 1] = dmabuf[src * ENVY24_REC_CHNUM + slot + 1].buffer; + dst += 2; + dst %= dsize; + src++; + src %= ssize; + } + + return; +} + +/* -------------------------------------------------------------------- */ + +/* channel interface */ +static void * +envy24chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +{ + struct sc_info *sc = (struct sc_info *)devinfo; + struct sc_chinfo *ch; + unsigned num; + +#if(0) + device_printf(sc->dev, "envy24chan_init(obj, devinfo, b, c, %d)\n", dir); +#endif + snd_mtxlock(sc->lock); + if ((sc->chnum > ENVY24_CHAN_PLAY_SPDIF && dir != PCMDIR_REC) || + (sc->chnum < ENVY24_CHAN_REC_ADC1 && dir != PCMDIR_PLAY)) { + snd_mtxunlock(sc->lock); + return NULL; + } + num = sc->chnum; + + ch = &sc->chan[num]; + ch->size = 8 * ENVY24_SAMPLE_NUM; + ch->data = kmalloc(ch->size, M_ENVY24, M_NOWAIT); + if (ch->data == NULL) { + ch->size = 0; + ch = NULL; + } + else { + ch->buffer = b; + ch->channel = c; + ch->parent = sc; + ch->dir = dir; + /* set channel map */ + ch->num = envy24_chanmap[num]; + snd_mtxunlock(sc->lock); + sndbuf_setup(ch->buffer, ch->data, ch->size); + snd_mtxlock(sc->lock); + /* these 2 values are dummy */ + ch->unit = 4; + ch->blk = 10240; + } + snd_mtxunlock(sc->lock); + + return ch; +} + +static int +envy24chan_free(kobj_t obj, void *data) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + +#if(0) + device_printf(sc->dev, "envy24chan_free()\n"); +#endif + snd_mtxlock(sc->lock); + if (ch->data != NULL) { + kfree(ch->data, M_ENVY24); + ch->data = NULL; + } + snd_mtxunlock(sc->lock); + + return 0; +} + +static int +envy24chan_setformat(kobj_t obj, void *data, u_int32_t format) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + struct envy24_emldma *emltab; + /* unsigned int bcnt, bsize; */ + int i; + +#if(0) + device_printf(sc->dev, "envy24chan_setformat(obj, data, 0x%08x)\n", format); +#endif + snd_mtxlock(sc->lock); + /* check and get format related information */ + if (ch->dir == PCMDIR_PLAY) + emltab = envy24_pemltab; + else + emltab = envy24_remltab; + if (emltab == NULL) { + snd_mtxunlock(sc->lock); + return -1; + } + for (i = 0; emltab[i].format != 0; i++) + if (emltab[i].format == format) + break; + if (emltab[i].format == 0) { + snd_mtxunlock(sc->lock); + return -1; + } + + /* set format information */ + ch->format = format; + ch->emldma = emltab[i].emldma; + if (ch->unit > emltab[i].unit) + ch->blk *= ch->unit / emltab[i].unit; + else + ch->blk /= emltab[i].unit / ch->unit; + ch->unit = emltab[i].unit; + + /* set channel buffer information */ + ch->size = ch->unit * ENVY24_SAMPLE_NUM; +#if 0 + if (ch->dir == PCMDIR_PLAY) + bsize = ch->blk * 4 / ENVY24_PLAY_BUFUNIT; + else + bsize = ch->blk * 4 / ENVY24_REC_BUFUNIT; + bsize *= ch->unit; + bcnt = ch->size / bsize; + sndbuf_resize(ch->buffer, bcnt, bsize); +#endif + snd_mtxunlock(sc->lock); + +#if(0) + device_printf(sc->dev, "envy24chan_setformat(): return 0x%08x\n", 0); +#endif + return 0; +} + +/* + IMPLEMENT NOTICE: In this driver, setspeed function only do setting + of speed information value. And real hardware speed setting is done + at start triggered(see envy24chan_trigger()). So, at this function + is called, any value that ENVY24 can use is able to set. But, at + start triggerd, some other channel is running, and that channel's + speed isn't same with, then trigger function will fail. +*/ +static int +envy24chan_setspeed(kobj_t obj, void *data, u_int32_t speed) +{ + struct sc_chinfo *ch = data; + u_int32_t val, prev; + int i; + +#if(0) + device_printf(ch->parent->dev, "envy24chan_setspeed(obj, data, %d)\n", speed); +#endif + prev = 0x7fffffff; + for (i = 0; (val = envy24_speed[i]) != 0; i++) { + if (abs(val - speed) < abs(prev - speed)) + prev = val; + else + break; + } + ch->speed = prev; + +#if(0) + device_printf(ch->parent->dev, "envy24chan_setspeed(): return %d\n", ch->speed); +#endif + return ch->speed; +} + +static int +envy24chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +{ + struct sc_chinfo *ch = data; + /* struct sc_info *sc = ch->parent; */ + u_int32_t size, prev; + unsigned int bcnt, bsize; + +#if(0) + device_printf(sc->dev, "envy24chan_setblocksize(obj, data, %d)\n", blocksize); +#endif + prev = 0x7fffffff; + /* snd_mtxlock(sc->lock); */ + for (size = ch->size / 2; size > 0; size /= 2) { + if (abs(size - blocksize) < abs(prev - blocksize)) + prev = size; + else + break; + } + + ch->blk = prev / ch->unit; + if (ch->dir == PCMDIR_PLAY) + ch->blk *= ENVY24_PLAY_BUFUNIT / 4; + else + ch->blk *= ENVY24_REC_BUFUNIT / 4; + /* set channel buffer information */ + /* ch->size = ch->unit * ENVY24_SAMPLE_NUM; */ + if (ch->dir == PCMDIR_PLAY) + bsize = ch->blk * 4 / ENVY24_PLAY_BUFUNIT; + else + bsize = ch->blk * 4 / ENVY24_REC_BUFUNIT; + bsize *= ch->unit; + bcnt = ch->size / bsize; + sndbuf_resize(ch->buffer, bcnt, bsize); + /* snd_mtxunlock(sc->lock); */ + +#if(0) + device_printf(sc->dev, "envy24chan_setblocksize(): return %d\n", prev); +#endif + return prev; +} + +/* semantic note: must start at beginning of buffer */ +static int +envy24chan_trigger(kobj_t obj, void *data, int go) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + u_int32_t ptr; + int slot; +#if 0 + int i; + + device_printf(sc->dev, "envy24chan_trigger(obj, data, %d)\n", go); +#endif + snd_mtxlock(sc->lock); + if (ch->dir == PCMDIR_PLAY) + slot = 0; + else + slot = 1; + switch (go) { + case PCMTRIG_START: +#if(0) + device_printf(sc->dev, "envy24chan_trigger(): start\n"); +#endif + /* check or set channel speed */ + if (sc->run[0] == 0 && sc->run[1] == 0) { + sc->speed = envy24_setspeed(sc, ch->speed); + sc->caps[0].minspeed = sc->caps[0].maxspeed = sc->speed; + sc->caps[1].minspeed = sc->caps[1].maxspeed = sc->speed; + } + else if (ch->speed != 0 && ch->speed != sc->speed) + return -1; + if (ch->speed == 0) + ch->channel->speed = sc->speed; + /* start or enable channel */ + sc->run[slot]++; + if (sc->run[slot] == 1) { + /* first channel */ + ch->offset = 0; + sc->blk[slot] = ch->blk; + } + else { + ptr = envy24_gethwptr(sc, ch->dir); + ch->offset = ((ptr / ch->blk + 1) * ch->blk % + (ch->size / 4)) * 4 / ch->unit; + if (ch->blk < sc->blk[slot]) + sc->blk[slot] = ch->blk; + } + if (ch->dir == PCMDIR_PLAY) { + ch->emldma(ch); + envy24_setvolume(sc, ch->num); + } + envy24_updintr(sc, ch->dir); + if (sc->run[slot] == 1) + envy24_start(sc, ch->dir); + ch->run = 1; + break; + case PCMTRIG_EMLDMAWR: +#if(0) + device_printf(sc->dev, "envy24chan_trigger(): emldmawr\n"); +#endif + if (ch->run != 1) + return -1; + ch->emldma(ch); + break; + case PCMTRIG_EMLDMARD: +#if(0) + device_printf(sc->dev, "envy24chan_trigger(): emldmard\n"); +#endif + if (ch->run != 1) + return -1; + ch->emldma(ch); + break; + case PCMTRIG_ABORT: + if (ch->run) { +#if(0) + device_printf(sc->dev, "envy24chan_trigger(): abort\n"); +#endif + ch->run = 0; + sc->run[slot]--; + if (ch->dir == PCMDIR_PLAY) + envy24_mutevolume(sc, ch->num); + if (sc->run[slot] == 0) { + envy24_stop(sc, ch->dir); + sc->intr[slot] = 0; + } +#if 0 + else if (ch->blk == sc->blk[slot]) { + sc->blk[slot] = ENVY24_SAMPLE_NUM / 2; + for (i = 0; i < ENVY24_CHAN_NUM; i++) { + if (sc->chan[i].dir == ch->dir && + sc->chan[i].run == 1 && + sc->chan[i].blk < sc->blk[slot]) + sc->blk[slot] = sc->chan[i].blk; + } + if (ch->blk != sc->blk[slot]) + envy24_updintr(sc, ch->dir); + } +#endif + } + break; + } + snd_mtxunlock(sc->lock); + + return 0; +} + +static int +envy24chan_getptr(kobj_t obj, void *data) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + u_int32_t ptr; + int rtn; + +#if(0) + device_printf(sc->dev, "envy24chan_getptr()\n"); +#endif + snd_mtxlock(sc->lock); + ptr = envy24_gethwptr(sc, ch->dir); + rtn = ptr * ch->unit; + snd_mtxunlock(sc->lock); + +#if(0) + device_printf(sc->dev, "envy24chan_getptr(): return %d\n", + rtn); +#endif + return rtn; +} + +static struct pcmchan_caps * +envy24chan_getcaps(kobj_t obj, void *data) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + struct pcmchan_caps *rtn; + +#if(0) + device_printf(sc->dev, "envy24chan_getcaps()\n"); +#endif + snd_mtxlock(sc->lock); + if (ch->dir == PCMDIR_PLAY) { + if (sc->run[0] == 0) + rtn = &envy24_playcaps; + else + rtn = &sc->caps[0]; + } + else { + if (sc->run[1] == 0) + rtn = &envy24_reccaps; + else + rtn = &sc->caps[1]; + } + snd_mtxunlock(sc->lock); + + return rtn; +} + +static kobj_method_t envy24chan_methods[] = { + KOBJMETHOD(channel_init, envy24chan_init), + KOBJMETHOD(channel_free, envy24chan_free), + KOBJMETHOD(channel_setformat, envy24chan_setformat), + KOBJMETHOD(channel_setspeed, envy24chan_setspeed), + KOBJMETHOD(channel_setblocksize, envy24chan_setblocksize), + KOBJMETHOD(channel_trigger, envy24chan_trigger), + KOBJMETHOD(channel_getptr, envy24chan_getptr), + KOBJMETHOD(channel_getcaps, envy24chan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(envy24chan); + +/* -------------------------------------------------------------------- */ + +/* mixer interface */ + +static int +envy24mixer_init(struct snd_mixer *m) +{ + struct sc_info *sc = mix_getdevinfo(m); + +#if(0) + device_printf(sc->dev, "envy24mixer_init()\n"); +#endif + if (sc == NULL) + return -1; + + /* set volume control rate */ + snd_mtxlock(sc->lock); + envy24_wrmt(sc, ENVY24_MT_VOLRATE, 0x30, 1); /* 0x30 is default value */ + + mix_setdevs(m, ENVY24_MIX_MASK); + mix_setrecdevs(m, ENVY24_MIX_REC_MASK); + snd_mtxunlock(sc->lock); + + return 0; +} + +static int +envy24mixer_reinit(struct snd_mixer *m) +{ + struct sc_info *sc = mix_getdevinfo(m); + + if (sc == NULL) + return -1; +#if(0) + device_printf(sc->dev, "envy24mixer_reinit()\n"); +#endif + + return 0; +} + +static int +envy24mixer_uninit(struct snd_mixer *m) +{ + struct sc_info *sc = mix_getdevinfo(m); + + if (sc == NULL) + return -1; +#if(0) + device_printf(sc->dev, "envy24mixer_uninit()\n"); +#endif + + return 0; +} + +static int +envy24mixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) +{ + struct sc_info *sc = mix_getdevinfo(m); + int ch = envy24_mixmap[dev]; + int hwch; + int i; + + if (sc == NULL) + return -1; + if (dev == 0 && sc->cfg->codec->setvolume == NULL) + return -1; + if (dev != 0 && ch == -1) + return -1; + hwch = envy24_chanmap[ch]; +#if(0) + device_printf(sc->dev, "envy24mixer_set(m, %d, %d, %d)\n", + dev, left, right); +#endif + + snd_mtxlock(sc->lock); + if (dev == 0) { + for (i = 0; i < sc->dacn; i++) { + sc->cfg->codec->setvolume(sc->dac[i], PCMDIR_PLAY, left, right); + } + } + else { + /* set volume value for hardware */ + if ((sc->left[hwch] = 100 - left) > ENVY24_VOL_MIN) + sc->left[hwch] = ENVY24_VOL_MUTE; + if ((sc->right[hwch] = 100 - right) > ENVY24_VOL_MIN) + sc->right[hwch] = ENVY24_VOL_MUTE; + + /* set volume for record channel and running play channel */ + if (hwch > ENVY24_CHAN_PLAY_SPDIF || sc->chan[ch].run) + envy24_setvolume(sc, hwch); + } + snd_mtxunlock(sc->lock); + + return right << 8 | left; +} + +static u_int32_t +envy24mixer_setrecsrc(struct snd_mixer *m, u_int32_t src) +{ + struct sc_info *sc = mix_getdevinfo(m); + int ch = envy24_mixmap[src]; +#if(0) + device_printf(sc->dev, "envy24mixer_setrecsrc(m, %d)\n", src); +#endif + + if (ch > ENVY24_CHAN_PLAY_SPDIF) + sc->src = ch; + return src; +} + +static kobj_method_t envy24mixer_methods[] = { + KOBJMETHOD(mixer_init, envy24mixer_init), + KOBJMETHOD(mixer_reinit, envy24mixer_reinit), + KOBJMETHOD(mixer_uninit, envy24mixer_uninit), + KOBJMETHOD(mixer_set, envy24mixer_set), + KOBJMETHOD(mixer_setrecsrc, envy24mixer_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(envy24mixer); + +/* -------------------------------------------------------------------- */ + +/* The interrupt handler */ +static void +envy24_intr(void *p) +{ + struct sc_info *sc = (struct sc_info *)p; + struct sc_chinfo *ch; + u_int32_t ptr, dsize, feed; + int i; + +#if(0) + device_printf(sc->dev, "envy24_intr()\n"); +#endif + snd_mtxlock(sc->lock); + if (envy24_checkintr(sc, PCMDIR_PLAY)) { +#if(0) + device_printf(sc->dev, "envy24_intr(): play\n"); +#endif + dsize = sc->psize / 4; + ptr = dsize - envy24_rdmt(sc, ENVY24_MT_PCNT, 2) - 1; +#if(0) + device_printf(sc->dev, "envy24_intr(): ptr = %d-->", ptr); +#endif + ptr -= ptr % sc->blk[0]; + feed = (ptr + dsize - sc->intr[0]) % dsize; +#if(0) + printf("%d intr = %d feed = %d\n", ptr, sc->intr[0], feed); +#endif + for (i = ENVY24_CHAN_PLAY_DAC1; i <= ENVY24_CHAN_PLAY_SPDIF; i++) { + ch = &sc->chan[i]; +#if(0) + if (ch->run) + device_printf(sc->dev, "envy24_intr(): chan[%d].blk = %d\n", i, ch->blk); +#endif + if (ch->run && ch->blk <= feed) { + snd_mtxunlock(sc->lock); + chn_intr(ch->channel); + snd_mtxlock(sc->lock); + } + } + sc->intr[0] = ptr; + envy24_updintr(sc, PCMDIR_PLAY); + } + if (envy24_checkintr(sc, PCMDIR_REC)) { +#if(0) + device_printf(sc->dev, "envy24_intr(): rec\n"); +#endif + dsize = sc->rsize / 4; + ptr = dsize - envy24_rdmt(sc, ENVY24_MT_RCNT, 2) - 1; + ptr -= ptr % sc->blk[1]; + feed = (ptr + dsize - sc->intr[1]) % dsize; + for (i = ENVY24_CHAN_REC_ADC1; i <= ENVY24_CHAN_REC_SPDIF; i++) { + ch = &sc->chan[i]; + if (ch->run && ch->blk <= feed) { + snd_mtxunlock(sc->lock); + chn_intr(ch->channel); + snd_mtxlock(sc->lock); + } + } + sc->intr[1] = ptr; + envy24_updintr(sc, PCMDIR_REC); + } + snd_mtxunlock(sc->lock); + + return; +} + +/* + * Probe and attach the card + */ + +static int +envy24_pci_probe(device_t dev) +{ + u_int16_t sv, sd; + int i; + +#if(0) + printf("envy24_pci_probe()\n"); +#endif + if (pci_get_device(dev) == PCID_ENVY24 && + pci_get_vendor(dev) == PCIV_ENVY24) { + sv = pci_get_subvendor(dev); + sd = pci_get_subdevice(dev); + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) { + if (cfg_table[i].subvendor == sv && + cfg_table[i].subdevice == sd) { + break; + } + } + device_set_desc(dev, cfg_table[i].name); +#if(0) + printf("envy24_pci_probe(): return 0\n"); +#endif + return 0; + } + else { +#if(0) + printf("envy24_pci_probe(): return ENXIO\n"); +#endif + return ENXIO; + } +} + +static void +envy24_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + /* struct sc_info *sc = (struct sc_info *)arg; */ + +#if(0) + device_printf(sc->dev, "envy24_dmapsetmap()\n"); + if (bootverbose) { + printf("envy24(play): setmap %lx, %lx; ", + (unsigned long)segs->ds_addr, + (unsigned long)segs->ds_len); + printf("%p -> %lx\n", sc->pmap, (unsigned long)vtophys(sc->pmap)); + } +#endif +} + +static void +envy24_dmarsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + /* struct sc_info *sc = (struct sc_info *)arg; */ + +#if(0) + device_printf(sc->dev, "envy24_dmarsetmap()\n"); + if (bootverbose) { + printf("envy24(record): setmap %lx, %lx; ", + (unsigned long)segs->ds_addr, + (unsigned long)segs->ds_len); + printf("%p -> %lx\n", sc->rmap, (unsigned long)vtophys(sc->pmap)); + } +#endif +} + +static void +envy24_dmafree(struct sc_info *sc) +{ +#if(0) + device_printf(sc->dev, "envy24_dmafree():"); + if (sc->rmap) printf(" sc->rmap(0x%08x)", (u_int32_t)sc->rmap); + else printf(" sc->rmap(null)"); + if (sc->pmap) printf(" sc->pmap(0x%08x)", (u_int32_t)sc->pmap); + else printf(" sc->pmap(null)"); + if (sc->rbuf) printf(" sc->rbuf(0x%08x)", (u_int32_t)sc->rbuf); + else printf(" sc->rbuf(null)"); + if (sc->pbuf) printf(" sc->pbuf(0x%08x)\n", (u_int32_t)sc->pbuf); + else printf(" sc->pbuf(null)\n"); +#endif +#if(0) + if (sc->rmap) + bus_dmamap_unload(sc->dmat, sc->rmap); + if (sc->pmap) + bus_dmamap_unload(sc->dmat, sc->pmap); + if (sc->rbuf) + bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap); + if (sc->pbuf) + bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap); +#else + bus_dmamap_unload(sc->dmat, sc->rmap); + bus_dmamap_unload(sc->dmat, sc->pmap); + bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap); + bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap); +#endif + + sc->rmap = sc->pmap = NULL; + sc->pbuf = NULL; + sc->rbuf = NULL; + + return; +} + +static int +envy24_dmainit(struct sc_info *sc) +{ + u_int32_t addr; + +#if(0) + device_printf(sc->dev, "envy24_dmainit()\n"); +#endif + /* init values */ + sc->psize = ENVY24_PLAY_BUFUNIT * ENVY24_SAMPLE_NUM; + sc->rsize = ENVY24_REC_BUFUNIT * ENVY24_SAMPLE_NUM; + sc->pbuf = NULL; + sc->rbuf = NULL; + sc->pmap = sc->rmap = NULL; + sc->blk[0] = sc->blk[1] = 0; + + /* allocate DMA buffer */ +#if(0) + device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_alloc(): sc->pbuf\n"); +#endif + if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_NOWAIT, &sc->pmap)) + goto bad; +#if(0) + device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_alloc(): sc->rbuf\n"); +#endif + if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_NOWAIT, &sc->rmap)) + goto bad; +#if(0) + device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_load(): sc->pmap\n"); +#endif + if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->psize, envy24_dmapsetmap, sc, 0)) + goto bad; +#if(0) + device_printf(sc->dev, "envy24_dmainit(): bus_dmamem_load(): sc->rmap\n"); +#endif + if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->rsize, envy24_dmarsetmap, sc, 0)) + goto bad; + bzero(sc->pbuf, sc->psize); + bzero(sc->rbuf, sc->rsize); + + /* set values to register */ + addr = vtophys(sc->pbuf); +#if(0) + device_printf(sc->dev, "pbuf(0x%08x)\n", addr); +#endif + envy24_wrmt(sc, ENVY24_MT_PADDR, addr, 4); +#if(0) + device_printf(sc->dev, "PADDR-->(0x%08x)\n", envy24_rdmt(sc, ENVY24_MT_PADDR, 4)); + device_printf(sc->dev, "psize(%ld)\n", sc->psize / 4 - 1); +#endif + envy24_wrmt(sc, ENVY24_MT_PCNT, sc->psize / 4 - 1, 2); +#if(0) + device_printf(sc->dev, "PCNT-->(%ld)\n", envy24_rdmt(sc, ENVY24_MT_PCNT, 2)); +#endif + addr = vtophys(sc->rbuf); + envy24_wrmt(sc, ENVY24_MT_RADDR, addr, 4); + envy24_wrmt(sc, ENVY24_MT_RCNT, sc->rsize / 4 - 1, 2); + + return 0; + bad: + envy24_dmafree(sc); + return ENOSPC; +} + +static void +envy24_putcfg(struct sc_info *sc) +{ + device_printf(sc->dev, "system configuration\n"); + kprintf(" SubVendorID: 0x%04x, SubDeviceID: 0x%04x\n", + sc->cfg->subvendor, sc->cfg->subdevice); + kprintf(" XIN2 Clock Source: "); + switch (sc->cfg->scfg & PCIM_SCFG_XIN2) { + case 0x00: + kprintf("22.5792MHz(44.1kHz*512)\n"); + break; + case 0x40: + kprintf("16.9344MHz(44.1kHz*384)\n"); + break; + case 0x80: + kprintf("from external clock synthesizer chip\n"); + break; + default: + kprintf("illeagal system setting\n"); + } + kprintf(" MPU-401 UART(s) #: "); + if (sc->cfg->scfg & PCIM_SCFG_MPU) + kprintf("2\n"); + else + kprintf("1\n"); + kprintf(" AC'97 codec: "); + if (sc->cfg->scfg & PCIM_SCFG_AC97) + kprintf("not exist\n"); + else + kprintf("exist\n"); + kprintf(" ADC #: "); + kprintf("%d\n", sc->adcn); + kprintf(" DAC #: "); + kprintf("%d\n", sc->dacn); + kprintf(" Multi-track converter type: "); + if ((sc->cfg->acl & PCIM_ACL_MTC) == 0) { + kprintf("AC'97(SDATA_OUT:"); + if (sc->cfg->acl & PCIM_ACL_OMODE) + kprintf("packed"); + else + kprintf("split"); + kprintf("|SDATA_IN:"); + if (sc->cfg->acl & PCIM_ACL_IMODE) + kprintf("packed"); + else + kprintf("split"); + kprintf(")\n"); + } + else { + kprintf("I2S("); + if (sc->cfg->i2s & PCIM_I2S_VOL) + kprintf("with volume, "); + if (sc->cfg->i2s & PCIM_I2S_96KHZ) + kprintf("96KHz support, "); + switch (sc->cfg->i2s & PCIM_I2S_RES) { + case PCIM_I2S_16BIT: + kprintf("16bit resolution, "); + break; + case PCIM_I2S_18BIT: + kprintf("18bit resolution, "); + break; + case PCIM_I2S_20BIT: + kprintf("20bit resolution, "); + break; + case PCIM_I2S_24BIT: + kprintf("24bit resolution, "); + break; + } + kprintf("ID#0x%x)\n", sc->cfg->i2s & PCIM_I2S_ID); + } + kprintf(" S/PDIF(IN/OUT): "); + if (sc->cfg->spdif & PCIM_SPDIF_IN) + kprintf("1/"); + else + kprintf("0/"); + if (sc->cfg->spdif & PCIM_SPDIF_OUT) + kprintf("1 "); + else + kprintf("0 "); + if (sc->cfg->spdif & (PCIM_SPDIF_IN | PCIM_SPDIF_OUT)) + kprintf("ID# 0x%02x\n", (sc->cfg->spdif & PCIM_SPDIF_ID) >> 2); + kprintf(" GPIO(mask/dir/state): 0x%02x/0x%02x/0x%02x\n", + sc->cfg->gpiomask, sc->cfg->gpiodir, sc->cfg->gpiostate); +} + +static int +envy24_init(struct sc_info *sc) +{ + u_int32_t data; +#if(0) + int rtn; +#endif + int i; + u_int32_t sv, sd; + + +#if(0) + device_printf(sc->dev, "envy24_init()\n"); +#endif + + /* reset chip */ + envy24_wrcs(sc, ENVY24_CCS_CTL, ENVY24_CCS_CTL_RESET | ENVY24_CCS_CTL_NATIVE, 1); + DELAY(200); + envy24_wrcs(sc, ENVY24_CCS_CTL, ENVY24_CCS_CTL_NATIVE, 1); + DELAY(200); + + /* legacy hardware disable */ + data = pci_read_config(sc->dev, PCIR_LAC, 2); + data |= PCIM_LAC_DISABLE; + pci_write_config(sc->dev, PCIR_LAC, data, 2); + + /* check system configuration */ + sc->cfg = NULL; + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) { + /* 1st: search configuration from table */ + sv = pci_get_subvendor(sc->dev); + sd = pci_get_subdevice(sc->dev); + if (sv == cfg_table[i].subvendor && sd == cfg_table[i].subdevice) { +#if(0) + device_printf(sc->dev, "Set configuration from table\n"); +#endif + sc->cfg = &cfg_table[i]; + break; + } + } + if (sc->cfg == NULL) { + /* 2nd: read configuration from table */ + sc->cfg = envy24_rom2cfg(sc); + } + sc->adcn = ((sc->cfg->scfg & PCIM_SCFG_ADC) >> 2) + 1; + sc->dacn = (sc->cfg->scfg & PCIM_SCFG_DAC) + 1; + + if (1 /* bootverbose */) { + envy24_putcfg(sc); + } + + /* set system configuration */ + pci_write_config(sc->dev, PCIR_SCFG, sc->cfg->scfg, 1); + pci_write_config(sc->dev, PCIR_ACL, sc->cfg->acl, 1); + pci_write_config(sc->dev, PCIR_I2S, sc->cfg->i2s, 1); + pci_write_config(sc->dev, PCIR_SPDIF, sc->cfg->spdif, 1); + envy24_gpiosetmask(sc, sc->cfg->gpiomask); + envy24_gpiosetdir(sc, sc->cfg->gpiodir); + envy24_gpiowr(sc, sc->cfg->gpiostate); + for (i = 0; i < sc->adcn; i++) { + sc->adc[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_REC, i); + sc->cfg->codec->init(sc->adc[i]); + } + for (i = 0; i < sc->dacn; i++) { + sc->dac[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_PLAY, i); + sc->cfg->codec->init(sc->dac[i]); + } + + /* initialize DMA buffer */ +#if(0) + device_printf(sc->dev, "envy24_init(): initialize DMA buffer\n"); +#endif + if (envy24_dmainit(sc)) + return ENOSPC; + + /* initialize status */ + sc->run[0] = sc->run[1] = 0; + sc->intr[0] = sc->intr[1] = 0; + sc->speed = 0; + sc->caps[0].fmtlist = envy24_playfmt; + sc->caps[1].fmtlist = envy24_recfmt; + + /* set channel router */ + envy24_route(sc, ENVY24_ROUTE_DAC_1, ENVY24_ROUTE_CLASS_MIX, 0, 0); + envy24_route(sc, ENVY24_ROUTE_DAC_SPDIF, ENVY24_ROUTE_CLASS_DMA, 0, 0); + /* envy24_route(sc, ENVY24_ROUTE_DAC_SPDIF, ENVY24_ROUTE_CLASS_MIX, 0, 0); */ + + /* set macro interrupt mask */ + data = envy24_rdcs(sc, ENVY24_CCS_IMASK, 1); + envy24_wrcs(sc, ENVY24_CCS_IMASK, data & ~ENVY24_CCS_IMASK_PMT, 1); + data = envy24_rdcs(sc, ENVY24_CCS_IMASK, 1); +#if(0) + device_printf(sc->dev, "envy24_init(): CCS_IMASK-->0x%02x\n", data); +#endif + + return 0; +} + +static int +envy24_alloc_resource(struct sc_info *sc) +{ + /* allocate I/O port resource */ + sc->csid = PCIR_CCS; + sc->cs = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, + &sc->csid, 0, ~0, 1, RF_ACTIVE); + sc->ddmaid = PCIR_DDMA; + sc->ddma = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, + &sc->ddmaid, 0, ~0, 1, RF_ACTIVE); + sc->dsid = PCIR_DS; + sc->ds = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, + &sc->dsid, 0, ~0, 1, RF_ACTIVE); + sc->mtid = PCIR_MT; + sc->mt = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, + &sc->mtid, 0, ~0, 1, RF_ACTIVE); + if (!sc->cs || !sc->ddma || !sc->ds || !sc->mt) { + device_printf(sc->dev, "unable to map IO port space\n"); + return ENXIO; + } + sc->cst = rman_get_bustag(sc->cs); + sc->csh = rman_get_bushandle(sc->cs); + sc->ddmat = rman_get_bustag(sc->ddma); + sc->ddmah = rman_get_bushandle(sc->ddma); + sc->dst = rman_get_bustag(sc->ds); + sc->dsh = rman_get_bushandle(sc->ds); + sc->mtt = rman_get_bustag(sc->mt); + sc->mth = rman_get_bushandle(sc->mt); +#if(0) + device_printf(sc->dev, + "IO port register values\nCCS: 0x%lx\nDDMA: 0x%lx\nDS: 0x%lx\nMT: 0x%lx\n", + pci_read_config(sc->dev, PCIR_CCS, 4), + pci_read_config(sc->dev, PCIR_DDMA, 4), + pci_read_config(sc->dev, PCIR_DS, 4), + pci_read_config(sc->dev, PCIR_MT, 4)); +#endif + + /* allocate interupt resource */ + sc->irqid = 0; + sc->irq = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &sc->irqid, + 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + if (!sc->irq || + snd_setup_intr(sc->dev, sc->irq, INTR_MPSAFE, envy24_intr, sc, &sc->ih)) { + device_printf(sc->dev, "unable to map interrupt\n"); + return ENXIO; + } + + /* allocate DMA resource */ + if (bus_dma_tag_create(/*parent*/NULL, + /*alignment*/4, + /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_ENVY24, + /*highaddr*/BUS_SPACE_MAXADDR_ENVY24, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/BUS_SPACE_MAXSIZE_ENVY24, + /*nsegments*/1, /*maxsegsz*/0x3ffff, + /*flags*/0, &sc->dmat) != 0) { + device_printf(sc->dev, "unable to create dma tag\n"); + return ENXIO; + } + + return 0; +} + +static int +envy24_pci_attach(device_t dev) +{ + u_int32_t data; + struct sc_info *sc; + char status[SND_STATUSLEN]; + int err = 0; + int i; + +#if(0) + device_printf(dev, "envy24_pci_attach()\n"); +#endif + /* get sc_info data area */ + if ((sc = kmalloc(sizeof(*sc), M_ENVY24, M_NOWAIT)) == NULL) { + device_printf(dev, "cannot allocate softc\n"); + return ENXIO; + } + + bzero(sc, sizeof(*sc)); + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_envy24 softc"); + sc->dev = dev; + + /* initialize PCI interface */ + data = pci_read_config(dev, PCIR_COMMAND, 2); + data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, data, 2); + data = pci_read_config(dev, PCIR_COMMAND, 2); + + /* allocate resources */ + err = envy24_alloc_resource(sc); + if (err) { + device_printf(dev, "unable to allocate system resources\n"); + goto bad; + } + + /* initialize card */ + err = envy24_init(sc); + if (err) { + device_printf(dev, "unable to initialize the card\n"); + goto bad; + } + + /* set multi track mixer */ + mixer_init(dev, &envy24mixer_class, sc); + + /* set channel information */ + err = pcm_register(dev, sc, 5, 2 + sc->adcn); + if (err) + goto bad; + sc->chnum = 0; + for (i = 0; i < 5; i++) { + pcm_addchan(dev, PCMDIR_PLAY, &envy24chan_class, sc); + sc->chnum++; + } + for (i = 0; i < 2 + sc->adcn; i++) { + pcm_addchan(dev, PCMDIR_REC, &envy24chan_class, sc); + sc->chnum++; + } + + /* set status iformation */ + ksnprintf(status, SND_STATUSLEN, + "at io 0x%lx:%ld,0x%lx:%ld,0x%lx:%ld,0x%lx:%ld irq %ld", + rman_get_start(sc->cs), + rman_get_end(sc->cs) - rman_get_start(sc->cs) + 1, + rman_get_start(sc->ddma), + rman_get_end(sc->ddma) - rman_get_start(sc->ddma) + 1, + rman_get_start(sc->ds), + rman_get_end(sc->ds) - rman_get_start(sc->ds) + 1, + rman_get_start(sc->mt), + rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1, + rman_get_start(sc->irq)); + pcm_setstatus(dev, status); + + return 0; + +bad: + if (sc->ih) + bus_teardown_intr(dev, sc->irq, sc->ih); + if (sc->irq) + bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); + envy24_dmafree(sc); + if (sc->dmat) + bus_dma_tag_destroy(sc->dmat); + if (sc->cfg->codec->destroy != NULL) { + for (i = 0; i < sc->adcn; i++) + sc->cfg->codec->destroy(sc->adc[i]); + for (i = 0; i < sc->dacn; i++) + sc->cfg->codec->destroy(sc->dac[i]); + } + envy24_cfgfree(sc->cfg); + if (sc->cs) + bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs); + if (sc->ddma) + bus_release_resource(dev, SYS_RES_IOPORT, sc->ddmaid, sc->ddma); + if (sc->ds) + bus_release_resource(dev, SYS_RES_IOPORT, sc->dsid, sc->ds); + if (sc->mt) + bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt); + if (sc->lock) + snd_mtxfree(sc->lock); + kfree(sc, M_ENVY24); + return err; +} + +static int +envy24_pci_detach(device_t dev) +{ + struct sc_info *sc; + int r; + int i; + +#if(0) + device_printf(dev, "envy24_pci_detach()\n"); +#endif + sc = pcm_getdevinfo(dev); + if (sc == NULL) + return 0; + r = pcm_unregister(dev); + if (r) + return r; + + envy24_dmafree(sc); + if (sc->cfg->codec->destroy != NULL) { + for (i = 0; i < sc->adcn; i++) + sc->cfg->codec->destroy(sc->adc[i]); + for (i = 0; i < sc->dacn; i++) + sc->cfg->codec->destroy(sc->dac[i]); + } + envy24_cfgfree(sc->cfg); + bus_dma_tag_destroy(sc->dmat); + bus_teardown_intr(dev, sc->irq, sc->ih); + bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); + bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs); + bus_release_resource(dev, SYS_RES_IOPORT, sc->ddmaid, sc->ddma); + bus_release_resource(dev, SYS_RES_IOPORT, sc->dsid, sc->ds); + bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt); + snd_mtxfree(sc->lock); + kfree(sc, M_ENVY24); + return 0; +} + +static device_method_t envy24_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, envy24_pci_probe), + DEVMETHOD(device_attach, envy24_pci_attach), + DEVMETHOD(device_detach, envy24_pci_detach), + { 0, 0 } +}; + +static driver_t envy24_driver = { + "pcm", + envy24_methods, +#if __FreeBSD_version > 500000 + PCM_SOFTC_SIZE, +#else + sizeof(struct snddev_info), +#endif +}; + +DRIVER_MODULE(snd_envy24, pci, envy24_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_envy24, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_DEPEND(snd_envy24, snd_spicds, 1, 1, 1); +MODULE_VERSION(snd_envy24, 1); diff --git a/sys/dev/sound/pci/envy24.h b/sys/dev/sound/pci/envy24.h new file mode 100644 index 0000000000..eda43dd5e7 --- /dev/null +++ b/sys/dev/sound/pci/envy24.h @@ -0,0 +1,496 @@ +/* + * Copyright (c) 2001 Katsurajima Naoto + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/sound/pci/envy24.h,v 1.1.2.2 2007/06/11 19:33:27 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/envy24.h,v 1.1 2007/06/16 19:48:05 hasso Exp $ + */ + + +/* -------------------------------------------------------------------- */ + +/* PCI device ID */ +#define PCIV_ENVY24 0x1412 +#define PCID_ENVY24 0x1712 + +/* PCI Registers */ + +#define PCIR_CCS 0x10 /* Controller I/O Base Address */ +#define PCIR_DDMA 0x14 /* DDMA I/O Base Address */ +#define PCIR_DS 0x18 /* DMA Path Registers I/O Base Address */ +#define PCIR_MT 0x1c /* Professional Multi-Track I/O Base Address */ + +#define PCIR_LAC 0x40 /* Legacy Audio Control */ +#define PCIM_LAC_DISABLE 0x8000 /* Legacy Audio Hardware disabled */ +#define PCIM_LAC_SBDMA0 0x0000 /* SB DMA Channel Select: 0 */ +#define PCIM_LAC_SBDMA1 0x0040 /* SB DMA Channel Select: 1 */ +#define PCIM_LAC_SBDMA3 0x00c0 /* SB DMA Channel Select: 3 */ +#define PCIM_LAC_IOADDR10 0x0020 /* I/O Address Alias Control */ +#define PCIM_LAC_MPU401 0x0008 /* MPU-401 I/O enable */ +#define PCIM_LAC_GAME 0x0004 /* Game Port enable (200h) */ +#define PCIM_LAC_FM 0x0002 /* FM I/O enable (AdLib 388h base) */ +#define PCIM_LAC_SB 0x0001 /* SB I/O enable */ + +#define PCIR_LCC 0x42 /* Legacy Configuration Control */ +#define PCIM_LCC_VINT 0xff00 /* Interrupt vector to be snooped */ +#define PCIM_LCC_SVIDRW 0x0080 /* SVID read/write enable */ +#define PCIM_LCC_SNPSB 0x0040 /* snoop SB 22C/24Ch I/O write cycle */ +#define PCIM_LCC_SNPPIC 0x0020 /* snoop PIC I/O R/W cycle */ +#define PCIM_LCC_SNPPCI 0x0010 /* snoop PCI bus interrupt acknowledge cycle */ +#define PCIM_LCC_SBBASE 0x0008 /* SB base 240h(1)/220h(0) */ +#define PCIM_LCC_MPUBASE 0x0006 /* MPU-401 base 300h-330h */ +#define PCIM_LCC_LDMA 0x0001 /* Legacy DMA enable */ + +#define PCIR_SCFG 0x60 /* System Configuration Register */ +#define PCIM_SCFG_XIN2 0xc0 /* XIN2 Clock Source Configuration */ + /* 00: 22.5792MHz(44.1kHz*512) */ + /* 01: 16.9344MHz(44.1kHz*384) */ + /* 10: from external clock synthesizer chip */ +#define PCIM_SCFG_MPU 0x20 /* 1(0)/2(1) MPU-401 UART(s) */ +#define PCIM_SCFG_AC97 0x10 /* 0: AC'97 codec exist */ + /* 1: AC'97 codec not exist */ +#define PCIM_SCFG_ADC 0x0c /* 1-4 stereo ADC connected */ +#define PCIM_SCFG_DAC 0x03 /* 1-4 stereo DAC connected */ + +#define PCIR_ACL 0x61 /* AC-Link Configuration Register */ +#define PCIM_ACL_MTC 0x80 /* Multi-track converter type: 0:AC'97 1:I2S */ +#define PCIM_ACL_OMODE 0x02 /* AC 97 codec SDATA_OUT 0:split 1:packed */ +#define PCIM_ACL_IMODE 0x01 /* AC 97 codec SDATA_IN 0:split 1:packed */ + +#define PCIR_I2S 0x62 /* I2S Converters Features Register */ +#define PCIM_I2S_VOL 0x80 /* I2S codec Volume and mute */ +#define PCIM_I2S_96KHZ 0x40 /* I2S converter 96kHz sampling rate support */ +#define PCIM_I2S_RES 0x30 /* Converter resolution */ +#define PCIM_I2S_16BIT 0x00 /* 16bit */ +#define PCIM_I2S_18BIT 0x10 /* 18bit */ +#define PCIM_I2S_20BIT 0x20 /* 20bit */ +#define PCIM_I2S_24BIT 0x30 /* 24bit */ +#define PCIM_I2S_ID 0x0f /* Other I2S IDs */ + +#define PCIR_SPDIF 0x63 /* S/PDIF Configuration Register */ +#define PCIM_SPDIF_ID 0xfc /* S/PDIF chip ID */ +#define PCIM_SPDIF_IN 0x02 /* S/PDIF Stereo In is present */ +#define PCIM_SPDIF_OUT 0x01 /* S/PDIF Stereo Out is present */ + +#define PCIR_POWER_STAT 0x84 /* Power Management Control and Status */ + +/* Controller Registers */ + +#define ENVY24_CCS_CTL 0x00 /* Control/Status Register */ +#define ENVY24_CCS_CTL_RESET 0x80 /* Entire Chip soft reset */ +#define ENVY24_CCS_CTL_DMAINT 0x40 /* DS DMA Channel-C interrupt */ +#define ENVY24_CCS_CTL_DOSVOL 0x10 /* set the DOS WT volume control */ +#define ENVY24_CCS_CTL_EDGE 0x08 /* SERR# edge (only one PCI clock width) */ +#define ENVY24_CCS_CTL_SBINT 0x02 /* SERR# assertion for SB interrupt */ +#define ENVY24_CCS_CTL_NATIVE 0x01 /* Mode select: 0:SB mode 1:native mode */ + +#define ENVY24_CCS_IMASK 0x01 /* Interrupt Mask Register */ +#define ENVY24_CCS_IMASK_PMIDI 0x80 /* Primary MIDI */ +#define ENVY24_CCS_IMASK_TIMER 0x40 /* Timer */ +#define ENVY24_CCS_IMASK_SMIDI 0x20 /* Secondary MIDI */ +#define ENVY24_CCS_IMASK_PMT 0x10 /* Professional Multi-track */ +#define ENVY24_CCS_IMASK_FM 0x08 /* FM/MIDI trapping */ +#define ENVY24_CCS_IMASK_PDMA 0x04 /* Playback DS DMA */ +#define ENVY24_CCS_IMASK_RDMA 0x02 /* Consumer record DMA */ +#define ENVY24_CCS_IMASK_SB 0x01 /* Consumer/SB mode playback */ + +#define ENVY24_CCS_ISTAT 0x02 /* Interrupt Status Register */ +#define ENVY24_CCS_ISTAT_PMIDI 0x80 /* Primary MIDI */ +#define ENVY24_CCS_ISTAT_TIMER 0x40 /* Timer */ +#define ENVY24_CCS_ISTAT_SMIDI 0x20 /* Secondary MIDI */ +#define ENVY24_CCS_ISTAT_PMT 0x10 /* Professional Multi-track */ +#define ENVY24_CCS_ISTAT_FM 0x08 /* FM/MIDI trapping */ +#define ENVY24_CCS_ISTAT_PDMA 0x04 /* Playback DS DMA */ +#define ENVY24_CCS_ISTAT_RDMA 0x02 /* Consumer record DMA */ +#define ENVY24_CCS_ISTAT_SB 0x01 /* Consumer/SB mode playback */ + +#define ENVY24_CCS_INDEX 0x03 /* Envy24 Index Register */ +#define ENVY24_CCS_DATA 0x04 /* Envy24 Data Register */ + +#define ENVY24_CCS_NMI1 0x05 /* NMI Status Register 1 */ +#define ENVY24_CCS_NMI1_PCI 0x80 /* PCI I/O read/write cycle */ +#define ENVY24_CCS_NMI1_SB 0x40 /* SB 22C/24C write */ +#define ENVY24_CCS_NMI1_SBDMA 0x10 /* SB interrupt (SB DMA/SB F2 command) */ +#define ENVY24_CCS_NMI1_DSDMA 0x08 /* DS channel C DMA interrupt */ +#define ENVY24_CCS_NMI1_MIDI 0x04 /* MIDI 330h or [PCI_10]h+Ch write */ +#define ENVY24_CCS_NMI1_FM 0x01 /* FM data register write */ + +#define ENVY24_CCS_NMIDAT 0x06 /* NMI Data Register */ +#define ENVY24_CCS_NMIIDX 0x07 /* NMI Index Register */ +#define ENVY24_CCS_AC97IDX 0x08 /* Consumer AC'97 Index Register */ + +#define ENVY24_CCS_AC97CMD 0x09 /* Consumer AC'97 Command/Status Register */ +#define ENVY24_CCS_AC97CMD_COLD 0x80 /* Cold reset */ +#define ENVY24_CCS_AC97CMD_WARM 0x40 /* Warm reset */ +#define ENVY24_CCS_AC97CMD_WRCODEC 0x20 /* Write to AC'97 codec registers */ +#define ENVY24_CCS_AC97CMD_RDCODEC 0x10 /* Read from AC'97 codec registers */ +#define ENVY24_CCS_AC97CMD_READY 0x08 /* AC'97 codec ready status bit */ +#define ENVY24_CCS_AC97CMD_PVSR 0x02 /* VSR for Playback */ +#define ENVY24_CCS_AC97CMD_RVSR 0x01 /* VSR for Record */ + +#define ENVY24_CCS_AC97DAT 0x0a /* Consumer AC'97 Data Port Register */ +#define ENVY24_CCS_PMIDIDAT 0x0c /* Primary MIDI UART Data Register */ +#define ENVY24_CCS_PMIDICMD 0x0d /* Primary MIDI UART Command/Status Register */ + +#define ENVY24_CCS_NMI2 0x0e /* NMI Status Register 2 */ +#define ENVY24_CCS_NMI2_FMBANK 0x30 /* FM bank indicator */ +#define ENVY24_CCS_NMI2_FM0 0x10 /* FM bank 0 (388h/220h/228h) */ +#define ENVY24_CCS_NMI2_FM1 0x20 /* FM bank 1 (38ah/222h) */ +#define ENVY24_CCS_NMI2_PICIO 0x0f /* PIC I/O cycle */ +#define ENVY24_CCS_NMI2_PIC20W 0x01 /* 20h write */ +#define ENVY24_CCS_NMI2_PICA0W 0x02 /* a0h write */ +#define ENVY24_CCS_NMI2_PIC21W 0x05 /* 21h write */ +#define ENVY24_CCS_NMI2_PICA1W 0x06 /* a1h write */ +#define ENVY24_CCS_NMI2_PIC20R 0x09 /* 20h read */ +#define ENVY24_CCS_NMI2_PICA0R 0x0a /* a0h read */ +#define ENVY24_CCS_NMI2_PIC21R 0x0d /* 21h read */ +#define ENVY24_CCS_NMI2_PICA1R 0x0e /* a1h read */ + +#define ENVY24_CCS_JOY 0x0f /* Game port register */ + +#define ENVY24_CCS_I2CDEV 0x10 /* I2C Port Device Address Register */ +#define ENVY24_CCS_I2CDEV_ADDR 0xfe /* I2C device address */ +#define ENVY24_CCS_I2CDEV_ROM 0xa0 /* reserved for the external I2C E2PROM */ +#define ENVY24_CCS_I2CDEV_WR 0x01 /* write */ +#define ENVY24_CCS_I2CDEV_RD 0x00 /* read */ + +#define ENVY24_CCS_I2CADDR 0x11 /* I2C Port Byte Address Register */ +#define ENVY24_CCS_I2CDATA 0x12 /* I2C Port Read/Write Data Register */ + +#define ENVY24_CCS_I2CSTAT 0x13 /* I2C Port Control and Status Register */ +#define ENVY24_CCS_I2CSTAT_ROM 0x80 /* external E2PROM exists */ +#define ENVY24_CCS_I2CSTAT_BSY 0x01 /* I2C port read/write status busy */ + +#define ENVY24_CCS_CDMABASE 0x14 /* Consumer Record DMA Current/Base Address Register */ +#define ENVY24_CCS_CDMACNT 0x18 /* Consumer Record DMA Current/Base Count Register */ +#define ENVY24_CCS_SERR 0x1b /* PCI Configuration SERR# Shadow Register */ +#define ENVY24_CCS_SMIDIDAT 0x1c /* Secondary MIDI UART Data Register */ +#define ENVY24_CCS_SMIDICMD 0x1d /* Secondary MIDI UART Command/Status Register */ + +#define ENVY24_CCS_TIMER 0x1e /* Timer Register */ +#define ENVY24_CCS_TIMER_EN 0x8000 /* Timer count enable */ +#define ENVY24_CCS_TIMER_MASK 0x7fff /* Timer counter mask */ + +/* Controller Indexed Registers */ + +#define ENVY24_CCI_PTCHIGH 0x00 /* Playback Terminal Count Register (High Byte) */ +#define ENVY24_CCI_PTCLOW 0x01 /* Playback Terminal Count Register (Low Byte) */ + +#define ENVY24_CCI_PCTL 0x02 /* Playback Control Register */ +#define ENVY24_CCI_PCTL_TURBO 0x80 /* 4x up sampling in the host by software */ +#define ENVY24_CCI_PCTL_U8 0x10 /* 8 bits unsigned */ +#define ENVY24_CCI_PCTL_S16 0x00 /* 16 bits signed */ +#define ENVY24_CCI_PCTL_STEREO 0x08 /* stereo */ +#define ENVY24_CCI_PCTL_MONO 0x00 /* mono */ +#define ENVY24_CCI_PCTL_FLUSH 0x04 /* FIFO flush (sticky bit. Requires toggling) */ +#define ENVY24_CCI_PCTL_PAUSE 0x02 /* Pause */ +#define ENVY24_CCI_PCTL_ENABLE 0x01 /* Playback enable */ + +#define ENVY24_CCI_PLVOL 0x03 /* Playback Left Volume/Pan Register */ +#define ENVY24_CCI_PRVOL 0x04 /* Playback Right Volume/Pan Register */ +#define ENVY24_CCI_VOL_MASK 0x3f /* Volume value mask */ + +#define ENVY24_CCI_SOFTVOL 0x05 /* Soft Volume/Mute Control Register */ +#define ENVY24_CCI_PSRLOW 0x06 /* Playback Sampling Rate Register (Low Byte) */ +#define ENVY24_CCI_PSRMID 0x07 /* Playback Sampling Rate Register (Middle Byte) */ +#define ENVY24_CCI_PSRHIGH 0x08 /* Playback Sampling Rate Register (High Byte) */ +#define ENVY24_CCI_RTCHIGH 0x10 /* Record Terminal Count Register (High Byte) */ +#define ENVY24_CCI_RTCLOW 0x11 /* Record Terminal Count Register (Low Byte) */ + +#define ENVY24_CCI_RCTL 0x12 /* Record Control Register */ +#define ENVY24_CCI_RCTL_DRTN 0x80 /* Digital return enable */ +#define ENVY24_CCI_RCTL_U8 0x04 /* 8 bits unsigned */ +#define ENVY24_CCI_RCTL_S16 0x00 /* 16 bits signed */ +#define ENVY24_CCI_RCTL_STEREO 0x00 /* stereo */ +#define ENVY24_CCI_RCTL_MONO 0x02 /* mono */ +#define ENVY24_CCI_RCTL_ENABLE 0x01 /* Record enable */ + +#define ENVY24_CCI_GPIODAT 0x20 /* GPIO Data Register */ +#define ENVY24_CCI_GPIOMASK 0x21 /* GPIO Write Mask Register */ + +#define ENVY24_CCI_GPIOCTL 0x22 /* GPIO Direction Control Register */ +#define ENVY24_CCI_GPIO_OUT 1 /* output */ +#define ENVY24_CCI_GPIO_IN 0 /* input */ + +#define ENVY24_CCI_CPDWN 0x30 /* Consumer Section Power Down Register */ +#define ENVY24_CCI_CPDWN_XTAL 0x80 /* Crystal clock generation power down for XTAL_1 */ +#define ENVY24_CCI_CPDWN_GAME 0x40 /* Game port analog power down */ +#define ENVY24_CCI_CPDWN_I2C 0x10 /* I2C port clock */ +#define ENVY24_CCI_CPDWN_MIDI 0x08 /* MIDI clock */ +#define ENVY24_CCI_CPDWN_AC97 0x04 /* AC'97 clock */ +#define ENVY24_CCI_CPDWN_DS 0x02 /* DS Block clock */ +#define ENVY24_CCI_CPDWN_PCI 0x01 /* PCI clock for SB, DMA controller */ + +#define ENVY24_CCI_MTPDWN 0x31 /* Multi-Track Section Power Down Register */ +#define ENVY24_CCI_MTPDWN_XTAL 0x80 /* Crystal clock generation power down for XTAL_2 */ +#define ENVY24_CCI_MTPDWN_SPDIF 0x04 /* S/PDIF clock */ +#define ENVY24_CCI_MTPDWN_MIX 0x02 /* Professional digital mixer clock */ +#define ENVY24_CCI_MTPDWN_I2S 0x01 /* Multi-track I2S serial interface clock */ + +/* DDMA Registers */ + +#define ENVY24_DDMA_ADDR0 0x00 /* DMA Base and Current Address bit 0-7 */ +#define ENVY24_DDMA_ADDR8 0x01 /* DMA Base and Current Address bit 8-15 */ +#define ENVY24_DDMA_ADDR16 0x02 /* DMA Base and Current Address bit 16-23 */ +#define ENVY24_DDMA_ADDR24 0x03 /* DMA Base and Current Address bit 24-31 */ +#define ENVY24_DDMA_CNT0 0x04 /* DMA Base and Current Count 0-7 */ +#define ENVY24_DDMA_CNT8 0x05 /* DMA Base and Current Count 8-15 */ +#define ENVY24_DDMA_CNT16 0x06 /* (not supported) */ +#define ENVY24_DDMA_CMD 0x08 /* Status and Command */ +#define ENVY24_DDMA_MODE 0x0b /* Mode */ +#define ENVY24_DDMA_RESET 0x0c /* Master reset */ +#define ENVY24_DDMA_CHAN 0x0f /* Channel Mask */ + +/* Consumer Section DMA Channel Registers */ + +#define ENVY24_CS_INTMASK 0x00 /* DirectSound DMA Interrupt Mask Register */ +#define ENVY24_CS_INTSTAT 0x02 /* DirectSound DMA Interrupt Status Register */ +#define ENVY24_CS_CHDAT 0x04 /* Channel Data register */ + +#define ENVY24_CS_CHIDX 0x08 /* Channel Index Register */ +#define ENVY24_CS_CHIDX_NUM 0xf0 /* Channel number */ +#define ENVY24_CS_CHIDX_ADDR0 0x00 /* Buffer_0 DMA base address */ +#define ENVY24_CS_CHIDX_CNT0 0x01 /* Buffer_0 DMA base count */ +#define ENVY24_CS_CHIDX_ADDR1 0x02 /* Buffer_1 DMA base address */ +#define ENVY24_CS_CHIDX_CNT1 0x03 /* Buffer_1 DMA base count */ +#define ENVY24_CS_CHIDX_CTL 0x04 /* Channel Control and Status register */ +#define ENVY24_CS_CHIDX_RATE 0x05 /* Channel Sampling Rate */ +#define ENVY24_CS_CHIDX_VOL 0x06 /* Channel left and right volume/pan control */ +/* Channel Control and Status Register at Index 4h */ +#define ENVY24_CS_CTL_BUF 0x80 /* indicating that the current active buffer */ +#define ENVY24_CS_CTL_AUTO1 0x40 /* Buffer_1 auto init. enable */ +#define ENVY24_CS_CTL_AUTO0 0x20 /* Buffer_0 auto init. enable */ +#define ENVY24_CS_CTL_FLUSH 0x10 /* Flush FIFO */ +#define ENVY24_CS_CTL_STEREO 0x08 /* stereo(or mono) */ +#define ENVY24_CS_CTL_U8 0x04 /* 8-bit unsigned(or 16-bit signed) */ +#define ENVY24_CS_CTL_PAUSE 0x02 /* DMA request 1:pause */ +#define ENVY24_CS_CTL_START 0x01 /* DMA request 1: start, 0:stop */ +/* Consumer mode Left/Right Volume Register at Index 06h */ +#define ENVY24_CS_VOL_RIGHT 0x3f00 +#define ENVY24_CS_VOL_LEFT 0x003f + +/* Professional Multi-Track Control Registers */ + +#define ENVY24_MT_INT 0x00 /* DMA Interrupt Mask and Status Register */ +#define ENVY24_MT_INT_RMASK 0x80 /* Multi-track record interrupt mask */ +#define ENVY24_MT_INT_PMASK 0x40 /* Multi-track playback interrupt mask */ +#define ENVY24_MT_INT_RSTAT 0x02 /* Multi-track record interrupt status */ +#define ENVY24_MT_INT_PSTAT 0x01 /* Multi-track playback interrupt status */ + +#define ENVY24_MT_RATE 0x01 /* Sampling Rate Select Register */ +#define ENVY24_MT_RATE_SPDIF 0x10 /* S/PDIF input clock as the master */ +#define ENVY24_MT_RATE_48000 0x00 +#define ENVY24_MT_RATE_24000 0x01 +#define ENVY24_MT_RATE_12000 0x02 +#define ENVY24_MT_RATE_9600 0x03 +#define ENVY24_MT_RATE_32000 0x04 +#define ENVY24_MT_RATE_16000 0x05 +#define ENVY24_MT_RATE_8000 0x06 +#define ENVY24_MT_RATE_96000 0x07 +#define ENVY24_MT_RATE_64000 0x0f +#define ENVY24_MT_RATE_44100 0x08 +#define ENVY24_MT_RATE_22050 0x09 +#define ENVY24_MT_RATE_11025 0x0a +#define ENVY24_MT_RATE_88200 0x0b +#define ENVY24_MT_RATE_MASK 0x0f + +#define ENVY24_MT_I2S 0x02 /* I2S Data Format Register */ +#define ENVY24_MT_I2S_MLR128 0x08 /* MCLK/LRCLK ratio 128x(or 256x) */ +#define ENVY24_MT_I2S_SLR48 0x04 /* SCLK/LRCLK ratio 48bpf(or 64bpf) */ +#define ENVY24_MT_I2S_FORM 0x00 /* I2S data format */ + +#define ENVY24_MT_AC97IDX 0x04 /* Index Register for AC'97 Codecs */ + +#define ENVY24_MT_AC97CMD 0x05 /* Command and Status Register for AC'97 Codecs */ +#define ENVY24_MT_AC97CMD_CLD 0x80 /* Cold reset */ +#define ENVY24_MT_AC97CMD_WRM 0x40 /* Warm reset */ +#define ENVY24_MT_AC97CMD_WR 0x20 /* write to AC'97 codec register */ +#define ENVY24_MT_AC97CMD_RD 0x10 /* read AC'97 CODEC register */ +#define ENVY24_MT_AC97CMD_RDY 0x08 /* AC'97 codec ready status bit */ +#define ENVY24_MT_AC97CMD_ID 0x03 /* ID(0-3) for external AC 97 registers */ + +#define ENVY24_MT_AC97DLO 0x06 /* AC'97 codec register data low byte */ +#define ENVY24_MT_AC97DHI 0x07 /* AC'97 codec register data high byte */ +#define ENVY24_MT_PADDR 0x10 /* Playback DMA Current/Base Address Register */ +#define ENVY24_MT_PCNT 0x14 /* Playback DMA Current/Base Count Register */ +#define ENVY24_MT_PTERM 0x16 /* Playback Current/Base Terminal Count Register */ +#define ENVY24_MT_PCTL 0x18 /* Playback and Record Control Register */ +#define ENVY24_MT_PCTL_RSTART 0x04 /* 1: Record start; 0: Record stop */ +#define ENVY24_MT_PCTL_PAUSE 0x02 /* 1: Pause; 0: Resume */ +#define ENVY24_MT_PCTL_PSTART 0x01 /* 1: Playback start; 0: Playback stop */ + +#define ENVY24_MT_RADDR 0x20 /* Record DMA Current/Base Address Register */ +#define ENVY24_MT_RCNT 0x24 /* Record DMA Current/Base Count Register */ +#define ENVY24_MT_RTERM 0x26 /* Record Current/Base Terminal Count Register */ +#define ENVY24_MT_RCTL 0x28 /* Record Control Register */ +#define ENVY24_MT_RCTL_RSTART 0x01 /* 1: Record start; 0: Record stop */ + +#define ENVY24_MT_PSDOUT 0x30 /* Routing Control Register for Data to PSDOUT[0:3] */ +#define ENVY24_MT_SPDOUT 0x32 /* Routing Control Register for SPDOUT */ +#define ENVY24_MT_RECORD 0x34 /* Captured (Recorded) data Routing Selection Register */ + +#define BUS_SPACE_MAXADDR_ENVY24 0x0fffffff /* Address space beyond 256MB is not supported */ +#define BUS_SPACE_MAXSIZE_ENVY24 0x3fffc /* 64k x 4byte(1dword) */ + +#define ENVY24_MT_VOLUME 0x38 /* Left/Right Volume Control Data Register */ +#define ENVY24_MT_VOLUME_L 0x007f /* Left Volume Mask */ +#define ENVY24_MT_VOLUME_R 0x7f00 /* Right Volume Mask */ + +#define ENVY24_MT_VOLIDX 0x3a /* Volume Control Stream Index Register */ +#define ENVY24_MT_VOLRATE 0x3b /* Volume Control Rate Register */ +#define ENVY24_MT_MONAC97 0x3c /* Digital Mixer Monitor Routing Control Register */ +#define ENVY24_MT_PEAKIDX 0x3e /* Peak Meter Index Register */ +#define ENVY24_MT_PEAKDAT 0x3f /* Peak Meter Data Register */ + +/* -------------------------------------------------------------------- */ + +/* ENVY24 mixer channel defines */ +/* + ENVY24 mixer has original line matrix. So, general mixer command is not + able to use for this. If system has consumer AC'97 output, AC'97 line is + used as master mixer, and it is able to control. +*/ +#define ENVY24_CHAN_NUM 11 /* Play * 5 + Record * 5 + Mix * 1 */ + +#define ENVY24_CHAN_PLAY_DAC1 0 +#define ENVY24_CHAN_PLAY_DAC2 1 +#define ENVY24_CHAN_PLAY_DAC3 2 +#define ENVY24_CHAN_PLAY_DAC4 3 +#define ENVY24_CHAN_PLAY_SPDIF 4 +#define ENVY24_CHAN_REC_ADC1 5 +#define ENVY24_CHAN_REC_ADC2 6 +#define ENVY24_CHAN_REC_ADC3 7 +#define ENVY24_CHAN_REC_ADC4 8 +#define ENVY24_CHAN_REC_SPDIF 9 +#define ENVY24_CHAN_REC_MIX 10 + +#define ENVY24_MIX_MASK 0x3ff +#define ENVY24_MIX_REC_MASK 0x3e0 + +/* volume value constants */ +#define ENVY24_VOL_MAX 0 /* 0db(negate) */ +#define ENVY24_VOL_MIN 96 /* -144db(negate) */ +#define ENVY24_VOL_MUTE 127 /* mute */ + +/* -------------------------------------------------------------------- */ + +/* ENVY24 routing control defines */ +/* + ENVY24 has input->output data routing matrix switch. But original ENVY24 + matrix control is so complex. So, in this driver, matrix control is + defined 4 parameters. + + 1: output DAC channels (include S/PDIF output) + 2: output data classes + a. direct output from DMA + b. MIXER output which mixed the DMA outputs and input channels + (NOTICE: this class is able to set only DAC-1 and S/PDIF output) + c. direct input from ADC + d. direct input from S/PDIF + 3: input ADC channel selection(when 2:c. is selected) + 4: left/right reverse + + These parameters matrix is bit reduced from original ENVY24 matrix + pattern(ex. route different ADC input to one DAC). But almost case + this is enough to use. +*/ +#define ENVY24_ROUTE_DAC_1 0 +#define ENVY24_ROUTE_DAC_2 1 +#define ENVY24_ROUTE_DAC_3 2 +#define ENVY24_ROUTE_DAC_4 3 +#define ENVY24_ROUTE_DAC_SPDIF 4 + +#define ENVY24_ROUTE_CLASS_DMA 0 +#define ENVY24_ROUTE_CLASS_MIX 1 +#define ENVY24_ROUTE_CLASS_ADC 2 +#define ENVY24_ROUTE_CLASS_SPDIF 3 + +#define ENVY24_ROUTE_ADC_1 0 +#define ENVY24_ROUTE_ADC_2 1 +#define ENVY24_ROUTE_ADC_3 2 +#define ENVY24_ROUTE_ADC_4 3 + +#define ENVY24_ROUTE_NORMAL 0 +#define ENVY24_ROUTE_REVERSE 1 +#define ENVY24_ROUTE_LEFT 0 +#define ENVY24_ROUTE_RIGHT 1 + +/* -------------------------------------------------------------------- */ + +/* + These map values are refferd from ALSA sound driver. +*/ +/* ENVY24 configuration E2PROM map */ +#define ENVY24_E2PROM_SUBVENDOR 0x00 +#define ENVY24_E2PROM_SUBDEVICE 0x02 +#define ENVY24_E2PROM_SIZE 0x04 +#define ENVY24_E2PROM_VERSION 0x05 +#define ENVY24_E2PROM_SCFG 0x06 +#define ENVY24_E2PROM_ACL 0x07 +#define ENVY24_E2PROM_I2S 0x08 +#define ENVY24_E2PROM_SPDIF 0x09 +#define ENVY24_E2PROM_GPIOMASK 0x0a +#define ENVY24_E2PROM_GPIOSTATE 0x0b +#define ENVY24_E2PROM_GPIODIR 0x0c +#define ENVY24_E2PROM_AC97MAIN 0x0d +#define ENVY24_E2PROM_AC97PCM 0x0f +#define ENVY24_E2PROM_AC97REC 0x11 +#define ENVY24_E2PROM_AC97RECSRC 0x13 +#define ENVY24_E2PROM_DACID 0x14 +#define ENVY24_E2PROM_ADCID 0x18 +#define ENVY24_E2PROM_EXTRA 0x1c + +/* GPIO connect map of M-Audio Delta series */ +#define ENVY24_GPIO_CS84X4_PRO 0x01 +#define ENVY24_GPIO_CS8414_STATUS 0x02 +#define ENVY24_GPIO_CS84X4_CLK 0x04 +#define ENVY24_GPIO_CS84X4_DATA 0x08 +#define ENVY24_GPIO_AK4524_CDTI 0x10 /* this value is duplicated to input select */ +#define ENVY24_GPIO_AK4524_CCLK 0x20 +#define ENVY24_GPIO_AK4524_CS0 0x40 +#define ENVY24_GPIO_AK4524_CS1 0x80 + +/* M-Audio Delta series S/PDIF(CS84[01]4) control pin values */ +#define ENVY24_CS8404_PRO_RATE 0x18 +#define ENVY24_CS8404_PRO_RATE32 0x00 +#define ENVY24_CS8404_PRO_RATE441 0x10 +#define ENVY24_CS8404_PRO_RATE48 0x08 + +/* M-Audio Delta series parameter */ +#define ENVY24_DELTA_AK4524_CIF 0 + +#define I2C_DELAY 1000 + +/* PCA9554 registers */ +#define PCA9554_I2CDEV 0x40 /* I2C device address */ +#define PCA9554_IN 0x00 /* input port */ +#define PCA9554_OUT 0x01 /* output port */ +#define PCA9554_INVERT 0x02 /* polarity invert */ +#define PCA9554_DIR 0x03 /* port directions */ + +/* PCF8574 registers */ +#define PCF8574_I2CDEV_DAC 0x48 +#define PCF8574_SENSE_MASK 0x40 + +/* end of file */ diff --git a/sys/dev/sound/pci/envy24ht.c b/sys/dev/sound/pci/envy24ht.c new file mode 100644 index 0000000000..2e69b89fa7 --- /dev/null +++ b/sys/dev/sound/pci/envy24ht.c @@ -0,0 +1,2604 @@ +/* + * Copyright (c) 2006 Konstantin Dimitrov + * Copyright (c) 2001 Katsurajima Naoto + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/sound/pci/envy24ht.c,v 1.11.2.2 2007/06/11 19:33:27 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/envy24ht.c,v 1.1 2007/06/16 19:48:05 hasso Exp $ + */ + +/* + * Konstantin Dimitrov's thanks list: + * + * A huge thanks goes to Spas Filipov for his friendship, support and his + * generous gift - an 'Audiotrak Prodigy HD2' audio card! I also want to + * thank Keiichi Iwasaki and his parents, because they helped Spas to get + * the card from Japan! Having hardware sample of Prodigy HD2 made adding + * support for that great card very easy and real fun and pleasure. + * + */ + +#include +#include +#include +#include + +#include +#include + +#include "mixer_if.h" + +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/envy24ht.c,v 1.1 2007/06/16 19:48:05 hasso Exp $"); + +MALLOC_DEFINE(M_ENVY24HT, "envy24ht", "envy24ht audio"); + +/* -------------------------------------------------------------------- */ + +struct sc_info; + +#define ENVY24HT_PLAY_CHNUM 8 +#define ENVY24HT_REC_CHNUM 2 +#define ENVY24HT_PLAY_BUFUNIT (4 /* byte/sample */ * 8 /* channel */) +#define ENVY24HT_REC_BUFUNIT (4 /* byte/sample */ * 2 /* channel */) +#define ENVY24HT_SAMPLE_NUM 4096 + +#define ENVY24HT_TIMEOUT 1000 + +#define ENVY24HT_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE) + +#define ENVY24HT_NAMELEN 32 + +#define abs(i) (i < 0 ? -i : i) + +struct envy24ht_sample { + volatile u_int32_t buffer; +}; + +typedef struct envy24ht_sample sample32_t; + +/* channel registers */ +struct sc_chinfo { + struct snd_dbuf *buffer; + struct pcm_channel *channel; + struct sc_info *parent; + int dir; + unsigned num; /* hw channel number */ + + /* channel information */ + u_int32_t format; + u_int32_t speed; + u_int32_t blk; /* hw block size(dword) */ + + /* format conversion structure */ + u_int8_t *data; + unsigned int size; /* data buffer size(byte) */ + int unit; /* sample size(byte) */ + unsigned int offset; /* samples number offset */ + void (*emldma)(struct sc_chinfo *); + + /* flags */ + int run; +}; + +/* codec interface entrys */ +struct codec_entry { + void *(*create)(device_t dev, void *devinfo, int dir, int num); + void (*destroy)(void *codec); + void (*init)(void *codec); + void (*reinit)(void *codec); + void (*setvolume)(void *codec, int dir, unsigned int left, unsigned int right); + void (*setrate)(void *codec, int which, int rate); +}; + +/* system configuration information */ +struct cfg_info { + char *name; + u_int16_t subvendor, subdevice; + u_int8_t scfg, acl, i2s, spdif; + u_int32_t gpiomask, gpiostate, gpiodir; + u_int32_t cdti, cclk, cs; + u_int8_t cif, type, free; + struct codec_entry *codec; +}; + +/* device private data */ +struct sc_info { + device_t dev; + struct spinlock *lock; + + /* Control/Status registor */ + struct resource *cs; + int csid; + bus_space_tag_t cst; + bus_space_handle_t csh; + /* MultiTrack registor */ + struct resource *mt; + int mtid; + bus_space_tag_t mtt; + bus_space_handle_t mth; + /* DMA tag */ + bus_dma_tag_t dmat; + /* IRQ resource */ + struct resource *irq; + int irqid; + void *ih; + + /* system configuration data */ + struct cfg_info *cfg; + + /* ADC/DAC number and info */ + int adcn, dacn; + void *adc[4], *dac[4]; + + /* mixer control data */ + u_int32_t src; + u_int8_t left[ENVY24HT_CHAN_NUM]; + u_int8_t right[ENVY24HT_CHAN_NUM]; + + /* Play/Record DMA fifo */ + sample32_t *pbuf; + sample32_t *rbuf; + u_int32_t psize, rsize; /* DMA buffer size(byte) */ + u_int16_t blk[2]; /* transfer check blocksize(dword) */ + bus_dmamap_t pmap, rmap; + + /* current status */ + u_int32_t speed; + int run[2]; + u_int16_t intr[2]; + struct pcmchan_caps caps[2]; + + /* channel info table */ + unsigned chnum; + struct sc_chinfo chan[11]; +}; + +/* -------------------------------------------------------------------- */ + +/* + * prototypes + */ + +/* DMA emulator */ +static void envy24ht_p8u(struct sc_chinfo *); +static void envy24ht_p16sl(struct sc_chinfo *); +static void envy24ht_p32sl(struct sc_chinfo *); +static void envy24ht_r16sl(struct sc_chinfo *); +static void envy24ht_r32sl(struct sc_chinfo *); + +/* channel interface */ +static void *envy24htchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int); +static int envy24htchan_setformat(kobj_t, void *, u_int32_t); +static int envy24htchan_setspeed(kobj_t, void *, u_int32_t); +static int envy24htchan_setblocksize(kobj_t, void *, u_int32_t); +static int envy24htchan_trigger(kobj_t, void *, int); +static int envy24htchan_getptr(kobj_t, void *); +static struct pcmchan_caps *envy24htchan_getcaps(kobj_t, void *); + +/* mixer interface */ +static int envy24htmixer_init(struct snd_mixer *); +static int envy24htmixer_reinit(struct snd_mixer *); +static int envy24htmixer_uninit(struct snd_mixer *); +static int envy24htmixer_set(struct snd_mixer *, unsigned, unsigned, unsigned); +static u_int32_t envy24htmixer_setrecsrc(struct snd_mixer *, u_int32_t); + +/* SPI codec access interface */ +static void *envy24ht_spi_create(device_t, void *, int, int); +static void envy24ht_spi_destroy(void *); +static void envy24ht_spi_init(void *); +static void envy24ht_spi_reinit(void *); +static void envy24ht_spi_setvolume(void *, int, unsigned int, unsigned int); + +/* -------------------------------------------------------------------- */ + +/* + system constant tables +*/ + +/* API -> hardware channel map */ +static unsigned envy24ht_chanmap[ENVY24HT_CHAN_NUM] = { + ENVY24HT_CHAN_PLAY_DAC1, /* 1 */ + ENVY24HT_CHAN_PLAY_DAC2, /* 2 */ + ENVY24HT_CHAN_PLAY_DAC3, /* 3 */ + ENVY24HT_CHAN_PLAY_DAC4, /* 4 */ + ENVY24HT_CHAN_PLAY_SPDIF, /* 0 */ + ENVY24HT_CHAN_REC_MIX, /* 5 */ + ENVY24HT_CHAN_REC_SPDIF, /* 6 */ + ENVY24HT_CHAN_REC_ADC1, /* 7 */ + ENVY24HT_CHAN_REC_ADC2, /* 8 */ + ENVY24HT_CHAN_REC_ADC3, /* 9 */ + ENVY24HT_CHAN_REC_ADC4, /* 10 */ +}; + +/* mixer -> API channel map. see above */ +static int envy24ht_mixmap[] = { + -1, /* Master output level. It is depend on codec support */ + -1, /* Treble level of all output channels */ + -1, /* Bass level of all output channels */ + -1, /* Volume of synthesier input */ + 0, /* Output level for the audio device */ + -1, /* Output level for the PC speaker */ + 7, /* line in jack */ + -1, /* microphone jack */ + -1, /* CD audio input */ + -1, /* Recording monitor */ + 1, /* alternative codec */ + -1, /* global recording level */ + -1, /* Input gain */ + -1, /* Output gain */ + 8, /* Input source 1 */ + 9, /* Input source 2 */ + 10, /* Input source 3 */ + 6, /* Digital (input) 1 */ + -1, /* Digital (input) 2 */ + -1, /* Digital (input) 3 */ + -1, /* Phone input */ + -1, /* Phone output */ + -1, /* Video/TV (audio) in */ + -1, /* Radio in */ + -1, /* Monitor volume */ +}; + +/* variable rate audio */ +static u_int32_t envy24ht_speed[] = { + 192000, 176400, 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, + 12000, 11025, 9600, 8000, 0 +}; + +/* known boards configuration */ +static struct codec_entry spi_codec = { + envy24ht_spi_create, + envy24ht_spi_destroy, + envy24ht_spi_init, + envy24ht_spi_reinit, + envy24ht_spi_setvolume, + NULL, /* setrate */ +}; + +static struct cfg_info cfg_table[] = { + { + "Envy24HT audio (Terratec Aureon 7.1 Space)", + 0x153b, 0x1145, + 0x0b, 0x80, 0xfc, 0xc3, + 0x21efff, 0x7fffff, 0x5e1000, + 0x40000, 0x80000, 0x1000, 0x00, 0x02, + 0, + &spi_codec, + }, + { + "Envy24HT audio (Terratec Aureon 5.1 Sky)", + 0x153b, 0x1147, + 0x0a, 0x80, 0xfc, 0xc3, + 0x21efff, 0x7fffff, 0x5e1000, + 0x40000, 0x80000, 0x1000, 0x00, 0x02, + 0, + &spi_codec, + }, + { + "Envy24HT audio (Terratec Aureon 7.1 Universe)", + 0x153b, 0x1153, + 0x0b, 0x80, 0xfc, 0xc3, + 0x21efff, 0x7fffff, 0x5e1000, + 0x40000, 0x80000, 0x1000, 0x00, 0x02, + 0, + &spi_codec, + }, + { + "Envy24HT audio (AudioTrak Prodigy 7.1)", + 0x4933, 0x4553, + 0x0b, 0x80, 0xfc, 0xc3, + 0x21efff, 0x7fffff, 0x5e1000, + 0x40000, 0x80000, 0x1000, 0x00, 0x02, + 0, + &spi_codec, + }, + { + "Envy24HT audio (Terratec PHASE 28)", + 0x153b, 0x1149, + 0x0b, 0x80, 0xfc, 0xc3, + 0x21efff, 0x7fffff, 0x5e1000, + 0x40000, 0x80000, 0x1000, 0x00, 0x02, + 0, + &spi_codec, + }, + { + "Envy24HT-S audio (Terratec PHASE 22)", + 0x153b, 0x1150, + 0x10, 0x80, 0xf0, 0xc3, + 0x7ffbc7, 0x7fffff, 0x438, + 0x20, 0x10, 0x400, 0x00, 0x00, + 0, + &spi_codec, + }, + { + "Envy24HT audio (AudioTrak Prodigy 7.1 LT)", + 0x3132, 0x4154, + 0x4b, 0x80, 0xfc, 0xc3, + 0x7ff8ff, 0x7fffff, 0x700, + 0x400, 0x200, 0x100, 0x00, 0x02, + 0, + &spi_codec, + }, + { + "Envy24HT audio (AudioTrak Prodigy 7.1 XT)", + 0x3136, 0x4154, + 0x4b, 0x80, 0xfc, 0xc3, + 0x7ff8ff, 0x7fffff, 0x700, + 0x400, 0x200, 0x100, 0x00, 0x02, + 0, + &spi_codec, + }, + { + "Envy24HT audio (M-Audio Revolution 7.1)", + 0x1412, 0x3630, + 0x43, 0x80, 0xf8, 0xc1, + 0x3fff85, 0x72, 0x4000fa, + 0x08, 0x02, 0x20, 0x00, 0x04, + 0, + &spi_codec, + }, + { + "Envy24GT audio (M-Audio Revolution 5.1)", + 0x1412, 0x3631, + 0x42, 0x80, 0xf8, 0xc1, + 0x3fff85, 0x72, 0x4000fa, + 0x08, 0x02, 0x10, 0x00, 0x03, + 0, + &spi_codec, + }, + { + "Envy24HT audio (M-Audio Audiophile 192)", + 0x1412, 0x3632, + 0x68, 0x80, 0xf8, 0xc3, + 0x45, 0x4000b5, 0x7fffba, + 0x08, 0x02, 0x10, 0x00, 0x03, + 0, + &spi_codec, + }, + { + "Envy24HT audio (AudioTrak Prodigy HD2)", + 0x3137, 0x4154, + 0x68, 0x80, 0x78, 0xc3, + 0xfff8ff, 0x200700, 0xdfffff, + 0x400, 0x200, 0x100, 0x00, 0x05, + 0, + &spi_codec, + }, + { + "Envy24HT audio (ESI Juli@)", + 0x3031, 0x4553, + 0x20, 0x80, 0xf8, 0xc3, + 0x7fff9f, 0x8016, 0x7fff9f, + 0x08, 0x02, 0x10, 0x00, 0x03, + 0, + &spi_codec, + }, + { + "Envy24HT audio (Generic)", + 0, 0, + 0x0b, 0x80, 0xfc, 0xc3, + 0x21efff, 0x7fffff, 0x5e1000, + 0x40000, 0x80000, 0x1000, 0x00, 0x02, + 0, + &spi_codec, /* default codec routines */ + } +}; + +static u_int32_t envy24ht_recfmt[] = { + AFMT_STEREO | AFMT_S16_LE, + AFMT_STEREO | AFMT_S32_LE, + 0 +}; +static struct pcmchan_caps envy24ht_reccaps = {8000, 96000, envy24ht_recfmt, 0}; + +static u_int32_t envy24ht_playfmt[] = { + AFMT_STEREO | AFMT_U8, + AFMT_STEREO | AFMT_S16_LE, + AFMT_STEREO | AFMT_S32_LE, + 0 +}; + +static struct pcmchan_caps envy24ht_playcaps = {8000, 192000, envy24ht_playfmt, 0}; + +struct envy24ht_emldma { + u_int32_t format; + void (*emldma)(struct sc_chinfo *); + int unit; +}; + +static struct envy24ht_emldma envy24ht_pemltab[] = { + {AFMT_STEREO | AFMT_U8, envy24ht_p8u, 2}, + {AFMT_STEREO | AFMT_S16_LE, envy24ht_p16sl, 4}, + {AFMT_STEREO | AFMT_S32_LE, envy24ht_p32sl, 8}, + {0, NULL, 0} +}; + +static struct envy24ht_emldma envy24ht_remltab[] = { + {AFMT_STEREO | AFMT_S16_LE, envy24ht_r16sl, 4}, + {AFMT_STEREO | AFMT_S32_LE, envy24ht_r32sl, 8}, + {0, NULL, 0} +}; + +/* -------------------------------------------------------------------- */ + +/* common routines */ +static u_int32_t +envy24ht_rdcs(struct sc_info *sc, int regno, int size) +{ + switch (size) { + case 1: + return bus_space_read_1(sc->cst, sc->csh, regno); + case 2: + return bus_space_read_2(sc->cst, sc->csh, regno); + case 4: + return bus_space_read_4(sc->cst, sc->csh, regno); + default: + return 0xffffffff; + } +} + +static void +envy24ht_wrcs(struct sc_info *sc, int regno, u_int32_t data, int size) +{ + switch (size) { + case 1: + bus_space_write_1(sc->cst, sc->csh, regno, data); + break; + case 2: + bus_space_write_2(sc->cst, sc->csh, regno, data); + break; + case 4: + bus_space_write_4(sc->cst, sc->csh, regno, data); + break; + } +} + +static u_int32_t +envy24ht_rdmt(struct sc_info *sc, int regno, int size) +{ + switch (size) { + case 1: + return bus_space_read_1(sc->mtt, sc->mth, regno); + case 2: + return bus_space_read_2(sc->mtt, sc->mth, regno); + case 4: + return bus_space_read_4(sc->mtt, sc->mth, regno); + default: + return 0xffffffff; + } +} + +static void +envy24ht_wrmt(struct sc_info *sc, int regno, u_int32_t data, int size) +{ + switch (size) { + case 1: + bus_space_write_1(sc->mtt, sc->mth, regno, data); + break; + case 2: + bus_space_write_2(sc->mtt, sc->mth, regno, data); + break; + case 4: + bus_space_write_4(sc->mtt, sc->mth, regno, data); + break; + } +} + +/* -------------------------------------------------------------------- */ + +/* I2C port/E2PROM access routines */ + +static int +envy24ht_rdi2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr) +{ + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr); +#endif + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); + if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24HT_TIMEOUT) { + return -1; + } + envy24ht_wrcs(sc, ENVY24HT_CCS_I2CADDR, addr, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDEV, + (dev & ENVY24HT_CCS_I2CDEV_ADDR) | ENVY24HT_CCS_I2CDEV_RD, 1); + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); + if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24HT_TIMEOUT) { + return -1; + } + data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CDATA, 1); + +#if(0) + device_printf(sc->dev, "envy24ht_rdi2c(): return 0x%x\n", data); +#endif + return (int)data; +} + +static int +envy24ht_wri2c(struct sc_info *sc, u_int32_t dev, u_int32_t addr, u_int32_t data) +{ + u_int32_t tmp; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_rdi2c(sc, 0x%02x, 0x%02x)\n", dev, addr); +#endif + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + tmp = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); + if ((tmp & ENVY24HT_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24HT_TIMEOUT) { + return -1; + } + envy24ht_wrcs(sc, ENVY24HT_CCS_I2CADDR, addr, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDATA, data, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_I2CDEV, + (dev & ENVY24HT_CCS_I2CDEV_ADDR) | ENVY24HT_CCS_I2CDEV_WR, 1); + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); + if ((data & ENVY24HT_CCS_I2CSTAT_BSY) == 0) + break; + DELAY(32); /* 31.25kHz */ + } + if (i == ENVY24HT_TIMEOUT) { + return -1; + } + + return 0; +} + +static int +envy24ht_rdrom(struct sc_info *sc, u_int32_t addr) +{ + u_int32_t data; + +#if(0) + device_printf(sc->dev, "envy24ht_rdrom(sc, 0x%02x)\n", addr); +#endif + data = envy24ht_rdcs(sc, ENVY24HT_CCS_I2CSTAT, 1); + if ((data & ENVY24HT_CCS_I2CSTAT_ROM) == 0) { +#if(0) + device_printf(sc->dev, "envy24ht_rdrom(): E2PROM not presented\n"); +#endif + return -1; + } + + return envy24ht_rdi2c(sc, ENVY24HT_CCS_I2CDEV_ROM, addr); +} + +static struct cfg_info * +envy24ht_rom2cfg(struct sc_info *sc) +{ + struct cfg_info *buff; + int size; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_rom2cfg(sc)\n"); +#endif + size = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SIZE); + if ((size < ENVY24HT_E2PROM_GPIOSTATE + 3) || (size == 0x78)) { +#if(0) + device_printf(sc->dev, "envy24ht_rom2cfg(): ENVY24HT_E2PROM_SIZE-->%d\n", size); +#endif + buff = kmalloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT); + if (buff == NULL) { +#if(0) + device_printf(sc->dev, "envy24ht_rom2cfg(): kmalloc()\n"); +#endif + return NULL; + } + buff->free = 1; + + /* no valid e2prom, using default values */ + buff->subvendor = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR) << 8; + buff->subvendor += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR + 1); + buff->subdevice = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE) << 8; + buff->subdevice += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE + 1); + buff->scfg = 0x0b; + buff->acl = 0x80; + buff->i2s = 0xfc; + buff->spdif = 0xc3; + buff->gpiomask = 0x21efff; + buff->gpiostate = 0x7fffff; + buff->gpiodir = 0x5e1000; + buff->cdti = 0x40000; + buff->cclk = 0x80000; + buff->cs = 0x1000; + buff->cif = 0x00; + buff->type = 0x02; + + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; +i++) + if (cfg_table[i].subvendor == buff->subvendor && + cfg_table[i].subdevice == buff->subdevice) + break; + buff->name = cfg_table[i].name; + buff->codec = cfg_table[i].codec; + + return buff; +#if 0 + return NULL; +#endif + } + buff = kmalloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT); + if (buff == NULL) { +#if(0) + device_printf(sc->dev, "envy24ht_rom2cfg(): kmalloc()\n"); +#endif + return NULL; + } + buff->free = 1; + + buff->subvendor = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR) << 8; + buff->subvendor += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBVENDOR + 1); + buff->subdevice = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE) << 8; + buff->subdevice += envy24ht_rdrom(sc, ENVY24HT_E2PROM_SUBDEVICE + 1); + buff->scfg = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SCFG); + buff->acl = envy24ht_rdrom(sc, ENVY24HT_E2PROM_ACL); + buff->i2s = envy24ht_rdrom(sc, ENVY24HT_E2PROM_I2S); + buff->spdif = envy24ht_rdrom(sc, ENVY24HT_E2PROM_SPDIF); + buff->gpiomask = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK) | \ + envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK + 1) << 8 | \ + envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOMASK + 2) << 16; + buff->gpiostate = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE) | \ + envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE + 1) << 8 | \ + envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIOSTATE + 2) << 16; + buff->gpiodir = envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR) | \ + envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR + 1) << 8 | \ + envy24ht_rdrom(sc, ENVY24HT_E2PROM_GPIODIR + 2) << 16; + + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) + if (cfg_table[i].subvendor == buff->subvendor && + cfg_table[i].subdevice == buff->subdevice) + break; + buff->name = cfg_table[i].name; + buff->codec = cfg_table[i].codec; + + return buff; +} + +static void +envy24ht_cfgfree(struct cfg_info *cfg) { + if (cfg == NULL) + return; + if (cfg->free) + kfree(cfg, M_ENVY24HT); + return; +} + +/* -------------------------------------------------------------------- */ + +/* AC'97 codec access routines */ + +#if 0 +static int +envy24ht_coldcd(struct sc_info *sc) +{ + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_coldcd()\n"); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_CLD, 1); + DELAY(10); + envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, 0, 1); + DELAY(1000); + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1); + if (data & ENVY24HT_MT_AC97CMD_RDY) { + return 0; + } + } + + return -1; +} + +static int +envy24ht_slavecd(struct sc_info *sc) +{ + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_slavecd()\n"); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, + ENVY24HT_MT_AC97CMD_CLD | ENVY24HT_MT_AC97CMD_WRM, 1); + DELAY(10); + envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, 0, 1); + DELAY(1000); + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1); + if (data & ENVY24HT_MT_AC97CMD_RDY) { + return 0; + } + } + + return -1; +} + +static int +envy24ht_rdcd(kobj_t obj, void *devinfo, int regno) +{ + struct sc_info *sc = (struct sc_info *)devinfo; + u_int32_t data; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_rdcd(obj, sc, 0x%02x)\n", regno); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_AC97IDX, (u_int32_t)regno, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_RD, 1); + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1); + if ((data & ENVY24HT_MT_AC97CMD_RD) == 0) + break; + } + data = envy24ht_rdmt(sc, ENVY24HT_MT_AC97DLO, 2); + +#if(0) + device_printf(sc->dev, "envy24ht_rdcd(): return 0x%x\n", data); +#endif + return (int)data; +} + +static int +envy24ht_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data) +{ + struct sc_info *sc = (struct sc_info *)devinfo; + u_int32_t cmd; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_wrcd(obj, sc, 0x%02x, 0x%04x)\n", regno, data); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_AC97IDX, (u_int32_t)regno, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_AC97DLO, (u_int32_t)data, 2); + envy24ht_wrmt(sc, ENVY24HT_MT_AC97CMD, ENVY24HT_MT_AC97CMD_WR, 1); + for (i = 0; i < ENVY24HT_TIMEOUT; i++) { + cmd = envy24ht_rdmt(sc, ENVY24HT_MT_AC97CMD, 1); + if ((cmd & ENVY24HT_MT_AC97CMD_WR) == 0) + break; + } + + return 0; +} + +static kobj_method_t envy24ht_ac97_methods[] = { + KOBJMETHOD(ac97_read, envy24ht_rdcd), + KOBJMETHOD(ac97_write, envy24ht_wrcd), + {0, 0} +}; +AC97_DECLARE(envy24ht_ac97); +#endif + +/* -------------------------------------------------------------------- */ + +/* GPIO access routines */ + +static u_int32_t +envy24ht_gpiord(struct sc_info *sc) +{ + if (sc->cfg->subvendor == 0x153b && sc->cfg->subdevice == 0x1150) + return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LDATA, 2); + else + return (envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_HDATA, 1) << 16 | envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LDATA, 2)); +} + +static void +envy24ht_gpiowr(struct sc_info *sc, u_int32_t data) +{ +#if(0) + device_printf(sc->dev, "envy24ht_gpiowr(sc, 0x%02x)\n", data & 0x7FFFFF); + return; +#endif + envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_LDATA, data, 2); + if (sc->cfg->subdevice != 0x1150) + envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_HDATA, data >> 16, 1); + return; +} + +#if 0 +static u_int32_t +envy24ht_gpiogetmask(struct sc_info *sc) +{ + return (envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_HMASK, 1) << 16 | envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_LMASK, 2)); +} +#endif + +static void +envy24ht_gpiosetmask(struct sc_info *sc, u_int32_t mask) +{ + envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_LMASK, mask, 2); + if (sc->cfg->subdevice != 0x1150) + envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_HMASK, mask >> 16, 1); + return; +} + +#if 0 +static u_int32_t +envy24ht_gpiogetdir(struct sc_info *sc) +{ + return envy24ht_rdcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, 4); +} +#endif + +static void +envy24ht_gpiosetdir(struct sc_info *sc, u_int32_t dir) +{ + if (sc->cfg->subvendor == 0x153b && sc->cfg->subdevice == 0x1150) + envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, dir, 2); + else + envy24ht_wrcs(sc, ENVY24HT_CCS_GPIO_CTLDIR, dir, 4); + return; +} + +/* -------------------------------------------------------------------- */ + +/* SPI codec access interface routine */ + +struct envy24ht_spi_codec { + struct spicds_info *info; + struct sc_info *parent; + int dir; + int num; + int cs, cclk, cdti; +}; + +static void +envy24ht_spi_ctl(void *codec, unsigned int cs, unsigned int cclk, unsigned int cdti) +{ + u_int32_t data = 0; + struct envy24ht_spi_codec *ptr = codec; + +#if(0) + device_printf(ptr->parent->dev, "--> %d, %d, %d\n", cs, cclk, cdti); +#endif + data = envy24ht_gpiord(ptr->parent); + data &= ~(ptr->cs | ptr->cclk | ptr->cdti); + if (cs) data += ptr->cs; + if (cclk) data += ptr->cclk; + if (cdti) data += ptr->cdti; + envy24ht_gpiowr(ptr->parent, data); + return; +} + +static void * +envy24ht_spi_create(device_t dev, void *info, int dir, int num) +{ + struct sc_info *sc = info; + struct envy24ht_spi_codec *buff = NULL; + +#if(0) + device_printf(sc->dev, "envy24ht_spi_create(dev, sc, %d, %d)\n", dir, num); +#endif + + buff = kmalloc(sizeof(*buff), M_ENVY24HT, M_NOWAIT); + if (buff == NULL) + return NULL; + + if (dir == PCMDIR_REC && sc->adc[num] != NULL) + buff->info = ((struct envy24ht_spi_codec *)sc->adc[num])->info; + else if (dir == PCMDIR_PLAY && sc->dac[num] != NULL) + buff->info = ((struct envy24ht_spi_codec *)sc->dac[num])->info; + else + buff->info = spicds_create(dev, buff, num, envy24ht_spi_ctl); + if (buff->info == NULL) { + kfree(buff, M_ENVY24HT); + return NULL; + } + + buff->parent = sc; + buff->dir = dir; + buff->num = num; + + return (void *)buff; +} + +static void +envy24ht_spi_destroy(void *codec) +{ + struct envy24ht_spi_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24ht_spi_destroy()\n"); +#endif + + if (ptr->dir == PCMDIR_PLAY) { + if (ptr->parent->dac[ptr->num] != NULL) + spicds_destroy(ptr->info); + } + else { + if (ptr->parent->adc[ptr->num] != NULL) + spicds_destroy(ptr->info); + } + + kfree(codec, M_ENVY24HT); +} + +static void +envy24ht_spi_init(void *codec) +{ + struct envy24ht_spi_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24ht_spicds_init()\n"); +#endif + ptr->cs = ptr->parent->cfg->cs; + ptr->cclk = ptr->parent->cfg->cclk; + ptr->cdti = ptr->parent->cfg->cdti; + spicds_settype(ptr->info, ptr->parent->cfg->type); + spicds_setcif(ptr->info, ptr->parent->cfg->cif); + if (ptr->parent->cfg->type == SPICDS_TYPE_AK4524 || \ + ptr->parent->cfg->type == SPICDS_TYPE_AK4528) { + spicds_setformat(ptr->info, + AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X); + spicds_setdvc(ptr->info, AK452X_DVC_DEMOFF); + } + + /* for the time being, init only first codec */ + if (ptr->num == 0) + spicds_init(ptr->info); +} + +static void +envy24ht_spi_reinit(void *codec) +{ + struct envy24ht_spi_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24ht_spi_reinit()\n"); +#endif + + spicds_reinit(ptr->info); +} + +static void +envy24ht_spi_setvolume(void *codec, int dir, unsigned int left, unsigned int right) +{ + struct envy24ht_spi_codec *ptr = codec; + if (ptr == NULL) + return; +#if(0) + device_printf(ptr->parent->dev, "envy24ht_spi_set()\n"); +#endif + + spicds_set(ptr->info, dir, left, right); +} + +/* -------------------------------------------------------------------- */ + +/* hardware access routeines */ + +static struct { + u_int32_t speed; + u_int32_t code; +} envy24ht_speedtab[] = { + {48000, ENVY24HT_MT_RATE_48000}, + {24000, ENVY24HT_MT_RATE_24000}, + {12000, ENVY24HT_MT_RATE_12000}, + {9600, ENVY24HT_MT_RATE_9600}, + {32000, ENVY24HT_MT_RATE_32000}, + {16000, ENVY24HT_MT_RATE_16000}, + {8000, ENVY24HT_MT_RATE_8000}, + {96000, ENVY24HT_MT_RATE_96000}, + {192000, ENVY24HT_MT_RATE_192000}, + {64000, ENVY24HT_MT_RATE_64000}, + {44100, ENVY24HT_MT_RATE_44100}, + {22050, ENVY24HT_MT_RATE_22050}, + {11025, ENVY24HT_MT_RATE_11025}, + {88200, ENVY24HT_MT_RATE_88200}, + {176400, ENVY24HT_MT_RATE_176400}, + {0, 0x10} +}; + +static int +envy24ht_setspeed(struct sc_info *sc, u_int32_t speed) { + u_int32_t code, i2sfmt; + int i = 0; + +#if(0) + device_printf(sc->dev, "envy24ht_setspeed(sc, %d)\n", speed); + if (speed == 0) { + code = ENVY24HT_MT_RATE_SPDIF; /* external master clock */ + envy24ht_slavecd(sc); + } + else { +#endif + for (i = 0; envy24ht_speedtab[i].speed != 0; i++) { + if (envy24ht_speedtab[i].speed == speed) + break; + } + code = envy24ht_speedtab[i].code; +#if 0 + } + device_printf(sc->dev, "envy24ht_setspeed(): speed %d/code 0x%04x\n", envy24ht_speedtab[i].speed, code); +#endif + if (code < 0x10) { + envy24ht_wrmt(sc, ENVY24HT_MT_RATE, code, 1); + if ((((sc->cfg->scfg & ENVY24HT_CCSM_SCFG_XIN2) == 0x00) && (code == ENVY24HT_MT_RATE_192000)) || \ + (code == ENVY24HT_MT_RATE_176400)) { + i2sfmt = envy24ht_rdmt(sc, ENVY24HT_MT_I2S, 1); + i2sfmt |= ENVY24HT_MT_I2S_MLR128; + envy24ht_wrmt(sc, ENVY24HT_MT_I2S, i2sfmt, 1); + } + else { + i2sfmt = envy24ht_rdmt(sc, ENVY24HT_MT_I2S, 1); + i2sfmt &= ~ENVY24HT_MT_I2S_MLR128; + envy24ht_wrmt(sc, ENVY24HT_MT_I2S, i2sfmt, 1); + } + code = envy24ht_rdmt(sc, ENVY24HT_MT_RATE, 1); + code &= ENVY24HT_MT_RATE_MASK; + for (i = 0; envy24ht_speedtab[i].code < 0x10; i++) { + if (envy24ht_speedtab[i].code == code) + break; + } + speed = envy24ht_speedtab[i].speed; + } + else + speed = 0; + +#if(0) + device_printf(sc->dev, "envy24ht_setspeed(): return %d\n", speed); +#endif + return speed; +} + +static void +envy24ht_setvolume(struct sc_info *sc, unsigned ch) +{ +#if(0) + device_printf(sc->dev, "envy24ht_setvolume(sc, %d)\n", ch); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, 0x7f00 | sc->left[ch], 2); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2 + 1, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, (sc->right[ch] << 8) | 0x7f, 2); +#endif +} + +static void +envy24ht_mutevolume(struct sc_info *sc, unsigned ch) +{ +#if 0 + u_int32_t vol; + + device_printf(sc->dev, "envy24ht_mutevolume(sc, %d)\n", ch); + vol = ENVY24HT_VOL_MUTE << 8 | ENVY24HT_VOL_MUTE; + envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, vol, 2); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLIDX, ch * 2 + 1, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_VOLUME, vol, 2); +#endif +} + +static u_int32_t +envy24ht_gethwptr(struct sc_info *sc, int dir) +{ + int unit, regno; + u_int32_t ptr, rtn; + +#if(0) + device_printf(sc->dev, "envy24ht_gethwptr(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) { + rtn = sc->psize / 4; + unit = ENVY24HT_PLAY_BUFUNIT / 4; + regno = ENVY24HT_MT_PCNT; + } + else { + rtn = sc->rsize / 4; + unit = ENVY24HT_REC_BUFUNIT / 4; + regno = ENVY24HT_MT_RCNT; + } + + ptr = envy24ht_rdmt(sc, regno, 2); + rtn -= (ptr + 1); + rtn /= unit; + +#if(0) + device_printf(sc->dev, "envy24ht_gethwptr(): return %d\n", rtn); +#endif + return rtn; +} + +static void +envy24ht_updintr(struct sc_info *sc, int dir) +{ + int regptr, regintr; + u_int32_t mask, intr; + u_int32_t ptr, size, cnt; + u_int16_t blk; + +#if(0) + device_printf(sc->dev, "envy24ht_updintr(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) { + blk = sc->blk[0]; + size = sc->psize / 4; + regptr = ENVY24HT_MT_PCNT; + regintr = ENVY24HT_MT_PTERM; + mask = ~ENVY24HT_MT_INT_PMASK; + } + else { + blk = sc->blk[1]; + size = sc->rsize / 4; + regptr = ENVY24HT_MT_RCNT; + regintr = ENVY24HT_MT_RTERM; + mask = ~ENVY24HT_MT_INT_RMASK; + } + + ptr = size - envy24ht_rdmt(sc, regptr, 2) - 1; + /* + cnt = blk - ptr % blk - 1; + if (cnt == 0) + cnt = blk - 1; + */ + cnt = blk - 1; +#if(0) + device_printf(sc->dev, "envy24ht_updintr():ptr = %d, blk = %d, cnt = %d\n", ptr, blk, cnt); +#endif + envy24ht_wrmt(sc, regintr, cnt, 2); + intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1); +#if(0) + device_printf(sc->dev, "envy24ht_updintr():intr = 0x%02x, mask = 0x%02x\n", intr, mask); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, intr & mask, 1); +#if(0) + device_printf(sc->dev, "envy24ht_updintr():INT-->0x%02x\n", + envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1)); +#endif + + return; +} + +#if 0 +static void +envy24ht_maskintr(struct sc_info *sc, int dir) +{ + u_int32_t mask, intr; + +#if(0) + device_printf(sc->dev, "envy24ht_maskintr(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) + mask = ENVY24HT_MT_INT_PMASK; + else + mask = ENVY24HT_MT_INT_RMASK; + intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_INT, intr | mask, 1); + + return; +} +#endif + +static int +envy24ht_checkintr(struct sc_info *sc, int dir) +{ + u_int32_t mask, stat, intr, rtn; + +#if(0) + device_printf(sc->dev, "envy24ht_checkintr(sc, %d)\n", dir); +#endif + intr = envy24ht_rdmt(sc, ENVY24HT_MT_INT_STAT, 1); + if (dir == PCMDIR_PLAY) { + if ((rtn = intr & ENVY24HT_MT_INT_PSTAT) != 0) { + mask = ~ENVY24HT_MT_INT_RSTAT; + envy24ht_wrmt(sc, 0x1a, 0x01, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_INT_STAT, (intr & mask) | ENVY24HT_MT_INT_PSTAT | 0x08, 1); + stat = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, stat | ENVY24HT_MT_INT_PMASK, 1); + } + } + else { + if ((rtn = intr & ENVY24HT_MT_INT_RSTAT) != 0) { + mask = ~ENVY24HT_MT_INT_PSTAT; +#if 0 + stat = ENVY24HT_MT_INT_RSTAT | ENVY24HT_MT_INT_RMASK; +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_INT_STAT, (intr & mask) | ENVY24HT_MT_INT_RSTAT, 1); + stat = envy24ht_rdmt(sc, ENVY24HT_MT_INT_MASK, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_INT_MASK, stat | ENVY24HT_MT_INT_RMASK, 1); + } + } + + return rtn; +} + +static void +envy24ht_start(struct sc_info *sc, int dir) +{ + u_int32_t stat, sw; + +#if(0) + device_printf(sc->dev, "envy24ht_start(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) + sw = ENVY24HT_MT_PCTL_PSTART; + else + sw = ENVY24HT_MT_PCTL_RSTART; + + stat = envy24ht_rdmt(sc, ENVY24HT_MT_PCTL, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_PCTL, stat | sw, 1); +#if(0) + DELAY(100); + device_printf(sc->dev, "PADDR:0x%08x\n", envy24ht_rdmt(sc, ENVY24HT_MT_PADDR, 4)); + device_printf(sc->dev, "PCNT:%ld\n", envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2)); +#endif + + return; +} + +static void +envy24ht_stop(struct sc_info *sc, int dir) +{ + u_int32_t stat, sw; + +#if(0) + device_printf(sc->dev, "envy24ht_stop(sc, %d)\n", dir); +#endif + if (dir == PCMDIR_PLAY) + sw = ~ENVY24HT_MT_PCTL_PSTART; + else + sw = ~ENVY24HT_MT_PCTL_RSTART; + + stat = envy24ht_rdmt(sc, ENVY24HT_MT_PCTL, 1); + envy24ht_wrmt(sc, ENVY24HT_MT_PCTL, stat & sw, 1); + + return; +} + +#if 0 +static int +envy24ht_route(struct sc_info *sc, int dac, int class, int adc, int rev) +{ + return 0; +} +#endif + +/* -------------------------------------------------------------------- */ + +/* buffer copy routines */ +static void +envy24ht_p32sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int32_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getready(ch->buffer) / 8; + dmabuf = ch->parent->pbuf; + data = (u_int32_t *)ch->data; + src = sndbuf_getreadyptr(ch->buffer) / 4; + dst = src / 2 + ch->offset; + ssize = ch->size / 4; + dsize = ch->size / 8; + slot = ch->num * 2; + + for (i = 0; i < length; i++) { + dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = data[src]; + dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = data[src + 1]; + dst++; + dst %= dsize; + src += 2; + src %= ssize; + } + + return; +} + +static void +envy24ht_p16sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int16_t *data; + int src, dst, ssize, dsize, slot; + int i; + +#if(0) + device_printf(ch->parent->dev, "envy24ht_p16sl()\n"); +#endif + length = sndbuf_getready(ch->buffer) / 4; + dmabuf = ch->parent->pbuf; + data = (u_int16_t *)ch->data; + src = sndbuf_getreadyptr(ch->buffer) / 2; + dst = src / 2 + ch->offset; + ssize = ch->size / 2; + dsize = ch->size / 4; + slot = ch->num * 2; +#if(0) + device_printf(ch->parent->dev, "envy24ht_p16sl():%lu-->%lu(%lu)\n", src, dst, length); +#endif + + for (i = 0; i < length; i++) { + dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = (u_int32_t)data[src] << 16; + dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = (u_int32_t)data[src + 1] << 16; +#if(0) + if (i < 16) { + kprintf("%08x", dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot]); + kprintf("%08x", dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1]); + } +#endif + dst++; + dst %= dsize; + src += 2; + src %= ssize; + } +#if(0) + kprintf("\n"); +#endif + + return; +} + +static void +envy24ht_p8u(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int8_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getready(ch->buffer) / 2; + dmabuf = ch->parent->pbuf; + data = (u_int8_t *)ch->data; + src = sndbuf_getreadyptr(ch->buffer); + dst = src / 2 + ch->offset; + ssize = ch->size; + dsize = ch->size / 4; + slot = ch->num * 2; + + for (i = 0; i < length; i++) { + dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot].buffer = ((u_int32_t)data[src] ^ 0x80) << 24; + dmabuf[dst * ENVY24HT_PLAY_CHNUM + slot + 1].buffer = ((u_int32_t)data[src + 1] ^ 0x80) << 24; + dst++; + dst %= dsize; + src += 2; + src %= ssize; + } + + return; +} + +static void +envy24ht_r32sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int32_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getfree(ch->buffer) / 8; + dmabuf = ch->parent->rbuf; + data = (u_int32_t *)ch->data; + dst = sndbuf_getfreeptr(ch->buffer) / 4; + src = dst / 2 + ch->offset; + dsize = ch->size / 4; + ssize = ch->size / 8; + slot = (ch->num - ENVY24HT_CHAN_REC_ADC1) * 2; + + for (i = 0; i < length; i++) { + data[dst] = dmabuf[src * ENVY24HT_REC_CHNUM + slot].buffer; + data[dst + 1] = dmabuf[src * ENVY24HT_REC_CHNUM + slot + 1].buffer; + dst += 2; + dst %= dsize; + src++; + src %= ssize; + } + + return; +} + +static void +envy24ht_r16sl(struct sc_chinfo *ch) +{ + int length; + sample32_t *dmabuf; + u_int16_t *data; + int src, dst, ssize, dsize, slot; + int i; + + length = sndbuf_getfree(ch->buffer) / 4; + dmabuf = ch->parent->rbuf; + data = (u_int16_t *)ch->data; + dst = sndbuf_getfreeptr(ch->buffer) / 2; + src = dst / 2 + ch->offset; + dsize = ch->size / 2; + ssize = ch->size / 8; + slot = (ch->num - ENVY24HT_CHAN_REC_ADC1) * 2; + + for (i = 0; i < length; i++) { + data[dst] = dmabuf[src * ENVY24HT_REC_CHNUM + slot].buffer; + data[dst + 1] = dmabuf[src * ENVY24HT_REC_CHNUM + slot + 1].buffer; + dst += 2; + dst %= dsize; + src++; + src %= ssize; + } + + return; +} + +/* -------------------------------------------------------------------- */ + +/* channel interface */ +static void * +envy24htchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +{ + struct sc_info *sc = (struct sc_info *)devinfo; + struct sc_chinfo *ch; + unsigned num; + +#if(0) + device_printf(sc->dev, "envy24htchan_init(obj, devinfo, b, c, %d)\n", dir); +#endif + snd_mtxlock(sc->lock); +#if 0 + if ((sc->chnum > ENVY24HT_CHAN_PLAY_SPDIF && dir != PCMDIR_REC) || + (sc->chnum < ENVY24HT_CHAN_REC_ADC1 && dir != PCMDIR_PLAY)) { + snd_mtxunlock(sc->lock); + return NULL; + } +#endif + num = sc->chnum; + + ch = &sc->chan[num]; + ch->size = 8 * ENVY24HT_SAMPLE_NUM; + ch->data = kmalloc(ch->size, M_ENVY24HT, M_NOWAIT); + if (ch->data == NULL) { + ch->size = 0; + ch = NULL; + } + else { + ch->buffer = b; + ch->channel = c; + ch->parent = sc; + ch->dir = dir; + /* set channel map */ + ch->num = envy24ht_chanmap[num]; + snd_mtxunlock(sc->lock); + sndbuf_setup(ch->buffer, ch->data, ch->size); + snd_mtxlock(sc->lock); + /* these 2 values are dummy */ + ch->unit = 4; + ch->blk = 10240; + } + snd_mtxunlock(sc->lock); + + return ch; +} + +static int +envy24htchan_free(kobj_t obj, void *data) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + +#if(0) + device_printf(sc->dev, "envy24htchan_free()\n"); +#endif + snd_mtxlock(sc->lock); + if (ch->data != NULL) { + kfree(ch->data, M_ENVY24HT); + ch->data = NULL; + } + snd_mtxunlock(sc->lock); + + return 0; +} + +static int +envy24htchan_setformat(kobj_t obj, void *data, u_int32_t format) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + struct envy24ht_emldma *emltab; + /* unsigned int bcnt, bsize; */ + int i; + +#if(0) + device_printf(sc->dev, "envy24htchan_setformat(obj, data, 0x%08x)\n", format); +#endif + snd_mtxlock(sc->lock); + /* check and get format related information */ + if (ch->dir == PCMDIR_PLAY) + emltab = envy24ht_pemltab; + else + emltab = envy24ht_remltab; + if (emltab == NULL) { + snd_mtxunlock(sc->lock); + return -1; + } + for (i = 0; emltab[i].format != 0; i++) + if (emltab[i].format == format) + break; + if (emltab[i].format == 0) { + snd_mtxunlock(sc->lock); + return -1; + } + + /* set format information */ + ch->format = format; + ch->emldma = emltab[i].emldma; + if (ch->unit > emltab[i].unit) + ch->blk *= ch->unit / emltab[i].unit; + else + ch->blk /= emltab[i].unit / ch->unit; + ch->unit = emltab[i].unit; + + /* set channel buffer information */ + ch->size = ch->unit * ENVY24HT_SAMPLE_NUM; +#if 0 + if (ch->dir == PCMDIR_PLAY) + bsize = ch->blk * 4 / ENVY24HT_PLAY_BUFUNIT; + else + bsize = ch->blk * 4 / ENVY24HT_REC_BUFUNIT; + bsize *= ch->unit; + bcnt = ch->size / bsize; + sndbuf_resize(ch->buffer, bcnt, bsize); +#endif + snd_mtxunlock(sc->lock); + +#if(0) + device_printf(sc->dev, "envy24htchan_setformat(): return 0x%08x\n", 0); +#endif + return 0; +} + +/* + IMPLEMENT NOTICE: In this driver, setspeed function only do setting + of speed information value. And real hardware speed setting is done + at start triggered(see envy24htchan_trigger()). So, at this function + is called, any value that ENVY24 can use is able to set. But, at + start triggerd, some other channel is running, and that channel's + speed isn't same with, then trigger function will fail. +*/ +static int +envy24htchan_setspeed(kobj_t obj, void *data, u_int32_t speed) +{ + struct sc_chinfo *ch = data; + u_int32_t val, prev; + int i; + +#if(0) + device_printf(ch->parent->dev, "envy24htchan_setspeed(obj, data, %d)\n", speed); +#endif + prev = 0x7fffffff; + for (i = 0; (val = envy24ht_speed[i]) != 0; i++) { + if (abs(val - speed) < abs(prev - speed)) + prev = val; + else + break; + } + ch->speed = prev; + +#if(0) + device_printf(ch->parent->dev, "envy24htchan_setspeed(): return %d\n", ch->speed); +#endif + return ch->speed; +} + +static int +envy24htchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +{ + struct sc_chinfo *ch = data; + /* struct sc_info *sc = ch->parent; */ + u_int32_t size, prev; + unsigned int bcnt, bsize; + +#if(0) + device_printf(sc->dev, "envy24htchan_setblocksize(obj, data, %d)\n", blocksize); +#endif + prev = 0x7fffffff; + /* snd_mtxlock(sc->lock); */ + for (size = ch->size / 2; size > 0; size /= 2) { + if (abs(size - blocksize) < abs(prev - blocksize)) + prev = size; + else + break; + } + + ch->blk = prev / ch->unit; + if (ch->dir == PCMDIR_PLAY) + ch->blk *= ENVY24HT_PLAY_BUFUNIT / 4; + else + ch->blk *= ENVY24HT_REC_BUFUNIT / 4; + /* set channel buffer information */ + /* ch->size = ch->unit * ENVY24HT_SAMPLE_NUM; */ + if (ch->dir == PCMDIR_PLAY) + bsize = ch->blk * 4 / ENVY24HT_PLAY_BUFUNIT; + else + bsize = ch->blk * 4 / ENVY24HT_REC_BUFUNIT; + bsize *= ch->unit; + bcnt = ch->size / bsize; + sndbuf_resize(ch->buffer, bcnt, bsize); + /* snd_mtxunlock(sc->lock); */ + +#if(0) + device_printf(sc->dev, "envy24htchan_setblocksize(): return %d\n", prev); +#endif + return prev; +} + +/* semantic note: must start at beginning of buffer */ +static int +envy24htchan_trigger(kobj_t obj, void *data, int go) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + u_int32_t ptr; + int slot; +#if 0 + int i; + + device_printf(sc->dev, "envy24htchan_trigger(obj, data, %d)\n", go); +#endif + snd_mtxlock(sc->lock); + if (ch->dir == PCMDIR_PLAY) + slot = 0; + else + slot = 1; + switch (go) { + case PCMTRIG_START: +#if(0) + device_printf(sc->dev, "envy24htchan_trigger(): start\n"); +#endif + /* check or set channel speed */ + if (sc->run[0] == 0 && sc->run[1] == 0) { + sc->speed = envy24ht_setspeed(sc, ch->speed); + sc->caps[0].minspeed = sc->caps[0].maxspeed = sc->speed; + sc->caps[1].minspeed = sc->caps[1].maxspeed = sc->speed; + } + else if (ch->speed != 0 && ch->speed != sc->speed) + return -1; + if (ch->speed == 0) + ch->channel->speed = sc->speed; + /* start or enable channel */ + sc->run[slot]++; + if (sc->run[slot] == 1) { + /* first channel */ + ch->offset = 0; + sc->blk[slot] = ch->blk; + } + else { + ptr = envy24ht_gethwptr(sc, ch->dir); + ch->offset = ((ptr / ch->blk + 1) * ch->blk % + (ch->size / 4)) * 4 / ch->unit; + if (ch->blk < sc->blk[slot]) + sc->blk[slot] = ch->blk; + } + if (ch->dir == PCMDIR_PLAY) { + ch->emldma(ch); + envy24ht_setvolume(sc, ch->num); + } + envy24ht_updintr(sc, ch->dir); + if (sc->run[slot] == 1) + envy24ht_start(sc, ch->dir); + ch->run = 1; + break; + case PCMTRIG_EMLDMAWR: +#if(0) + device_printf(sc->dev, "envy24htchan_trigger(): emldmawr\n"); +#endif + if (ch->run != 1) + return -1; + ch->emldma(ch); + break; + case PCMTRIG_EMLDMARD: +#if(0) + device_printf(sc->dev, "envy24htchan_trigger(): emldmard\n"); +#endif + if (ch->run != 1) + return -1; + ch->emldma(ch); + break; + case PCMTRIG_ABORT: + if (ch->run) { +#if(0) + device_printf(sc->dev, "envy24htchan_trigger(): abort\n"); +#endif + ch->run = 0; + sc->run[slot]--; + if (ch->dir == PCMDIR_PLAY) + envy24ht_mutevolume(sc, ch->num); + if (sc->run[slot] == 0) { + envy24ht_stop(sc, ch->dir); + sc->intr[slot] = 0; + } +/* else if (ch->blk == sc->blk[slot]) { + sc->blk[slot] = ENVY24HT_SAMPLE_NUM / 2; + for (i = 0; i < ENVY24HT_CHAN_NUM; i++) { + if (sc->chan[i].dir == ch->dir && + sc->chan[i].run == 1 && + sc->chan[i].blk < sc->blk[slot]) + sc->blk[slot] = sc->chan[i].blk; + } + if (ch->blk != sc->blk[slot]) + envy24ht_updintr(sc, ch->dir); + }*/ + } + break; + } + snd_mtxunlock(sc->lock); + + return 0; +} + +static int +envy24htchan_getptr(kobj_t obj, void *data) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + u_int32_t ptr; + int rtn; + +#if(0) + device_printf(sc->dev, "envy24htchan_getptr()\n"); +#endif + snd_mtxlock(sc->lock); + ptr = envy24ht_gethwptr(sc, ch->dir); + rtn = ptr * ch->unit; + snd_mtxunlock(sc->lock); + +#if(0) + device_printf(sc->dev, "envy24htchan_getptr(): return %d\n", + rtn); +#endif + return rtn; +} + +static struct pcmchan_caps * +envy24htchan_getcaps(kobj_t obj, void *data) +{ + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + struct pcmchan_caps *rtn; + +#if(0) + device_printf(sc->dev, "envy24htchan_getcaps()\n"); +#endif + snd_mtxlock(sc->lock); + if (ch->dir == PCMDIR_PLAY) { + if (sc->run[0] == 0) + rtn = &envy24ht_playcaps; + else + rtn = &sc->caps[0]; + } + else { + if (sc->run[1] == 0) + rtn = &envy24ht_reccaps; + else + rtn = &sc->caps[1]; + } + snd_mtxunlock(sc->lock); + + return rtn; +} + +static kobj_method_t envy24htchan_methods[] = { + KOBJMETHOD(channel_init, envy24htchan_init), + KOBJMETHOD(channel_free, envy24htchan_free), + KOBJMETHOD(channel_setformat, envy24htchan_setformat), + KOBJMETHOD(channel_setspeed, envy24htchan_setspeed), + KOBJMETHOD(channel_setblocksize, envy24htchan_setblocksize), + KOBJMETHOD(channel_trigger, envy24htchan_trigger), + KOBJMETHOD(channel_getptr, envy24htchan_getptr), + KOBJMETHOD(channel_getcaps, envy24htchan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(envy24htchan); + +/* -------------------------------------------------------------------- */ + +/* mixer interface */ + +static int +envy24htmixer_init(struct snd_mixer *m) +{ + struct sc_info *sc = mix_getdevinfo(m); + +#if(0) + device_printf(sc->dev, "envy24htmixer_init()\n"); +#endif + if (sc == NULL) + return -1; + + /* set volume control rate */ + snd_mtxlock(sc->lock); +#if 0 + envy24ht_wrmt(sc, ENVY24HT_MT_VOLRATE, 0x30, 1); /* 0x30 is default value */ +#endif + + pcm_setflags(sc->dev, pcm_getflags(sc->dev) | SD_F_SOFTPCMVOL); + + mix_setdevs(m, ENVY24HT_MIX_MASK); + mix_setrecdevs(m, ENVY24HT_MIX_REC_MASK); + + snd_mtxunlock(sc->lock); + + return 0; +} + +static int +envy24htmixer_reinit(struct snd_mixer *m) +{ + struct sc_info *sc = mix_getdevinfo(m); + + if (sc == NULL) + return -1; +#if(0) + device_printf(sc->dev, "envy24htmixer_reinit()\n"); +#endif + + return 0; +} + +static int +envy24htmixer_uninit(struct snd_mixer *m) +{ + struct sc_info *sc = mix_getdevinfo(m); + + if (sc == NULL) + return -1; +#if(0) + device_printf(sc->dev, "envy24htmixer_uninit()\n"); +#endif + + return 0; +} + +static int +envy24htmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) +{ + struct sc_info *sc = mix_getdevinfo(m); + int ch = envy24ht_mixmap[dev]; + int hwch; + int i; + + if (sc == NULL) + return -1; + if (dev == 0 && sc->cfg->codec->setvolume == NULL) + return -1; + if (dev != 0 && ch == -1) + return -1; + hwch = envy24ht_chanmap[ch]; +#if(0) + device_printf(sc->dev, "envy24htmixer_set(m, %d, %d, %d)\n", + dev, left, right); +#endif + + snd_mtxlock(sc->lock); + if (dev == 0) { + for (i = 0; i < sc->dacn; i++) { + sc->cfg->codec->setvolume(sc->dac[i], PCMDIR_PLAY, left, right); + } + } + else { + /* set volume value for hardware */ + if ((sc->left[hwch] = 100 - left) > ENVY24HT_VOL_MIN) + sc->left[hwch] = ENVY24HT_VOL_MUTE; + if ((sc->right[hwch] = 100 - right) > ENVY24HT_VOL_MIN) + sc->right[hwch] = ENVY24HT_VOL_MUTE; + + /* set volume for record channel and running play channel */ + if (hwch > ENVY24HT_CHAN_PLAY_SPDIF || sc->chan[ch].run) + envy24ht_setvolume(sc, hwch); + } + snd_mtxunlock(sc->lock); + + return right << 8 | left; +} + +static u_int32_t +envy24htmixer_setrecsrc(struct snd_mixer *m, u_int32_t src) +{ + struct sc_info *sc = mix_getdevinfo(m); + int ch = envy24ht_mixmap[src]; +#if(0) + device_printf(sc->dev, "envy24htmixer_setrecsrc(m, %d)\n", src); +#endif + + if (ch > ENVY24HT_CHAN_PLAY_SPDIF) + sc->src = ch; + return src; +} + +static kobj_method_t envy24htmixer_methods[] = { + KOBJMETHOD(mixer_init, envy24htmixer_init), + KOBJMETHOD(mixer_reinit, envy24htmixer_reinit), + KOBJMETHOD(mixer_uninit, envy24htmixer_uninit), + KOBJMETHOD(mixer_set, envy24htmixer_set), + KOBJMETHOD(mixer_setrecsrc, envy24htmixer_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(envy24htmixer); + +/* -------------------------------------------------------------------- */ + +/* The interrupt handler */ +static void +envy24ht_intr(void *p) +{ + struct sc_info *sc = (struct sc_info *)p; + struct sc_chinfo *ch; + u_int32_t ptr, dsize, feed; + int i; + +#if(0) + device_printf(sc->dev, "envy24ht_intr()\n"); +#endif + snd_mtxlock(sc->lock); + if (envy24ht_checkintr(sc, PCMDIR_PLAY)) { +#if(0) + device_printf(sc->dev, "envy24ht_intr(): play\n"); +#endif + dsize = sc->psize / 4; + ptr = dsize - envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2) - 1; +#if(0) + device_printf(sc->dev, "envy24ht_intr(): ptr = %d-->", ptr); +#endif + ptr -= ptr % sc->blk[0]; + feed = (ptr + dsize - sc->intr[0]) % dsize; +#if(0) + kprintf("%d intr = %d feed = %d\n", ptr, sc->intr[0], feed); +#endif + for (i = ENVY24HT_CHAN_PLAY_DAC1; i <= ENVY24HT_CHAN_PLAY_SPDIF; i++) { + ch = &sc->chan[i]; +#if(0) + if (ch->run) + device_printf(sc->dev, "envy24ht_intr(): chan[%d].blk = %d\n", i, ch->blk); +#endif + if (ch->run && ch->blk <= feed) { + snd_mtxunlock(sc->lock); + chn_intr(ch->channel); + snd_mtxlock(sc->lock); + } + } + sc->intr[0] = ptr; + envy24ht_updintr(sc, PCMDIR_PLAY); + } + if (envy24ht_checkintr(sc, PCMDIR_REC)) { +#if(0) + device_printf(sc->dev, "envy24ht_intr(): rec\n"); +#endif + dsize = sc->rsize / 4; + ptr = dsize - envy24ht_rdmt(sc, ENVY24HT_MT_RCNT, 2) - 1; + ptr -= ptr % sc->blk[1]; + feed = (ptr + dsize - sc->intr[1]) % dsize; + for (i = ENVY24HT_CHAN_REC_ADC1; i <= ENVY24HT_CHAN_REC_SPDIF; i++) { + ch = &sc->chan[i]; + if (ch->run && ch->blk <= feed) { + snd_mtxunlock(sc->lock); + chn_intr(ch->channel); + snd_mtxlock(sc->lock); + } + } + sc->intr[1] = ptr; + envy24ht_updintr(sc, PCMDIR_REC); + } + snd_mtxunlock(sc->lock); + + return; +} + +/* + * Probe and attach the card + */ + +static int +envy24ht_pci_probe(device_t dev) +{ + u_int16_t sv, sd; + int i; + +#if(0) + kprintf("envy24ht_pci_probe()\n"); +#endif + if (pci_get_device(dev) == PCID_ENVY24HT && + pci_get_vendor(dev) == PCIV_ENVY24) { + sv = pci_get_subvendor(dev); + sd = pci_get_subdevice(dev); + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) { + if (cfg_table[i].subvendor == sv && + cfg_table[i].subdevice == sd) { + break; + } + } + device_set_desc(dev, cfg_table[i].name); +#if(0) + kprintf("envy24ht_pci_probe(): return 0\n"); +#endif + return 0; + } + else { +#if(0) + kprintf("envy24ht_pci_probe(): return ENXIO\n"); +#endif + return ENXIO; + } +} + +static void +envy24ht_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + /* struct sc_info *sc = (struct sc_info *)arg; */ + +#if(0) + device_printf(sc->dev, "envy24ht_dmapsetmap()\n"); + if (bootverbose) { + kprintf("envy24ht(play): setmap %lx, %lx; ", + (unsigned long)segs->ds_addr, + (unsigned long)segs->ds_len); + kprintf("%p -> %lx\n", sc->pmap, (unsigned long)vtophys(sc->pmap)); + } +#endif +} + +static void +envy24ht_dmarsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + /* struct sc_info *sc = (struct sc_info *)arg; */ + +#if(0) + device_printf(sc->dev, "envy24ht_dmarsetmap()\n"); + if (bootverbose) { + kprintf("envy24ht(record): setmap %lx, %lx; ", + (unsigned long)segs->ds_addr, + (unsigned long)segs->ds_len); + kprintf("%p -> %lx\n", sc->rmap, (unsigned long)vtophys(sc->pmap)); + } +#endif +} + +static void +envy24ht_dmafree(struct sc_info *sc) +{ +#if(0) + device_printf(sc->dev, "envy24ht_dmafree():"); + if (sc->rmap) kprintf(" sc->rmap(0x%08x)", (u_int32_t)sc->rmap); + else kprintf(" sc->rmap(null)"); + if (sc->pmap) kprintf(" sc->pmap(0x%08x)", (u_int32_t)sc->pmap); + else kprintf(" sc->pmap(null)"); + if (sc->rbuf) kprintf(" sc->rbuf(0x%08x)", (u_int32_t)sc->rbuf); + else kprintf(" sc->rbuf(null)"); + if (sc->pbuf) kprintf(" sc->pbuf(0x%08x)\n", (u_int32_t)sc->pbuf); + else kprintf(" sc->pbuf(null)\n"); +#endif +#if(0) + if (sc->rmap) + bus_dmamap_unload(sc->dmat, sc->rmap); + if (sc->pmap) + bus_dmamap_unload(sc->dmat, sc->pmap); + if (sc->rbuf) + bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap); + if (sc->pbuf) + bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap); +#else + bus_dmamap_unload(sc->dmat, sc->rmap); + bus_dmamap_unload(sc->dmat, sc->pmap); + bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap); + bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap); +#endif + + sc->rmap = sc->pmap = NULL; + sc->pbuf = NULL; + sc->rbuf = NULL; + + return; +} + +static int +envy24ht_dmainit(struct sc_info *sc) +{ + u_int32_t addr; + +#if(0) + device_printf(sc->dev, "envy24ht_dmainit()\n"); +#endif + /* init values */ + sc->psize = ENVY24HT_PLAY_BUFUNIT * ENVY24HT_SAMPLE_NUM; + sc->rsize = ENVY24HT_REC_BUFUNIT * ENVY24HT_SAMPLE_NUM; + sc->pbuf = NULL; + sc->rbuf = NULL; + sc->pmap = sc->rmap = NULL; + sc->blk[0] = sc->blk[1] = 0; + + /* allocate DMA buffer */ +#if(0) + device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_alloc(): sc->pbuf\n"); +#endif + if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_NOWAIT, &sc->pmap)) + goto bad; +#if(0) + device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_alloc(): sc->rbuf\n"); +#endif + if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_NOWAIT, &sc->rmap)) + goto bad; +#if(0) + device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_load(): sc->pmap\n"); +#endif + if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->psize, envy24ht_dmapsetmap, sc, 0)) + goto bad; +#if(0) + device_printf(sc->dev, "envy24ht_dmainit(): bus_dmamem_load(): sc->rmap\n"); +#endif + if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->rsize, envy24ht_dmarsetmap, sc, 0)) + goto bad; + bzero(sc->pbuf, sc->psize); + bzero(sc->rbuf, sc->rsize); + + /* set values to register */ + addr = vtophys(sc->pbuf); +#if(0) + device_printf(sc->dev, "pbuf(0x%08x)\n", addr); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_PADDR, addr, 4); +#if(0) + device_printf(sc->dev, "PADDR-->(0x%08x)\n", envy24ht_rdmt(sc, ENVY24HT_MT_PADDR, 4)); + device_printf(sc->dev, "psize(%ld)\n", sc->psize / 4 - 1); +#endif + envy24ht_wrmt(sc, ENVY24HT_MT_PCNT, sc->psize / 4 - 1, 2); +#if(0) + device_printf(sc->dev, "PCNT-->(%ld)\n", envy24ht_rdmt(sc, ENVY24HT_MT_PCNT, 2)); +#endif + addr = vtophys(sc->rbuf); + envy24ht_wrmt(sc, ENVY24HT_MT_RADDR, addr, 4); + envy24ht_wrmt(sc, ENVY24HT_MT_RCNT, sc->rsize / 4 - 1, 2); + + return 0; + bad: + envy24ht_dmafree(sc); + return ENOSPC; +} + +static void +envy24ht_putcfg(struct sc_info *sc) +{ + device_printf(sc->dev, "system configuration\n"); + kprintf(" SubVendorID: 0x%04x, SubDeviceID: 0x%04x\n", + sc->cfg->subvendor, sc->cfg->subdevice); + kprintf(" XIN2 Clock Source: "); + switch (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_XIN2) { + case 0x00: + kprintf("24.576MHz(96kHz*256)\n"); + break; + case 0x40: + kprintf("49.152MHz(192kHz*256)\n"); + break; + case 0x80: + kprintf("reserved\n"); + break; + default: + kprintf("illeagal system setting\n"); + } + kprintf(" MPU-401 UART(s) #: "); + if (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_MPU) + kprintf("1\n"); + else + kprintf("not implemented\n"); + switch (sc->adcn) { + case 0x01 || 0x02: + kprintf(" ADC #: "); + kprintf("%d\n", sc->adcn); + break; + case 0x03: + kprintf(" ADC #: "); + kprintf("%d", 1); + kprintf(" and SPDIF receiver connected\n"); + break; + default: + kprintf(" no physical inputs\n"); + } + kprintf(" DAC #: "); + kprintf("%d\n", sc->dacn); + kprintf(" Multi-track converter type: "); + if ((sc->cfg->acl & ENVY24HT_CCSM_ACL_MTC) == 0) { + kprintf("AC'97(SDATA_OUT:"); + if (sc->cfg->acl & ENVY24HT_CCSM_ACL_OMODE) + kprintf("packed"); + else + kprintf("split"); + kprintf(")\n"); + } + else { + kprintf("I2S("); + if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_VOL) + kprintf("with volume, "); + if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_192KHZ) + kprintf("192KHz support, "); + else + if (sc->cfg->i2s & ENVY24HT_CCSM_I2S_96KHZ) + kprintf("192KHz support, "); + else + kprintf("48KHz support, "); + switch (sc->cfg->i2s & ENVY24HT_CCSM_I2S_RES) { + case ENVY24HT_CCSM_I2S_16BIT: + kprintf("16bit resolution, "); + break; + case ENVY24HT_CCSM_I2S_18BIT: + kprintf("18bit resolution, "); + break; + case ENVY24HT_CCSM_I2S_20BIT: + kprintf("20bit resolution, "); + break; + case ENVY24HT_CCSM_I2S_24BIT: + kprintf("24bit resolution, "); + break; + } + kprintf("ID#0x%x)\n", sc->cfg->i2s & ENVY24HT_CCSM_I2S_ID); + } + kprintf(" S/PDIF(IN/OUT): "); + if (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_IN) + kprintf("1/"); + else + kprintf("0/"); + if (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_OUT) + kprintf("1 "); + else + kprintf("0 "); + if (sc->cfg->spdif & (ENVY24HT_CCSM_SPDIF_IN | ENVY24HT_CCSM_SPDIF_OUT)) + kprintf("ID# 0x%02x\n", (sc->cfg->spdif & ENVY24HT_CCSM_SPDIF_ID) >> 2); + kprintf(" GPIO(mask/dir/state): 0x%02x/0x%02x/0x%02x\n", + sc->cfg->gpiomask, sc->cfg->gpiodir, sc->cfg->gpiostate); +} + +static int +envy24ht_init(struct sc_info *sc) +{ + u_int32_t data; +#if(0) + int rtn; +#endif + int i; + u_int32_t sv, sd; + + +#if(0) + device_printf(sc->dev, "envy24ht_init()\n"); +#endif + + /* reset chip */ +#if 0 + envy24ht_wrcs(sc, ENVY24HT_CCS_CTL, ENVY24HT_CCS_CTL_RESET, 1); + DELAY(200); + envy24ht_wrcs(sc, ENVY24HT_CCS_CTL, ENVY24HT_CCS_CTL_NATIVE, 1); + DELAY(200); + + /* legacy hardware disable */ + data = pci_read_config(sc->dev, PCIR_LAC, 2); + data |= PCIM_LAC_DISABLE; + pci_write_config(sc->dev, PCIR_LAC, data, 2); +#endif + + /* check system configuration */ + sc->cfg = NULL; + for (i = 0; cfg_table[i].subvendor != 0 || cfg_table[i].subdevice != 0; i++) { + /* 1st: search configuration from table */ + sv = pci_get_subvendor(sc->dev); + sd = pci_get_subdevice(sc->dev); + if (sv == cfg_table[i].subvendor && sd == cfg_table[i].subdevice) { +#if(0) + device_printf(sc->dev, "Set configuration from table\n"); +#endif + sc->cfg = &cfg_table[i]; + break; + } + } + if (sc->cfg == NULL) { + /* 2nd: read configuration from table */ + sc->cfg = envy24ht_rom2cfg(sc); + } + sc->adcn = ((sc->cfg->scfg & ENVY24HT_CCSM_SCFG_ADC) >> 2) + 1; /* need to be fixed */ + sc->dacn = (sc->cfg->scfg & ENVY24HT_CCSM_SCFG_DAC) + 1; + + if (1 /* bootverbose */) { + envy24ht_putcfg(sc); + } + + /* set system configuration */ + envy24ht_wrcs(sc, ENVY24HT_CCS_SCFG, sc->cfg->scfg, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_ACL, sc->cfg->acl, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_I2S, sc->cfg->i2s, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_SPDIF, sc->cfg->spdif, 1); + envy24ht_gpiosetmask(sc, sc->cfg->gpiomask); + envy24ht_gpiosetdir(sc, sc->cfg->gpiodir); + envy24ht_gpiowr(sc, sc->cfg->gpiostate); + + if ((sc->cfg->subvendor == 0x3031) && (sc->cfg->subdevice == 0x4553)) { + envy24ht_wri2c(sc, 0x22, 0x00, 0x07); + envy24ht_wri2c(sc, 0x22, 0x04, 0x5f | 0x80); + envy24ht_wri2c(sc, 0x22, 0x05, 0x5f | 0x80); + } + + for (i = 0; i < sc->adcn; i++) { + sc->adc[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_REC, i); + sc->cfg->codec->init(sc->adc[i]); + } + for (i = 0; i < sc->dacn; i++) { + sc->dac[i] = sc->cfg->codec->create(sc->dev, sc, PCMDIR_PLAY, i); + sc->cfg->codec->init(sc->dac[i]); + } + + /* initialize DMA buffer */ +#if(0) + device_printf(sc->dev, "envy24ht_init(): initialize DMA buffer\n"); +#endif + if (envy24ht_dmainit(sc)) + return ENOSPC; + + /* initialize status */ + sc->run[0] = sc->run[1] = 0; + sc->intr[0] = sc->intr[1] = 0; + sc->speed = 0; + sc->caps[0].fmtlist = envy24ht_playfmt; + sc->caps[1].fmtlist = envy24ht_recfmt; + + /* set channel router */ +#if 0 + envy24ht_route(sc, ENVY24HT_ROUTE_DAC_1, ENVY24HT_ROUTE_CLASS_MIX, 0, 0); + envy24ht_route(sc, ENVY24HT_ROUTE_DAC_SPDIF, ENVY24HT_ROUTE_CLASS_DMA, 0, 0); + envy24ht_route(sc, ENVY24HT_ROUTE_DAC_SPDIF, ENVY24HT_ROUTE_CLASS_MIX, 0, 0); +#endif + + /* set macro interrupt mask */ + data = envy24ht_rdcs(sc, ENVY24HT_CCS_IMASK, 1); + envy24ht_wrcs(sc, ENVY24HT_CCS_IMASK, data & ~ENVY24HT_CCS_IMASK_PMT, 1); + data = envy24ht_rdcs(sc, ENVY24HT_CCS_IMASK, 1); +#if(0) + device_printf(sc->dev, "envy24ht_init(): CCS_IMASK-->0x%02x\n", data); +#endif + + return 0; +} + +static int +envy24ht_alloc_resource(struct sc_info *sc) +{ + /* allocate I/O port resource */ + sc->csid = PCIR_CCS; + sc->cs = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, + &sc->csid, 0, ~0, 1, RF_ACTIVE); + sc->mtid = ENVY24HT_PCIR_MT; + sc->mt = bus_alloc_resource(sc->dev, SYS_RES_IOPORT, + &sc->mtid, 0, ~0, 1, RF_ACTIVE); + if (!sc->cs || !sc->mt) { + device_printf(sc->dev, "unable to map IO port space\n"); + return ENXIO; + } + sc->cst = rman_get_bustag(sc->cs); + sc->csh = rman_get_bushandle(sc->cs); + sc->mtt = rman_get_bustag(sc->mt); + sc->mth = rman_get_bushandle(sc->mt); +#if(0) + device_printf(sc->dev, + "IO port register values\nCCS: 0x%lx\nMT: 0x%lx\n", + pci_read_config(sc->dev, PCIR_CCS, 4), + pci_read_config(sc->dev, PCIR_MT, 4)); +#endif + + /* allocate interupt resource */ + sc->irqid = 0; + sc->irq = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &sc->irqid, + 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + if (!sc->irq || + snd_setup_intr(sc->dev, sc->irq, 0, envy24ht_intr, sc, &sc->ih)) { + device_printf(sc->dev, "unable to map interrupt\n"); + return ENXIO; + } + + /* allocate DMA resource */ + if (bus_dma_tag_create(/*parent*/NULL, + /*alignment*/4, + /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_ENVY24, + /*highaddr*/BUS_SPACE_MAXADDR_ENVY24, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/BUS_SPACE_MAXSIZE_ENVY24, + /*nsegments*/1, /*maxsegsz*/0x3ffff, + /*flags*/0 , &sc->dmat) != 0) { + device_printf(sc->dev, "unable to create dma tag\n"); + return ENXIO; + } + + return 0; +} + +static int +envy24ht_pci_attach(device_t dev) +{ + u_int32_t data; + struct sc_info *sc; + char status[SND_STATUSLEN]; + int err = 0; + int i; + +#if(0) + device_printf(dev, "envy24ht_pci_attach()\n"); +#endif + /* get sc_info data area */ + if ((sc = kmalloc(sizeof(*sc), M_ENVY24HT, M_NOWAIT)) == NULL) { + device_printf(dev, "cannot allocate softc\n"); + return ENXIO; + } + + bzero(sc, sizeof(*sc)); + sc->lock = snd_mtxcreate(device_get_nameunit(dev), + "snd_envy24ht softc"); + sc->dev = dev; + + /* initialize PCI interface */ + data = pci_read_config(dev, PCIR_COMMAND, 2); + data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, data, 2); + data = pci_read_config(dev, PCIR_COMMAND, 2); + + /* allocate resources */ + err = envy24ht_alloc_resource(sc); + if (err) { + device_printf(dev, "unable to allocate system resources\n"); + goto bad; + } + + /* initialize card */ + err = envy24ht_init(sc); + if (err) { + device_printf(dev, "unable to initialize the card\n"); + goto bad; + } + + /* set multi track mixer */ + mixer_init(dev, &envy24htmixer_class, sc); + + /* set channel information */ + /* err = pcm_register(dev, sc, 5, 2 + sc->adcn); */ + err = pcm_register(dev, sc, 1, 2 + sc->adcn); + if (err) + goto bad; + sc->chnum = 0; + /* for (i = 0; i < 5; i++) { */ + pcm_addchan(dev, PCMDIR_PLAY, &envy24htchan_class, sc); + sc->chnum++; + /* } */ + for (i = 0; i < 2 + sc->adcn; i++) { + pcm_addchan(dev, PCMDIR_REC, &envy24htchan_class, sc); + sc->chnum++; + } + + /* set status iformation */ + ksnprintf(status, SND_STATUSLEN, + "at io 0x%lx:%ld,0x%lx:%ld irq %ld", + rman_get_start(sc->cs), + rman_get_end(sc->cs) - rman_get_start(sc->cs) + 1, + rman_get_start(sc->mt), + rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1, + rman_get_start(sc->irq)); + pcm_setstatus(dev, status); + + return 0; + +bad: + if (sc->ih) + bus_teardown_intr(dev, sc->irq, sc->ih); + if (sc->irq) + bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); + envy24ht_dmafree(sc); + if (sc->dmat) + bus_dma_tag_destroy(sc->dmat); + if (sc->cfg->codec->destroy != NULL) { + for (i = 0; i < sc->adcn; i++) + sc->cfg->codec->destroy(sc->adc[i]); + for (i = 0; i < sc->dacn; i++) + sc->cfg->codec->destroy(sc->dac[i]); + } + envy24ht_cfgfree(sc->cfg); + if (sc->cs) + bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs); + if (sc->mt) + bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt); + if (sc->lock) + snd_mtxfree(sc->lock); + kfree(sc, M_ENVY24HT); + return err; +} + +static int +envy24ht_pci_detach(device_t dev) +{ + struct sc_info *sc; + int r; + int i; + +#if(0) + device_printf(dev, "envy24ht_pci_detach()\n"); +#endif + sc = pcm_getdevinfo(dev); + if (sc == NULL) + return 0; + r = pcm_unregister(dev); + if (r) + return r; + + envy24ht_dmafree(sc); + if (sc->cfg->codec->destroy != NULL) { + for (i = 0; i < sc->adcn; i++) + sc->cfg->codec->destroy(sc->adc[i]); + for (i = 0; i < sc->dacn; i++) + sc->cfg->codec->destroy(sc->dac[i]); + } + envy24ht_cfgfree(sc->cfg); + bus_dma_tag_destroy(sc->dmat); + bus_teardown_intr(dev, sc->irq, sc->ih); + bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); + bus_release_resource(dev, SYS_RES_IOPORT, sc->csid, sc->cs); + bus_release_resource(dev, SYS_RES_IOPORT, sc->mtid, sc->mt); + snd_mtxfree(sc->lock); + kfree(sc, M_ENVY24HT); + return 0; +} + +static device_method_t envy24ht_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, envy24ht_pci_probe), + DEVMETHOD(device_attach, envy24ht_pci_attach), + DEVMETHOD(device_detach, envy24ht_pci_detach), + { 0, 0 } +}; + +static driver_t envy24ht_driver = { + "pcm", + envy24ht_methods, +#if __FreeBSD_version > 500000 + PCM_SOFTC_SIZE, +#else + sizeof(struct snddev_info), +#endif +}; + +DRIVER_MODULE(snd_envy24ht, pci, envy24ht_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_envy24ht, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_DEPEND(snd_envy24ht, snd_spicds, 1, 1, 1); +MODULE_VERSION(snd_envy24ht, 1); diff --git a/sys/dev/sound/pci/envy24ht.h b/sys/dev/sound/pci/envy24ht.h new file mode 100644 index 0000000000..17bd792476 --- /dev/null +++ b/sys/dev/sound/pci/envy24ht.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2006 Konstantin Dimitrov + * Copyright (c) 2001 Katsurajima Naoto + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/sound/pci/envy24ht.h,v 1.4.2.2 2007/06/11 19:33:27 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/envy24ht.h,v 1.1 2007/06/16 19:48:05 hasso Exp $ + */ + + +/* -------------------------------------------------------------------- */ + +/* PCI device ID */ +#define PCIV_ENVY24 0x1412 +#define PCID_ENVY24HT 0x1724 + +#define PCIR_CCS 0x10 /* Controller I/O Base Address */ +#define ENVY24HT_PCIR_MT 0x14 /* Multi-Track I/O Base Address */ + +/* Controller Registers */ + +#define ENVY24HT_CCS_CTL 0x00 /* Control/Status Register */ +#define ENVY24HT_CCS_CTL_RESET 0x80 /* Entire Chip soft reset */ + +#define ENVY24HT_CCS_IMASK 0x01 /* Interrupt Mask Register */ +#define ENVY24HT_CCS_IMASK_PMT 0x10 /* Professional Multi-track */ + +#define ENVY24HT_CCS_I2CDEV 0x10 /* I2C Port Device Address Register */ +#define ENVY24HT_CCS_I2CDEV_ADDR 0xfe /* I2C device address */ +#define ENVY24HT_CCS_I2CDEV_ROM 0xa0 /* reserved for the external I2C E2PROM */ +#define ENVY24HT_CCS_I2CDEV_WR 0x01 /* write */ +#define ENVY24HT_CCS_I2CDEV_RD 0x00 /* read */ + +#define ENVY24HT_CCS_I2CADDR 0x11 /* I2C Port Byte Address Register */ +#define ENVY24HT_CCS_I2CDATA 0x12 /* I2C Port Read/Write Data Register */ + +#define ENVY24HT_CCS_I2CSTAT 0x13 /* I2C Port Control and Status Register */ +#define ENVY24HT_CCS_I2CSTAT_ROM 0x80 /* external E2PROM exists */ +#define ENVY24HT_CCS_I2CSTAT_BSY 0x01 /* I2C port read/write status busy */ + +#define ENVY24HT_CCS_SCFG 0x04 /* System Configuration Register */ +#define ENVY24HT_CCSM_SCFG_XIN2 0xc0 /* XIN2 Clock Source Configuration */ + /* 00: 24.576MHz(96kHz*256) */ + /* 01: 49.152MHz(192kHz*256) */ + /* 1x: Reserved */ +#define ENVY24HT_CCSM_SCFG_MPU 0x20 /* 0(not implemented)/1(1) MPU-401 UART */ +#define ENVY24HT_CCSM_SCFG_ADC 0x0c /* 1-2 stereo ADC connected, S/PDIF receiver connected */ +#define ENVY24HT_CCSM_SCFG_DAC 0x03 /* 1-4 stereo DAC connected */ + +#define ENVY24HT_CCS_ACL 0x05 /* AC-Link Configuration Register */ +#define ENVY24HT_CCSM_ACL_MTC 0x80 /* Multi-track converter type: 0:AC'97 1:I2S */ +#define ENVY24HT_CCSM_ACL_OMODE 0x02 /* AC 97 codec SDATA_OUT 0:split 1:packed */ + +#define ENVY24HT_CCS_I2S 0x06 /* I2S Converters Features Register */ +#define ENVY24HT_CCSM_I2S_VOL 0x80 /* I2S codec Volume and mute */ +#define ENVY24HT_CCSM_I2S_96KHZ 0x40 /* I2S converter 96kHz sampling rate support */ +#define ENVY24HT_CCSM_I2S_192KHZ 0x08 /* I2S converter 192kHz sampling rate support */ +#define ENVY24HT_CCSM_I2S_RES 0x30 /* Converter resolution */ +#define ENVY24HT_CCSM_I2S_16BIT 0x00 /* 16bit */ +#define ENVY24HT_CCSM_I2S_18BIT 0x10 /* 18bit */ +#define ENVY24HT_CCSM_I2S_20BIT 0x20 /* 20bit */ +#define ENVY24HT_CCSM_I2S_24BIT 0x30 /* 24bit */ +#define ENVY24HT_CCSM_I2S_ID 0x07 /* Other I2S IDs */ + +#define ENVY24HT_CCS_SPDIF 0x07 /* S/PDIF Configuration Register */ +#define ENVY24HT_CCSM_SPDIF_INT_EN 0x80 /* Enable integrated S/PDIF transmitter */ +#define ENVY24HT_CCSM_SPDIF_INT_OUT 0x40 /* Internal S/PDIF Out implemented */ +#define ENVY24HT_CCSM_SPDIF_ID 0x3c /* S/PDIF chip ID */ +#define ENVY24HT_CCSM_SPDIF_IN 0x02 /* S/PDIF Stereo In is present */ +#define ENVY24HT_CCSM_SPDIF_OUT 0x01 /* External S/PDIF Out implemented */ + +/* Professional Multi-Track Control Registers */ + +#define ENVY24HT_MT_INT_STAT 0x00 /* DMA Interrupt Mask and Status Register */ +#define ENVY24HT_MT_INT_RSTAT 0x02 /* Multi-track record interrupt status */ +#define ENVY24HT_MT_INT_PSTAT 0x01 /* Multi-track playback interrupt status */ +#define ENVY24HT_MT_INT_MASK 0x03 +#define ENVY24HT_MT_INT_RMASK 0x02 /* Multi-track record interrupt mask */ +#define ENVY24HT_MT_INT_PMASK 0x01 /* Multi-track playback interrupt mask */ + +#define ENVY24HT_MT_RATE 0x01 /* Sampling Rate Select Register */ +#define ENVY24HT_MT_RATE_SPDIF 0x10 /* S/PDIF input clock as the master */ +#define ENVY24HT_MT_RATE_48000 0x00 +#define ENVY24HT_MT_RATE_24000 0x01 +#define ENVY24HT_MT_RATE_12000 0x02 +#define ENVY24HT_MT_RATE_9600 0x03 +#define ENVY24HT_MT_RATE_32000 0x04 +#define ENVY24HT_MT_RATE_16000 0x05 +#define ENVY24HT_MT_RATE_8000 0x06 +#define ENVY24HT_MT_RATE_96000 0x07 +#define ENVY24HT_MT_RATE_192000 0x0e +#define ENVY24HT_MT_RATE_64000 0x0f +#define ENVY24HT_MT_RATE_44100 0x08 +#define ENVY24HT_MT_RATE_22050 0x09 +#define ENVY24HT_MT_RATE_11025 0x0a +#define ENVY24HT_MT_RATE_88200 0x0b +#define ENVY24HT_MT_RATE_176400 0x0c +#define ENVY24HT_MT_RATE_MASK 0x0f + +#define ENVY24HT_MT_I2S 0x02 /* I2S Data Format Register */ +#define ENVY24HT_MT_I2S_MLR128 0x08 /* MCLK/LRCLK ratio 128x (or 256x) */ + +#define ENVY24HT_MT_PADDR 0x10 /* Playback DMA Current/Base Address Register */ +#define ENVY24HT_MT_PCNT 0x14 /* Playback DMA Current/Base Count Register */ +#define ENVY24HT_MT_PTERM 0x1C /* Playback Current/Base Terminal Count Register */ + +#define ENVY24HT_MT_PCTL 0x18 /* Global Playback and Record DMA Start/Stop Register */ +#define ENVY24HT_MT_PCTL_RSTART 0x02 /* 1: Record start; 0: Record stop */ +#define ENVY24HT_MT_PCTL_PSTART 0x01 /* 1: Playback start; 0: Playback stop */ + +#define ENVY24HT_MT_RADDR 0x20 /* Record DMA Current/Base Address Register */ +#define ENVY24HT_MT_RCNT 0x24 /* Record DMA Current/Base Count Register */ +#define ENVY24HT_MT_RTERM 0x26 /* Record Current/Base Terminal Count Register */ + +/* + These map values are refferd from ALSA sound driver. +*/ +/* ENVY24 configuration E2PROM map */ +#define ENVY24HT_E2PROM_SUBVENDOR 0x02 +#define ENVY24HT_E2PROM_SUBDEVICE 0x00 +#define ENVY24HT_E2PROM_SIZE 0x04 +#define ENVY24HT_E2PROM_VERSION 0x05 +#define ENVY24HT_E2PROM_SCFG 0x06 +#define ENVY24HT_E2PROM_ACL 0x07 +#define ENVY24HT_E2PROM_I2S 0x08 +#define ENVY24HT_E2PROM_SPDIF 0x09 +#define ENVY24HT_E2PROM_GPIOMASK 0x0d +#define ENVY24HT_E2PROM_GPIOSTATE 0x10 +#define ENVY24HT_E2PROM_GPIODIR 0x0a + +/* ENVY24 mixer channel defines */ +/* + ENVY24 mixer has original line matrix. So, general mixer command is not + able to use for this. If system has consumer AC'97 output, AC'97 line is + used as master mixer, and it is able to control. +*/ +#define ENVY24HT_CHAN_NUM 11 /* Play * 5 + Record * 5 + Mix * 1 */ + +#define ENVY24HT_CHAN_PLAY_DAC1 0 +#define ENVY24HT_CHAN_PLAY_DAC2 1 +#define ENVY24HT_CHAN_PLAY_DAC3 2 +#define ENVY24HT_CHAN_PLAY_DAC4 3 +#define ENVY24HT_CHAN_PLAY_SPDIF 4 +#define ENVY24HT_CHAN_REC_ADC1 5 +#define ENVY24HT_CHAN_REC_ADC2 6 +#define ENVY24HT_CHAN_REC_ADC3 7 +#define ENVY24HT_CHAN_REC_ADC4 8 +#define ENVY24HT_CHAN_REC_SPDIF 9 +#define ENVY24HT_CHAN_REC_MIX 10 + +#define ENVY24HT_MIX_MASK 0x3fd +#define ENVY24HT_MIX_REC_MASK 0x3e0 + +/* volume value constants */ +#define ENVY24HT_VOL_MAX 0 /* 0db(negate) */ +#define ENVY24HT_VOL_MIN 96 /* -144db(negate) */ +#define ENVY24HT_VOL_MUTE 127 /* mute */ + +#define BUS_SPACE_MAXADDR_ENVY24 0x0fffffff /* Address space beyond 256MB is not + supported */ +#define BUS_SPACE_MAXSIZE_ENVY24 0x3fffc /* 64k x 4byte(1dword) */ + +#define ENVY24HT_CCS_GPIO_HDATA 0x1E +#define ENVY24HT_CCS_GPIO_LDATA 0x14 +#define ENVY24HT_CCS_GPIO_LMASK 0x16 +#define ENVY24HT_CCS_GPIO_HMASK 0x1F +#define ENVY24HT_CCS_GPIO_CTLDIR 0x18 + diff --git a/sys/dev/sound/pci/hda/hda_reg.h b/sys/dev/sound/pci/hda/hda_reg.h index 04bc7ced31..172fc9cc24 100644 --- a/sys/dev/sound/pci/hda/hda_reg.h +++ b/sys/dev/sound/pci/hda/hda_reg.h @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/hda/hda_reg.h,v 1.1 2006/10/01 11:12:59 ariff Exp $ - * $DragonFly: src/sys/dev/sound/pci/hda/hda_reg.h,v 1.1 2007/01/04 21:47:03 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pci/hda/hda_reg.h,v 1.2.2.1 2007/05/13 21:09:24 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/hda/hda_reg.h,v 1.2 2007/06/16 19:48:05 hasso Exp $ */ #ifndef _HDA_REG_H_ @@ -871,30 +871,30 @@ #define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_SHIFT 17 #define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_MASK 0x00010000 #define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_SHIFT 16 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK 0x00000800 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT 11 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK 0x00000400 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT 10 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK 0x00000200 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT 9 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK 0x00000100 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT 8 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK 0x00000080 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT 7 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK 0x00000040 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT 6 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK 0x00000020 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT 5 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK 0x00000010 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT 4 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK 0x00000008 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT 3 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK 0x000000004 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT 2 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK 0x000000002 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT 1 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK 0x000000001 -#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT 0 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK 0x00000001 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT 0 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK 0x00000002 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT 1 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK 0x00000004 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT 2 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK 0x00000008 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT 3 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK 0x00000010 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT 4 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK 0x00000020 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT 5 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK 0x00000040 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT 6 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK 0x00000080 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT 7 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK 0x00000100 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT 8 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK 0x00000200 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT 9 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK 0x00000400 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT 10 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK 0x00000800 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT 11 #define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(param) \ (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_MASK) >> \ diff --git a/sys/dev/sound/pci/hda/hdac.c b/sys/dev/sound/pci/hda/hdac.c index 2c218983c1..8a00f74a6c 100644 --- a/sys/dev/sound/pci/hda/hdac.c +++ b/sys/dev/sound/pci/hda/hdac.c @@ -24,8 +24,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/hda/hdac.c,v 1.6 2006/10/12 04:19:37 ariff Exp $ - * $DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11 01:03:06 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/hda/hdac.c,v 1.36.2.2 2007/06/13 01:52:26 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.4 2007/06/16 19:48:05 hasso Exp $ */ /* @@ -47,7 +47,7 @@ * http://people.freebsd.org/~ariff/HDA/parser.rb . This crude * ruby parser take the verbose dmesg dump as its input. Refer to * http://www.microsoft.com/whdc/device/audio/default.mspx for various - * interesting documents, especiall UAA (Universal Audio Architecture). + * interesting documents, especially UAA (Universal Audio Architecture). * 4) Possible vendor specific support. * (snd_hda_intel, snd_hda_ati, etc..) * @@ -83,26 +83,15 @@ #include "mixer_if.h" -#define HDA_DRV_TEST_REV "20061009_0031" +#define HDA_DRV_TEST_REV "20070611_0045" #define HDA_WIDGET_PARSER_REV 1 -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11 01:03:06 dillon Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.4 2007/06/16 19:48:05 hasso Exp $"); -#undef HDA_DEBUG_ENABLED -#define HDA_DEBUG_ENABLED 1 - -#ifdef HDA_DEBUG_ENABLED -#define HDA_DEBUG(stmt) do { \ - stmt \ -} while(0) -#else -#define HDA_DEBUG(stmt) -#endif - -#define HDA_BOOTVERBOSE(stmt) do { \ - if (bootverbose) { \ - stmt \ - } \ +#define HDA_BOOTVERBOSE(stmt) do { \ + if (bootverbose != 0) { \ + stmt \ + } \ } while(0) #if 1 @@ -113,7 +102,19 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11 #define hdac_lock(sc) snd_mtxlock((sc)->lock) #define hdac_unlock(sc) snd_mtxunlock((sc)->lock) #define hdac_lockassert(sc) snd_mtxassert((sc)->lock) -#define hdac_lockowned(sc) (1)/*mtx_owned((sc)->lock)*/ +#define hdac_lockowned(sc) (1)/* mtx_owned((sc)->lock) */ + +#if 0 /* TODO: No uncacheable DMA support in DragonFly. */ +#include +#define HDAC_DMA_ATTR(sc, v, s, attr) do { \ + vm_offset_t va = (vm_offset_t)(v); \ + vm_size_t sz = (vm_size_t)(s); \ + if ((sc) != NULL && (sc)->nocache != 0 && va != 0 && sz != 0) \ + (void)pmap_change_attr(va, sz, (attr)); \ +} while(0) +#else +#define HDAC_DMA_ATTR(...) +#endif #define HDA_FLAG_MATCH(fl, v) (((fl) & (v)) == (v)) #define HDA_DEV_MATCH(fl, v) ((fl) == (v) || \ @@ -125,6 +126,9 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11 #define HDA_MATCH_ALL 0xffffffff #define HDAC_INVALID 0xffffffff +/* Default controller / jack sense poll: 250ms */ +#define HDAC_POLL_INTERVAL max(hz >> 2, 1) + #define HDA_MODEL_CONSTRUCT(vendor, model) \ (((uint32_t)(model) << 16) | ((vendor##_VENDORID) & 0xffff)) @@ -166,17 +170,28 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11 /* OEM/subvendors */ +/* Intel */ +#define INTEL_D101GGC_SUBVENDOR HDA_MODEL_CONSTRUCT(INTEL, 0xd600) + /* HP/Compaq */ #define HP_VENDORID 0x103c #define HP_V3000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b5) #define HP_NX7400_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a2) #define HP_NX6310_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30aa) +#define HP_NX6325_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b0) +#define HP_XW4300_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3013) +#define HP_3010_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3010) +#define HP_DV5000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a5) #define HP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0xffff) +/* What is wrong with XN 2563 anyway? (Got the picture ?) */ +#define HP_NX6325_SUBVENDORX 0x103c30b0 /* Dell */ #define DELL_VENDORID 0x1028 #define DELL_D820_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01cc) #define DELL_I1300_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01c9) +#define DELL_XPSM1210_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01d7) +#define DELL_OPLX745_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01da) #define DELL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0xffff) /* Clevo */ @@ -186,11 +201,25 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11 /* Acer */ #define ACER_VENDORID 0x1025 +#define ACER_A5050_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x010f) +#define ACER_3681WXM_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0110) #define ACER_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0xffff) /* Asus */ #define ASUS_VENDORID 0x1043 #define ASUS_M5200_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1993) +#define ASUS_U5F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) +#define ASUS_A8JC_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1153) +#define ASUS_P1AH2_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) +#define ASUS_A7M_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1323) +#define ASUS_A7T_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x13c2) +#define ASUS_W6F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) +#define ASUS_W2J_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1971) +#define ASUS_F3JC_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1338) +#define ASUS_M2V_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81e7) +#define ASUS_M2N_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x8234) +#define ASUS_M2NPVMX_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) +#define ASUS_P5BWD_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81ec) #define ASUS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0xffff) /* IBM / Lenovo */ @@ -198,6 +227,52 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11 #define IBM_M52_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0x02f6) #define IBM_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0xffff) +/* Lenovo */ +#define LENOVO_VENDORID 0x17aa +#define LENOVO_3KN100_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2066) +#define LENOVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0xffff) + +/* Samsung */ +#define SAMSUNG_VENDORID 0x144d +#define SAMSUNG_Q1_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xc027) +#define SAMSUNG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xffff) + +/* Medion ? */ +#define MEDION_VENDORID 0x161f +#define MEDION_MD95257_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0x203d) +#define MEDION_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0xffff) + +/* + * Apple Intel MacXXXX seems using Sigmatel codec/vendor id + * instead of their own, which is beyond my comprehension + * (see HDA_CODEC_STAC9221 below). + */ +#define APPLE_INTEL_MAC 0x76808384 + +/* LG Electronics */ +#define LG_VENDORID 0x1854 +#define LG_LW20_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0x0018) +#define LG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0xffff) + +/* Fujitsu Siemens */ +#define FS_VENDORID 0x1734 +#define FS_PA1510_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10b8) +#define FS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0xffff) + +/* Toshiba */ +#define TOSHIBA_VENDORID 0x1179 +#define TOSHIBA_U200_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0x0001) +#define TOSHIBA_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xffff) + +/* Micro-Star International (MSI) */ +#define MSI_VENDORID 0x1462 +#define MSI_MS1034_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x0349) +#define MSI_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0xffff) + +/* Uniwill ? */ +#define UNIWILL_VENDORID 0x1584 +#define UNIWILL_9075_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9075) + /* Misc constants.. */ #define HDA_AMP_MUTE_DEFAULT (0xffffffff) @@ -213,18 +288,48 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.3 2007/05/11 #define HDA_ADC_PATH (1 << 1) #define HDA_ADC_RECSEL (1 << 2) +#define HDA_DAC_LOCKED (1 << 3) +#define HDA_ADC_LOCKED (1 << 4) + #define HDA_CTL_OUT (1 << 0) #define HDA_CTL_IN (1 << 1) #define HDA_CTL_BOTH (HDA_CTL_IN | HDA_CTL_OUT) -#define HDA_GPIO_MAX 15 -/* 0 - 14 = GPIO */ +#define HDA_GPIO_MAX 8 +/* 0 - 7 = GPIO , 8 = Flush */ #define HDA_QUIRK_GPIO0 (1 << 0) #define HDA_QUIRK_GPIO1 (1 << 1) #define HDA_QUIRK_GPIO2 (1 << 2) -#define HDA_QUIRK_SOFTPCMVOL (1 << 15) -#define HDA_QUIRK_FIXEDRATE (1 << 16) -#define HDA_QUIRK_FORCESTEREO (1 << 17) +#define HDA_QUIRK_GPIO3 (1 << 3) +#define HDA_QUIRK_GPIO4 (1 << 4) +#define HDA_QUIRK_GPIO5 (1 << 5) +#define HDA_QUIRK_GPIO6 (1 << 6) +#define HDA_QUIRK_GPIO7 (1 << 7) +#define HDA_QUIRK_GPIOFLUSH (1 << 8) + +/* 9 - 25 = anything else */ +#define HDA_QUIRK_SOFTPCMVOL (1 << 9) +#define HDA_QUIRK_FIXEDRATE (1 << 10) +#define HDA_QUIRK_FORCESTEREO (1 << 11) +#define HDA_QUIRK_EAPDINV (1 << 12) +#define HDA_QUIRK_DMAPOS (1 << 13) + +/* 26 - 31 = vrefs */ +#define HDA_QUIRK_IVREF50 (1 << 26) +#define HDA_QUIRK_IVREF80 (1 << 27) +#define HDA_QUIRK_IVREF100 (1 << 28) +#define HDA_QUIRK_OVREF50 (1 << 29) +#define HDA_QUIRK_OVREF80 (1 << 30) +#define HDA_QUIRK_OVREF100 (1 << 31) + +#define HDA_QUIRK_IVREF (HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF80 | \ + HDA_QUIRK_IVREF100) +#define HDA_QUIRK_OVREF (HDA_QUIRK_OVREF50 | HDA_QUIRK_OVREF80 | \ + HDA_QUIRK_OVREF100) +#define HDA_QUIRK_VREF (HDA_QUIRK_IVREF | HDA_QUIRK_OVREF) + +#define SOUND_MASK_SKIP (1 << 30) +#define SOUND_MASK_DISABLE (1 << 31) static const struct { char *key; @@ -233,9 +338,26 @@ static const struct { { "gpio0", HDA_QUIRK_GPIO0 }, { "gpio1", HDA_QUIRK_GPIO1 }, { "gpio2", HDA_QUIRK_GPIO2 }, + { "gpio3", HDA_QUIRK_GPIO3 }, + { "gpio4", HDA_QUIRK_GPIO4 }, + { "gpio5", HDA_QUIRK_GPIO5 }, + { "gpio6", HDA_QUIRK_GPIO6 }, + { "gpio7", HDA_QUIRK_GPIO7 }, + { "gpioflush", HDA_QUIRK_GPIOFLUSH }, { "softpcmvol", HDA_QUIRK_SOFTPCMVOL }, { "fixedrate", HDA_QUIRK_FIXEDRATE }, { "forcestereo", HDA_QUIRK_FORCESTEREO }, + { "eapdinv", HDA_QUIRK_EAPDINV }, + { "dmapos", HDA_QUIRK_DMAPOS }, + { "ivref50", HDA_QUIRK_IVREF50 }, + { "ivref80", HDA_QUIRK_IVREF80 }, + { "ivref100", HDA_QUIRK_IVREF100 }, + { "ovref50", HDA_QUIRK_OVREF50 }, + { "ovref80", HDA_QUIRK_OVREF80 }, + { "ovref100", HDA_QUIRK_OVREF100 }, + { "ivref", HDA_QUIRK_IVREF }, + { "ovref", HDA_QUIRK_OVREF }, + { "vref", HDA_QUIRK_VREF }, }; #define HDAC_QUIRKS_TAB_LEN \ (sizeof(hdac_quirks_tab) / sizeof(hdac_quirks_tab[0])) @@ -244,15 +366,19 @@ static const struct { #define HDA_BDL_MAX 256 #define HDA_BDL_DEFAULT HDA_BDL_MIN +#define HDA_BLK_MIN HDAC_DMA_ALIGNMENT +#define HDA_BLK_ALIGN (~(HDA_BLK_MIN - 1)) + #define HDA_BUFSZ_MIN 4096 #define HDA_BUFSZ_MAX 65536 #define HDA_BUFSZ_DEFAULT 16384 #define HDA_PARSE_MAXDEPTH 10 -#define HDAC_UNSOLTAG_EVENT_HP 0x00 +#define HDAC_UNSOLTAG_EVENT_HP 0x00 +#define HDAC_UNSOLTAG_EVENT_TEST 0x01 -static MALLOC_DEFINE(M_HDAC, "hdac", "High Definition Audio Controller"); +MALLOC_DEFINE(M_HDAC, "hdac", "High Definition Audio Controller"); enum { HDA_PARSE_MIXER, @@ -294,6 +420,19 @@ static const struct { }; #define HDAC_DEVICES_LEN (sizeof(hdac_devices) / sizeof(hdac_devices[0])) +static const struct { + uint16_t vendor; + uint8_t reg; + uint8_t mask; + uint8_t enable; +} hdac_pcie_snoop[] = { + { INTEL_VENDORID, 0x00, 0x00, 0x00 }, + { ATI_VENDORID, 0x42, 0xf8, 0x02 }, + { NVIDIA_VENDORID, 0x4e, 0xf0, 0x0f }, +}; +#define HDAC_PCIESNOOP_LEN \ + (sizeof(hdac_pcie_snoop) / sizeof(hdac_pcie_snoop[0])) + static const struct { uint32_t rate; int valid; @@ -346,19 +485,25 @@ static const struct { /* Realtek */ #define REALTEK_VENDORID 0x10ec #define HDA_CODEC_ALC260 HDA_CODEC_CONSTRUCT(REALTEK, 0x0260) +#define HDA_CODEC_ALC262 HDA_CODEC_CONSTRUCT(REALTEK, 0x0262) +#define HDA_CODEC_ALC660 HDA_CODEC_CONSTRUCT(REALTEK, 0x0660) #define HDA_CODEC_ALC861 HDA_CODEC_CONSTRUCT(REALTEK, 0x0861) -#define HDA_CODEC_ALC862 HDA_CODEC_CONSTRUCT(REALTEK, 0x0862) +#define HDA_CODEC_ALC861VD HDA_CODEC_CONSTRUCT(REALTEK, 0x0862) #define HDA_CODEC_ALC880 HDA_CODEC_CONSTRUCT(REALTEK, 0x0880) #define HDA_CODEC_ALC882 HDA_CODEC_CONSTRUCT(REALTEK, 0x0882) #define HDA_CODEC_ALC883 HDA_CODEC_CONSTRUCT(REALTEK, 0x0883) +#define HDA_CODEC_ALC885 HDA_CODEC_CONSTRUCT(REALTEK, 0x0885) +#define HDA_CODEC_ALC888 HDA_CODEC_CONSTRUCT(REALTEK, 0x0888) #define HDA_CODEC_ALCXXXX HDA_CODEC_CONSTRUCT(REALTEK, 0xffff) -/* Analog Device */ -#define ANALOGDEVICE_VENDORID 0x11d4 -#define HDA_CODEC_AD1981HD HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0x1981) -#define HDA_CODEC_AD1983 HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0x1983) -#define HDA_CODEC_AD1986A HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0x1986) -#define HDA_CODEC_ADXXXX HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0xffff) +/* Analog Devices */ +#define ANALOGDEVICES_VENDORID 0x11d4 +#define HDA_CODEC_AD1981HD HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1981) +#define HDA_CODEC_AD1983 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1983) +#define HDA_CODEC_AD1986A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1986) +#define HDA_CODEC_AD1988 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1988) +#define HDA_CODEC_AD1988B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x198b) +#define HDA_CODEC_ADXXXX HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0xffff) /* CMedia */ #define CMEDIA_VENDORID 0x434d @@ -371,6 +516,8 @@ static const struct { #define HDA_CODEC_STAC9221D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7683) #define HDA_CODEC_STAC9220 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7690) #define HDA_CODEC_STAC922XD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7681) +#define HDA_CODEC_STAC9227 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7618) +#define HDA_CODEC_STAC9271D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7627) #define HDA_CODEC_STACXXXX HDA_CODEC_CONSTRUCT(SIGMATEL, 0xffff) /* @@ -388,6 +535,21 @@ static const struct { #define HDA_CODEC_CXWAIKIKI HDA_CODEC_CONSTRUCT(CONEXANT, 0x5047) #define HDA_CODEC_CXXXXX HDA_CODEC_CONSTRUCT(CONEXANT, 0xffff) +/* VIA */ +#define HDA_CODEC_VT1708_8 HDA_CODEC_CONSTRUCT(VIA, 0x1708) +#define HDA_CODEC_VT1708_9 HDA_CODEC_CONSTRUCT(VIA, 0x1709) +#define HDA_CODEC_VT1708_A HDA_CODEC_CONSTRUCT(VIA, 0x170a) +#define HDA_CODEC_VT1708_B HDA_CODEC_CONSTRUCT(VIA, 0x170b) +#define HDA_CODEC_VT1709_0 HDA_CODEC_CONSTRUCT(VIA, 0xe710) +#define HDA_CODEC_VT1709_1 HDA_CODEC_CONSTRUCT(VIA, 0xe711) +#define HDA_CODEC_VT1709_2 HDA_CODEC_CONSTRUCT(VIA, 0xe712) +#define HDA_CODEC_VT1709_3 HDA_CODEC_CONSTRUCT(VIA, 0xe713) +#define HDA_CODEC_VT1709_4 HDA_CODEC_CONSTRUCT(VIA, 0xe714) +#define HDA_CODEC_VT1709_5 HDA_CODEC_CONSTRUCT(VIA, 0xe715) +#define HDA_CODEC_VT1709_6 HDA_CODEC_CONSTRUCT(VIA, 0xe716) +#define HDA_CODEC_VT1709_7 HDA_CODEC_CONSTRUCT(VIA, 0xe717) +#define HDA_CODEC_VTXXXX HDA_CODEC_CONSTRUCT(VIA, 0xffff) + /* Codecs */ static const struct { @@ -395,64 +557,119 @@ static const struct { char *name; } hdac_codecs[] = { { HDA_CODEC_ALC260, "Realtek ALC260" }, + { HDA_CODEC_ALC262, "Realtek ALC262" }, + { HDA_CODEC_ALC660, "Realtek ALC660" }, { HDA_CODEC_ALC861, "Realtek ALC861" }, - { HDA_CODEC_ALC862, "Realtek ALC862" }, + { HDA_CODEC_ALC861VD, "Realtek ALC861-VD" }, { HDA_CODEC_ALC880, "Realtek ALC880" }, { HDA_CODEC_ALC882, "Realtek ALC882" }, { HDA_CODEC_ALC883, "Realtek ALC883" }, - { HDA_CODEC_AD1981HD, "Analog Device AD1981HD" }, - { HDA_CODEC_AD1983, "Analog Device AD1983" }, - { HDA_CODEC_AD1986A, "Analog Device AD1986A" }, + { HDA_CODEC_ALC885, "Realtek ALC885" }, + { HDA_CODEC_ALC888, "Realtek ALC888" }, + { HDA_CODEC_AD1981HD, "Analog Devices AD1981HD" }, + { HDA_CODEC_AD1983, "Analog Devices AD1983" }, + { HDA_CODEC_AD1986A, "Analog Devices AD1986A" }, + { HDA_CODEC_AD1988, "Analog Devices AD1988" }, + { HDA_CODEC_AD1988B, "Analog Devices AD1988B" }, { HDA_CODEC_CMI9880, "CMedia CMI9880" }, { HDA_CODEC_STAC9221, "Sigmatel STAC9221" }, { HDA_CODEC_STAC9221D, "Sigmatel STAC9221D" }, { HDA_CODEC_STAC9220, "Sigmatel STAC9220" }, { HDA_CODEC_STAC922XD, "Sigmatel STAC9220D/9223D" }, + { HDA_CODEC_STAC9227, "Sigmatel STAC9227" }, + { HDA_CODEC_STAC9271D, "Sigmatel STAC9271D" }, { HDA_CODEC_CXVENICE, "Conexant Venice" }, { HDA_CODEC_CXWAIKIKI, "Conexant Waikiki" }, + { HDA_CODEC_VT1708_8, "VIA VT1708_8" }, + { HDA_CODEC_VT1708_9, "VIA VT1708_9" }, + { HDA_CODEC_VT1708_A, "VIA VT1708_A" }, + { HDA_CODEC_VT1708_B, "VIA VT1708_B" }, + { HDA_CODEC_VT1709_0, "VIA VT1709_0" }, + { HDA_CODEC_VT1709_1, "VIA VT1709_1" }, + { HDA_CODEC_VT1709_2, "VIA VT1709_2" }, + { HDA_CODEC_VT1709_3, "VIA VT1709_3" }, + { HDA_CODEC_VT1709_4, "VIA VT1709_4" }, + { HDA_CODEC_VT1709_5, "VIA VT1709_5" }, + { HDA_CODEC_VT1709_6, "VIA VT1709_6" }, + { HDA_CODEC_VT1709_7, "VIA VT1709_7" }, /* Unknown codec */ { HDA_CODEC_ALCXXXX, "Realtek (Unknown)" }, - { HDA_CODEC_ADXXXX, "Analog Device (Unknown)" }, + { HDA_CODEC_ADXXXX, "Analog Devices (Unknown)" }, { HDA_CODEC_CMIXXXX, "CMedia (Unknown)" }, { HDA_CODEC_STACXXXX, "Sigmatel (Unknown)" }, { HDA_CODEC_CXXXXX, "Conexant (Unknown)" }, + { HDA_CODEC_VTXXXX, "VIA (Unknown)" }, }; #define HDAC_CODECS_LEN (sizeof(hdac_codecs) / sizeof(hdac_codecs[0])) enum { HDAC_HP_SWITCH_CTL, - HDAC_HP_SWITCH_CTRL + HDAC_HP_SWITCH_CTRL, + HDAC_HP_SWITCH_DEBUG }; static const struct { uint32_t model; uint32_t id; int type; + int inverted; + int polling; + int execsense; nid_t hpnid; nid_t spkrnid[8]; nid_t eapdnid; } hdac_hp_switch[] = { /* Specific OEM models */ - { HP_V3000_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL, - 17, { 16, -1 }, 16 }, + { HP_V3000_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 17, { 16, -1 }, 16 }, + /* { HP_XW4300_SUBVENDOR, HDA_CODEC_ALC260, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 21, { 16, 17, -1 }, -1 } */ + /*{ HP_3010_SUBVENDOR, HDA_CODEC_ALC260, HDAC_HP_SWITCH_DEBUG, + 0, 1, 0, 16, { 15, 18, 19, 20, 21, -1 }, -1 },*/ { HP_NX7400_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, - 6, { 5, -1 }, 5 }, + 0, 0, -1, 6, { 5, -1 }, 5 }, { HP_NX6310_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, - 6, { 5, -1 }, 5 }, + 0, 0, -1, 6, { 5, -1 }, 5 }, + { HP_NX6325_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 6, { 5, -1 }, 5 }, + { TOSHIBA_U200_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 6, { 5, -1 }, -1 }, { DELL_D820_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, - 13, { 14, -1 }, -1 }, + 0, 0, -1, 13, { 14, -1 }, -1 }, { DELL_I1300_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, - 13, { 14, -1 }, -1 }, + 0, 0, -1, 13, { 14, -1 }, -1 }, + { DELL_OPLX745_SUBVENDOR, HDA_CODEC_AD1983, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 6, { 5, 7, -1 }, -1 }, + { APPLE_INTEL_MAC, HDA_CODEC_STAC9221, HDAC_HP_SWITCH_CTRL, + 0, 0, -1, 10, { 13, -1 }, -1 }, + { LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, HDAC_HP_SWITCH_CTL, + 1, 0, -1, 26, { 27, -1 }, -1 }, + { LG_LW20_SUBVENDOR, HDA_CODEC_ALC880, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 27, { 20, -1 }, -1 }, + { ACER_A5050_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 20, { 21, -1 }, -1 }, + { ACER_3681WXM_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 20, { 21, -1 }, -1 }, + { MSI_MS1034_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 20, { 27, -1 }, -1 }, /* * All models that at least come from the same vendor with * simmilar codec. */ - { HP_ALL_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL, - 17, { 16, -1 }, 16 }, + { HP_ALL_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 17, { 16, -1 }, 16 }, { HP_ALL_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, - 6, { 5, -1 }, 5 }, + 0, 0, -1, 6, { 5, -1 }, 5 }, + { TOSHIBA_ALL_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 6, { 5, -1 }, -1 }, { DELL_ALL_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, - 13, { 14, -1 }, -1 }, + 0, 0, -1, 13, { 14, -1 }, -1 }, + { LENOVO_ALL_SUBVENDOR, HDA_CODEC_AD1986A, HDAC_HP_SWITCH_CTL, + 1, 0, -1, 26, { 27, -1 }, -1 }, +#if 0 + { ACER_ALL_SUBVENDOR, HDA_CODEC_ALC883, HDAC_HP_SWITCH_CTL, + 0, 0, -1, 20, { 21, -1 }, -1 }, +#endif }; #define HDAC_HP_SWITCH_LEN \ (sizeof(hdac_hp_switch) / sizeof(hdac_hp_switch[0])) @@ -479,7 +696,7 @@ static int hdac_get_capabilities(struct hdac_softc *); static void hdac_dma_cb(void *, bus_dma_segment_t *, int, int); static int hdac_dma_alloc(struct hdac_softc *, struct hdac_dma *, bus_size_t); -static void hdac_dma_free(struct hdac_dma *); +static void hdac_dma_free(struct hdac_softc *, struct hdac_dma *); static int hdac_mem_alloc(struct hdac_softc *); static void hdac_mem_free(struct hdac_softc *); static int hdac_irq_alloc(struct hdac_softc *); @@ -513,6 +730,9 @@ static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *, static int hdac_audio_ctl_ossmixer_getnextdev(struct hdac_devinfo *); static struct hdac_widget *hdac_widget_get(struct hdac_devinfo *, nid_t); +static int hdac_rirb_flush(struct hdac_softc *sc); +static int hdac_unsolq_flush(struct hdac_softc *sc); + #define hdac_command(a1, a2, a3) \ hdac_command_sendone_internal(a1, a2, a3) @@ -621,7 +841,7 @@ hdac_hp_switch_handler(struct hdac_devinfo *devinfo) struct hdac_softc *sc; struct hdac_widget *w; struct hdac_audio_ctl *ctl; - uint32_t id, res; + uint32_t val, id, res; int i = 0, j, forcemute; nid_t cad; @@ -650,6 +870,10 @@ hdac_hp_switch_handler(struct hdac_devinfo *devinfo) HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD) ? 0 : 1; } + if (hdac_hp_switch[i].execsense != -1) + hdac_command(sc, + HDA_CMD_SET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid, + hdac_hp_switch[i].execsense), cad); res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid), cad); HDA_BOOTVERBOSE( @@ -657,30 +881,36 @@ hdac_hp_switch_handler(struct hdac_devinfo *devinfo) "HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n", hdac_hp_switch[i].hpnid, res); ); - res >>= 31; + res = HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(res); + res ^= hdac_hp_switch[i].inverted; switch (hdac_hp_switch[i].type) { case HDAC_HP_SWITCH_CTL: ctl = hdac_audio_ctl_amp_get(devinfo, hdac_hp_switch[i].hpnid, 0, 1); if (ctl != NULL) { - ctl->muted = (res != 0 && forcemute == 0) ? + val = (res != 0 && forcemute == 0) ? HDA_AMP_MUTE_NONE : HDA_AMP_MUTE_ALL; - hdac_audio_ctl_amp_set(ctl, - HDA_AMP_MUTE_DEFAULT, ctl->left, - ctl->right); - } - for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { - ctl = hdac_audio_ctl_amp_get(devinfo, - hdac_hp_switch[i].spkrnid[j], 0, 1); - if (ctl != NULL) { - ctl->muted = (res != 0 || forcemute == 1) ? - HDA_AMP_MUTE_ALL : HDA_AMP_MUTE_NONE; + if (val != ctl->muted) { + ctl->muted = val; hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, ctl->left, ctl->right); } } + for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { + ctl = hdac_audio_ctl_amp_get(devinfo, + hdac_hp_switch[i].spkrnid[j], 0, 1); + if (ctl == NULL) + continue; + val = (res != 0 || forcemute == 1) ? + HDA_AMP_MUTE_ALL : HDA_AMP_MUTE_NONE; + if (val == ctl->muted) + continue; + ctl->muted = val; + hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, + ctl->left, ctl->right); + } break; case HDAC_HP_SWITCH_CTRL: if (res != 0) { @@ -689,58 +919,93 @@ hdac_hp_switch_handler(struct hdac_devinfo *devinfo) if (w != NULL && w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { if (forcemute == 0) - w->wclass.pin.ctrl |= + val = w->wclass.pin.ctrl | HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; else - w->wclass.pin.ctrl &= + val = w->wclass.pin.ctrl & ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - hdac_command(sc, - HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, - w->wclass.pin.ctrl), cad); + if (val != w->wclass.pin.ctrl) { + w->wclass.pin.ctrl = val; + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, + w->nid, w->wclass.pin.ctrl), cad); + } } for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { w = hdac_widget_get(devinfo, hdac_hp_switch[i].spkrnid[j]); - if (w != NULL && w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - w->wclass.pin.ctrl &= - ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - hdac_command(sc, - HDA_CMD_SET_PIN_WIDGET_CTRL(cad, - w->nid, - w->wclass.pin.ctrl), cad); - } + if (w == NULL || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + val = w->wclass.pin.ctrl & + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (val == w->wclass.pin.ctrl) + continue; + w->wclass.pin.ctrl = val; + hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL( + cad, w->nid, w->wclass.pin.ctrl), cad); } } else { /* HP out */ w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); if (w != NULL && w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - w->wclass.pin.ctrl &= + val = w->wclass.pin.ctrl & ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - hdac_command(sc, - HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, - w->wclass.pin.ctrl), cad); + if (val != w->wclass.pin.ctrl) { + w->wclass.pin.ctrl = val; + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, + w->nid, w->wclass.pin.ctrl), cad); + } } for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { w = hdac_widget_get(devinfo, hdac_hp_switch[i].spkrnid[j]); - if (w != NULL && w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - if (forcemute == 0) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - else - w->wclass.pin.ctrl &= - ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - hdac_command(sc, - HDA_CMD_SET_PIN_WIDGET_CTRL(cad, - w->nid, - w->wclass.pin.ctrl), cad); - } + if (w == NULL || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (forcemute == 0) + val = w->wclass.pin.ctrl | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + else + val = w->wclass.pin.ctrl & + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (val == w->wclass.pin.ctrl) + continue; + w->wclass.pin.ctrl = val; + hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL( + cad, w->nid, w->wclass.pin.ctrl), cad); } } break; + case HDAC_HP_SWITCH_DEBUG: + if (hdac_hp_switch[i].execsense != -1) + hdac_command(sc, + HDA_CMD_SET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid, + hdac_hp_switch[i].execsense), cad); + res = hdac_command(sc, + HDA_CMD_GET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid), cad); + device_printf(sc->dev, + "[ 0] HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n", + hdac_hp_switch[i].hpnid, res); + for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { + w = hdac_widget_get(devinfo, + hdac_hp_switch[i].spkrnid[j]); + if (w == NULL || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (hdac_hp_switch[i].execsense != -1) + hdac_command(sc, + HDA_CMD_SET_PIN_SENSE(cad, w->nid, + hdac_hp_switch[i].execsense), cad); + res = hdac_command(sc, + HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad); + device_printf(sc->dev, + "[%2d] HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n", + j + 1, w->nid, res); + } + break; default: break; } @@ -784,12 +1049,15 @@ hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag) case HDAC_UNSOLTAG_EVENT_HP: hdac_hp_switch_handler(devinfo); break; + case HDAC_UNSOLTAG_EVENT_TEST: + device_printf(sc->dev, "Unsol Test!\n"); + break; default: break; } } -static void +static int hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch) { /* XXX to be removed */ @@ -798,7 +1066,7 @@ hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch) #endif if (ch->blkcnt == 0) - return; + return (0); /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA @@ -823,16 +1091,13 @@ hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch) #ifdef HDAC_INTR_EXTRA if (res & HDAC_SDSTS_BCIS) { #endif - ch->prevptr = ch->ptr; - ch->ptr += sndbuf_getblksz(ch->b); - ch->ptr %= sndbuf_getsize(ch->b); - hdac_unlock(sc); - chn_intr(ch->c); - hdac_lock(sc); + return (1); /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA } #endif + + return (0); } /**************************************************************************** @@ -846,14 +1111,16 @@ hdac_intr_handler(void *context) struct hdac_softc *sc; uint32_t intsts; uint8_t rirbsts; - uint8_t rirbwp; - struct hdac_rirb *rirb_base, *rirb; - nid_t ucad; - uint32_t utag; + struct hdac_rirb *rirb_base; + uint32_t trigger = 0; sc = (struct hdac_softc *)context; hdac_lock(sc); + if (sc->polling != 0) { + hdac_unlock(sc); + return; + } /* Do we have anything to do? */ intsts = HDAC_READ_4(&sc->mem, HDAC_INTSTS); if (!HDA_FLAG_MATCH(intsts, HDAC_INTSTS_GIS)) { @@ -867,26 +1134,9 @@ hdac_intr_handler(void *context) rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); /* Get as many responses that we can */ while (HDA_FLAG_MATCH(rirbsts, HDAC_RIRBSTS_RINTFL)) { - HDAC_WRITE_1(&sc->mem, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL); - rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP); - bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, - BUS_DMASYNC_POSTREAD); - while (sc->rirb_rp != rirbwp) { - sc->rirb_rp++; - sc->rirb_rp %= sc->rirb_size; - rirb = &rirb_base[sc->rirb_rp]; - if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) { - ucad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex); - utag = rirb->response >> 26; - if (ucad > -1 && ucad < HDAC_CODEC_MAX && - sc->codecs[ucad] != NULL) { - sc->unsolq[sc->unsolq_wp++] = - (ucad << 16) | - (utag & 0xffff); - sc->unsolq_wp %= HDAC_UNSOLQ_MAX; - } - } - } + HDAC_WRITE_1(&sc->mem, + HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL); + hdac_rirb_flush(sc); rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); } /* XXX to be removed */ @@ -895,29 +1145,29 @@ hdac_intr_handler(void *context) HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, HDAC_INTSTS_CIS); #endif } + + hdac_unsolq_flush(sc); + if (intsts & HDAC_INTSTS_SIS_MASK) { - if (intsts & (1 << sc->num_iss)) - hdac_stream_intr(sc, &sc->play); - if (intsts & (1 << 0)) - hdac_stream_intr(sc, &sc->rec); + if ((intsts & (1 << sc->num_iss)) && + hdac_stream_intr(sc, &sc->play) != 0) + trigger |= 1; + if ((intsts & (1 << 0)) && + hdac_stream_intr(sc, &sc->rec) != 0) + trigger |= 2; /* XXX to be removed */ #ifdef HDAC_INTR_EXTRA - HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts & HDAC_INTSTS_SIS_MASK); + HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts & + HDAC_INTSTS_SIS_MASK); #endif } - if (sc->unsolq_st == HDAC_UNSOLQ_READY) { - sc->unsolq_st = HDAC_UNSOLQ_BUSY; - while (sc->unsolq_rp != sc->unsolq_wp) { - ucad = sc->unsolq[sc->unsolq_rp] >> 16; - utag = sc->unsolq[sc->unsolq_rp++] & 0xffff; - sc->unsolq_rp %= HDAC_UNSOLQ_MAX; - hdac_unsolicited_handler(sc->codecs[ucad], utag); - } - sc->unsolq_st = HDAC_UNSOLQ_READY; - } - hdac_unlock(sc); + + if (trigger & 1) + chn_intr(sc->play.c); + if (trigger & 2) + chn_intr(sc->rec.c); } /**************************************************************************** @@ -942,11 +1192,17 @@ hdac_reset(struct hdac_softc *sc) HDAC_WRITE_4(&sc->mem, HDAC_BSDCTL(sc, i), 0x0); /* - * Stop Control DMA engines + * Stop Control DMA engines. */ HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, 0x0); HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, 0x0); + /* + * Reset DMA position buffer. + */ + HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE, 0x0); + HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, 0x0); + /* * Reset the controller. The reset must remain asserted for * a minimum of 100us. @@ -1068,16 +1324,6 @@ hdac_dma_cb(void *callback_arg, bus_dma_segment_t *segs, int nseg, int error) } } -static void -hdac_dma_nocache(void *ptr) -{ -#if defined(__i386__) || defined(__amd64__) - vm_offset_t va; - - va = (vm_offset_t)ptr; - pmap_kmodify_nc(va); -#endif -} /**************************************************************************** * int hdac_dma_alloc @@ -1088,9 +1334,11 @@ hdac_dma_nocache(void *ptr) static int hdac_dma_alloc(struct hdac_softc *sc, struct hdac_dma *dma, bus_size_t size) { + bus_size_t roundsz; int result; int lowaddr; + roundsz = roundup2(size, HDAC_DMA_ALIGNMENT); lowaddr = (sc->support_64bit) ? BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT; bzero(dma, sizeof(*dma)); @@ -1105,69 +1353,90 @@ hdac_dma_alloc(struct hdac_softc *sc, struct hdac_dma *dma, bus_size_t size) BUS_SPACE_MAXADDR, /* highaddr */ NULL, /* filtfunc */ NULL, /* fistfuncarg */ - size, /* maxsize */ + roundsz, /* maxsize */ 1, /* nsegments */ - size, /* maxsegsz */ + roundsz, /* maxsegsz */ 0, /* flags */ &dma->dma_tag); /* dmat */ if (result != 0) { device_printf(sc->dev, "%s: bus_dma_tag_create failed (%x)\n", __func__, result); - goto fail; + goto hdac_dma_alloc_fail; } /* * Allocate DMA memory */ +#if 0 /* TODO: No uncacheable DMA support in DragonFly. */ + result = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr, + BUS_DMA_NOWAIT | BUS_DMA_ZERO | + ((sc->nocache != 0) ? BUS_DMA_NOCACHE : 0), &dma->dma_map); +#else result = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr, - BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &dma->dma_map); + BUS_DMA_NOWAIT | BUS_DMA_ZERO, &dma->dma_map); +#endif if (result != 0) { device_printf(sc->dev, "%s: bus_dmamem_alloc failed (%x)\n", __func__, result); - goto fail; + goto hdac_dma_alloc_fail; } + dma->dma_size = roundsz; + /* * Map the memory */ result = bus_dmamap_load(dma->dma_tag, dma->dma_map, - (void *)dma->dma_vaddr, size, hdac_dma_cb, (void *)dma, - BUS_DMA_NOWAIT); + (void *)dma->dma_vaddr, roundsz, hdac_dma_cb, (void *)dma, 0); if (result != 0 || dma->dma_paddr == 0) { + if (result == 0) + result = ENOMEM; device_printf(sc->dev, "%s: bus_dmamem_load failed (%x)\n", __func__, result); - goto fail; + goto hdac_dma_alloc_fail; } - bzero((void *)dma->dma_vaddr, size); - hdac_dma_nocache(dma->dma_vaddr); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "%s: size=%ju -> roundsz=%ju\n", + __func__, (uintmax_t)size, (uintmax_t)roundsz); + ); return (0); -fail: - if (dma->dma_map != NULL) - bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); - if (dma->dma_tag != NULL) - bus_dma_tag_destroy(dma->dma_tag); + +hdac_dma_alloc_fail: + hdac_dma_free(sc, dma); + return (result); } /**************************************************************************** - * void hdac_dma_free(struct hdac_dma *) + * void hdac_dma_free(struct hdac_softc *, struct hdac_dma *) * * Free a struct dhac_dma that has been previously allocated via the * hdac_dma_alloc function. ****************************************************************************/ static void -hdac_dma_free(struct hdac_dma *dma) +hdac_dma_free(struct hdac_softc *sc, struct hdac_dma *dma) { - if (dma->dma_tag != NULL) { + if (dma->dma_map != NULL) { +#if 0 /* Flush caches */ bus_dmamap_sync(dma->dma_tag, dma->dma_map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); +#endif bus_dmamap_unload(dma->dma_tag, dma->dma_map); + } + if (dma->dma_vaddr != NULL) { bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); + dma->dma_vaddr = NULL; + } + dma->dma_map = NULL; + if (dma->dma_tag != NULL) { bus_dma_tag_destroy(dma->dma_tag); + dma->dma_tag = NULL; } + dma->dma_size = 0; } /**************************************************************************** @@ -1210,6 +1479,7 @@ hdac_mem_free(struct hdac_softc *sc) if (mem->mem_res != NULL) bus_release_resource(sc->dev, SYS_RES_MEMORY, mem->mem_rid, mem->mem_res); + mem->mem_res = NULL; } /**************************************************************************** @@ -1230,23 +1500,22 @@ hdac_irq_alloc(struct hdac_softc *sc) if (irq->irq_res == NULL) { device_printf(sc->dev, "%s: Unable to allocate irq\n", __func__); - goto fail; + goto hdac_irq_alloc_fail; } result = snd_setup_intr(sc->dev, irq->irq_res, INTR_MPSAFE, - hdac_intr_handler, sc, &irq->irq_handle); + hdac_intr_handler, sc, &irq->irq_handle); if (result != 0) { device_printf(sc->dev, "%s: Unable to setup interrupt handler (%x)\n", __func__, result); - goto fail; + goto hdac_irq_alloc_fail; } return (0); -fail: - if (irq->irq_res != NULL) - bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid, - irq->irq_res); +hdac_irq_alloc_fail: + hdac_irq_free(sc); + return (ENXIO); } @@ -1261,11 +1530,13 @@ hdac_irq_free(struct hdac_softc *sc) struct hdac_irq *irq; irq = &sc->irq; - if (irq->irq_handle != NULL) + if (irq->irq_res != NULL && irq->irq_handle != NULL) bus_teardown_intr(sc->dev, irq->irq_res, irq->irq_handle); if (irq->irq_res != NULL) bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid, irq->irq_res); + irq->irq_handle = NULL; + irq->irq_res = NULL; } /**************************************************************************** @@ -1356,17 +1627,20 @@ hdac_rirb_init(struct hdac_softc *sc) sc->rirb_rp = 0; HDAC_WRITE_2(&sc->mem, HDAC_RIRBWP, HDAC_RIRBWP_RIRBWPRST); - /* Setup the interrupt threshold */ - HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2); + if (sc->polling == 0) { + /* Setup the interrupt threshold */ + HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2); - /* Enable Overrun and response received reporting */ + /* Enable Overrun and response received reporting */ #if 0 - HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, - HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL); + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, + HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL); #else - HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL); + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL); #endif + } +#if 0 /* * Make sure that the Host CPU cache doesn't contain any dirty * cache lines that falls in the rirb. If I understood correctly, it @@ -1375,6 +1649,7 @@ hdac_rirb_init(struct hdac_softc *sc) */ bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, BUS_DMASYNC_PREREAD); +#endif } /**************************************************************************** @@ -1424,15 +1699,15 @@ hdac_scan_codecs(struct hdac_softc *sc) for (i = 0; i < HDAC_CODEC_MAX; i++) { if (HDAC_STATESTS_SDIWAKE(statests, i)) { /* We have found a codec. */ - hdac_unlock(sc); codec = (struct hdac_codec *)kmalloc(sizeof(*codec), M_HDAC, M_ZERO | M_NOWAIT); - hdac_lock(sc); if (codec == NULL) { device_printf(sc->dev, "Unable to allocate memory for codec\n"); continue; } + codec->commands = NULL; + codec->responses_received = 0; codec->verbs_sent = 0; codec->sc = sc; codec->cad = i; @@ -1476,13 +1751,6 @@ hdac_probe_codec(struct hdac_codec *codec) startnode = HDA_PARAM_SUB_NODE_COUNT_START(subnode); endnode = startnode + HDA_PARAM_SUB_NODE_COUNT_TOTAL(subnode); - if (vendorid == HDAC_INVALID || - revisionid == HDAC_INVALID || - subnode == HDAC_INVALID) { - device_printf(sc->dev, "invalid response\n"); - return (0); - } - HDA_BOOTVERBOSE( device_printf(sc->dev, "HDA_DEBUG: \tstartnode=%d endnode=%d\n", startnode, endnode); @@ -1530,10 +1798,8 @@ hdac_probe_function(struct hdac_codec *codec, nid_t nid) if (fctgrptype != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) return (NULL); - hdac_unlock(sc); devinfo = (struct hdac_devinfo *)kmalloc(sizeof(*devinfo), M_HDAC, M_NOWAIT | M_ZERO); - hdac_lock(sc); if (devinfo == NULL) { device_printf(sc->dev, "%s: Unable to allocate ivar\n", __func__); @@ -1562,60 +1828,96 @@ hdac_widget_connection_parse(struct hdac_widget *w) { struct hdac_softc *sc = w->devinfo->codec->sc; uint32_t res; - int i, j, max, found, entnum, cnid; + int i, j, max, ents, entnum; nid_t cad = w->devinfo->codec->cad; nid_t nid = w->nid; + nid_t cnid, addcnid, prevcnid; + + w->nconns = 0; res = hdac_command(sc, HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_CONN_LIST_LENGTH), cad); - w->nconns = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res); + ents = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res); - if (w->nconns < 1) + if (ents < 1) return; entnum = HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(res) ? 2 : 4; - res = 0; - i = 0; - found = 0; max = (sizeof(w->conns) / sizeof(w->conns[0])) - 1; + prevcnid = 0; + +#define CONN_RMASK(e) (1 << ((32 / (e)) - 1)) +#define CONN_NMASK(e) (CONN_RMASK(e) - 1) +#define CONN_RESVAL(r, e, n) ((r) >> ((32 / (e)) * (n))) +#define CONN_RANGE(r, e, n) (CONN_RESVAL(r, e, n) & CONN_RMASK(e)) +#define CONN_CNID(r, e, n) (CONN_RESVAL(r, e, n) & CONN_NMASK(e)) - while (i < w->nconns) { + for (i = 0; i < ents; i += entnum) { res = hdac_command(sc, HDA_CMD_GET_CONN_LIST_ENTRY(cad, nid, i), cad); for (j = 0; j < entnum; j++) { - cnid = res; - cnid >>= (32 / entnum) * j; - cnid &= (1 << (32 / entnum)) - 1; - if (cnid == 0) - continue; - if (found > max) { + cnid = CONN_CNID(res, entnum, j); + if (cnid == 0) { + if (w->nconns < ents) + device_printf(sc->dev, + "%s: nid=%d WARNING: zero cnid " + "entnum=%d j=%d index=%d " + "entries=%d found=%d res=0x%08x\n", + __func__, nid, entnum, j, i, + ents, w->nconns, res); + else + goto getconns_out; + } + if (cnid < w->devinfo->startnode || + cnid >= w->devinfo->endnode) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "%s: GHOST: nid=%d j=%d " + "entnum=%d index=%d res=0x%08x\n", + __func__, nid, j, entnum, i, res); + ); + } + if (CONN_RANGE(res, entnum, j) == 0) + addcnid = cnid; + else if (prevcnid == 0 || prevcnid >= cnid) { device_printf(sc->dev, - "node %d: Adding %d: " - "Max connection reached!\n", - nid, cnid); - continue; + "%s: WARNING: Invalid child range " + "nid=%d index=%d j=%d entnum=%d " + "prevcnid=%d cnid=%d res=0x%08x\n", + __func__, nid, i, j, entnum, prevcnid, + cnid, res); + addcnid = cnid; + } else + addcnid = prevcnid + 1; + while (addcnid <= cnid) { + if (w->nconns > max) { + device_printf(sc->dev, + "%s: nid=%d: Adding %d: " + "Max connection reached! max=%d\n", + __func__, nid, addcnid, max + 1); + goto getconns_out; + } + w->conns[w->nconns++] = addcnid++; } - w->conns[found++] = cnid; + prevcnid = cnid; } - i += entnum; } +getconns_out: HDA_BOOTVERBOSE( - if (w->nconns != found) { - device_printf(sc->dev, - "HDA_DEBUG: nid=%d WARNING!!! Connection " - "length=%d != found=%d\n", - nid, w->nconns, found); - } + device_printf(sc->dev, + "HDA_DEBUG: %s: nid=%d entries=%d found=%d\n", + __func__, nid, ents, w->nconns); ); + return; } static uint32_t hdac_widget_pin_getconfig(struct hdac_widget *w) { struct hdac_softc *sc; - uint32_t config, id; + uint32_t config, orig, id; nid_t cad, nid; sc = w->devinfo->codec->sc; @@ -1626,10 +1928,25 @@ hdac_widget_pin_getconfig(struct hdac_widget *w) config = hdac_command(sc, HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid), cad); + orig = config; + /* * XXX REWRITE!!!! Don't argue! */ - if (id == HDA_CODEC_ALC880 && + if (id == HDA_CODEC_ALC880 && sc->pci_subvendor == LG_LW20_SUBVENDOR) { + switch (nid) { + case 26: + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 27: + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT; + break; + default: + break; + } + } else if (id == HDA_CODEC_ALC880 && (sc->pci_subvendor == CLEVO_D900T_SUBVENDOR || sc->pci_subvendor == ASUS_M5200_SUBVENDOR)) { /* @@ -1671,11 +1988,139 @@ hdac_widget_pin_getconfig(struct hdac_widget *w) default: break; } + } else if (id == HDA_CODEC_ALC883 && + HDA_DEV_MATCH(ACER_ALL_SUBVENDOR, sc->pci_subvendor)) { + switch (nid) { + case 25: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 28: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + default: + break; + } + } else if (id == HDA_CODEC_CXVENICE && sc->pci_subvendor == + HP_V3000_SUBVENDOR) { + switch (nid) { + case 18: + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; + config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; + break; + case 20: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 21: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + default: + break; + } + } else if (id == HDA_CODEC_CXWAIKIKI && sc->pci_subvendor == + HP_DV5000_SUBVENDOR) { + switch (nid) { + case 20: + case 21: + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; + config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; + break; + default: + break; + } + } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == + ASUS_W6F_SUBVENDOR) { + switch (nid) { + case 11: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 15: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); + break; + default: + break; + } + } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == + UNIWILL_9075_SUBVENDOR) { + switch (nid) { + case 15: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); + break; + default: + break; + } + } else if (id == HDA_CODEC_AD1986A && sc->pci_subvendor == + ASUS_M2NPVMX_SUBVENDOR) { + switch (nid) { + case 28: /* LINE */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 29: /* MIC */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; + break; + default: + break; + } } + HDA_BOOTVERBOSE( + if (config != orig) + device_printf(sc->dev, + "HDA_DEBUG: Pin config nid=%u 0x%08x -> 0x%08x\n", + nid, orig, config); + ); + return (config); } +static uint32_t +hdac_widget_pin_getcaps(struct hdac_widget *w) +{ + struct hdac_softc *sc; + uint32_t caps, orig, id; + nid_t cad, nid; + + sc = w->devinfo->codec->sc; + cad = w->devinfo->codec->cad; + nid = w->nid; + id = hdac_codec_id(w->devinfo); + + caps = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad); + orig = caps; + + HDA_BOOTVERBOSE( + if (caps != orig) + device_printf(sc->dev, + "HDA_DEBUG: Pin caps nid=%u 0x%08x -> 0x%08x\n", + nid, orig, caps); + ); + + return (caps); +} + static void hdac_widget_pin_parse(struct hdac_widget *w) { @@ -1688,15 +2133,15 @@ hdac_widget_pin_parse(struct hdac_widget *w) config = hdac_widget_pin_getconfig(w); w->wclass.pin.config = config; - pincap = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad); + pincap = hdac_widget_pin_getcaps(w); w->wclass.pin.cap = pincap; w->wclass.pin.ctrl = hdac_command(sc, - HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad) & - ~(HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | - HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | - HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE); + HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad) & + ~(HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK); if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)) w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; @@ -1908,6 +2353,149 @@ hdac_widget_get(struct hdac_devinfo *devinfo, nid_t nid) return (&devinfo->widget[nid - devinfo->startnode]); } +static __inline int +hda_poll_channel(struct hdac_chan *ch) +{ + uint32_t sz, delta; + volatile uint32_t ptr; + + if (ch->active == 0) + return (0); + + sz = ch->blksz * ch->blkcnt; + if (ch->dmapos != NULL) + ptr = *(ch->dmapos); + else + ptr = HDAC_READ_4(&ch->devinfo->codec->sc->mem, + ch->off + HDAC_SDLPIB); + ch->ptr = ptr; + ptr %= sz; + ptr &= ~(ch->blksz - 1); + delta = (sz + ptr - ch->prevptr) % sz; + + if (delta < ch->blksz) + return (0); + + ch->prevptr = ptr; + + return (1); +} + +#define hda_chan_active(sc) ((sc)->play.active + (sc)->rec.active) + +static void +hda_poll_callback(void *arg) +{ + struct hdac_softc *sc = arg; + uint32_t trigger = 0; + + if (sc == NULL) + return; + + hdac_lock(sc); + if (sc->polling == 0 || hda_chan_active(sc) == 0) { + hdac_unlock(sc); + return; + } + + trigger |= (hda_poll_channel(&sc->play) != 0) ? 1 : 0; + trigger |= (hda_poll_channel(&sc->rec) != 0) ? 2 : 0; + + /* XXX */ + callout_reset(&sc->poll_hda, 1/*sc->poll_ticks*/, + hda_poll_callback, sc); + + hdac_unlock(sc); + + if (trigger & 1) + chn_intr(sc->play.c); + if (trigger & 2) + chn_intr(sc->rec.c); +} + +static int +hdac_rirb_flush(struct hdac_softc *sc) +{ + struct hdac_rirb *rirb_base, *rirb; + struct hdac_codec *codec; + struct hdac_command_list *commands; + nid_t cad; + uint32_t resp; + uint8_t rirbwp; + int ret = 0; + + rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; + rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP); +#if 0 + bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, + BUS_DMASYNC_POSTREAD); +#endif + + while (sc->rirb_rp != rirbwp) { + sc->rirb_rp++; + sc->rirb_rp %= sc->rirb_size; + rirb = &rirb_base[sc->rirb_rp]; + cad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex); + if (cad < 0 || cad >= HDAC_CODEC_MAX || + sc->codecs[cad] == NULL) + continue; + resp = rirb->response; + codec = sc->codecs[cad]; + commands = codec->commands; + if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) { + sc->unsolq[sc->unsolq_wp++] = (cad << 16) | + ((resp >> 26) & 0xffff); + sc->unsolq_wp %= HDAC_UNSOLQ_MAX; + } else if (commands != NULL && commands->num_commands > 0 && + codec->responses_received < commands->num_commands) + commands->responses[codec->responses_received++] = + resp; + ret++; + } + + return (ret); +} + +static int +hdac_unsolq_flush(struct hdac_softc *sc) +{ + nid_t cad; + uint32_t tag; + int ret = 0; + + if (sc->unsolq_st == HDAC_UNSOLQ_READY) { + sc->unsolq_st = HDAC_UNSOLQ_BUSY; + while (sc->unsolq_rp != sc->unsolq_wp) { + cad = sc->unsolq[sc->unsolq_rp] >> 16; + tag = sc->unsolq[sc->unsolq_rp++] & 0xffff; + sc->unsolq_rp %= HDAC_UNSOLQ_MAX; + hdac_unsolicited_handler(sc->codecs[cad], tag); + ret++; + } + sc->unsolq_st = HDAC_UNSOLQ_READY; + } + + return (ret); +} + +static void +hdac_poll_callback(void *arg) +{ + struct hdac_softc *sc = arg; + if (sc == NULL) + return; + + hdac_lock(sc); + if (sc->polling == 0 || sc->poll_ival == 0) { + hdac_unlock(sc); + return; + } + hdac_rirb_flush(sc); + hdac_unsolq_flush(sc); + callout_reset(&sc->poll_hdac, sc->poll_ival, hdac_poll_callback, sc); + hdac_unlock(sc); +} + static void hdac_stream_stop(struct hdac_chan *ch) { @@ -1919,9 +2507,50 @@ hdac_stream_stop(struct hdac_chan *ch) HDAC_SDCTL_RUN); HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); - ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); - ctl &= ~(1 << (ch->off >> 5)); - HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + ch->active = 0; + + if (sc->polling != 0) { + int pollticks; + + if (hda_chan_active(sc) == 0) { + callout_stop(&sc->poll_hda); + sc->poll_ticks = 1; + } else { + if (sc->play.active != 0) + ch = &sc->play; + else + ch = &sc->rec; + pollticks = ((uint64_t)hz * ch->blksz) / + ((uint64_t)sndbuf_getbps(ch->b) * + sndbuf_getspd(ch->b)); + pollticks >>= 2; + if (pollticks > hz) + pollticks = hz; + if (pollticks < 1) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "%s: pollticks=%d < 1 !\n", + __func__, pollticks); + ); + pollticks = 1; + } + if (pollticks > sc->poll_ticks) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "%s: pollticks %d -> %d\n", + __func__, sc->poll_ticks, + pollticks); + ); + sc->poll_ticks = pollticks; + callout_reset(&sc->poll_hda, 1, + hda_poll_callback, sc); + } + } + } else { + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl &= ~(1 << (ch->off >> 5)); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + } } static void @@ -1930,14 +2559,52 @@ hdac_stream_start(struct hdac_chan *ch) struct hdac_softc *sc = ch->devinfo->codec->sc; uint32_t ctl; - ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); - ctl |= 1 << (ch->off >> 5); - HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + if (sc->polling != 0) { + int pollticks; - ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); - ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | - HDAC_SDCTL_RUN; + pollticks = ((uint64_t)hz * ch->blksz) / + ((uint64_t)sndbuf_getbps(ch->b) * sndbuf_getspd(ch->b)); + pollticks >>= 2; + if (pollticks > hz) + pollticks = hz; + if (pollticks < 1) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "%s: pollticks=%d < 1 !\n", + __func__, pollticks); + ); + pollticks = 1; + } + if (hda_chan_active(sc) == 0 || pollticks < sc->poll_ticks) { + HDA_BOOTVERBOSE( + if (hda_chan_active(sc) == 0) { + device_printf(sc->dev, + "%s: pollticks=%d\n", + __func__, pollticks); + } else { + device_printf(sc->dev, + "%s: pollticks %d -> %d\n", + __func__, sc->poll_ticks, + pollticks); + } + ); + sc->poll_ticks = pollticks; + callout_reset(&sc->poll_hda, 1, hda_poll_callback, + sc); + } + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_RUN; + } else { + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl |= 1 << (ch->off >> 5); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | + HDAC_SDCTL_RUN; + } HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); + + ch->active = 1; } static void @@ -1989,31 +2656,42 @@ static void hdac_bdl_setup(struct hdac_chan *ch) { struct hdac_softc *sc = ch->devinfo->codec->sc; - uint64_t addr; - int blks, size, blocksize; struct hdac_bdle *bdle; + uint64_t addr; + uint32_t blksz, blkcnt; int i; addr = (uint64_t)sndbuf_getbufaddr(ch->b); - size = sndbuf_getsize(ch->b); - blocksize = sndbuf_getblksz(ch->b); - blks = size / blocksize; - bdle = (struct hdac_bdle*)ch->bdl_dma.dma_vaddr; + bdle = (struct hdac_bdle *)ch->bdl_dma.dma_vaddr; + + if (sc->polling != 0) { + blksz = ch->blksz * ch->blkcnt; + blkcnt = 1; + } else { + blksz = ch->blksz; + blkcnt = ch->blkcnt; + } - for (i = 0; i < blks; i++, bdle++) { + for (i = 0; i < blkcnt; i++, bdle++) { bdle->addrl = (uint32_t)addr; bdle->addrh = (uint32_t)(addr >> 32); - bdle->len = blocksize; - bdle->ioc = 1; - - addr += blocksize; + bdle->len = blksz; + bdle->ioc = 1 ^ sc->polling; + addr += blksz; } - HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, size); - HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blks - 1); + HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, blksz * blkcnt); + HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blkcnt - 1); addr = ch->bdl_dma.dma_paddr; HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPL, (uint32_t)addr); HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPU, (uint32_t)(addr >> 32)); + if (ch->dmapos != NULL && + !(HDAC_READ_4(&sc->mem, HDAC_DPIBLBASE) & 0x00000001)) { + addr = sc->pos_dma.dma_paddr; + HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE, + ((uint32_t)addr & HDAC_DPLBASE_DPLBASE_MASK) | 0x00000001); + HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, (uint32_t)(addr >> 32)); + } } static int @@ -2028,7 +2706,6 @@ hdac_bdl_alloc(struct hdac_chan *ch) device_printf(sc->dev, "can't alloc bdl\n"); return (rc); } - hdac_dma_nocache(ch->bdl_dma.dma_vaddr); return (0); } @@ -2047,7 +2724,7 @@ hdac_audio_ctl_amp_set_internal(struct hdac_softc *sc, nid_t cad, nid_t nid, v = (1 << (15 - dir)) | (1 << 13) | (index << 8) | (lmute << 7) | left; hdac_command(sc, - HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); + HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); v = (1 << (15 - dir)) | (1 << 12) | (index << 8) | (rmute << 7) | right; } else @@ -2143,14 +2820,12 @@ hdac_command_send_internal(struct hdac_softc *sc, struct hdac_codec *codec; int corbrp; uint32_t *corb; - uint8_t rirbwp; int timeout; int retry = 10; - struct hdac_rirb *rirb_base, *rirb; - nid_t ucad; - uint32_t utag; + struct hdac_rirb *rirb_base; - if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL) + if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL || + commands->num_commands < 1) return; codec = sc->codecs[cad]; @@ -2164,8 +2839,10 @@ hdac_command_send_internal(struct hdac_softc *sc, if (codec->verbs_sent != commands->num_commands) { /* Queue as many verbs as possible */ corbrp = HDAC_READ_2(&sc->mem, HDAC_CORBRP); +#if 0 bus_dmamap_sync(sc->corb_dma.dma_tag, sc->corb_dma.dma_map, BUS_DMASYNC_PREWRITE); +#endif while (codec->verbs_sent != commands->num_commands && ((sc->corb_wp + 1) % sc->corb_size) != corbrp) { sc->corb_wp++; @@ -2175,61 +2852,30 @@ hdac_command_send_internal(struct hdac_softc *sc, } /* Send the verbs to the codecs */ +#if 0 bus_dmamap_sync(sc->corb_dma.dma_tag, sc->corb_dma.dma_map, BUS_DMASYNC_POSTWRITE); +#endif HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); } - timeout = 10; - do { - rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP); - bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, - BUS_DMASYNC_POSTREAD); - if (sc->rirb_rp != rirbwp) { - do { - sc->rirb_rp++; - sc->rirb_rp %= sc->rirb_size; - rirb = &rirb_base[sc->rirb_rp]; - if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) { - ucad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex); - utag = rirb->response >> 26; - if (ucad > -1 && ucad < HDAC_CODEC_MAX && - sc->codecs[ucad] != NULL) { - sc->unsolq[sc->unsolq_wp++] = - (ucad << 16) | - (utag & 0xffff); - sc->unsolq_wp %= HDAC_UNSOLQ_MAX; - } - } else if (codec->responses_received < commands->num_commands) - codec->commands->responses[codec->responses_received++] = - rirb->response; - } while (sc->rirb_rp != rirbwp); - break; - } + timeout = 1000; + while (hdac_rirb_flush(sc) == 0 && --timeout) DELAY(10); - } while (--timeout); } while ((codec->verbs_sent != commands->num_commands || - codec->responses_received != commands->num_commands) && - --retry); + codec->responses_received != commands->num_commands) && --retry); if (retry == 0) device_printf(sc->dev, - "%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n", - __func__, commands->num_commands, - codec->verbs_sent, codec->responses_received); + "%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n", + __func__, commands->num_commands, codec->verbs_sent, + codec->responses_received); + codec->commands = NULL; + codec->responses_received = 0; codec->verbs_sent = 0; - if (sc->unsolq_st == HDAC_UNSOLQ_READY) { - sc->unsolq_st = HDAC_UNSOLQ_BUSY; - while (sc->unsolq_rp != sc->unsolq_wp) { - ucad = sc->unsolq[sc->unsolq_rp] >> 16; - utag = sc->unsolq[sc->unsolq_rp++] & 0xffff; - sc->unsolq_rp %= HDAC_UNSOLQ_MAX; - hdac_unsolicited_handler(sc->codecs[ucad], utag); - } - sc->unsolq_st = HDAC_UNSOLQ_READY; - } + hdac_unsolq_flush(sc); } @@ -2261,21 +2907,21 @@ hdac_probe(device_t dev) for (i = 0; i < HDAC_DEVICES_LEN; i++) { if (hdac_devices[i].model == model) { strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); - result = 0; + result = BUS_PROBE_DEFAULT; break; } if (HDA_DEV_MATCH(hdac_devices[i].model, model) && class == PCIC_MULTIMEDIA && subclass == PCIS_MULTIMEDIA_HDA) { strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); - result = 0; + result = BUS_PROBE_GENERIC; break; } } if (result == ENXIO && class == PCIC_MULTIMEDIA && subclass == PCIS_MULTIMEDIA_HDA) { strlcpy(desc, "Generic", sizeof(desc)); - result = 0; + result = BUS_PROBE_GENERIC; } if (result != ENXIO) { strlcat(desc, " High Definition Audio Controller", @@ -2298,14 +2944,10 @@ hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, if (dir == PCMDIR_PLAY) { ch = &sc->play; ch->off = (sc->num_iss + devinfo->function.audio.playcnt) << 5; - ch->dir = PCMDIR_PLAY; - ch->sid = ++sc->streamcnt; devinfo->function.audio.playcnt++; } else { ch = &sc->rec; ch->off = devinfo->function.audio.reccnt << 5; - ch->dir = PCMDIR_REC; - ch->sid = ++sc->streamcnt; devinfo->function.audio.reccnt++; } if (devinfo->function.audio.quirks & HDA_QUIRK_FIXEDRATE) { @@ -2313,6 +2955,13 @@ hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, ch->pcmrates[0] = 48000; ch->pcmrates[1] = 0; } + if (sc->pos_dma.dma_vaddr != NULL) + ch->dmapos = (uint32_t *)(sc->pos_dma.dma_vaddr + + (sc->streamcnt * 8)); + else + ch->dmapos = NULL; + ch->sid = ++sc->streamcnt; + ch->dir = dir; ch->b = b; ch->c = c; ch->devinfo = devinfo; @@ -2328,11 +2977,29 @@ hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, if (sndbuf_alloc(ch->b, sc->chan_dmat, sc->chan_size) != 0) return (NULL); - hdac_dma_nocache(sndbuf_getbuf(ch->b)); + HDAC_DMA_ATTR(sc, sndbuf_getbuf(ch->b), sndbuf_getmaxsize(ch->b), + PAT_UNCACHEABLE); return (ch); } +static int +hdac_channel_free(kobj_t obj, void *data) +{ + struct hdac_softc *sc; + struct hdac_chan *ch; + + ch = (struct hdac_chan *)data; + sc = (ch != NULL && ch->devinfo != NULL && ch->devinfo->codec != NULL) ? + ch->devinfo->codec->sc : NULL; + if (ch != NULL && sc != NULL) { + HDAC_DMA_ATTR(sc, sndbuf_getbuf(ch->b), + sndbuf_getmaxsize(ch->b), PAT_WRITE_BACK); + } + + return (1); +} + static int hdac_channel_setformat(kobj_t obj, void *data, uint32_t format) { @@ -2353,16 +3020,18 @@ static int hdac_channel_setspeed(kobj_t obj, void *data, uint32_t speed) { struct hdac_chan *ch = data; - uint32_t spd = 0; + uint32_t spd = 0, threshold; int i; for (i = 0; ch->pcmrates[i] != 0; i++) { spd = ch->pcmrates[i]; - if (spd >= speed) + threshold = spd + ((ch->pcmrates[i + 1] != 0) ? + ((ch->pcmrates[i + 1] - spd) >> 1) : 0); + if (speed < threshold) break; } - if (spd == 0) + if (spd == 0) /* impossible */ ch->spd = 48000; else ch->spd = spd; @@ -2417,11 +3086,51 @@ hdac_stream_setup(struct hdac_chan *ch) } static int -hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blocksize) +hdac_channel_setfragments(kobj_t obj, void *data, + uint32_t blksz, uint32_t blkcnt) +{ + struct hdac_chan *ch = data; + struct hdac_softc *sc = ch->devinfo->codec->sc; + + blksz &= HDA_BLK_ALIGN; + + if (blksz > (sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN)) + blksz = sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN; + if (blksz < HDA_BLK_MIN) + blksz = HDA_BLK_MIN; + if (blkcnt > HDA_BDL_MAX) + blkcnt = HDA_BDL_MAX; + if (blkcnt < HDA_BDL_MIN) + blkcnt = HDA_BDL_MIN; + + while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->b)) { + if ((blkcnt >> 1) >= HDA_BDL_MIN) + blkcnt >>= 1; + else if ((blksz >> 1) >= HDA_BLK_MIN) + blksz >>= 1; + else + break; + } + + if ((sndbuf_getblksz(ch->b) != blksz || + sndbuf_getblkcnt(ch->b) != blkcnt) && + sndbuf_resize(ch->b, blkcnt, blksz) != 0) + device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n", + __func__, blksz, blkcnt); + + ch->blksz = sndbuf_getblksz(ch->b); + ch->blkcnt = sndbuf_getblkcnt(ch->b); + + return (1); +} + +static int +hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz) { struct hdac_chan *ch = data; + struct hdac_softc *sc = ch->devinfo->codec->sc; - sndbuf_resize(ch->b, ch->blkcnt, ch->blksz); + hdac_channel_setfragments(obj, data, blksz, sc->chan_blkcnt); return (ch->blksz); } @@ -2461,6 +3170,9 @@ hdac_channel_trigger(kobj_t obj, void *data, int go) struct hdac_chan *ch = data; struct hdac_softc *sc = ch->devinfo->codec->sc; + if (!(go == PCMTRIG_START || go == PCMTRIG_STOP || go == PCMTRIG_ABORT)) + return (0); + hdac_lock(sc); switch (go) { case PCMTRIG_START: @@ -2470,6 +3182,8 @@ hdac_channel_trigger(kobj_t obj, void *data, int go) case PCMTRIG_ABORT: hdac_channel_stop(sc, ch); break; + default: + break; } hdac_unlock(sc); @@ -2481,26 +3195,22 @@ hdac_channel_getptr(kobj_t obj, void *data) { struct hdac_chan *ch = data; struct hdac_softc *sc = ch->devinfo->codec->sc; - int sz, delta; uint32_t ptr; hdac_lock(sc); - ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB); + if (sc->polling != 0) + ptr = ch->ptr; + else if (ch->dmapos != NULL) + ptr = *(ch->dmapos); + else + ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB); hdac_unlock(sc); - sz = sndbuf_getsize(ch->b); - ptr %= sz; - - if (ch->dir == PCMDIR_REC) { - delta = ptr % sndbuf_getblksz(ch->b); - if (delta != 0) { - ptr -= delta; - if (ptr < delta) - ptr = sz - delta; - else - ptr -= delta; - } - } + /* + * Round to available space and force 128 bytes aligment. + */ + ptr %= ch->blksz * ch->blkcnt; + ptr &= HDA_BLK_ALIGN; return (ptr); } @@ -2513,6 +3223,7 @@ hdac_channel_getcaps(kobj_t obj, void *data) static kobj_method_t hdac_channel_methods[] = { KOBJMETHOD(channel_init, hdac_channel_init), + KOBJMETHOD(channel_free, hdac_channel_free), KOBJMETHOD(channel_setformat, hdac_channel_setformat), KOBJMETHOD(channel_setspeed, hdac_channel_setspeed), KOBJMETHOD(channel_setblocksize, hdac_channel_setblocksize), @@ -2523,6 +3234,27 @@ static kobj_method_t hdac_channel_methods[] = { }; CHANNEL_DECLARE(hdac_channel); +static void +hdac_jack_poll_callback(void *arg) +{ + struct hdac_devinfo *devinfo = arg; + struct hdac_softc *sc; + + if (devinfo == NULL || devinfo->codec == NULL || + devinfo->codec->sc == NULL) + return; + sc = devinfo->codec->sc; + hdac_lock(sc); + if (sc->poll_ival == 0) { + hdac_unlock(sc); + return; + } + hdac_hp_switch_handler(devinfo); + callout_reset(&sc->poll_jack, sc->poll_ival, + hdac_jack_poll_callback, devinfo); + hdac_unlock(sc); +} + static int hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) { @@ -2543,31 +3275,35 @@ hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) cad = devinfo->codec->cad; for (i = 0; i < HDAC_HP_SWITCH_LEN; i++) { if (!(HDA_DEV_MATCH(hdac_hp_switch[i].model, - sc->pci_subvendor) && - hdac_hp_switch[i].id == id)) + sc->pci_subvendor) && hdac_hp_switch[i].id == id)) continue; w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); - if (w != NULL && w->enable != 0 - && w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && - HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) { + if (w == NULL || w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (hdac_hp_switch[i].polling != 0) + callout_reset(&sc->poll_jack, 1, + hdac_jack_poll_callback, devinfo); + else if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) hdac_command(sc, - HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, - w->nid, - HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE| + HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, w->nid, + HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE | HDAC_UNSOLTAG_EVENT_HP), cad); - hdac_hp_switch_handler(devinfo); - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "HDA_DEBUG: Enabling headphone/speaker " - "audio routing switching:\n"); - device_printf(sc->dev, - "HDA_DEBUG: \tindex=%d nid=%d " - "pci_subvendor=0x%08x " - "codec=0x%08x\n", - i, w->nid, sc->pci_subvendor, id); - ); - } + else + continue; + hdac_hp_switch_handler(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Enabling headphone/speaker " + "audio routing switching:\n"); + device_printf(sc->dev, + "HDA_DEBUG: \tindex=%d nid=%d " + "pci_subvendor=0x%08x " + "codec=0x%08x [%s]\n", + i, w->nid, sc->pci_subvendor, id, + (hdac_hp_switch[i].polling != 0) ? "POLL" : + "UNSOL"); + ); break; } for (i = 0; i < HDAC_EAPD_SWITCH_LEN; i++) { @@ -2619,19 +3355,13 @@ hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) } if (softpcmvol == 1 || ctl == NULL) { - device_printf(sc->dev, "no softpcm support available\n"); -#if notyet - struct snddev_info *d = NULL; - d = device_get_softc(sc->dev); - if (d != NULL) { - d->flags |= SD_F_SOFTPCMVOL; - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "HDA_DEBUG: %s Soft PCM volume\n", - (softpcmvol == 1) ? - "Forcing" : "Enabling"); - ); - } + pcm_setflags(sc->dev, pcm_getflags(sc->dev) | SD_F_SOFTPCMVOL); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: %s Soft PCM volume\n", + (softpcmvol == 1) ? + "Forcing" : "Enabling"); + ); i = 0; /* * XXX Temporary quirk for STAC9220, until the parser @@ -2649,6 +3379,24 @@ hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) } else ctl->ossmask &= ~SOUND_MASK_VOLUME; } + } else if (id == HDA_CODEC_STAC9221) { + mask |= SOUND_MASK_VOLUME; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != + NULL) { + if (ctl->widget == NULL) + continue; + if (ctl->widget->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT && + ctl->index == 0 && (ctl->widget->nid == 2 || + ctl->widget->enable != 0)) { + ctl->enable = 1; + ctl->ossmask = SOUND_MASK_VOLUME; + ctl->ossval = 100 | (100 << 8); + } else if (ctl->enable == 0) + continue; + else + ctl->ossmask &= ~SOUND_MASK_VOLUME; + } } else { mix_setparentchild(m, SOUND_MIXER_VOLUME, SOUND_MASK_PCM); @@ -2666,10 +3414,13 @@ hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) ctl->enable = 0; } } -#endif } - recmask &= ~(SOUND_MASK_PCM | SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER); + recmask &= ~(SOUND_MASK_PCM | SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER | + SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_IGAIN | + SOUND_MASK_OGAIN); + recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1; + mask &= (1 << SOUND_MIXER_NRDEVICES) - 1; mix_setrecdevs(m, recmask); mix_setdevs(m, mask); @@ -2722,11 +3473,16 @@ hdac_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, else w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; if (orig != w->param.eapdbtl) { + uint32_t val; + if (hdac_eapd_switch[i].hp_switch != 0) hdac_hp_switch_handler(devinfo); + val = w->param.eapdbtl; + if (devinfo->function.audio.quirks & HDA_QUIRK_EAPDINV) + val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; hdac_command(sc, HDA_CMD_SET_EAPD_BTL_ENABLE(devinfo->codec->cad, - w->nid, w->param.eapdbtl), devinfo->codec->cad); + w->nid, val), devinfo->codec->cad); } hdac_unlock(sc); return (left | (left << 8)); @@ -2820,7 +3576,8 @@ hdac_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)) continue; if (cw->ctlflags & target) { - hdac_widget_connection_select(w, j); + if (!(w->pflags & HDA_ADC_LOCKED)) + hdac_widget_connection_select(w, j); ret = target; j += w->nconns; } @@ -2852,7 +3609,9 @@ hdac_attach(device_t dev) { struct hdac_softc *sc; int result; - int i = 0; + int i; + uint16_t vendor; + uint8_t v; sc = kmalloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO); if (sc == NULL) { @@ -2861,21 +3620,36 @@ hdac_attach(device_t dev) } sc->lock = snd_mtxcreate(device_get_nameunit(dev), HDAC_MTX_NAME); - if (sc->lock == NULL) { - device_printf(dev, "mutex creation failed\n"); - kfree(sc, M_DEVBUF); - return (ENOMEM); - } - sc->dev = dev; sc->pci_subvendor = (uint32_t)pci_get_subdevice(sc->dev) << 16; sc->pci_subvendor |= (uint32_t)pci_get_subvendor(sc->dev) & 0x0000ffff; + vendor = pci_get_vendor(dev); + + if (sc->pci_subvendor == HP_NX6325_SUBVENDORX) { + /* Screw nx6325 - subdevice/subvendor swapped */ + sc->pci_subvendor = HP_NX6325_SUBVENDOR; + } + + callout_init(&sc->poll_hda); + callout_init(&sc->poll_hdac); + callout_init(&sc->poll_jack); + + sc->poll_ticks = 1; + sc->poll_ival = HDAC_POLL_INTERVAL; + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "polling", &i) == 0 && i != 0) + sc->polling = 1; + else + sc->polling = 0; sc->chan_size = pcm_getbuffersize(dev, - HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_DEFAULT); - if (resource_int_value(device_get_name(sc->dev), - device_get_unit(sc->dev), "blocksize", &i) == 0 && - i > 0) { + HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_MAX); + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "blocksize", &i) == 0 && i > 0) { + i &= HDA_BLK_ALIGN; + if (i < HDA_BLK_MIN) + i = HDA_BLK_MIN; sc->chan_blkcnt = sc->chan_size / i; i = 0; while (sc->chan_blkcnt >> i) @@ -2895,13 +3669,13 @@ hdac_attach(device_t dev) BUS_SPACE_MAXADDR, /* highaddr */ NULL, /* filtfunc */ NULL, /* fistfuncarg */ - sc->chan_size, /* maxsize */ + sc->chan_size, /* maxsize */ 1, /* nsegments */ - sc->chan_size, /* maxsegsz */ + sc->chan_size, /* maxsegsz */ 0, /* flags */ &sc->chan_dmat); /* dmat */ if (result != 0) { - device_printf(sc->dev, "%s: bus_dma_tag_create failed (%x)\n", + device_printf(dev, "%s: bus_dma_tag_create failed (%x)\n", __func__, result); snd_mtxfree(sc->lock); kfree(sc, M_DEVBUF); @@ -2915,6 +3689,70 @@ hdac_attach(device_t dev) pci_enable_busmaster(dev); + if (vendor == INTEL_VENDORID) { + /* TCSEL -> TC0 */ + v = pci_read_config(dev, 0x44, 1); + pci_write_config(dev, 0x44, v & 0xf8, 1); + HDA_BOOTVERBOSE( + device_printf(dev, "TCSEL: 0x%02d -> 0x%02d\n", v, + pci_read_config(dev, 0x44, 1)); + ); + } + +#if 0 /* TODO: No uncacheable DMA support in DragonFly. */ + sc->nocache = 1; + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "snoop", &i) == 0 && i != 0) { +#else + sc->nocache = 0; +#endif + /* + * Try to enable PCIe snoop to avoid messing around with + * uncacheable DMA attribute. Since PCIe snoop register + * config is pretty much vendor specific, there are no + * general solutions on how to enable it, forcing us (even + * Microsoft) to enable uncacheable or write combined DMA + * by default. + * + * http://msdn2.microsoft.com/en-us/library/ms790324.aspx + */ + for (i = 0; i < HDAC_PCIESNOOP_LEN; i++) { + if (hdac_pcie_snoop[i].vendor != vendor) + continue; + sc->nocache = 0; + if (hdac_pcie_snoop[i].reg == 0x00) + break; + v = pci_read_config(dev, hdac_pcie_snoop[i].reg, 1); + if ((v & hdac_pcie_snoop[i].enable) == + hdac_pcie_snoop[i].enable) + break; + v &= hdac_pcie_snoop[i].mask; + v |= hdac_pcie_snoop[i].enable; + pci_write_config(dev, hdac_pcie_snoop[i].reg, v, 1); + v = pci_read_config(dev, hdac_pcie_snoop[i].reg, 1); + if ((v & hdac_pcie_snoop[i].enable) != + hdac_pcie_snoop[i].enable) { + HDA_BOOTVERBOSE( + device_printf(dev, + "WARNING: Failed to enable PCIe " + "snoop!\n"); + ); +#if 0 /* TODO: No uncacheable DMA support in DragonFly. */ + sc->nocache = 1; +#endif + } + break; + } +#if 0 /* TODO: No uncacheable DMA support in DragonFly. */ + } +#endif + + HDA_BOOTVERBOSE( + device_printf(dev, "DMA Coherency: %s / vendor=0x%04x\n", + (sc->nocache == 0) ? "PCIe snoop" : "Uncacheable", vendor); + ); + /* Allocate resources */ result = hdac_mem_alloc(sc); if (result != 0) @@ -2941,10 +3779,6 @@ hdac_attach(device_t dev) /* Quiesce everything */ hdac_reset(sc); - /* Disable PCI-Express QOS */ - pci_write_config(sc->dev, 0x44, - pci_read_config(sc->dev, 0x44, 1) & 0xf8, 1); - /* Initialize the CORB and RIRB */ hdac_corb_init(sc); hdac_rirb_init(sc); @@ -2960,9 +3794,9 @@ hdac_attach(device_t dev) return (0); hdac_attach_fail: - hdac_dma_free(&sc->rirb_dma); - hdac_dma_free(&sc->corb_dma); hdac_irq_free(sc); + hdac_dma_free(sc, &sc->rirb_dma); + hdac_dma_free(sc, &sc->corb_dma); hdac_mem_free(sc); snd_mtxfree(sc->lock); kfree(sc, M_DEVBUF); @@ -2994,6 +3828,10 @@ hdac_audio_parse(struct hdac_devinfo *devinfo) devinfo->startnode = HDA_PARAM_SUB_NODE_COUNT_START(res); devinfo->endnode = devinfo->startnode + devinfo->nodecnt; + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_GPIO_COUNT), cad); + devinfo->function.audio.gpio = res; + HDA_BOOTVERBOSE( device_printf(sc->dev, " Vendor: 0x%08x\n", devinfo->vendor_id); @@ -3008,6 +3846,19 @@ hdac_audio_parse(struct hdac_devinfo *devinfo) device_printf(sc->dev, " Nodes: start=%d " "endnode=%d total=%d\n", devinfo->startnode, devinfo->endnode, devinfo->nodecnt); + device_printf(sc->dev, " CORB size: %d\n", sc->corb_size); + device_printf(sc->dev, " RIRB size: %d\n", sc->rirb_size); + device_printf(sc->dev, " Streams: ISS=%d OSS=%d BSS=%d\n", + sc->num_iss, sc->num_oss, sc->num_bss); + device_printf(sc->dev, " GPIO: 0x%08x\n", + devinfo->function.audio.gpio); + device_printf(sc->dev, " NumGPIO=%d NumGPO=%d " + "NumGPI=%d GPIWake=%d GPIUnsol=%d\n", + HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); ); res = hdac_command(sc, @@ -3030,13 +3881,11 @@ hdac_audio_parse(struct hdac_devinfo *devinfo) cad); devinfo->function.audio.inamp_cap = res; - if (devinfo->nodecnt > 0) { - hdac_unlock(sc); + if (devinfo->nodecnt > 0) devinfo->widget = (struct hdac_widget *)kmalloc( sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAC, M_NOWAIT | M_ZERO); - hdac_lock(sc); - } else + else devinfo->widget = NULL; if (devinfo->widget == NULL) { @@ -3104,10 +3953,8 @@ hdac_audio_ctl_parse(struct hdac_devinfo *devinfo) if (max < 1) return; - hdac_unlock(sc); ctls = (struct hdac_audio_ctl *)kmalloc( sizeof(*ctls) * max, M_HDAC, M_ZERO | M_NOWAIT); - hdac_lock(sc); if (ctls == NULL) { /* Blekh! */ @@ -3224,19 +4071,46 @@ static const struct { uint32_t set, unset; } hdac_quirks[] = { /* - * XXX Fixed rate quirk. Other than 48000 - * sounds pretty much like train wreck. - * * XXX Force stereo quirk. Monoural recording / playback * on few codecs (especially ALC880) seems broken or * perhaps unsupported. */ { HDA_MATCH_ALL, HDA_MATCH_ALL, - HDA_QUIRK_FIXEDRATE | HDA_QUIRK_FORCESTEREO, 0 }, + HDA_QUIRK_FORCESTEREO | HDA_QUIRK_IVREF, 0 }, { ACER_ALL_SUBVENDOR, HDA_MATCH_ALL, - HDA_QUIRK_GPIO1, 0 }, + HDA_QUIRK_GPIO0, 0 }, { ASUS_M5200_SUBVENDOR, HDA_CODEC_ALC880, + HDA_QUIRK_GPIO0, 0 }, + { ASUS_A7M_SUBVENDOR, HDA_CODEC_ALC880, + HDA_QUIRK_GPIO0, 0 }, + { ASUS_A7T_SUBVENDOR, HDA_CODEC_ALC882, + HDA_QUIRK_GPIO0, 0 }, + { ASUS_W2J_SUBVENDOR, HDA_CODEC_ALC882, + HDA_QUIRK_GPIO0, 0 }, + { ASUS_U5F_SUBVENDOR, HDA_CODEC_AD1986A, + HDA_QUIRK_EAPDINV, 0 }, + { ASUS_A8JC_SUBVENDOR, HDA_CODEC_AD1986A, + HDA_QUIRK_EAPDINV, 0 }, + { ASUS_F3JC_SUBVENDOR, HDA_CODEC_ALC861, + HDA_QUIRK_OVREF, 0 }, + { ASUS_W6F_SUBVENDOR, HDA_CODEC_ALC861, + HDA_QUIRK_OVREF, 0 }, + { UNIWILL_9075_SUBVENDOR, HDA_CODEC_ALC861, + HDA_QUIRK_OVREF, 0 }, + /*{ ASUS_M2N_SUBVENDOR, HDA_CODEC_AD1988, + HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 },*/ + { MEDION_MD95257_SUBVENDOR, HDA_CODEC_ALC880, HDA_QUIRK_GPIO1, 0 }, + { LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, + HDA_QUIRK_EAPDINV, 0 }, + { SAMSUNG_Q1_SUBVENDOR, HDA_CODEC_AD1986A, + HDA_QUIRK_EAPDINV, 0 }, + { APPLE_INTEL_MAC, HDA_CODEC_STAC9221, + HDA_QUIRK_GPIO0 | HDA_QUIRK_GPIO1, 0 }, + { HDA_MATCH_ALL, HDA_CODEC_AD1988, + HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 }, + { HDA_MATCH_ALL, HDA_CODEC_AD1988B, + HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 }, { HDA_MATCH_ALL, HDA_CODEC_CXVENICE, 0, HDA_QUIRK_FORCESTEREO }, { HDA_MATCH_ALL, HDA_CODEC_STACXXXX, @@ -3248,6 +4122,7 @@ static void hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) { struct hdac_widget *w; + struct hdac_audio_ctl *ctl; uint32_t id, subvendor; int i; @@ -3281,6 +4156,34 @@ hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) if (w->nid != 5) w->enable = 0; } + if (subvendor == HP_XW4300_SUBVENDOR) { + ctl = hdac_audio_ctl_amp_get(devinfo, 16, 0, 1); + if (ctl != NULL && ctl->widget != NULL) { + ctl->ossmask = SOUND_MASK_SPEAKER; + ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 17, 0, 1); + if (ctl != NULL && ctl->widget != NULL) { + ctl->ossmask = SOUND_MASK_SPEAKER; + ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; + } + } else if (subvendor == HP_3010_SUBVENDOR) { + ctl = hdac_audio_ctl_amp_get(devinfo, 17, 0, 1); + if (ctl != NULL && ctl->widget != NULL) { + ctl->ossmask = SOUND_MASK_SPEAKER; + ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 21, 0, 1); + if (ctl != NULL && ctl->widget != NULL) { + ctl->ossmask = SOUND_MASK_SPEAKER; + ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; + } + } + break; + case HDA_CODEC_ALC861: + ctl = hdac_audio_ctl_amp_get(devinfo, 21, 2, 1); + if (ctl != NULL) + ctl->muted = HDA_AMP_MUTE_ALL; break; case HDA_CODEC_ALC880: for (i = devinfo->startnode; i < devinfo->endnode; i++) { @@ -3305,13 +4208,40 @@ hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) } } break; + case HDA_CODEC_ALC883: + /* + * nid: 24/25 = External (jack) or Internal (fixed) Mic. + * Clear vref cap for jack connectivity. + */ + w = hdac_widget_get(devinfo, 24); + if (w != NULL && w->enable != 0 && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + (w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK) + w->wclass.pin.cap &= ~( + HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK | + HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK | + HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK); + w = hdac_widget_get(devinfo, 25); + if (w != NULL && w->enable != 0 && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + (w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK) + w->wclass.pin.cap &= ~( + HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK | + HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK | + HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK); + /* + * nid: 26 = Line-in, leave it alone. + */ + break; case HDA_CODEC_AD1981HD: w = hdac_widget_get(devinfo, 11); if (w != NULL && w->enable != 0 && w->nconns > 3) w->selconn = 3; if (subvendor == IBM_M52_SUBVENDOR) { - struct hdac_audio_ctl *ctl; - ctl = hdac_audio_ctl_amp_get(devinfo, 7, 0, 1); if (ctl != NULL) ctl->ossmask = SOUND_MASK_SPEAKER; @@ -3328,8 +4258,75 @@ hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) if (w->nid != 3) w->enable = 0; } + if (subvendor == ASUS_M2NPVMX_SUBVENDOR) { + /* nid 28 is mic, nid 29 is line-in */ + w = hdac_widget_get(devinfo, 15); + if (w != NULL) + w->selconn = 2; + w = hdac_widget_get(devinfo, 16); + if (w != NULL) + w->selconn = 1; + } + break; + case HDA_CODEC_AD1988: + case HDA_CODEC_AD1988B: + /*w = hdac_widget_get(devinfo, 12); + if (w != NULL) { + w->selconn = 1; + w->pflags |= HDA_ADC_LOCKED; + } + w = hdac_widget_get(devinfo, 13); + if (w != NULL) { + w->selconn = 4; + w->pflags |= HDA_ADC_LOCKED; + } + w = hdac_widget_get(devinfo, 14); + if (w != NULL) { + w->selconn = 2; + w->pflags |= HDA_ADC_LOCKED; + }*/ + ctl = hdac_audio_ctl_amp_get(devinfo, 57, 0, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_IGAIN; + ctl->widget->ctlflags |= SOUND_MASK_IGAIN; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 58, 0, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_IGAIN; + ctl->widget->ctlflags |= SOUND_MASK_IGAIN; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 60, 0, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_IGAIN; + ctl->widget->ctlflags |= SOUND_MASK_IGAIN; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 32, 0, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_MIC | SOUND_MASK_VOLUME; + ctl->widget->ctlflags |= SOUND_MASK_MIC; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 32, 4, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_MIC | SOUND_MASK_VOLUME; + ctl->widget->ctlflags |= SOUND_MASK_MIC; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 32, 1, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_LINE | SOUND_MASK_VOLUME; + ctl->widget->ctlflags |= SOUND_MASK_LINE; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 32, 7, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_SPEAKER | SOUND_MASK_VOLUME; + ctl->widget->ctlflags |= SOUND_MASK_SPEAKER; + } break; case HDA_CODEC_STAC9221: + /* + * Dell XPS M1210 need all DACs for each output jacks + */ + if (subvendor == DELL_XPSM1210_SUBVENDOR) + break; for (i = devinfo->startnode; i < devinfo->endnode; i++) { w = hdac_widget_get(devinfo, i); if (w == NULL || w->enable == 0) @@ -3353,6 +4350,48 @@ hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) } break; + case HDA_CODEC_STAC9227: + w = hdac_widget_get(devinfo, 8); + if (w != NULL) + w->enable = 0; + w = hdac_widget_get(devinfo, 9); + if (w != NULL) + w->enable = 0; + break; + case HDA_CODEC_CXWAIKIKI: + if (subvendor == HP_DV5000_SUBVENDOR) { + w = hdac_widget_get(devinfo, 27); + if (w != NULL) + w->enable = 0; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 16, 0, 1); + if (ctl != NULL) + ctl->ossmask = SOUND_MASK_SKIP; + ctl = hdac_audio_ctl_amp_get(devinfo, 25, 0, 1); + if (ctl != NULL && ctl->childwidget != NULL && + ctl->childwidget->enable != 0) { + ctl->ossmask = SOUND_MASK_PCM | SOUND_MASK_VOLUME; + ctl->childwidget->ctlflags |= SOUND_MASK_PCM; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 25, 1, 1); + if (ctl != NULL && ctl->childwidget != NULL && + ctl->childwidget->enable != 0) { + ctl->ossmask = SOUND_MASK_LINE | SOUND_MASK_VOLUME; + ctl->childwidget->ctlflags |= SOUND_MASK_LINE; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 25, 2, 1); + if (ctl != NULL && ctl->childwidget != NULL && + ctl->childwidget->enable != 0) { + ctl->ossmask = SOUND_MASK_MIC | SOUND_MASK_VOLUME; + ctl->childwidget->ctlflags |= SOUND_MASK_MIC; + } + ctl = hdac_audio_ctl_amp_get(devinfo, 26, 0, 1); + if (ctl != NULL) { + ctl->ossmask = SOUND_MASK_SKIP; + /* XXX mixer \=rec mic broken.. why?!? */ + /* ctl->widget->ctlflags |= SOUND_MASK_MIC; */ + } + break; default: break; } @@ -3374,6 +4413,7 @@ hdac_audio_ctl_ossmixer_getnextdev(struct hdac_devinfo *devinfo) case SOUND_MIXER_MIC: case SOUND_MIXER_CD: case SOUND_MIXER_RECLEV: + case SOUND_MIXER_IGAIN: case SOUND_MIXER_OGAIN: /* reserved for EAPD switch */ (*dev)++; break; @@ -3502,7 +4542,7 @@ hdac_audio_ctl_outamp_build(struct hdac_devinfo *devinfo, if (ctl->enable == 0 || ctl->widget == NULL) continue; /* XXX This should be compressed! */ - if ((ctl->widget->nid == w->nid) || + if (((ctl->widget->nid == w->nid) || (ctl->widget->nid == pnid && ctl->index == index && (ctl->dir & HDA_CTL_IN)) || (ctl->widget->nid == pnid && pw != NULL && @@ -3512,7 +4552,8 @@ hdac_audio_ctl_outamp_build(struct hdac_devinfo *devinfo, pw->selconn == -1) && (ctl->dir & HDA_CTL_OUT)) || (strategy == HDA_PARSE_DIRECT && - ctl->widget->nid == w->nid)) { + ctl->widget->nid == w->nid)) && + !(ctl->ossmask & ~SOUND_MASK_VOLUME)) { /*if (pw != NULL && pw->selconn == -1) pw->selconn = index; fl |= SOUND_MASK_VOLUME; @@ -3543,8 +4584,8 @@ hdac_audio_ctl_outamp_build(struct hdac_devinfo *devinfo, } w->ctlflags |= fl; return (fl); - } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX - && HDA_PARAM_PIN_CAP_INPUT_CAP(w->wclass.pin.cap) && + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + HDA_PARAM_PIN_CAP_INPUT_CAP(w->wclass.pin.cap) && (w->pflags & HDA_ADC_PATH)) { conndev = w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; @@ -3846,37 +4887,91 @@ hdac_audio_build_tree(struct hdac_devinfo *devinfo) #define HDA_COMMIT_CTRL (1 << 1) #define HDA_COMMIT_EAPD (1 << 2) #define HDA_COMMIT_GPIO (1 << 3) +#define HDA_COMMIT_MISC (1 << 4) #define HDA_COMMIT_ALL (HDA_COMMIT_CONN | HDA_COMMIT_CTRL | \ - HDA_COMMIT_EAPD | HDA_COMMIT_GPIO) + HDA_COMMIT_EAPD | HDA_COMMIT_GPIO | HDA_COMMIT_MISC) static void hdac_audio_commit(struct hdac_devinfo *devinfo, uint32_t cfl) { struct hdac_softc *sc = devinfo->codec->sc; struct hdac_widget *w; - nid_t cad, nid; - int i, gpioval; + nid_t cad; + int i; if (!(cfl & HDA_COMMIT_ALL)) return; cad = devinfo->codec->cad; + if ((cfl & HDA_COMMIT_MISC)) { + if (sc->pci_subvendor == APPLE_INTEL_MAC) + hdac_command(sc, HDA_CMD_12BIT(cad, devinfo->nid, + 0x7e7, 0), cad); + } + if (cfl & HDA_COMMIT_GPIO) { - nid = devinfo->nid; - for (i = 0; i < HDA_GPIO_MAX; i++) { - if (!(devinfo->function.audio.quirks & (1 << i))) - continue; - gpioval = (1 << i) - 1; + uint32_t gdata, gmask, gdir; + int commitgpio, numgpio; + + gdata = 0; + gmask = 0; + gdir = 0; + commitgpio = 0; + + numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO( + devinfo->function.audio.gpio); + + if (devinfo->function.audio.quirks & HDA_QUIRK_GPIOFLUSH) + commitgpio = (numgpio > 0) ? 1 : 0; + else { + for (i = 0; i < numgpio && i < HDA_GPIO_MAX; i++) { + if (!(devinfo->function.audio.quirks & + (1 << i))) + continue; + if (commitgpio == 0) { + commitgpio = 1; + HDA_BOOTVERBOSE( + gdata = hdac_command(sc, + HDA_CMD_GET_GPIO_DATA(cad, + devinfo->nid), cad); + gmask = hdac_command(sc, + HDA_CMD_GET_GPIO_ENABLE_MASK(cad, + devinfo->nid), cad); + gdir = hdac_command(sc, + HDA_CMD_GET_GPIO_DIRECTION(cad, + devinfo->nid), cad); + device_printf(sc->dev, + "GPIO init: data=0x%08x " + "mask=0x%08x dir=0x%08x\n", + gdata, gmask, gdir); + gdata = 0; + gmask = 0; + gdir = 0; + ); + } + gdata |= 1 << i; + gmask |= 1 << i; + gdir |= 1 << i; + } + } + + if (commitgpio != 0) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "GPIO commit: data=0x%08x mask=0x%08x " + "dir=0x%08x\n", + gdata, gmask, gdir); + ); hdac_command(sc, - HDA_CMD_SET_GPIO_ENABLE_MASK(cad, nid, gpioval), - cad); + HDA_CMD_SET_GPIO_ENABLE_MASK(cad, devinfo->nid, + gmask), cad); hdac_command(sc, - HDA_CMD_SET_GPIO_DIRECTION(cad, nid, gpioval), - cad); + HDA_CMD_SET_GPIO_DIRECTION(cad, devinfo->nid, + gdir), cad); hdac_command(sc, - HDA_CMD_SET_GPIO_DATA(cad, nid, gpioval), - cad); + HDA_CMD_SET_GPIO_DATA(cad, devinfo->nid, + gdata), cad); } } @@ -3892,6 +4987,10 @@ hdac_audio_commit(struct hdac_devinfo *devinfo, uint32_t cfl) } if ((cfl & HDA_COMMIT_CTRL) && w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + uint32_t pincap; + + pincap = w->wclass.pin.cap; + if ((w->pflags & (HDA_DAC_PATH | HDA_ADC_PATH)) == (HDA_DAC_PATH | HDA_ADC_PATH)) device_printf(sc->dev, "WARNING: node %d " @@ -3904,25 +5003,63 @@ hdac_audio_commit(struct hdac_devinfo *devinfo, uint32_t cfl) HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT) w->wclass.pin.ctrl &= ~HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; + if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF100) && + HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); + else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF80) && + HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); + else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF50) && + HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); } else if (w->pflags & HDA_ADC_PATH) { w->wclass.pin.ctrl &= ~(HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE); + if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF100) && + HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); + else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF80) && + HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); + else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF50) && + HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); } else w->wclass.pin.ctrl &= ~( HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | - HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE); + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK); hdac_command(sc, HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, w->wclass.pin.ctrl), cad); } if ((cfl & HDA_COMMIT_EAPD) && - w->param.eapdbtl != HDAC_INVALID) + w->param.eapdbtl != HDAC_INVALID) { + uint32_t val; + + val = w->param.eapdbtl; + if (devinfo->function.audio.quirks & + HDA_QUIRK_EAPDINV) + val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; hdac_command(sc, HDA_CMD_SET_EAPD_BTL_ENABLE(cad, w->nid, - w->param.eapdbtl), cad); + val), cad); + } DELAY(1000); } } @@ -3959,7 +5096,7 @@ hdac_audio_ctl_commit(struct hdac_devinfo *devinfo) kprintf(" childnid=%d", ctl->childwidget->nid); kprintf(" Bind to NONE\n"); - } + } ); if (ctl->step > 0) { ctl->ossval = (ctl->left * 100) / ctl->step; @@ -4018,9 +5155,14 @@ hdac_pcmchannel_setup(struct hdac_devinfo *devinfo, int dir) }*/ if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap)) continue; + if (ret == 0) { + fmtcap = w->param.supp_stream_formats; + pcmcap = w->param.supp_pcm_size_rate; + } else { + fmtcap &= w->param.supp_stream_formats; + pcmcap &= w->param.supp_pcm_size_rate; + } ch->io[ret++] = i; - fmtcap &= w->param.supp_stream_formats; - pcmcap &= w->param.supp_pcm_size_rate; } ch->io[ret] = -1; @@ -4109,7 +5251,8 @@ hdac_dump_ctls(struct hdac_devinfo *devinfo, const char *banner, uint32_t flag) i = 0; while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->enable == 0 || ctl->widget == NULL || - ctl->widget->enable == 0) + ctl->widget->enable == 0 || (ctl->ossmask & + (SOUND_MASK_SKIP | SOUND_MASK_DISABLE))) continue; if ((flag == 0 && (ctl->ossmask & ~fl)) || (flag != 0 && (ctl->ossmask & flag))) { @@ -4232,6 +5375,20 @@ hdac_dump_pin(struct hdac_softc *sc, struct hdac_widget *w) kprintf(" IN"); if (HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(pincap)) kprintf(" BAL"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)) { + kprintf(" VREF["); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + kprintf(" 50"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + kprintf(" 80"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + kprintf(" 100"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(pincap)) + kprintf(" GROUND"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(pincap)) + kprintf(" HIZ"); + kprintf(" ]"); + } if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) kprintf(" EAPD"); if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(wcap)) @@ -4484,9 +5641,21 @@ hdac_release_resources(struct hdac_softc *sc) return; hdac_lock(sc); + sc->polling = 0; + sc->poll_ival = 0; + callout_stop(&sc->poll_hdac); + callout_stop(&sc->poll_jack); hdac_reset(sc); hdac_unlock(sc); - snd_mtxfree(sc->lock); +#if 0 /* TODO: No callout_drain() in DragonFly. */ + callout_drain(&sc->poll_hdac); + callout_drain(&sc->poll_jack); +#else + callout_stop(&sc->poll_hdac); + callout_stop(&sc->poll_jack); +#endif + + hdac_irq_free(sc); device_get_children(sc->dev, &devlist, &devcount); for (i = 0; devlist != NULL && i < devcount; i++) { @@ -4511,16 +5680,20 @@ hdac_release_resources(struct hdac_softc *sc) sc->codecs[i] = NULL; } - hdac_dma_free(&sc->rirb_dma); - hdac_dma_free(&sc->corb_dma); + hdac_dma_free(sc, &sc->pos_dma); + hdac_dma_free(sc, &sc->rirb_dma); + hdac_dma_free(sc, &sc->corb_dma); if (sc->play.blkcnt > 0) - hdac_dma_free(&sc->play.bdl_dma); + hdac_dma_free(sc, &sc->play.bdl_dma); if (sc->rec.blkcnt > 0) - hdac_dma_free(&sc->rec.bdl_dma); - hdac_irq_free(sc); + hdac_dma_free(sc, &sc->rec.bdl_dma); + if (sc->chan_dmat != NULL) { + bus_dma_tag_destroy(sc->chan_dmat); + sc->chan_dmat = NULL; + } hdac_mem_free(sc); + snd_mtxfree(sc->lock); kfree(sc, M_DEVBUF); - } /* This function surely going to make its way into upper level someday. */ @@ -4583,6 +5756,205 @@ hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off) } } +#ifdef SND_DYNSYSCTL +static int +sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) +{ + struct hdac_softc *sc; + struct hdac_devinfo *devinfo; + device_t dev; + uint32_t ctl; + int err, val; + + dev = oidp->oid_arg1; + devinfo = pcm_getdevinfo(dev); + if (devinfo == NULL || devinfo->codec == NULL || + devinfo->codec->sc == NULL) + return (EINVAL); + sc = devinfo->codec->sc; + hdac_lock(sc); + val = sc->polling; + hdac_unlock(sc); + err = sysctl_handle_int(oidp, &val, 0, req); + + if (err != 0 || req->newptr == NULL) + return (err); + if (val < 0 || val > 1) + return (EINVAL); + + hdac_lock(sc); + if (val != sc->polling) { + if (hda_chan_active(sc) != 0) + err = EBUSY; + else if (val == 0) { + callout_stop(&sc->poll_hdac); + hdac_unlock(sc); +#if 0 /* TODO: No callout_drain() in DragonFly. */ + callout_drain(&sc->poll_hdac); +#else + callout_stop(&sc->poll_hdac); +#endif + hdac_lock(sc); + HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, + sc->rirb_size / 2); + ctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL); + ctl |= HDAC_RIRBCTL_RINTCTL; + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, ctl); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, + HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); + sc->polling = 0; + DELAY(1000); + } else { + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, 0); + HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, 0); + ctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL); + ctl &= ~HDAC_RIRBCTL_RINTCTL; + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, ctl); + callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, + sc); + sc->polling = 1; + DELAY(1000); + } + } + hdac_unlock(sc); + + return (err); +} + +static int +sysctl_hdac_polling_interval(SYSCTL_HANDLER_ARGS) +{ + struct hdac_softc *sc; + struct hdac_devinfo *devinfo; + device_t dev; + int err, val; + + dev = oidp->oid_arg1; + devinfo = pcm_getdevinfo(dev); + if (devinfo == NULL || devinfo->codec == NULL || + devinfo->codec->sc == NULL) + return (EINVAL); + sc = devinfo->codec->sc; + hdac_lock(sc); + val = ((uint64_t)sc->poll_ival * 1000) / hz; + hdac_unlock(sc); + err = sysctl_handle_int(oidp, &val, 0, req); + + if (err != 0 || req->newptr == NULL) + return (err); + + if (val < 1) + val = 1; + if (val > 5000) + val = 5000; + val = ((uint64_t)val * hz) / 1000; + if (val < 1) + val = 1; + if (val > (hz * 5)) + val = hz * 5; + + hdac_lock(sc); + sc->poll_ival = val; + hdac_unlock(sc); + + return (err); +} + +#ifdef SND_DEBUG +static int +sysctl_hdac_dump(SYSCTL_HANDLER_ARGS) +{ + struct hdac_softc *sc; + struct hdac_devinfo *devinfo; + struct hdac_widget *w; + device_t dev; + uint32_t res, execres; + int i, err, val; + nid_t cad; + + dev = oidp->oid_arg1; + devinfo = pcm_getdevinfo(dev); + if (devinfo == NULL || devinfo->codec == NULL || + devinfo->codec->sc == NULL) + return (EINVAL); + val = 0; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err != 0 || req->newptr == NULL || val == 0) + return (err); + sc = devinfo->codec->sc; + cad = devinfo->codec->cad; + hdac_lock(sc); + device_printf(dev, "HDAC Dump AFG [nid=%d]:\n", devinfo->nid); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + execres = hdac_command(sc, HDA_CMD_SET_PIN_SENSE(cad, w->nid, 0), + cad); + res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad); + device_printf(dev, "nid=%-3d exec=0x%08x sense=0x%08x [%s]\n", + w->nid, execres, res, + (w->enable == 0) ? "DISABLED" : "ENABLED"); + } + device_printf(dev, + "NumGPIO=%d NumGPO=%d NumGPI=%d GPIWake=%d GPIUnsol=%d\n", + HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), + HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); + if (1 || HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio) > 0) { + device_printf(dev, " GPI:"); + res = hdac_command(sc, + HDA_CMD_GET_GPI_DATA(cad, devinfo->nid), cad); + kprintf(" data=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(cad, devinfo->nid), + cad); + kprintf(" wake=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), + cad); + kprintf(" unsol=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPI_STICKY_MASK(cad, devinfo->nid), cad); + kprintf(" sticky=0x%08x\n", res); + } + if (1 || HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio) > 0) { + device_printf(dev, " GPO:"); + res = hdac_command(sc, + HDA_CMD_GET_GPO_DATA(cad, devinfo->nid), cad); + kprintf(" data=0x%08x\n", res); + } + if (1 || HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio) > 0) { + device_printf(dev, "GPI0:"); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_DATA(cad, devinfo->nid), cad); + kprintf(" data=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_ENABLE_MASK(cad, devinfo->nid), cad); + kprintf(" enable=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_DIRECTION(cad, devinfo->nid), cad); + kprintf(" direction=0x%08x\n", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(cad, devinfo->nid), cad); + device_printf(dev, " wake=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), + cad); + kprintf(" unsol=0x%08x", res); + res = hdac_command(sc, + HDA_CMD_GET_GPIO_STICKY_MASK(cad, devinfo->nid), cad); + kprintf(" sticky=0x%08x\n", res); + } + hdac_unlock(sc); + return (0); +} +#endif +#endif + static void hdac_attach2(void *arg) { @@ -4628,7 +6000,9 @@ hdac_attach2(void *arg) device_printf(sc->dev, "HDA_DEBUG: Enabling controller interrupt...\n"); ); - HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); + if (sc->polling == 0) + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, + HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) | HDAC_GCTL_UNSOL); @@ -4697,10 +6071,11 @@ hdac_attach2(void *arg) while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { if (ctl->widget == NULL) continue; - w = ctl->widget; - if (w->enable == 0) + if (ctl->ossmask & SOUND_MASK_DISABLE) ctl->enable = 0; - if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) + w = ctl->widget; + if (w->enable == 0 || + HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) ctl->enable = 0; w = ctl->childwidget; if (w == NULL) @@ -4715,6 +6090,11 @@ hdac_attach2(void *arg) ); hdac_audio_build_tree(devinfo); + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->ossmask & (SOUND_MASK_SKIP | SOUND_MASK_DISABLE)) + ctl->ossmask = 0; + } HDA_BOOTVERBOSE( device_printf(sc->dev, "HDA_DEBUG: AFG commit...\n"); ); @@ -4760,29 +6140,57 @@ hdac_attach2(void *arg) sc->registered++; + if ((devinfo->function.audio.quirks & HDA_QUIRK_DMAPOS) && + hdac_dma_alloc(sc, &sc->pos_dma, + (sc->num_iss + sc->num_oss + sc->num_bss) * 8) != 0) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "Failed to allocate DMA pos buffer (non-fatal)\n"); + ); + } + for (i = 0; i < pcnt; i++) pcm_addchan(sc->dev, PCMDIR_PLAY, &hdac_channel_class, devinfo); for (i = 0; i < rcnt; i++) pcm_addchan(sc->dev, PCMDIR_REC, &hdac_channel_class, devinfo); +#ifdef SND_DYNSYSCTL + SYSCTL_ADD_PROC(snd_sysctl_tree(sc->dev), + SYSCTL_CHILDREN(snd_sysctl_tree_top(sc->dev)), OID_AUTO, + "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), + sysctl_hdac_polling, "I", "Enable polling mode"); + SYSCTL_ADD_PROC(snd_sysctl_tree(sc->dev), + SYSCTL_CHILDREN(snd_sysctl_tree_top(sc->dev)), OID_AUTO, + "polling_interval", CTLTYPE_INT | CTLFLAG_RW, sc->dev, + sizeof(sc->dev), sysctl_hdac_polling_interval, "I", + "Controller/Jack Sense polling interval (1-1000 ms)"); +#ifdef SND_DEBUG + SYSCTL_ADD_PROC(snd_sysctl_tree(sc->dev), + SYSCTL_CHILDREN(snd_sysctl_tree_top(sc->dev)), OID_AUTO, + "dump", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), + sysctl_hdac_dump, "I", "Dump states"); +#endif +#endif + ksnprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s [%s]", - rman_get_start(sc->mem.mem_res), - rman_get_start(sc->irq.irq_res), - "snd_hda", HDA_DRV_TEST_REV); + rman_get_start(sc->mem.mem_res), rman_get_start(sc->irq.irq_res), + PCM_KLDSTRING(snd_hda), HDA_DRV_TEST_REV); pcm_setstatus(sc->dev, status); device_printf(sc->dev, "\n", hdac_codec_name(devinfo)); HDA_BOOTVERBOSE( device_printf(sc->dev, "\n", hdac_codec_id(devinfo)); ); - device_printf(sc->dev, "\n", HDA_DRV_TEST_REV); + device_printf(sc->dev, "\n", + HDA_DRV_TEST_REV); HDA_BOOTVERBOSE( if (devinfo->function.audio.quirks != 0) { device_printf(sc->dev, "\n"); device_printf(sc->dev, "HDA config/quirks:"); for (i = 0; i < HDAC_QUIRKS_TAB_LEN; i++) { - if (devinfo->function.audio.quirks & + if ((devinfo->function.audio.quirks & + hdac_quirks_tab[i].value) == hdac_quirks_tab[i].value) kprintf(" %s", hdac_quirks_tab[i].key); } @@ -4830,6 +6238,12 @@ hdac_attach2(void *arg) device_printf(sc->dev, "+--------------------------------------+\n"); hdac_dump_pcmchannels(sc, pcnt, rcnt); ); + + if (sc->polling != 0) { + hdac_lock(sc); + callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); + hdac_unlock(sc); + } } /**************************************************************************** diff --git a/sys/dev/sound/pci/hda/hdac_private.h b/sys/dev/sound/pci/hda/hdac_private.h index 79cb9b7323..641749d192 100644 --- a/sys/dev/sound/pci/hda/hdac_private.h +++ b/sys/dev/sound/pci/hda/hdac_private.h @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/hda/hdac_private.h,v 1.2 2006/10/06 18:59:27 ariff Exp $ - * $DragonFly: src/sys/dev/sound/pci/hda/hdac_private.h,v 1.2 2007/01/20 21:32:36 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/hda/hdac_private.h,v 1.6.2.1 2007/05/13 21:09:24 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/hda/hdac_private.h,v 1.3 2007/06/16 19:48:05 hasso Exp $ */ #ifndef _HDAC_PRIVATE_H_ @@ -32,7 +32,7 @@ /**************************************************************************** - * Miscellanious defines + * Miscellaneous defines ****************************************************************************/ #define HDAC_DMA_ALIGNMENT 128 #define HDAC_CODEC_MAX 16 @@ -84,6 +84,11 @@ #define HDAC_BSDBDPU(sc, n) (_HDAC_BSDBDPU((n), (sc)->num_iss, (sc)->num_oss)) +/**************************************************************************** + * Custom hdac malloc type + ****************************************************************************/ +MALLOC_DECLARE(M_HDAC); + /**************************************************************************** * struct hdac_mem * @@ -119,6 +124,7 @@ struct hdac_dma { bus_dma_tag_t dma_tag; bus_dmamap_t dma_map; bus_addr_t dma_paddr; + bus_size_t dma_size; caddr_t dma_vaddr; }; @@ -246,6 +252,7 @@ struct hdac_devinfo { struct hdac_audio_ctl *ctl; uint32_t mvol; uint32_t quirks; + uint32_t gpio; int ossidx; int playcnt, reccnt; int parsing_strategy; @@ -262,7 +269,9 @@ struct hdac_chan { struct hdac_dma bdl_dma; uint32_t spd, fmt, fmtlist[8], pcmrates[16]; uint32_t supp_stream_formats, supp_pcm_size_rate; - int ptr, prevptr, blkcnt, blksz; + uint32_t ptr, prevptr, blkcnt, blksz; + uint32_t *dmapos; + int active; int dir; int off; int sid; @@ -286,6 +295,7 @@ struct hdac_softc { struct hdac_irq irq; uint32_t pci_subvendor; + int nocache; int num_iss; int num_oss; @@ -301,11 +311,23 @@ struct hdac_softc { struct hdac_dma rirb_dma; int rirb_rp; + struct hdac_dma pos_dma; + struct hdac_chan play, rec; bus_dma_tag_t chan_dmat; int chan_size; int chan_blkcnt; + /* + * Polling + */ + int polling; + int poll_ticks; + int poll_ival; + struct callout poll_hda; + struct callout poll_hdac; + struct callout poll_jack; + #define HDAC_UNSOLQ_MAX 64 #define HDAC_UNSOLQ_READY 0 #define HDAC_UNSOLQ_BUSY 1 diff --git a/sys/dev/sound/pci/ich.c b/sys/dev/sound/pci/ich.c index 8a609ead53..9f6c248df4 100644 --- a/sys/dev/sound/pci/ich.c +++ b/sys/dev/sound/pci/ich.c @@ -24,8 +24,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/ich.c,v 1.53.2.9 2006/12/24 05:44:10 ariff Exp $ - * $DragonFly: src/sys/dev/sound/pci/ich.c,v 1.14 2007/01/08 01:38:02 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/ich.c,v 1.53.2.11 2007/05/28 21:07:41 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/ich.c,v 1.15 2007/06/16 19:48:05 hasso Exp $ */ #include @@ -35,14 +35,19 @@ #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/ich.c,v 1.14 2007/01/08 01:38:02 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/ich.c,v 1.15 2007/06/16 19:48:05 hasso Exp $"); /* -------------------------------------------------------------------- */ -#define ICH_TIMEOUT 1000 /* semaphore timeout polling count */ -#define ICH_DTBL_LENGTH 32 -#define ICH_DEFAULT_BUFSZ 16384 -#define ICH_MAX_BUFSZ 65536 +#define ICH_TIMEOUT 1000 /* semaphore timeout polling count */ +#define ICH_DTBL_LENGTH 32 +#define ICH_DEFAULT_BUFSZ 16384 +#define ICH_MAX_BUFSZ 65536 +#define ICH_MIN_BUFSZ 4096 +#define ICH_DEFAULT_BLKCNT 2 +#define ICH_MAX_BLKCNT 32 +#define ICH_MIN_BLKCNT 2 +#define ICH_MIN_BLKSZ 64 #define INTEL_VENDORID 0x8086 #define SIS_VENDORID 0x1039 @@ -75,6 +80,34 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/ich.c,v 1.14 2007/01/08 01:3 #define ICH_UNLOCK(sc) snd_mtxunlock((sc)->ich_lock) #define ICH_LOCK_ASSERT(sc) snd_mtxassert((sc)->ich_lock) +#if 0 +#define ICH_DEBUG(stmt) do { \ + stmt \ +} while(0) +#else +#define ICH_DEBUG(...) +#endif + +#define ICH_CALIBRATE_DONE (1 << 0) +#define ICH_IGNORE_PCR (1 << 1) +#define ICH_IGNORE_RESET (1 << 2) +#define ICH_FIXED_RATE (1 << 3) +#define ICH_DMA_NOCACHE (1 << 4) +#define ICH_HIGH_LATENCY (1 << 5) + +#if 0 /* TODO: No uncacheable DMA support in DragonFly. */ +#include +#define ICH_DMA_ATTR(sc, v, s, attr) do { \ + vm_offset_t va = (vm_offset_t)(v); \ + vm_size_t sz = (vm_size_t)(s); \ + if ((sc) != NULL && ((sc)->flags & ICH_DMA_NOCACHE) && \ + va != 0 && sz != 0) \ + (void)pmap_change_attr(va, sz, (attr)); \ +} while(0) +#else +#define ICH_DMA_ATTR(...) +#endif + static const struct ich_type { uint16_t vendor; uint16_t devid; @@ -128,19 +161,19 @@ static const struct ich_type { /* buffer descriptor */ struct ich_desc { - volatile u_int32_t buffer; - volatile u_int32_t length; + volatile uint32_t buffer; + volatile uint32_t length; }; struct sc_info; /* channel registers */ struct sc_chinfo { - u_int32_t num:8, run:1, run_save:1; - u_int32_t blksz, blkcnt, spd; - u_int32_t regbase, spdreg; - u_int32_t imask; - u_int32_t civ; + uint32_t num:8, run:1, run_save:1; + uint32_t blksz, blkcnt, spd; + uint32_t regbase, spdreg; + uint32_t imask; + uint32_t civ; struct snd_dbuf *buffer; struct pcm_channel *channel; @@ -154,14 +187,14 @@ struct sc_chinfo { struct sc_info { device_t dev; int hasvra, hasvrm, hasmic; - unsigned int chnum, bufsz; + unsigned int chnum, bufsz, blkcnt; int sample_size, swap_reg; struct resource *nambar, *nabmbar, *irq; int regtype, nambarid, nabmbarid, irqid; bus_space_tag_t nambart, nabmbart; bus_space_handle_t nambarh, nabmbarh; - bus_dma_tag_t dmat; + bus_dma_tag_t dmat, chan_dmat; bus_dmamap_t dtmap; void *ih; @@ -169,19 +202,18 @@ struct sc_info { struct sc_chinfo ch[3]; int ac97rate; struct ich_desc *dtbl; + unsigned int dtbl_size; bus_addr_t desc_addr; struct intr_config_hook intrhook; - int use_intrhook; uint16_t vendor; uint16_t devid; uint32_t flags; -#define IGNORE_PCR 0x01 struct spinlock *ich_lock; }; /* -------------------------------------------------------------------- */ -static u_int32_t ich_fmt[] = { +static uint32_t ich_fmt[] = { AFMT_STEREO | AFMT_S16_LE, 0 }; @@ -190,23 +222,23 @@ static struct pcmchan_caps ich_caps = {48000, 48000, ich_fmt, 0}; /* -------------------------------------------------------------------- */ /* Hardware */ -static __inline u_int32_t +static __inline uint32_t ich_rd(struct sc_info *sc, int regno, int size) { switch (size) { case 1: - return bus_space_read_1(sc->nabmbart, sc->nabmbarh, regno); + return (bus_space_read_1(sc->nabmbart, sc->nabmbarh, regno)); case 2: - return bus_space_read_2(sc->nabmbart, sc->nabmbarh, regno); + return (bus_space_read_2(sc->nabmbart, sc->nabmbarh, regno)); case 4: - return bus_space_read_4(sc->nabmbart, sc->nabmbarh, regno); + return (bus_space_read_4(sc->nabmbart, sc->nabmbarh, regno)); default: - return 0xffffffff; + return (0xffffffff); } } static __inline void -ich_wr(struct sc_info *sc, int regno, u_int32_t data, int size) +ich_wr(struct sc_info *sc, int regno, uint32_t data, int size) { switch (size) { case 1: @@ -225,20 +257,20 @@ ich_wr(struct sc_info *sc, int regno, u_int32_t data, int size) static int ich_waitcd(void *devinfo) { - int i; - u_int32_t data; struct sc_info *sc = (struct sc_info *)devinfo; + uint32_t data; + int i; for (i = 0; i < ICH_TIMEOUT; i++) { data = ich_rd(sc, ICH_REG_ACC_SEMA, 1); if ((data & 0x01) == 0) - return 0; + return (0); DELAY(1); } - if ((sc->flags & IGNORE_PCR) != 0) + if ((sc->flags & ICH_IGNORE_PCR) != 0) return (0); device_printf(sc->dev, "CODEC semaphore timeout\n"); - return ETIMEDOUT; + return (ETIMEDOUT); } static int @@ -249,11 +281,11 @@ ich_rdcd(kobj_t obj, void *devinfo, int regno) regno &= 0xff; ich_waitcd(sc); - return bus_space_read_2(sc->nambart, sc->nambarh, regno); + return (bus_space_read_2(sc->nambart, sc->nambarh, regno)); } static int -ich_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data) +ich_wrcd(kobj_t obj, void *devinfo, int regno, uint16_t data) { struct sc_info *sc = (struct sc_info *)devinfo; @@ -261,7 +293,7 @@ ich_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data) ich_waitcd(sc); bus_space_write_2(sc->nambart, sc->nambarh, regno, data); - return 0; + return (0); } static kobj_method_t ich_ac97_methods[] = { @@ -278,13 +310,17 @@ static void ich_filldtbl(struct sc_chinfo *ch) { struct sc_info *sc = ch->parent; - u_int32_t base; + uint32_t base; int i; base = sndbuf_getbufaddr(ch->buffer); - if (ch->blksz > sc->bufsz / ch->blkcnt) - ch->blksz = sc->bufsz / ch->blkcnt; - sndbuf_resize(ch->buffer, ch->blkcnt, ch->blksz); + if ((ch->blksz * ch->blkcnt) > sndbuf_getmaxsize(ch->buffer)) + ch->blksz = sndbuf_getmaxsize(ch->buffer) / ch->blkcnt; + if ((sndbuf_getblksz(ch->buffer) != ch->blksz || + sndbuf_getblkcnt(ch->buffer) != ch->blkcnt) && + sndbuf_resize(ch->buffer, ch->blkcnt, ch->blksz) != 0) + device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n", + __func__, ch->blksz, ch->blkcnt); ch->blksz = sndbuf_getblksz(ch->buffer); for (i = 0; i < ICH_DTBL_LENGTH; i++) { @@ -306,7 +342,7 @@ ich_resetchan(struct sc_info *sc, int num) else if (num == 2) regbase = ICH_REG_MC_BASE; else - return ENXIO; + return (ENXIO); ich_wr(sc, regbase + ICH_REG_X_CR, 0, 1); #if 1 @@ -319,11 +355,22 @@ ich_resetchan(struct sc_info *sc, int num) for (i = 0; i < ICH_TIMEOUT; i++) { cr = ich_rd(sc, regbase + ICH_REG_X_CR, 1); if (cr == 0) - return 0; + return (0); + DELAY(1); } + if (sc->flags & ICH_IGNORE_RESET) + return (0); +#if 0 + else if (sc->vendor == NVIDIA_VENDORID) { + sc->flags |= ICH_IGNORE_RESET; + device_printf(sc->dev, "ignoring reset failure!\n"); + return (0); + } +#endif + device_printf(sc->dev, "cannot reset channel %d\n", num); - return ENXIO; + return (ENXIO); } /* -------------------------------------------------------------------- */ @@ -345,60 +392,99 @@ ichchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel * ch->parent = sc; ch->run = 0; ch->dtbl = sc->dtbl + (ch->num * ICH_DTBL_LENGTH); - ch->desc_addr = sc->desc_addr + (ch->num * ICH_DTBL_LENGTH) * - sizeof(struct ich_desc); - ch->blkcnt = 2; + ch->desc_addr = sc->desc_addr + + (ch->num * ICH_DTBL_LENGTH * sizeof(struct ich_desc)); + ch->blkcnt = sc->blkcnt; ch->blksz = sc->bufsz / ch->blkcnt; switch(ch->num) { case 0: /* play */ KASSERT(dir == PCMDIR_PLAY, ("wrong direction")); ch->regbase = ICH_REG_PO_BASE; - ch->spdreg = sc->hasvra? AC97_REGEXT_FDACRATE : 0; + ch->spdreg = (sc->hasvra) ? AC97_REGEXT_FDACRATE : 0; ch->imask = ICH_GLOB_STA_POINT; break; case 1: /* record */ KASSERT(dir == PCMDIR_REC, ("wrong direction")); ch->regbase = ICH_REG_PI_BASE; - ch->spdreg = sc->hasvra? AC97_REGEXT_LADCRATE : 0; + ch->spdreg = (sc->hasvra) ? AC97_REGEXT_LADCRATE : 0; ch->imask = ICH_GLOB_STA_PIINT; break; case 2: /* mic */ KASSERT(dir == PCMDIR_REC, ("wrong direction")); ch->regbase = ICH_REG_MC_BASE; - ch->spdreg = sc->hasvrm? AC97_REGEXT_MADCRATE : 0; + ch->spdreg = (sc->hasvrm) ? AC97_REGEXT_MADCRATE : 0; ch->imask = ICH_GLOB_STA_MINT; break; default: - return NULL; + return (NULL); } + if (sc->flags & ICH_FIXED_RATE) + ch->spdreg = 0; + ICH_UNLOCK(sc); - if (sndbuf_alloc(ch->buffer, sc->dmat, sc->bufsz) != 0) - return NULL; + if (sndbuf_alloc(ch->buffer, sc->chan_dmat, sc->bufsz) != 0) + return (NULL); + + ICH_DMA_ATTR(sc, sndbuf_getbuf(ch->buffer), + sndbuf_getmaxsize(ch->buffer), PAT_UNCACHEABLE); ICH_LOCK(sc); - ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4); + ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (uint32_t)(ch->desc_addr), 4); ICH_UNLOCK(sc); - return ch; + return (ch); } static int -ichchan_setformat(kobj_t obj, void *data, u_int32_t format) +ichchan_free(kobj_t obj, void *data) { - return 0; + struct sc_chinfo *ch; + struct sc_info *sc; + + ch = (struct sc_chinfo *)data; + sc = (ch != NULL) ? ch->parent : NULL; + if (ch != NULL && sc != NULL) { + ICH_DMA_ATTR(sc, sndbuf_getbuf(ch->buffer), + sndbuf_getmaxsize(ch->buffer), PAT_WRITE_BACK); + } + + return (1); } static int -ichchan_setspeed(kobj_t obj, void *data, u_int32_t speed) +ichchan_setformat(kobj_t obj, void *data, uint32_t format) +{ + + ICH_DEBUG( + struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + if (!(sc->flags & ICH_CALIBRATE_DONE)) + device_printf(sc->dev, + "WARNING: %s() called before calibration!\n", + __func__); + ); + + return (0); +} + +static int +ichchan_setspeed(kobj_t obj, void *data, uint32_t speed) { struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; + ICH_DEBUG( + if (!(sc->flags & ICH_CALIBRATE_DONE)) + device_printf(sc->dev, + "WARNING: %s() called before calibration!\n", + __func__); + ); + if (ch->spdreg) { int r, ac97rate; @@ -409,30 +495,43 @@ ichchan_setspeed(kobj_t obj, void *data, u_int32_t speed) ICH_UNLOCK(sc); r = (speed * 48000) / ac97rate; /* - * Cast the return value of ac97_setrate() to u_int so that + * Cast the return value of ac97_setrate() to uint64 so that * the math don't overflow into the negative range. */ - ch->spd = ((u_int)ac97_setrate(sc->codec, ch->spdreg, r) * + ch->spd = ((uint64_t)ac97_setrate(sc->codec, ch->spdreg, r) * ac97rate) / 48000; } else { ch->spd = 48000; } - return ch->spd; + return (ch->spd); } static int -ichchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +ichchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) { struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; + ICH_DEBUG( + if (!(sc->flags & ICH_CALIBRATE_DONE)) + device_printf(sc->dev, + "WARNING: %s() called before calibration!\n", + __func__); + ); + + if (sc->flags & ICH_HIGH_LATENCY) + blocksize = sndbuf_getmaxsize(ch->buffer) / ch->blkcnt; + + if (blocksize < ICH_MIN_BLKSZ) + blocksize = ICH_MIN_BLKSZ; + blocksize &= ~(ICH_MIN_BLKSZ - 1); ch->blksz = blocksize; ich_filldtbl(ch); ICH_LOCK(sc); ich_wr(sc, ch->regbase + ICH_REG_X_LVI, ch->blkcnt - 1, 1); ICH_UNLOCK(sc); - return ch->blksz; + return (ch->blksz); } static int @@ -441,11 +540,18 @@ ichchan_trigger(kobj_t obj, void *data, int go) struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; + ICH_DEBUG( + if (!(sc->flags & ICH_CALIBRATE_DONE)) + device_printf(sc->dev, + "WARNING: %s() called before calibration!\n", + __func__); + ); + switch (go) { case PCMTRIG_START: ch->run = 1; ICH_LOCK(sc); - ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4); + ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (uint32_t)(ch->desc_addr), 4); ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RPBM | ICH_X_CR_LVBIE | ICH_X_CR_IOCE, 1); ICH_UNLOCK(sc); break; @@ -457,7 +563,7 @@ ichchan_trigger(kobj_t obj, void *data, int go) ch->run = 0; break; } - return 0; + return (0); } static int @@ -465,7 +571,14 @@ ichchan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; - u_int32_t pos; + uint32_t pos; + + ICH_DEBUG( + if (!(sc->flags & ICH_CALIBRATE_DONE)) + device_printf(sc->dev, + "WARNING: %s() called before calibration!\n", + __func__); + ); ICH_LOCK(sc); ch->civ = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1) % ch->blkcnt; @@ -473,7 +586,7 @@ ichchan_getptr(kobj_t obj, void *data) pos = ch->civ * ch->blksz; - return pos; + return (pos); } static struct pcmchan_caps * @@ -481,11 +594,21 @@ ichchan_getcaps(kobj_t obj, void *data) { struct sc_chinfo *ch = data; - return ch->spdreg? &ich_vrcaps : &ich_caps; + ICH_DEBUG( + struct sc_info *sc = ch->parent; + + if (!(sc->flags & ICH_CALIBRATE_DONE)) + device_printf(ch->parent->dev, + "WARNING: %s() called before calibration!\n", + __func__); + ); + + return ((ch->spdreg) ? &ich_vrcaps : &ich_caps); } static kobj_method_t ichchan_methods[] = { KOBJMETHOD(channel_init, ichchan_init), + KOBJMETHOD(channel_free, ichchan_free), KOBJMETHOD(channel_setformat, ichchan_setformat), KOBJMETHOD(channel_setspeed, ichchan_setspeed), KOBJMETHOD(channel_setblocksize, ichchan_setblocksize), @@ -504,10 +627,18 @@ ich_intr(void *p) { struct sc_info *sc = (struct sc_info *)p; struct sc_chinfo *ch; - u_int32_t cbi, lbi, lvi, st, gs; + uint32_t cbi, lbi, lvi, st, gs; int i; ICH_LOCK(sc); + + ICH_DEBUG( + if (!(sc->flags & ICH_CALIBRATE_DONE)) + device_printf(sc->dev, + "WARNING: %s() called before calibration!\n", + __func__); + ); + gs = ich_rd(sc, ICH_REG_GLOB_STA, 4) & ICH_GLOB_STA_IMASK; if (gs & (ICH_GLOB_STA_PRES | ICH_GLOB_STA_SRES)) { /* Clear resume interrupt(s) - nothing doing with them */ @@ -521,7 +652,7 @@ ich_intr(void *p) continue; gs &= ~ch->imask; st = ich_rd(sc, ch->regbase + - (sc->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR), + ((sc->swap_reg) ? ICH_REG_X_PICB : ICH_REG_X_SR), 2); st &= ICH_X_SR_FIFOE | ICH_X_SR_BCIS | ICH_X_SR_LVBCI; if (st & (ICH_X_SR_BCIS | ICH_X_SR_LVBCI)) { @@ -548,7 +679,7 @@ ich_intr(void *p) } /* clear status bit */ ich_wr(sc, ch->regbase + - (sc->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR), + ((sc->swap_reg) ? ICH_REG_X_PICB : ICH_REG_X_SR), st, 2); } ICH_UNLOCK(sc); @@ -573,7 +704,24 @@ ich_initsys(struct sc_info* sc) &sc->ac97rate, 48000, "AC97 link rate (default = 48000)"); #endif /* SND_DYNSYSCTL */ - return 0; + return (0); +} + +static void +ich_setstatus(struct sc_info *sc) +{ + char status[SND_STATUSLEN]; + + ksnprintf(status, SND_STATUSLEN, + "at io 0x%lx, 0x%lx irq %ld bufsz %u %s", + rman_get_start(sc->nambar), rman_get_start(sc->nabmbar), + rman_get_start(sc->irq), sc->bufsz,PCM_KLDSTRING(snd_ich)); + + if (bootverbose && (sc->flags & ICH_DMA_NOCACHE)) + device_printf(sc->dev, + "PCI Master abort workaround enabled\n"); + + pcm_setstatus(sc->dev, status); } /* -------------------------------------------------------------------- */ @@ -581,20 +729,23 @@ ich_initsys(struct sc_info* sc) * function of the ac97 codec initialization code (to be investigated). */ -static -void ich_calibrate(void *arg) +static void +ich_calibrate(void *arg) { struct sc_info *sc; struct sc_chinfo *ch; struct timeval t1, t2; - u_int8_t ociv, nciv; - u_int32_t wait_us, actual_48k_rate, bytes; + uint8_t ociv, nciv; + uint32_t wait_us, actual_48k_rate, oblkcnt; sc = (struct sc_info *)arg; + ICH_LOCK(sc); ch = &sc->ch[1]; - if (sc->use_intrhook) + if (sc->intrhook.ich_func != NULL) { config_intrhook_disestablish(&sc->intrhook); + sc->intrhook.ich_func = NULL; + } /* * Grab audio from input for fixed interval and compare how @@ -605,8 +756,13 @@ void ich_calibrate(void *arg) KASSERT(ch->regbase == ICH_REG_PI_BASE, ("wrong direction")); - bytes = sndbuf_getsize(ch->buffer) / 2; - ichchan_setblocksize(0, ch, bytes); + oblkcnt = ch->blkcnt; + ch->blkcnt = 2; + sc->flags |= ICH_CALIBRATE_DONE; + ICH_UNLOCK(sc); + ichchan_setblocksize(0, ch, sndbuf_getmaxsize(ch->buffer) >> 1); + ICH_LOCK(sc); + sc->flags &= ~ICH_CALIBRATE_DONE; /* * our data format is stereo, 16 bit so each sample is 4 bytes. @@ -623,20 +779,19 @@ void ich_calibrate(void *arg) /* prepare */ ociv = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1); nciv = ociv; - ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4); + ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (uint32_t)(ch->desc_addr), 4); /* start */ microtime(&t1); ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RPBM, 1); /* wait */ - while (nciv == ociv) { + do { microtime(&t2); if (t2.tv_sec - t1.tv_sec > 1) break; nciv = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1); - } - microtime(&t2); + } while (nciv == ociv); /* stop */ ich_wr(sc, ch->regbase + ICH_REG_X_CR, 0, 1); @@ -644,16 +799,20 @@ void ich_calibrate(void *arg) /* reset */ DELAY(100); ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RR, 1); + ch->blkcnt = oblkcnt; /* turn time delta into us */ wait_us = ((t2.tv_sec - t1.tv_sec) * 1000000) + t2.tv_usec - t1.tv_usec; if (nciv == ociv) { device_printf(sc->dev, "ac97 link rate calibration timed out after %d us\n", wait_us); + sc->flags |= ICH_CALIBRATE_DONE; + ICH_UNLOCK(sc); + ich_setstatus(sc); return; } - actual_48k_rate = (bytes * 250000) / wait_us; + actual_48k_rate = ((uint64_t)ch->blksz * 250000) / wait_us; if (actual_48k_rate < 47500 || actual_48k_rate > 48500) { sc->ac97rate = actual_48k_rate; @@ -667,6 +826,10 @@ void ich_calibrate(void *arg) kprintf(", will use %d Hz", sc->ac97rate); kprintf("\n"); } + sc->flags |= ICH_CALIBRATE_DONE; + ICH_UNLOCK(sc); + + ich_setstatus(sc); return; } @@ -685,7 +848,7 @@ ich_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) static int ich_init(struct sc_info *sc) { - u_int32_t stat; + uint32_t stat; ich_wr(sc, ICH_REG_GLOB_CNT, ICH_GLOB_CTL_COLD, 4); DELAY(600000); @@ -697,7 +860,7 @@ ich_init(struct sc_info *sc) sc->devid == INTEL_82801DB || sc->devid == INTEL_82801EB || sc->devid == INTEL_6300ESB || sc->devid == INTEL_82801FB || sc->devid == INTEL_82801GB)) { - sc->flags |= IGNORE_PCR; + sc->flags |= ICH_IGNORE_PCR; device_printf(sc->dev, "primary codec not ready!\n"); } } @@ -709,11 +872,11 @@ ich_init(struct sc_info *sc) #endif if (ich_resetchan(sc, 0) || ich_resetchan(sc, 1)) - return ENXIO; + return (ENXIO); if (sc->hasmic && ich_resetchan(sc, 2)) - return ENXIO; + return (ENXIO); - return 0; + return (0); } static int @@ -741,17 +904,17 @@ static int ich_pci_attach(device_t dev) { uint32_t subdev; - u_int16_t extcaps; + uint16_t extcaps; uint16_t devid, vendor; struct sc_info *sc; - char status[SND_STATUSLEN]; + int i; if ((sc = kmalloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { device_printf(dev, "cannot allocate softc\n"); - return ENXIO; + return (ENXIO); } - sc->ich_lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); + sc->ich_lock = snd_mtxcreate(device_get_nameunit(dev), "snd_ich softc"); sc->dev = dev; vendor = sc->vendor = pci_get_vendor(dev); @@ -769,6 +932,15 @@ ich_pci_attach(device_t dev) sc->sample_size = 2; } + /* + * Intel 440MX Errata #36 + * - AC97 Soft Audio and Soft Modem Master Abort Errata + * + * http://www.intel.com/design/chipsets/specupdt/245051.htm + */ + if (vendor == INTEL_VENDORID && devid == INTEL_82440MX) + sc->flags |= ICH_DMA_NOCACHE; + /* * Enable bus master. On ich4/5 this may prevent the detection of * the primary codec becoming ready in ich_init(). @@ -810,18 +982,38 @@ ich_pci_attach(device_t dev) sc->nabmbart = rman_get_bustag(sc->nabmbar); sc->nabmbarh = rman_get_bushandle(sc->nabmbar); - sc->bufsz = pcm_getbuffersize(dev, 4096, ICH_DEFAULT_BUFSZ, ICH_MAX_BUFSZ); - if (bus_dma_tag_create(NULL, 8, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, - NULL, NULL, sc->bufsz, 1, 0x3ffff, 0, - &sc->dmat) != 0) { - device_printf(dev, "unable to create dma tag\n"); - goto bad; + sc->bufsz = pcm_getbuffersize(dev, + ICH_MIN_BUFSZ, ICH_DEFAULT_BUFSZ, ICH_MAX_BUFSZ); + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "blocksize", &i) == 0 && i > 0) { + sc->blkcnt = sc->bufsz / i; + i = 0; + while (sc->blkcnt >> i) + i++; + sc->blkcnt = 1 << (i - 1); + if (sc->blkcnt < ICH_MIN_BLKCNT) + sc->blkcnt = ICH_MIN_BLKCNT; + else if (sc->blkcnt > ICH_MAX_BLKCNT) + sc->blkcnt = ICH_MAX_BLKCNT; + } else + sc->blkcnt = ICH_DEFAULT_BLKCNT; + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "highlatency", &i) == 0 && i != 0) { + sc->flags |= ICH_HIGH_LATENCY; + sc->blkcnt = ICH_MIN_BLKCNT; } + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "fixedrate", &i) == 0 && i != 0) + sc->flags |= ICH_FIXED_RATE; + sc->irqid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, - RF_ACTIVE | RF_SHAREABLE); - if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, ich_intr, sc, &sc->ih)) { + RF_ACTIVE | RF_SHAREABLE); + if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, ich_intr, + sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } @@ -831,15 +1023,6 @@ ich_pci_attach(device_t dev) goto bad; } - if (bus_dmamem_alloc(sc->dmat, (void **)&sc->dtbl, - BUS_DMA_NOWAIT, &sc->dtmap)) - goto bad; - - if (bus_dmamap_load(sc->dmat, sc->dtmap, sc->dtbl, - sizeof(struct ich_desc) * ICH_DTBL_LENGTH * 3, - ich_setmap, sc, 0)) - goto bad; - sc->codec = AC97_CREATE(dev, sc, ich_ac97); if (sc->codec == NULL) goto bad; @@ -875,7 +1058,39 @@ ich_pci_attach(device_t dev) sc->hasmic = ac97_getcaps(sc->codec) & AC97_CAP_MICCHANNEL; ac97_setextmode(sc->codec, sc->hasvra | sc->hasvrm); - if (pcm_register(dev, sc, 1, sc->hasmic? 2 : 1)) + sc->dtbl_size = sizeof(struct ich_desc) * ICH_DTBL_LENGTH * + ((sc->hasmic) ? 3 : 2); + + /* BDL tag */ + if (bus_dma_tag_create(NULL, 8, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + sc->dtbl_size, 1, 0x3ffff, 0, &sc->dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto bad; + } + + /* PCM channel tag */ + if (bus_dma_tag_create(NULL, ICH_MIN_BLKSZ, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + sc->bufsz, 1, 0x3ffff, 0, &sc->chan_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto bad; + } +#if 0 /* TODO: No uncacheable DMA support in DragonFly. */ + if (bus_dmamem_alloc(sc->dmat, (void **)&sc->dtbl, BUS_DMA_NOWAIT | + ((sc->flags & ICH_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0), + &sc->dtmap)) +#else + if (bus_dmamem_alloc(sc->dmat, (void **)&sc->dtbl, BUS_DMA_NOWAIT, + &sc->dtmap)) +#endif + goto bad; + + if (bus_dmamap_load(sc->dmat, sc->dtmap, sc->dtbl, sc->dtbl_size, + ich_setmap, sc, 0)) + goto bad; + + if (pcm_register(dev, sc, 1, (sc->hasmic) ? 2 : 1)) goto bad; pcm_addchan(dev, PCMDIR_PLAY, &ichchan_class, sc); /* play */ @@ -883,23 +1098,22 @@ ich_pci_attach(device_t dev) if (sc->hasmic) pcm_addchan(dev, PCMDIR_REC, &ichchan_class, sc); /* record mic */ - ksnprintf(status, SND_STATUSLEN, "at io 0x%lx, 0x%lx irq %ld bufsz %u %s", - rman_get_start(sc->nambar), rman_get_start(sc->nabmbar), rman_get_start(sc->irq), sc->bufsz,PCM_KLDSTRING(snd_ich)); - - pcm_setstatus(dev, status); - - ich_initsys(sc); - - sc->intrhook.ich_func = ich_calibrate; - sc->intrhook.ich_arg = sc; - sc->use_intrhook = 1; - if (config_intrhook_establish(&sc->intrhook) != 0) { - device_printf(dev, "Cannot establish calibration hook, will calibrate now\n"); - sc->use_intrhook = 0; - ich_calibrate(sc); + if (sc->flags & ICH_FIXED_RATE) { + sc->flags |= ICH_CALIBRATE_DONE; + ich_setstatus(sc); + } else { + ich_initsys(sc); + + sc->intrhook.ich_func = ich_calibrate; + sc->intrhook.ich_arg = sc; + if (cold == 0 || + config_intrhook_establish(&sc->intrhook) != 0) { + sc->intrhook.ich_func = NULL; + ich_calibrate(sc); + } } - return 0; + return (0); bad: if (sc->codec) @@ -916,12 +1130,16 @@ bad: sc->nabmbarid, sc->nabmbar); if (sc->dtmap) bus_dmamap_unload(sc->dmat, sc->dtmap); + if (sc->dtbl) + bus_dmamem_free(sc->dmat, sc->dtbl, sc->dtmap); + if (sc->chan_dmat) + bus_dma_tag_destroy(sc->chan_dmat); if (sc->dmat) bus_dma_tag_destroy(sc->dmat); if (sc->ich_lock) snd_mtxfree(sc->ich_lock); kfree(sc, M_DEVBUF); - return ENXIO; + return (ENXIO); } static int @@ -932,7 +1150,7 @@ ich_pci_detach(device_t dev) r = pcm_unregister(dev); if (r) - return r; + return (r); sc = pcm_getdevinfo(dev); bus_teardown_intr(dev, sc->irq, sc->ih); @@ -940,10 +1158,12 @@ ich_pci_detach(device_t dev) bus_release_resource(dev, sc->regtype, sc->nambarid, sc->nambar); bus_release_resource(dev, sc->regtype, sc->nabmbarid, sc->nabmbar); bus_dmamap_unload(sc->dmat, sc->dtmap); + bus_dmamem_free(sc->dmat, sc->dtbl, sc->dtmap); + bus_dma_tag_destroy(sc->chan_dmat); bus_dma_tag_destroy(sc->dmat); snd_mtxfree(sc->ich_lock); kfree(sc, M_DEVBUF); - return 0; + return (0); } static void @@ -985,7 +1205,7 @@ ich_pci_suspend(device_t dev) } } ICH_UNLOCK(sc); - return 0; + return (0); } static int @@ -1007,7 +1227,7 @@ ich_pci_resume(device_t dev) if (ich_init(sc) == -1) { device_printf(dev, "unable to reinitialize the card\n"); ICH_UNLOCK(sc); - return ENXIO; + return (ENXIO); } /* Reinit mixer */ ich_pci_codec_reset(sc); @@ -1015,7 +1235,7 @@ ich_pci_resume(device_t dev) ac97_setextmode(sc->codec, sc->hasvra | sc->hasvrm); if (mixer_reinit(dev) == -1) { device_printf(dev, "unable to reinitialize the mixer\n"); - return ENXIO; + return (ENXIO); } /* Re-start DMA engines */ for (i = 0 ; i < 3; i++) { @@ -1026,7 +1246,7 @@ ich_pci_resume(device_t dev) ichchan_trigger(0, ch, PCMTRIG_START); } } - return 0; + return (0); } static device_method_t ich_methods[] = { diff --git a/sys/dev/sound/pci/maestro3.c b/sys/dev/sound/pci/maestro3.c index 147018254c..24a1305320 100644 --- a/sys/dev/sound/pci/maestro3.c +++ b/sys/dev/sound/pci/maestro3.c @@ -24,8 +24,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/maestro3.c,v 1.28.2.1 2005/09/21 03:18:30 yongari Exp $ - * $DragonFly: src/sys/dev/sound/pci/maestro3.c,v 1.10 2007/01/04 21:47:02 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pci/maestro3.c,v 1.28.2.2 2007/03/12 02:03:25 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/maestro3.c,v 1.11 2007/06/16 19:48:05 hasso Exp $ */ /* @@ -64,7 +64,7 @@ #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/maestro3.c,v 1.10 2007/01/04 21:47:02 corecode Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/maestro3.c,v 1.11 2007/06/16 19:48:05 hasso Exp $"); /* -------------------------------------------------------------------- */ @@ -89,7 +89,7 @@ static struct m3_card_type { { 0, 0, 0, 0, NULL } }; -#define M3_BUFSIZE_MIN 1024 +#define M3_BUFSIZE_MIN 4096 #define M3_BUFSIZE_MAX 65536 #define M3_BUFSIZE_DEFAULT 4096 #define M3_PCHANS 4 /* create /dev/dsp0.[0-N] to use more than one */ @@ -108,6 +108,8 @@ struct sc_pchinfo { u_int32_t dac_data; u_int32_t dac_idx; u_int32_t active; + u_int32_t ptr; + u_int32_t prevptr; }; struct sc_rchinfo { @@ -120,6 +122,8 @@ struct sc_rchinfo { u_int32_t adc_data; u_int32_t adc_idx; u_int32_t active; + u_int32_t ptr; + u_int32_t prevptr; }; struct sc_info { @@ -165,7 +169,8 @@ static int m3_pchan_setspeed(kobj_t, void *, u_int32_t); static int m3_pchan_setblocksize(kobj_t, void *, u_int32_t); static int m3_pchan_trigger(kobj_t, void *, int); static int m3_pchan_trigger_locked(kobj_t, void *, int); -static int m3_pchan_getptr(kobj_t, void *); +static u_int32_t m3_pchan_getptr_internal(struct sc_pchinfo *); +static u_int32_t m3_pchan_getptr(kobj_t, void *); static struct pcmchan_caps *m3_pchan_getcaps(kobj_t, void *); /* record channel interface */ @@ -176,9 +181,12 @@ static int m3_rchan_setspeed(kobj_t, void *, u_int32_t); static int m3_rchan_setblocksize(kobj_t, void *, u_int32_t); static int m3_rchan_trigger(kobj_t, void *, int); static int m3_rchan_trigger_locked(kobj_t, void *, int); -static int m3_rchan_getptr(kobj_t, void *); +static u_int32_t m3_rchan_getptr_internal(struct sc_rchinfo *); +static u_int32_t m3_rchan_getptr(kobj_t, void *); static struct pcmchan_caps *m3_rchan_getcaps(kobj_t, void *); +static int m3_chan_active(struct sc_info *); + /* talk to the codec - called from ac97.c */ static int m3_initcd(kobj_t, void *); static int m3_rdcd(kobj_t, void *, int); @@ -558,7 +566,7 @@ m3_pchan_setblocksize(kobj_t kobj, void *chdata, u_int32_t blocksize) M3_DEBUG(CHANGE, ("m3_pchan_setblocksize(dac=%d, blocksize=%d)\n", ch->dac_idx, blocksize)); - return blocksize; + return (sndbuf_getblksz(ch->buffer)); } static int @@ -575,6 +583,22 @@ m3_pchan_trigger(kobj_t kobj, void *chdata, int go) return (ret); } +static int +m3_chan_active(struct sc_info *sc) +{ + int i, ret; + + ret = 0; + + for (i = 0; i < sc->pch_cnt; i++) + ret += sc->pch[i].active; + + for (i = 0; i < sc->rch_cnt; i++) + ret += sc->rch[i].active; + + return (ret); +} + static int m3_pchan_trigger_locked(kobj_t kobj, void *chdata, int go) { @@ -598,13 +622,17 @@ m3_pchan_trigger_locked(kobj_t kobj, void *chdata, int go) return 0; } ch->active = 1; + ch->ptr = 0; + ch->prevptr = 0; sc->pch_active_cnt++; /*[[inc_timer_users]]*/ - m3_wr_assp_data(sc, KDATA_TIMER_COUNT_RELOAD, 240); - m3_wr_assp_data(sc, KDATA_TIMER_COUNT_CURRENT, 240); - data = m3_rd_2(sc, HOST_INT_CTRL); - m3_wr_2(sc, HOST_INT_CTRL, data | CLKRUN_GEN_ENABLE); + if (m3_chan_active(sc) == 1) { + m3_wr_assp_data(sc, KDATA_TIMER_COUNT_RELOAD, 240); + m3_wr_assp_data(sc, KDATA_TIMER_COUNT_CURRENT, 240); + data = m3_rd_2(sc, HOST_INT_CTRL); + m3_wr_2(sc, HOST_INT_CTRL, data | CLKRUN_GEN_ENABLE); + } m3_wr_assp_data(sc, ch->dac_data + CDATA_INSTANCE_READY, 1); m3_wr_assp_data(sc, KDATA_MIXER_TASK_NUMBER, @@ -621,10 +649,12 @@ m3_pchan_trigger_locked(kobj_t kobj, void *chdata, int go) /* XXX should the channel be drained? */ /*[[dec_timer_users]]*/ - m3_wr_assp_data(sc, KDATA_TIMER_COUNT_RELOAD, 0); - m3_wr_assp_data(sc, KDATA_TIMER_COUNT_CURRENT, 0); - data = m3_rd_2(sc, HOST_INT_CTRL); - m3_wr_2(sc, HOST_INT_CTRL, data & ~CLKRUN_GEN_ENABLE); + if (m3_chan_active(sc) == 0) { + m3_wr_assp_data(sc, KDATA_TIMER_COUNT_RELOAD, 0); + m3_wr_assp_data(sc, KDATA_TIMER_COUNT_CURRENT, 0); + data = m3_rd_2(sc, HOST_INT_CTRL); + m3_wr_2(sc, HOST_INT_CTRL, data & ~CLKRUN_GEN_ENABLE); + } m3_wr_assp_data(sc, ch->dac_data + CDATA_INSTANCE_READY, 0); m3_wr_assp_data(sc, KDATA_MIXER_TASK_NUMBER, @@ -641,14 +671,12 @@ m3_pchan_trigger_locked(kobj_t kobj, void *chdata, int go) return 0; } -static int -m3_pchan_getptr(kobj_t kobj, void *chdata) +static u_int32_t +m3_pchan_getptr_internal(struct sc_pchinfo *ch) { - struct sc_pchinfo *ch = chdata; struct sc_info *sc = ch->parent; u_int32_t hi, lo, bus_base, bus_crnt; - M3_LOCK(sc); bus_base = sndbuf_getbufaddr(ch->buffer); hi = m3_rd_assp_data(sc, ch->dac_data + CDATA_HOST_SRC_CURRENTH); lo = m3_rd_assp_data(sc, ch->dac_data + CDATA_HOST_SRC_CURRENTL); @@ -656,11 +684,24 @@ m3_pchan_getptr(kobj_t kobj, void *chdata) M3_DEBUG(CALL, ("m3_pchan_getptr(dac=%d) result=%d\n", ch->dac_idx, bus_crnt - bus_base)); - M3_UNLOCK(sc); return (bus_crnt - bus_base); /* current byte offset of channel */ } +static u_int32_t +m3_pchan_getptr(kobj_t kobj, void *chdata) +{ + struct sc_pchinfo *ch = chdata; + struct sc_info *sc = ch->parent; + u_int32_t ptr; + + M3_LOCK(sc); + ptr = ch->ptr; + M3_UNLOCK(sc); + + return (ptr); +} + static struct pcmchan_caps * m3_pchan_getcaps(kobj_t kobj, void *chdata) { @@ -868,7 +909,7 @@ m3_rchan_setblocksize(kobj_t kobj, void *chdata, u_int32_t blocksize) M3_DEBUG(CHANGE, ("m3_rchan_setblocksize(adc=%d, blocksize=%d)\n", ch->adc_idx, blocksize)); - return blocksize; + return (sndbuf_getblksz(ch->buffer)); } static int @@ -908,12 +949,16 @@ m3_rchan_trigger_locked(kobj_t kobj, void *chdata, int go) return 0; } ch->active = 1; + ch->ptr = 0; + ch->prevptr = 0; /*[[inc_timer_users]]*/ - m3_wr_assp_data(sc, KDATA_TIMER_COUNT_RELOAD, 240); - m3_wr_assp_data(sc, KDATA_TIMER_COUNT_CURRENT, 240); - data = m3_rd_2(sc, HOST_INT_CTRL); - m3_wr_2(sc, HOST_INT_CTRL, data | CLKRUN_GEN_ENABLE); + if (m3_chan_active(sc) == 1) { + m3_wr_assp_data(sc, KDATA_TIMER_COUNT_RELOAD, 240); + m3_wr_assp_data(sc, KDATA_TIMER_COUNT_CURRENT, 240); + data = m3_rd_2(sc, HOST_INT_CTRL); + m3_wr_2(sc, HOST_INT_CTRL, data | CLKRUN_GEN_ENABLE); + } m3_wr_assp_data(sc, KDATA_ADC1_REQUEST, 1); m3_wr_assp_data(sc, ch->adc_data + CDATA_INSTANCE_READY, 1); @@ -927,10 +972,12 @@ m3_rchan_trigger_locked(kobj_t kobj, void *chdata, int go) ch->active = 0; /*[[dec_timer_users]]*/ - m3_wr_assp_data(sc, KDATA_TIMER_COUNT_RELOAD, 0); - m3_wr_assp_data(sc, KDATA_TIMER_COUNT_CURRENT, 0); - data = m3_rd_2(sc, HOST_INT_CTRL); - m3_wr_2(sc, HOST_INT_CTRL, data & ~CLKRUN_GEN_ENABLE); + if (m3_chan_active(sc) == 0) { + m3_wr_assp_data(sc, KDATA_TIMER_COUNT_RELOAD, 0); + m3_wr_assp_data(sc, KDATA_TIMER_COUNT_CURRENT, 0); + data = m3_rd_2(sc, HOST_INT_CTRL); + m3_wr_2(sc, HOST_INT_CTRL, data & ~CLKRUN_GEN_ENABLE); + } m3_wr_assp_data(sc, ch->adc_data + CDATA_INSTANCE_READY, 0); m3_wr_assp_data(sc, KDATA_ADC1_REQUEST, 0); @@ -946,14 +993,12 @@ m3_rchan_trigger_locked(kobj_t kobj, void *chdata, int go) return 0; } -static int -m3_rchan_getptr(kobj_t kobj, void *chdata) +static u_int32_t +m3_rchan_getptr_internal(struct sc_rchinfo *ch) { - struct sc_rchinfo *ch = chdata; struct sc_info *sc = ch->parent; u_int32_t hi, lo, bus_base, bus_crnt; - M3_LOCK(sc); bus_base = sndbuf_getbufaddr(ch->buffer); hi = m3_rd_assp_data(sc, ch->adc_data + CDATA_HOST_SRC_CURRENTH); lo = m3_rd_assp_data(sc, ch->adc_data + CDATA_HOST_SRC_CURRENTL); @@ -961,11 +1006,24 @@ m3_rchan_getptr(kobj_t kobj, void *chdata) M3_DEBUG(CALL, ("m3_rchan_getptr(adc=%d) result=%d\n", ch->adc_idx, bus_crnt - bus_base)); - M3_UNLOCK(sc); return (bus_crnt - bus_base); /* current byte offset of channel */ } +static u_int32_t +m3_rchan_getptr(kobj_t kobj, void *chdata) +{ + struct sc_rchinfo *ch = chdata; + struct sc_info *sc = ch->parent; + u_int32_t ptr; + + M3_LOCK(sc); + ptr = ch->ptr; + M3_UNLOCK(sc); + + return (ptr); +} + static struct pcmchan_caps * m3_rchan_getcaps(kobj_t kobj, void *chdata) { @@ -983,7 +1041,9 @@ static void m3_intr(void *p) { struct sc_info *sc = (struct sc_info *)p; - u_int32_t status, ctl, i; + struct sc_pchinfo *pch; + struct sc_rchinfo *rch; + u_int32_t status, ctl, i, delta; M3_DEBUG(INTR, ("m3_intr\n")); @@ -1027,25 +1087,44 @@ m3_intr(void *p) m3_wr_1(sc, ASSP_HOST_INT_STATUS, DSP2HOST_REQ_TIMER); /*[[ess_update_ptr]]*/ + goto m3_handle_channel_intr; } } } + goto m3_handle_channel_intr_out; + +m3_handle_channel_intr: for (i=0 ; ipch_cnt ; i++) { - if (sc->pch[i].active) { + pch = &sc->pch[i]; + if (pch->active) { + pch->ptr = m3_pchan_getptr_internal(pch); + delta = pch->bufsize + pch->ptr - pch->prevptr; + delta %= pch->bufsize; + if (delta < sndbuf_getblksz(pch->buffer)) + continue; + pch->prevptr = pch->ptr; M3_UNLOCK(sc); - chn_intr(sc->pch[i].channel); + chn_intr(pch->channel); M3_LOCK(sc); } } for (i=0 ; irch_cnt ; i++) { - if (sc->rch[i].active) { + rch = &sc->rch[i]; + if (rch->active) { + rch->ptr = m3_rchan_getptr_internal(rch); + delta = rch->bufsize + rch->ptr - rch->prevptr; + delta %= rch->bufsize; + if (delta < sndbuf_getblksz(rch->buffer)) + continue; + rch->prevptr = rch->ptr; M3_UNLOCK(sc); - chn_intr(sc->rch[i].channel); + chn_intr(rch->channel); M3_LOCK(sc); } } +m3_handle_channel_intr_out: M3_UNLOCK(sc); } @@ -1176,10 +1255,10 @@ m3_pci_attach(device_t dev) { struct sc_info *sc; struct ac97_info *codec = NULL; - u_int32_t data, i; + u_int32_t data; char status[SND_STATUSLEN]; struct m3_card_type *card; - int len; + int i, len, dacn, adcn; M3_DEBUG(CALL, ("m3_pci_attach\n")); @@ -1206,6 +1285,19 @@ m3_pci_attach(device_t dev) } } + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "dac", &i) == 0) { + if (i < 1) + dacn = 1; + else if (i > M3_PCHANS) + dacn = M3_PCHANS; + else + dacn = i; + } else + dacn = M3_PCHANS; + + adcn = M3_RCHANS; + data = pci_read_config(dev, PCIR_COMMAND, 2); data |= (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, data, 2); @@ -1239,7 +1331,7 @@ m3_pci_attach(device_t dev) goto bad; } - sc->bufsz = pcm_getbuffersize(dev, M3_BUFSIZE_MAX, M3_BUFSIZE_DEFAULT, + sc->bufsz = pcm_getbuffersize(dev, M3_BUFSIZE_MIN, M3_BUFSIZE_DEFAULT, M3_BUFSIZE_MAX); if (bus_dma_tag_create( @@ -1280,17 +1372,17 @@ m3_pci_attach(device_t dev) m3_enable_ints(sc); - if (pcm_register(dev, sc, M3_PCHANS, M3_RCHANS)) { + if (pcm_register(dev, sc, dacn, adcn)) { device_printf(dev, "pcm_register error\n"); goto bad; } - for (i=0 ; i + * Copyright (c) 2001 Katsurajima Naoto + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/sound/pci/spicds.c,v 1.5.2.2 2007/06/11 19:33:27 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/spicds.c,v 1.1 2007/06/16 19:48:05 hasso Exp $ + */ + +#include + +#include + +MALLOC_DEFINE(M_SPICDS, "spicds", "SPI codec"); + +#define SPICDS_NAMELEN 16 +struct spicds_info { + device_t dev; + spicds_ctrl ctrl; + void *devinfo; + int num; /* number of this device */ + unsigned int type; /* codec type */ + unsigned int cif; /* Controll data Interface Format (0/1) */ + unsigned int format; /* data format and master clock frequency */ + unsigned int dvc; /* De-emphasis and Volume Control */ + unsigned int left, right; + char name[SPICDS_NAMELEN]; + struct spinlock *lock; +}; + +static void +spicds_wrbit(struct spicds_info *codec, int bit) +{ + unsigned int cs, cdti; + if (codec->cif) + cs = 1; + else + cs = 0; + if (bit) + cdti = 1; + else + cdti = 0; + codec->ctrl(codec->devinfo, cs, 0, cdti); + DELAY(1); + codec->ctrl(codec->devinfo, cs, 1, cdti); + DELAY(1); + + return; +} + +static void +spicds_wrcd(struct spicds_info *codec, int reg, u_int16_t val) +{ + int mask; + +#if(0) + device_printf(codec->dev, "spicds_wrcd(codec, 0x%02x, 0x%02x)\n", reg, val); +#endif + /* start */ + if (codec->cif) + codec->ctrl(codec->devinfo, 1, 1, 0); + else + codec->ctrl(codec->devinfo, 0, 1, 0); + DELAY(1); + if (codec->type != SPICDS_TYPE_WM8770) { + if (codec->type == SPICDS_TYPE_AK4381) { + /* AK4381 chip address */ + spicds_wrbit(codec, 0); + spicds_wrbit(codec, 1); + } + else if (codec->type == SPICDS_TYPE_AK4396) + { + /* AK4396 chip address */ + spicds_wrbit(codec, 0); + spicds_wrbit(codec, 0); + } + else { + /* chip address */ + spicds_wrbit(codec, 1); + spicds_wrbit(codec, 0); + } + /* write */ + spicds_wrbit(codec, 1); + /* register address */ + for (mask = 0x10; mask != 0; mask >>= 1) + spicds_wrbit(codec, reg & mask); + /* data */ + for (mask = 0x80; mask != 0; mask >>= 1) + spicds_wrbit(codec, val & mask); + /* stop */ + DELAY(1); + } + else { + /* register address */ + for (mask = 0x40; mask != 0; mask >>= 1) + spicds_wrbit(codec, reg & mask); + /* data */ + for (mask = 0x100; mask != 0; mask >>= 1) + spicds_wrbit(codec, val & mask); + /* stop */ + DELAY(1); + } + if (codec->cif) { + codec->ctrl(codec->devinfo, 0, 1, 0); + DELAY(1); + codec->ctrl(codec->devinfo, 1, 1, 0); + } + else { + codec->ctrl(codec->devinfo, 1, 1, 0); + } + + return; +} + +struct spicds_info * +spicds_create(device_t dev, void *devinfo, int num, spicds_ctrl ctrl) +{ + struct spicds_info *codec; + +#if(0) + device_printf(dev, "spicds_create(dev, devinfo, %d, ctrl)\n", num); +#endif + codec = (struct spicds_info *)malloc(sizeof *codec, M_SPICDS, M_NOWAIT); + if (codec == NULL) + return NULL; + + snprintf(codec->name, SPICDS_NAMELEN, "%s:spicds%d", device_get_nameunit(dev), num); + codec->lock = snd_mtxcreate(codec->name, codec->name); + codec->dev = dev; + codec->ctrl = ctrl; + codec->devinfo = devinfo; + codec->num = num; + codec->type = SPICDS_TYPE_AK4524; + codec->cif = 0; + codec->format = AK452X_FORMAT_I2S | AK452X_FORMAT_256FSN | AK452X_FORMAT_1X; + codec->dvc = AK452X_DVC_DEMOFF | AK452X_DVC_ZTM1024 | AK452X_DVC_ZCE; + + return codec; +} + +void +spicds_destroy(struct spicds_info *codec) +{ + snd_mtxfree(codec->lock); + free(codec, M_SPICDS); +} + +void +spicds_settype(struct spicds_info *codec, unsigned int type) +{ + snd_mtxlock(codec->lock); + codec->type = type; + snd_mtxunlock(codec->lock); +} + +void +spicds_setcif(struct spicds_info *codec, unsigned int cif) +{ + snd_mtxlock(codec->lock); + codec->cif = cif; + snd_mtxunlock(codec->lock); +} + +void +spicds_setformat(struct spicds_info *codec, unsigned int format) +{ + snd_mtxlock(codec->lock); + codec->format = format; + snd_mtxunlock(codec->lock); +} + +void +spicds_setdvc(struct spicds_info *codec, unsigned int dvc) +{ + snd_mtxlock(codec->lock); + codec->dvc = dvc; + snd_mtxunlock(codec->lock); +} + +void +spicds_init(struct spicds_info *codec) +{ +#if(0) + device_printf(codec->dev, "spicds_init(codec)\n"); +#endif + snd_mtxlock(codec->lock); + if (codec->type == SPICDS_TYPE_AK4524 ||\ + codec->type == SPICDS_TYPE_AK4528) { + /* power off */ + spicds_wrcd(codec, AK4524_POWER, 0); + /* set parameter */ + spicds_wrcd(codec, AK4524_FORMAT, codec->format); + spicds_wrcd(codec, AK4524_DVC, codec->dvc); + /* power on */ + spicds_wrcd(codec, AK4524_POWER, AK452X_POWER_PWDA | AK452X_POWER_PWAD | AK452X_POWER_PWVR); + /* free reset register */ + spicds_wrcd(codec, AK4524_RESET, AK452X_RESET_RSDA | AK452X_RESET_RSAD); + } + if (codec->type == SPICDS_TYPE_WM8770) { + /* WM8770 init values are taken from ALSA */ + /* These come first to reduce init pop noise */ + spicds_wrcd(codec, 0x1b, 0x044); /* ADC Mux (AC'97 source) */ + spicds_wrcd(codec, 0x1c, 0x00B); /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */ + spicds_wrcd(codec, 0x1d, 0x009); /* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */ + + spicds_wrcd(codec, 0x18, 0x000); /* All power-up */ + + spicds_wrcd(codec, 0x16, 0x122); /* I2S, normal polarity, 24bit */ + spicds_wrcd(codec, 0x17, 0x022); /* 256fs, slave mode */ + + spicds_wrcd(codec, 0x19, 0x000); /* -12dB ADC/L */ + spicds_wrcd(codec, 0x1a, 0x000); /* -12dB ADC/R */ + } + if (codec->type == SPICDS_TYPE_AK4358) + spicds_wrcd(codec, 0x00, 0x07); /* I2S, 24bit, power-up */ + if (codec->type == SPICDS_TYPE_AK4381) + spicds_wrcd(codec, 0x00, 0x0f); /* I2S, 24bit, power-up */ + if (codec->type == SPICDS_TYPE_AK4396) + spicds_wrcd(codec, 0x00, 0x07); /* I2S, 24bit, power-up */ + snd_mtxunlock(codec->lock); +} + +void +spicds_reinit(struct spicds_info *codec) +{ + snd_mtxlock(codec->lock); + if (codec->type != SPICDS_TYPE_WM8770) { + /* reset */ + spicds_wrcd(codec, AK4524_RESET, 0); + /* set parameter */ + spicds_wrcd(codec, AK4524_FORMAT, codec->format); + spicds_wrcd(codec, AK4524_DVC, codec->dvc); + /* free reset register */ + spicds_wrcd(codec, AK4524_RESET, AK452X_RESET_RSDA | AK452X_RESET_RSAD); + } + else { + /* WM8770 reinit */ + /* AK4358 reinit */ + /* AK4381 reinit */ + } + snd_mtxunlock(codec->lock); +} + +void +spicds_set(struct spicds_info *codec, int dir, unsigned int left, unsigned int right) +{ +#if(0) + device_printf(codec->dev, "spicds_set(codec, %d, %d, %d)\n", dir, left, right); +#endif + snd_mtxlock(codec->lock); + if (left >= 100) + if ((codec->type == SPICDS_TYPE_AK4381) || \ + (codec->type == SPICDS_TYPE_AK4396)) + left = 255; + else + left = 127; + else + switch (codec->type) { + case SPICDS_TYPE_WM8770: + left = left + 27; + break; + case SPICDS_TYPE_AK4381 || SPICDS_TYPE_AK4396: + left = left * 255 / 100; + break; + default: + left = left * 127 / 100; + } + if (right >= 100) + if ((codec->type == SPICDS_TYPE_AK4381) || \ + (codec->type == SPICDS_TYPE_AK4396)) + right = 255; + else + right = 127; + else + switch (codec->type) { + case SPICDS_TYPE_WM8770: + right = right + 27; + break; + case SPICDS_TYPE_AK4381 || SPICDS_TYPE_AK4396: + right = right * 255 / 100; + break; + default: + right = right * 127 / 100; + } + if (dir == PCMDIR_REC && codec->type == SPICDS_TYPE_AK4524) { +#if(0) + device_printf(codec->dev, "spicds_set(): AK4524(REC) %d/%d\n", left, right); +#endif + spicds_wrcd(codec, AK4524_LIPGA, left); + spicds_wrcd(codec, AK4524_RIPGA, right); + } + if (dir == PCMDIR_PLAY && codec->type == SPICDS_TYPE_AK4524) { +#if(0) + device_printf(codec->dev, "spicds_set(): AK4524(PLAY) %d/%d\n", left, right); +#endif + spicds_wrcd(codec, AK4524_LOATT, left); + spicds_wrcd(codec, AK4524_ROATT, right); + } + if (dir == PCMDIR_PLAY && codec->type == SPICDS_TYPE_AK4528) { +#if(0) + device_printf(codec->dev, "spicds_set(): AK4528(PLAY) %d/%d\n", left, right); +#endif + spicds_wrcd(codec, AK4528_LOATT, left); + spicds_wrcd(codec, AK4528_ROATT, right); + } + if (dir == PCMDIR_PLAY && codec->type == SPICDS_TYPE_WM8770) { +#if(0) + device_printf(codec->dev, "spicds_set(): WM8770(PLAY) %d/%d\n", left, right); +#endif + spicds_wrcd(codec, WM8770_AOATT_L1, left | WM8770_AOATT_UPDATE); + spicds_wrcd(codec, WM8770_AOATT_R1, right | WM8770_AOATT_UPDATE); + } + if (dir == PCMDIR_PLAY && codec->type == SPICDS_TYPE_AK4358) { +#if(0) + device_printf(codec->dev, "spicds_set(): AK4358(PLAY) %d/%d\n", left, right); +#endif + spicds_wrcd(codec, AK4358_LO1ATT, left | AK4358_OATT_ENABLE); + spicds_wrcd(codec, AK4358_RO1ATT, right | AK4358_OATT_ENABLE); + } + if (dir == PCMDIR_PLAY && codec->type == SPICDS_TYPE_AK4381) { +#if(0) + device_printf(codec->dev, "spicds_set(): AK4381(PLAY) %d/%d\n", left, right); +#endif + spicds_wrcd(codec, AK4381_LOATT, left); + spicds_wrcd(codec, AK4381_ROATT, right); + } + + if (dir == PCMDIR_PLAY && codec->type == SPICDS_TYPE_AK4396) { +#if(0) + device_printf(codec->dev, "spicds_set(): AK4396(PLAY) %d/%d\n", left, right); +#endif + spicds_wrcd(codec, AK4396_LOATT, left); + spicds_wrcd(codec, AK4396_ROATT, right); + } + + snd_mtxunlock(codec->lock); +} + +MODULE_DEPEND(snd_spicds, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_spicds, 1); diff --git a/sys/dev/sound/pci/spicds.h b/sys/dev/sound/pci/spicds.h new file mode 100644 index 0000000000..208c60d910 --- /dev/null +++ b/sys/dev/sound/pci/spicds.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2006 Konstantin Dimitrov + * Copyright (c) 2001 Katsurajima Naoto + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/sound/pci/spicds.h,v 1.3.2.2 2007/06/11 19:33:28 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/spicds.h,v 1.1 2007/06/16 19:48:05 hasso Exp $ + */ + +/* supported CODECs */ +#define SPICDS_TYPE_AK4524 0 +#define SPICDS_TYPE_AK4528 1 +#define SPICDS_TYPE_WM8770 2 +#define SPICDS_TYPE_AK4358 3 +#define SPICDS_TYPE_AK4381 4 +#define SPICDS_TYPE_AK4396 5 + +/* AK4524/AK4528 control registers */ +#define AK4524_POWER 0x00 +#define AK4528_POWER 0x00 +#define AK452X_POWER_PWDA 0x01 +#define AK452X_POWER_PWAD 0x02 +#define AK452X_POWER_PWVR 0x04 +#define AK4524_RESET 0x01 +#define AK4528_RESET 0x01 +#define AK452X_RESET_RSDA 0x01 +#define AK452X_RESET_RSAD 0x02 +#define AK4524_FORMAT 0x02 +#define AK4528_FORMAT 0x02 +#define AK452X_FORMAT_1X 0x00 +#define AK452X_FORMAT_2X 0x01 +#define AK452X_FORMAT_4X1 0x02 +#define AK452X_FORMAT_4X2 0x03 +#define AK452X_FORMAT_256FSN 0x00 +#define AK452X_FORMAT_512FSN 0x04 +#define AK452X_FORMAT_1024FSN 0x08 +#define AK452X_FORMAT_384FSN 0x10 +#define AK452X_FORMAT_768FSN 0x14 +#define AK452X_FORMAT_OM24IL16 0x00 +#define AK452X_FORMAT_OM24IL20 0x20 +#define AK452X_FORMAT_OM24IM24 0x40 +#define AK452X_FORMAT_I2S 0x60 +#define AK452X_FORMAT_OM24IL24 0x80 +#define AK4524_DVC 0x03 +#define AK452X_DVC_DEM441 0x00 +#define AK452X_DVC_DEMOFF 0x01 +#define AK452X_DVC_DEM48 0x02 +#define AK452X_DVC_DEM32 0x03 +#define AK452X_DVC_ZTM256 0x00 +#define AK452X_DVC_ZTM512 0x04 +#define AK452X_DVC_ZTM1024 0x08 +#define AK452X_DVC_ZTM2048 0x0c +#define AK452X_DVC_ZCE 0x10 +#define AK452X_DVC_HPFL 0x04 +#define AK452X_DVC_HPFR 0x08 +#define AK452X_DVC_SMUTE 0x80 +#define AK4524_LIPGA 0x04 +#define AK4524_RIPGA 0x05 +#define AK4524_LOATT 0x06 +#define AK4524_ROATT 0x07 +#define AK4528_LOATT 0x04 +#define AK4528_ROATT 0x05 + +/* WM8770 control registers */ +#define WM8770_AOATT_L1 0x00 +#define WM8770_AOATT_R1 0x01 +#define WM8770_AOATT_L2 0x02 +#define WM8770_AOATT_R2 0x03 +#define WM8770_AOATT_L3 0x04 +#define WM8770_AOATT_R3 0x05 +#define WM8770_AOATT_L4 0x06 +#define WM8770_AOATT_R4 0x07 +#define WM8770_AOATT_MAST 0x08 +#define WM8770_AOATT_UPDATE 0x100 + +/* AK4358 control registers */ +#define AK4358_LO1ATT 0x04 +#define AK4358_RO1ATT 0x05 +#define AK4358_OATT_ENABLE 0x80 + +/* AK4381 control registers */ +#define AK4381_LOATT 0x03 +#define AK4381_ROATT 0x04 + +/* AK4396 control registers */ +#define AK4396_LOATT 0x03 +#define AK4396_ROATT 0x04 + +struct spicds_info; + +typedef void (*spicds_ctrl)(void *, unsigned int, unsigned int, unsigned int); + +struct spicds_info *spicds_create(device_t dev, void *devinfo, int num, spicds_ctrl); +void spicds_destroy(struct spicds_info *codec); +void spicds_settype(struct spicds_info *codec, unsigned int type); +void spicds_setcif(struct spicds_info *codec, unsigned int cif); +void spicds_setformat(struct spicds_info *codec, unsigned int format); +void spicds_setdvc(struct spicds_info *codec, unsigned int dvc); +void spicds_init(struct spicds_info *codec); +void spicds_reinit(struct spicds_info *codec); +void spicds_set(struct spicds_info *codec, int dir, unsigned int left, unsigned int right); diff --git a/sys/dev/sound/pci/via8233.c b/sys/dev/sound/pci/via8233.c index 0939f5c053..84111c04e9 100644 --- a/sys/dev/sound/pci/via8233.c +++ b/sys/dev/sound/pci/via8233.c @@ -25,8 +25,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/via8233.c,v 1.20.2.2 2006/04/25 14:39:23 ariff Exp $ - * $DragonFly: src/sys/dev/sound/pci/via8233.c,v 1.8 2007/01/04 21:47:02 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pci/via8233.c,v 1.20.2.3 2007/04/26 08:21:44 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/via8233.c,v 1.9 2007/06/16 19:48:05 hasso Exp $ */ /* @@ -47,7 +47,7 @@ #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/via8233.c,v 1.8 2007/01/04 21:47:02 corecode Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/via8233.c,v 1.9 2007/06/16 19:48:05 hasso Exp $"); #define VIA8233_PCI_ID 0x30591106 @@ -1052,6 +1052,7 @@ bad: if (via->irq) bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq); if (via->parent_dmat) bus_dma_tag_destroy(via->parent_dmat); if (via->sgd_dmamap) bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap); + if (via->sgd_table) bus_dmamem_free(via->sgd_dmat, via->sgd_table, via->sgd_dmamap); if (via->sgd_dmat) bus_dma_tag_destroy(via->sgd_dmat); if (via->lock) snd_mtxfree(via->lock); if (via) kfree(via, M_DEVBUF); @@ -1073,6 +1074,7 @@ via_detach(device_t dev) bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq); bus_dma_tag_destroy(via->parent_dmat); bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap); + bus_dmamem_free(via->sgd_dmat, via->sgd_table, via->sgd_dmamap); bus_dma_tag_destroy(via->sgd_dmat); snd_mtxfree(via->lock); kfree(via, M_DEVBUF); diff --git a/sys/dev/sound/pci/via82c686.c b/sys/dev/sound/pci/via82c686.c index f24ae28ea2..73add05216 100644 --- a/sys/dev/sound/pci/via82c686.c +++ b/sys/dev/sound/pci/via82c686.c @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/via82c686.c,v 1.34.2.1 2005/12/30 19:55:53 netchild Exp $ - * $DragonFly: src/sys/dev/sound/pci/via82c686.c,v 1.8 2007/01/04 21:47:02 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pci/via82c686.c,v 1.34.2.2 2007/04/26 08:21:44 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/via82c686.c,v 1.9 2007/06/16 19:48:05 hasso Exp $ */ #include @@ -36,7 +36,7 @@ #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/via82c686.c,v 1.8 2007/01/04 21:47:02 corecode Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/via82c686.c,v 1.9 2007/06/16 19:48:05 hasso Exp $"); #define VIA_PCI_ID 0x30581106 #define NSEGS 4 /* Number of segments in SGD table */ @@ -610,6 +610,7 @@ bad: if (via->irq) bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq); if (via->parent_dmat) bus_dma_tag_destroy(via->parent_dmat); if (via->sgd_dmamap) bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap); + if (via->sgd_table) bus_dmamem_free(via->sgd_dmat, via->sgd_table, via->sgd_dmamap); if (via->sgd_dmat) bus_dma_tag_destroy(via->sgd_dmat); if (via->lock) snd_mtxfree(via->lock); if (via) kfree(via, M_DEVBUF); @@ -632,6 +633,7 @@ via_detach(device_t dev) bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq); bus_dma_tag_destroy(via->parent_dmat); bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap); + bus_dmamem_free(via->sgd_dmat, via->sgd_table, via->sgd_dmamap); bus_dma_tag_destroy(via->sgd_dmat); snd_mtxfree(via->lock); kfree(via, M_DEVBUF); diff --git a/sys/dev/sound/pcm/ac97.c b/sys/dev/sound/pcm/ac97.c index 42ab5c8c81..40f889dfd9 100644 --- a/sys/dev/sound/pcm/ac97.c +++ b/sys/dev/sound/pcm/ac97.c @@ -23,17 +23,19 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/ac97.c,v 1.53.2.3 2006/01/09 02:06:42 ariff Exp $ - * $DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.23 2007/01/04 21:47:03 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/ac97.c,v 1.53.2.5 2007/05/13 20:53:39 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.24 2007/06/16 19:48:05 hasso Exp $ */ #include #include #include +#include + #include "mixer_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.23 2007/01/04 21:47:03 corecode Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.24 2007/06/16 19:48:05 hasso Exp $"); MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); @@ -55,6 +57,7 @@ struct ac97_info { device_t dev; void *devinfo; u_int32_t id; + u_int32_t subvendor; unsigned count, caps, se, extcaps, extid, extstat, noext:1; u_int32_t flags; struct ac97mixtable_entry mix[32]; @@ -139,7 +142,7 @@ static struct ac97_codecid ac97codecid[] = { { 0x41445368, 0x00, 0, "AD1888", ad198x_patch }, { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, { 0x41445372, 0x00, 0, "AD1981A", 0 }, - { 0x41445374, 0x00, 0, "AD1981B", 0 }, + { 0x41445374, 0x00, 0, "AD1981B", ad1981b_patch }, { 0x41445375, 0x00, 0, "AD1985", ad198x_patch }, { 0x41445378, 0x00, 0, "AD1986", ad198x_patch }, { 0x414b4d00, 0x00, 1, "AK4540", 0 }, @@ -153,7 +156,7 @@ static struct ac97_codecid ac97codecid[] = { { 0x414c4740, 0x0f, 0, "ALC202", 0 }, { 0x414c4720, 0x0f, 0, "ALC650", 0 }, { 0x414c4752, 0x0f, 0, "ALC250", 0 }, - { 0x414c4760, 0x0f, 0, "ALC655", 0 }, + { 0x414c4760, 0x0f, 0, "ALC655", alc655_patch }, { 0x414c4770, 0x0f, 0, "ALC203", 0 }, { 0x414c4780, 0x0f, 0, "ALC658", 0 }, { 0x414c4790, 0x0f, 0, "ALC850", 0 }, @@ -406,6 +409,12 @@ ac97_getcaps(struct ac97_info *codec) return codec->caps; } +u_int32_t +ac97_getsubvendor(struct ac97_info *codec) +{ + return codec->subvendor; +} + static int ac97_setrecsrc(struct ac97_info *codec, int channel) { @@ -539,6 +548,23 @@ ac97_fix_auxout(struct ac97_info *codec) static void ac97_fix_tone(struct ac97_info *codec) { + /* + * YMF chips does not indicate tone and 3D enhancement capability + * in the AC97_REG_RESET register. + */ + switch (codec->id) { + case 0x594d4800: /* YMF743 */ + case 0x594d4803: /* YMF753 */ + codec->caps |= AC97_CAP_TONE; + codec->se |= 0x04; + break; + case 0x594d4802: /* YMF752 */ + codec->se |= 0x04; + break; + default: + break; + } + /* Hide treble and bass if they don't exist */ if ((codec->caps & AC97_CAP_TONE) == 0) { bzero(&codec->mix[SOUND_MIXER_BASS], @@ -558,9 +584,8 @@ ac97_fix_volume(struct ac97_info *codec) ac97_wrcd(codec, AC97_MIX_PCM, 0); bzero(&codec->mix[SOUND_MIXER_PCM], sizeof(codec->mix[SOUND_MIXER_PCM])); - codec->flags |= AC97_F_SOFTVOL; if (d) - d->flags |= SD_F_SOFTVOL; + d->flags |= SD_F_SOFTPCMVOL; return; #endif switch (codec->id) { @@ -577,9 +602,8 @@ ac97_fix_volume(struct ac97_info *codec) } bzero(&codec->mix[SOUND_MIXER_PCM], sizeof(codec->mix[SOUND_MIXER_PCM])); - codec->flags |= AC97_F_SOFTVOL; if (d) - d->flags |= SD_F_SOFTVOL; + d->flags |= SD_F_SOFTPCMVOL; } static const char* @@ -645,6 +669,9 @@ ac97_initmixer(struct ac97_info *codec) } codec->id = id; + codec->subvendor = (u_int32_t)pci_get_subdevice(codec->dev) << 16; + codec->subvendor |= (u_int32_t)pci_get_subvendor(codec->dev) & + 0x0000ffff; codec->noext = 0; codec_patch = NULL; @@ -761,8 +788,6 @@ ac97_initmixer(struct ac97_info *codec) if (bootverbose) { if (codec->flags & AC97_F_RDCD_BUG) device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n"); - if (codec->flags & AC97_F_SOFTVOL) - device_printf(codec->dev, "Soft PCM volume\n"); device_printf(codec->dev, "Codec features "); for (i = j = 0; i < 10; i++) if (codec->caps & (1 << i)) @@ -964,5 +989,3 @@ ac97_getmixerclass(void) { return &ac97mixer_class; } - - diff --git a/sys/dev/sound/pcm/ac97.h b/sys/dev/sound/pcm/ac97.h index a3b9fa2751..7e13e066de 100644 --- a/sys/dev/sound/pcm/ac97.h +++ b/sys/dev/sound/pcm/ac97.h @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/ac97.h,v 1.16.2.1 2005/12/30 19:55:54 netchild Exp $ - * $DragonFly: src/sys/dev/sound/pcm/ac97.h,v 1.4 2007/01/04 21:47:03 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/ac97.h,v 1.16.2.3 2007/05/13 20:53:39 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/ac97.h,v 1.5 2007/06/16 19:48:05 hasso Exp $ */ #define AC97_MUTE 0x8080 @@ -83,7 +83,6 @@ #define AC97_F_EAPD_INV 0x00000001 #define AC97_F_RDCD_BUG 0x00000002 -#define AC97_F_SOFTVOL 0x00000004 #define AC97_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, sizeof(struct kobj)) #define AC97_CREATE(dev, devinfo, cls) ac97_create(dev, devinfo, &cls ## _class) @@ -103,7 +102,7 @@ int ac97_setextmode(struct ac97_info *codec, u_int16_t mode); u_int16_t ac97_getextmode(struct ac97_info *codec); u_int16_t ac97_getextcaps(struct ac97_info *codec); u_int16_t ac97_getcaps(struct ac97_info *codec); +u_int32_t ac97_getsubvendor(struct ac97_info *codec); u_int16_t ac97_rdcd(struct ac97_info *codec, int reg); void ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val); - diff --git a/sys/dev/sound/pcm/ac97_patch.c b/sys/dev/sound/pcm/ac97_patch.c index 4a957be1c3..6b2e9ed505 100644 --- a/sys/dev/sound/pcm/ac97_patch.c +++ b/sys/dev/sound/pcm/ac97_patch.c @@ -22,15 +22,15 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/ac97_patch.c,v 1.3.2.1 2005/12/30 19:55:54 netchild Exp $ - * $DragonFly: src/sys/dev/sound/pcm/ac97_patch.c,v 1.4 2007/01/04 21:47:03 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/ac97_patch.c,v 1.3.2.3 2007/06/08 17:33:38 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/ac97_patch.c,v 1.5 2007/06/16 19:48:05 hasso Exp $ */ #include #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/ac97_patch.c,v 1.4 2007/01/04 21:47:03 corecode Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/ac97_patch.c,v 1.5 2007/06/16 19:48:05 hasso Exp $"); void ad1886_patch(struct ac97_info* codec) { @@ -49,13 +49,60 @@ void ad198x_patch(struct ac97_info* codec) ac97_wrcd(codec, 0x76, ac97_rdcd(codec, 0x76) | 0x0420); } +void ad1981b_patch(struct ac97_info* codec) +{ + /* + * Enable headphone jack sensing. + */ + switch (ac97_getsubvendor(codec)) { + case 0x02d91014: /* IBM Thinkcentre */ + ac97_wrcd(codec, AC97_AD_JACK_SPDIF, + ac97_rdcd(codec, AC97_AD_JACK_SPDIF) | 0x0800); + break; + default: + break; + } +} + void cmi9739_patch(struct ac97_info* codec) { /* - * Few laptops (notably ASUS W1000N) need extra register - * initialization to power up the internal speakers. + * Few laptops need extra register initialization + * to power up the internal speakers. + */ + switch (ac97_getsubvendor(codec)) { + case 0x18431043: /* ASUS W1000N */ + ac97_wrcd(codec, AC97_REG_POWER, 0x000f); + ac97_wrcd(codec, AC97_MIXEXT_CLFE, 0x0000); + ac97_wrcd(codec, 0x64, 0x7110); + break; + default: + break; + } +} + +void alc655_patch(struct ac97_info* codec) +{ + /* + * MSI (Micro-Star International) specific EAPD quirk. */ - ac97_wrcd(codec, AC97_REG_POWER, 0x000f); - ac97_wrcd(codec, AC97_MIXEXT_CLFE, 0x0000); - ac97_wrcd(codec, 0x64, 0x7110); + switch (ac97_getsubvendor(codec)) { + case 0x00611462: /* MSI S250 */ + case 0x01311462: /* MSI S270 */ + case 0x01611462: /* LG K1 Express */ + case 0x03511462: /* MSI L725 */ + ac97_wrcd(codec, 0x7a, ac97_rdcd(codec, 0x7a) & 0xfffd); + break; + case 0x10ca1734: + /* + * Amilo Pro V2055 with ALC655 has phone out by default + * disabled (surround on), leaving us only with internal + * speakers. This should really go to mixer. We write the + * Data Flow Control reg. + */ + ac97_wrcd(codec, 0x6a, ac97_rdcd(codec, 0x6a) | 0x0001); + break; + default: + break; + } } diff --git a/sys/dev/sound/pcm/ac97_patch.h b/sys/dev/sound/pcm/ac97_patch.h index 888aa143a3..dc600ac19b 100644 --- a/sys/dev/sound/pcm/ac97_patch.h +++ b/sys/dev/sound/pcm/ac97_patch.h @@ -22,12 +22,14 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/ac97_patch.h,v 1.3.2.1 2005/12/30 19:55:54 netchild Exp $ - * $DragonFly: src/sys/dev/sound/pcm/ac97_patch.h,v 1.4 2007/01/04 21:47:03 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/ac97_patch.h,v 1.3.2.2 2007/04/26 08:30:52 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/ac97_patch.h,v 1.5 2007/06/16 19:48:05 hasso Exp $ */ typedef void (*ac97_patch)(struct ac97_info*); void ad1886_patch(struct ac97_info*); void ad198x_patch(struct ac97_info*); +void ad1981b_patch(struct ac97_info*); void cmi9739_patch(struct ac97_info*); +void alc655_patch(struct ac97_info*); diff --git a/sys/dev/sound/pcm/buffer.c b/sys/dev/sound/pcm/buffer.c index c2f20ba54b..71ca492b9e 100644 --- a/sys/dev/sound/pcm/buffer.c +++ b/sys/dev/sound/pcm/buffer.c @@ -23,15 +23,15 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/buffer.c,v 1.25.2.1 2005/12/30 19:55:54 netchild Exp $ - * $DragonFly: src/sys/dev/sound/pcm/buffer.c,v 1.8 2007/01/04 21:47:03 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/buffer.c,v 1.25.2.3 2007/04/26 08:21:43 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/buffer.c,v 1.9 2007/06/16 19:48:05 hasso Exp $ */ #include #include "feeder_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/buffer.c,v 1.8 2007/01/04 21:47:03 corecode Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/buffer.c,v 1.9 2007/06/16 19:48:05 hasso Exp $"); struct snd_dbuf * sndbuf_create(device_t dev, char *drv, char *desc, struct pcm_channel *channel) @@ -49,6 +49,7 @@ sndbuf_create(device_t dev, char *drv, char *desc, struct pcm_channel *channel) void sndbuf_destroy(struct snd_dbuf *b) { + sndbuf_free(b); kfree(b, M_DEVBUF); } @@ -88,25 +89,31 @@ sndbuf_alloc(struct snd_dbuf *b, bus_dma_tag_t dmatag, unsigned int size) b->maxsize = size; b->bufsize = b->maxsize; b->buf_addr = 0; + b->flags |= SNDBUF_F_MANAGED; if (bus_dmamem_alloc(b->dmatag, (void **)&b->buf, BUS_DMA_NOWAIT, - &b->dmamap)) + &b->dmamap)) { + sndbuf_free(b); return (ENOMEM); + } if (bus_dmamap_load(b->dmatag, b->dmamap, b->buf, b->maxsize, sndbuf_setmap, b, 0) != 0 || b->buf_addr == 0) { - bus_dmamem_free(b->dmatag, b->buf, b->dmamap); - b->dmamap = NULL; + sndbuf_free(b); return (ENOMEM); } ret = sndbuf_resize(b, 2, b->maxsize / 2); if (ret != 0) sndbuf_free(b); + return (ret); } int sndbuf_setup(struct snd_dbuf *b, void *buf, unsigned int size) { + b->flags &= ~SNDBUF_F_MANAGED; + if (buf) + b->flags |= SNDBUF_F_MANAGED; b->buf = buf; b->maxsize = size; b->bufsize = b->maxsize; @@ -118,15 +125,20 @@ sndbuf_free(struct snd_dbuf *b) { if (b->tmpbuf) kfree(b->tmpbuf, M_DEVBUF); - b->tmpbuf = NULL; - - if (b->dmamap) - bus_dmamap_unload(b->dmatag, b->dmamap); + if (b->buf) { + if (b->flags & SNDBUF_F_MANAGED) { + if (b->dmamap) + bus_dmamap_unload(b->dmatag, b->dmamap); + if (b->dmatag) + bus_dmamem_free(b->dmatag, b->buf, b->dmamap); + } else + kfree(b->buf, M_DEVBUF); + } - if (b->dmamap && b->buf) - bus_dmamem_free(b->dmatag, b->buf, b->dmamap); - b->dmamap = NULL; + b->tmpbuf = NULL; b->buf = NULL; + b->dmatag = NULL; + b->dmamap = NULL; } int diff --git a/sys/dev/sound/pcm/buffer.h b/sys/dev/sound/pcm/buffer.h index fcae33d140..89458faae4 100644 --- a/sys/dev/sound/pcm/buffer.h +++ b/sys/dev/sound/pcm/buffer.h @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/buffer.h,v 1.10 2005/01/06 01:43:20 imp Exp $ - * $DragonFly: src/sys/dev/sound/pcm/buffer.h,v 1.3 2007/01/04 21:47:03 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/buffer.h,v 1.10.2.2 2007/05/13 20:50:31 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/buffer.h,v 1.4 2007/06/16 19:48:05 hasso Exp $ */ #define SND_DMA(b) (sndbuf_getflags((b)) & SNDBUF_F_DMA) @@ -33,6 +33,7 @@ #define SNDBUF_F_DMA 0x00000001 #define SNDBUF_F_XRUN 0x00000002 #define SNDBUF_F_RUNNING 0x00000004 +#define SNDBUF_F_MANAGED 0x00000008 #define SNDBUF_NAMELEN 48 @@ -52,7 +53,7 @@ struct snd_dbuf { u_int32_t flags; bus_dmamap_t dmamap; bus_dma_tag_t dmatag; - u_int32_t buf_addr; + bus_addr_t buf_addr; struct selinfo sel; struct pcm_channel *channel; char name[SNDBUF_NAMELEN]; diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c index f75bc03027..9f7d0b4686 100644 --- a/sys/dev/sound/pcm/channel.c +++ b/sys/dev/sound/pcm/channel.c @@ -24,8 +24,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/channel.c,v 1.99.2.4 2006/04/04 17:37:51 ariff Exp $ - * $DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.12 2007/06/14 21:48:36 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/channel.c,v 1.99.2.5 2007/05/13 20:53:39 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.13 2007/06/16 19:48:05 hasso Exp $ */ #include "use_isa.h" @@ -35,7 +35,7 @@ #include "feeder_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.12 2007/06/14 21:48:36 corecode Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.13 2007/06/16 19:48:05 hasso Exp $"); #define MIN_CHUNK_SIZE 256 /* for uiomove etc. */ #if 0 @@ -1358,7 +1358,7 @@ chn_buildfeeder(struct pcm_channel *c) c->feederflags &= ~(1 << FEEDER_VOLUME); if (c->direction == PCMDIR_PLAY && !(c->flags & CHN_F_VIRTUAL) && - c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTVOL) && + c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTPCMVOL) && c->parentsnddev->mixer_dev) c->feederflags |= 1 << FEEDER_VOLUME; flags = c->feederflags; @@ -1417,7 +1417,10 @@ chn_buildfeeder(struct pcm_channel *c) if ((flags & (1 << FEEDER_VOLUME))) { struct dev_ioctl_args map; - int vol = 100 | (100 << 8); + u_int32_t parent = SOUND_MIXER_NONE; + int vol, left, right; + + vol = 100 | (100 << 8); CHN_UNLOCK(c); /* @@ -1431,9 +1434,30 @@ chn_buildfeeder(struct pcm_channel *c) map.a_fflag = -1; map.a_cred = NULL; if (mixer_ioctl(&map) != 0) - device_printf(c->dev, "Soft Volume: Failed to read default value\n"); + device_printf(c->dev, "Soft PCM Volume: Failed to read default value\n"); + left = vol & 0x7f; + right = (vol >> 8) & 0x7f; + if (c->parentsnddev != NULL && + c->parentsnddev->mixer_dev != NULL && + c->parentsnddev->mixer_dev->si_drv1 != NULL) + parent = mix_getparent( + c->parentsnddev->mixer_dev->si_drv1, + SOUND_MIXER_PCM); + if (parent != SOUND_MIXER_NONE) { + vol = 100 | (100 << 8); + map.a_head.a_dev = c->parentsnddev->mixer_dev; + map.a_cmd = MIXER_READ(parent); + map.a_data = (caddr_t)&vol; + map.a_fflag = -1; + map.a_cred = NULL; + if (mixer_ioctl(&map) != 0) + device_printf(c->dev, "Soft Volume: Failed to read parent default value\n"); + left = (left * (vol & 0x7f)) / 100; + right = (right * ((vol >> 8) & 0x7f)) / 100; + } + CHN_LOCK(c); - chn_setvolume(c, vol & 0x7f, (vol >> 8) & 0x7f); + chn_setvolume(c, left, right); } return 0; diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c index 6b75c5d60b..961c90d406 100644 --- a/sys/dev/sound/pcm/mixer.c +++ b/sys/dev/sound/pcm/mixer.c @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/mixer.c,v 1.43.2.4 2006/04/04 17:43:48 ariff Exp $ - * $DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.15 2007/06/14 21:48:36 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/mixer.c,v 1.43.2.5 2007/05/13 20:53:39 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.16 2007/06/16 19:48:05 hasso Exp $ */ #include @@ -32,7 +32,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.15 2007/06/14 21:48:36 corecode Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.16 2007/06/16 19:48:05 hasso Exp $"); MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); @@ -51,6 +51,9 @@ struct snd_mixer { u_int32_t recdevs; u_int32_t recsrc; u_int16_t level[32]; + u_int8_t parent[32]; + u_int32_t child[32]; + u_int8_t realdev[32]; char name[MIXER_NAMELEN]; struct spinlock *lock; }; @@ -118,41 +121,87 @@ mixer_lookup(char *devname) * Always called with mixer->lock held. */ static int -mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev) +mixer_set_softpcmvol(struct snd_mixer *mixer, struct snddev_info *d, + unsigned left, unsigned right) +{ + struct snddev_channel *sce; + struct pcm_channel *ch; + + snd_mtxunlock(mixer->lock); + SLIST_FOREACH(sce, &d->channels, link) { + ch = sce->channel; + CHN_LOCK(ch); + if (ch->direction == PCMDIR_PLAY && + (ch->feederflags & (1 << FEEDER_VOLUME))) + chn_setvolume(ch, left, right); + CHN_UNLOCK(ch); + } + snd_mtxlock(mixer->lock); + return 0; +} + +static int +mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev) { struct snddev_info *d; - unsigned l, r; - int v; + unsigned l, r, tl, tr; + u_int32_t parent = SOUND_MIXER_NONE, child = 0; + u_int32_t realdev; + int i; - if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev)))) + if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || + (0 == (m->devs & (1 << dev)))) return -1; l = min((lev & 0x00ff), 100); r = min(((lev & 0xff00) >> 8), 100); + realdev = m->realdev[dev]; + + d = device_get_softc(m->dev); + if (d == NULL) + return -1; - d = device_get_softc(mixer->dev); - if (dev == SOUND_MIXER_PCM && d && - (d->flags & SD_F_SOFTVOL)) { - struct snddev_channel *sce; - struct pcm_channel *ch; - - snd_mtxunlock(mixer->lock); - SLIST_FOREACH(sce, &d->channels, link) { - ch = sce->channel; - CHN_LOCK(ch); - if (ch->direction == PCMDIR_PLAY && - (ch->feederflags & (1 << FEEDER_VOLUME))) - chn_setvolume(ch, l, r); - CHN_UNLOCK(ch); + /* TODO: recursive handling */ + parent = m->parent[dev]; + if (parent >= SOUND_MIXER_NRDEVICES) + parent = SOUND_MIXER_NONE; + if (parent == SOUND_MIXER_NONE) + child = m->child[dev]; + + if (parent != SOUND_MIXER_NONE) { + tl = (l * (m->level[parent] & 0x00ff)) / 100; + tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100; + if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) + mixer_set_softpcmvol(m, d, tl, tr); + else if (realdev != SOUND_MIXER_NONE && + MIXER_SET(m, realdev, tl, tr) < 0) + return -1; + } else if (child != 0) { + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(child & (1 << i)) || m->parent[i] != dev) + continue; + realdev = m->realdev[i]; + tl = (l * (m->level[i] & 0x00ff)) / 100; + tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100; + if (i == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) + mixer_set_softpcmvol(m, d, tl, tr); + else if (realdev != SOUND_MIXER_NONE) + MIXER_SET(m, realdev, tl, tr); } - snd_mtxlock(mixer->lock); + realdev = m->realdev[dev]; + if (realdev != SOUND_MIXER_NONE && + MIXER_SET(m, realdev, l, r) < 0) + return -1; } else { - v = MIXER_SET(mixer, dev, l, r); - if (v < 0) + if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) + mixer_set_softpcmvol(m, d, l, r); + else if (realdev != SOUND_MIXER_NONE && + MIXER_SET(m, realdev, l, r) < 0) return -1; } - mixer->level[dev] = l | (r << 8); + m->level[dev] = l | (r << 8); + return 0; } @@ -183,9 +232,20 @@ mixer_getrecsrc(struct snd_mixer *mixer) void mix_setdevs(struct snd_mixer *m, u_int32_t v) { - struct snddev_info *d = device_get_softc(m->dev); - if (d && (d->flags & SD_F_SOFTVOL)) + struct snddev_info *d; + int i; + + if (m == NULL) + return; + + d = device_get_softc(m->dev); + if (d != NULL && (d->flags & SD_F_SOFTPCMVOL)) v |= SOUND_MASK_PCM; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (m->parent[i] < SOUND_MIXER_NRDEVICES) + v |= 1 << m->parent[i]; + v |= m->child[i]; + } m->devs = v; } @@ -195,6 +255,54 @@ mix_setrecdevs(struct snd_mixer *m, u_int32_t v) m->recdevs = v; } +void +mix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs) +{ + u_int32_t mask = 0; + int i; + + if (m == NULL || parent >= SOUND_MIXER_NRDEVICES) + return; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (i == parent) + continue; + if (childs & (1 << i)) { + mask |= 1 << i; + if (m->parent[i] < SOUND_MIXER_NRDEVICES) + m->child[m->parent[i]] &= ~(1 << i); + m->parent[i] = parent; + m->child[i] = 0; + } + } + mask &= ~(1 << parent); + m->child[parent] = mask; +} + +void +mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev) +{ + if (m == NULL || dev >= SOUND_MIXER_NRDEVICES || + !(realdev == SOUND_MIXER_NONE || realdev < SOUND_MIXER_NRDEVICES)) + return; + m->realdev[dev] = realdev; +} + +u_int32_t +mix_getparent(struct snd_mixer *m, u_int32_t dev) +{ + if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) + return SOUND_MIXER_NONE; + return m->parent[dev]; +} + +u_int32_t +mix_getchild(struct snd_mixer *m, u_int32_t dev) +{ + if (m == NULL || dev >= SOUND_MIXER_NRDEVICES) + return 0; + return m->child[dev]; +} + u_int32_t mix_getdevs(struct snd_mixer *m) { @@ -229,6 +337,11 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo) m->devinfo = devinfo; m->busy = 0; m->dev = dev; + for (i = 0; i < 32; i++) { + m->parent[i] = SOUND_MIXER_NONE; + m->child[i] = 0; + m->realdev[i] = i; + } if (MIXER_INIT(m)) goto bad; @@ -259,6 +372,30 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo) snddev = device_get_softc(dev); snddev->mixer_dev = pdev; + if (bootverbose) { + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (!(m->devs & (1 << i))) + continue; + if (m->realdev[i] != i) { + device_printf(dev, "Mixer \"%s\" -> \"%s\":", + snd_mixernames[i], + (m->realdev[i] < SOUND_MIXER_NRDEVICES) ? + snd_mixernames[m->realdev[i]] : "none"); + } else { + device_printf(dev, "Mixer \"%s\":", + snd_mixernames[i]); + } + if (m->parent[i] < SOUND_MIXER_NRDEVICES) + kprintf(" parent=\"%s\"", + snd_mixernames[m->parent[i]]); + if (m->child[i] != 0) + kprintf(" child=0x%08x", m->child[i]); + kprintf("\n"); + } + if (snddev->flags & SD_F_SOFTPCMVOL) + device_printf(dev, "Soft PCM mixer ENABLED\n"); + } + return 0; bad: diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h index 92219083c9..12d9cb3b56 100644 --- a/sys/dev/sound/pcm/mixer.h +++ b/sys/dev/sound/pcm/mixer.h @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/mixer.h,v 1.14 2005/01/06 01:43:21 imp Exp $ - * $DragonFly: src/sys/dev/sound/pcm/mixer.h,v 1.5 2007/01/04 21:47:03 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/mixer.h,v 1.14.2.1 2007/05/13 20:53:39 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/mixer.h,v 1.6 2007/06/16 19:48:05 hasso Exp $ */ int mixer_init(device_t dev, kobj_class_t cls, void *devinfo); @@ -40,6 +40,10 @@ void mix_setdevs(struct snd_mixer *m, u_int32_t v); void mix_setrecdevs(struct snd_mixer *m, u_int32_t v); u_int32_t mix_getdevs(struct snd_mixer *m); u_int32_t mix_getrecdevs(struct snd_mixer *m); +void mix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs); +void mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev); +u_int32_t mix_getparent(struct snd_mixer *m, u_int32_t dev); +u_int32_t mix_getchild(struct snd_mixer *m, u_int32_t dev); void *mix_getdevinfo(struct snd_mixer *m); /* diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c index d949f97f00..2570f93993 100644 --- a/sys/dev/sound/pcm/sound.c +++ b/sys/dev/sound/pcm/sound.c @@ -24,8 +24,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/sound.c,v 1.93.2.3 2006/04/04 17:43:48 ariff Exp $ - * $DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.9 2007/06/14 21:48:36 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/sound.c,v 1.93.2.5 2007/06/04 09:06:05 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.10 2007/06/16 19:48:05 hasso Exp $ */ #include @@ -35,7 +35,7 @@ #include "feeder_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.9 2007/06/14 21:48:36 corecode Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.10 2007/06/16 19:48:05 hasso Exp $"); devclass_t pcm_devclass; @@ -46,7 +46,7 @@ int snd_unit = 0; TUNABLE_INT("hw.snd.unit", &snd_unit); #endif -int snd_maxautovchans = 0; +int snd_maxautovchans = 4; TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); @@ -373,7 +373,8 @@ sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) unit = snd_unit; error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); if (error == 0 && req->newptr != NULL) { - if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) + if (unit < 0 || (pcm_devclass != NULL && + unit >= devclass_get_maxunit(pcm_devclass))) return EINVAL; d = devclass_get_softc(pcm_devclass, unit); if (d == NULL || SLIST_EMPTY(&d->channels)) diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h index b64d75d1e4..e5d6842572 100644 --- a/sys/dev/sound/pcm/sound.h +++ b/sys/dev/sound/pcm/sound.h @@ -24,8 +24,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/sound.h,v 1.63.2.2 2006/04/04 17:43:48 ariff Exp $ - * $DragonFly: src/sys/dev/sound/pcm/sound.h,v 1.11 2007/06/14 21:48:36 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/sound.h,v 1.63.2.3 2007/05/13 20:53:39 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/sound.h,v 1.12 2007/06/16 19:48:05 hasso Exp $ */ /* @@ -133,7 +133,7 @@ nomenclature: #define SD_F_SIMPLEX 0x00000001 #define SD_F_AUTOVCHAN 0x00000002 -#define SD_F_SOFTVOL 0x00000004 +#define SD_F_SOFTPCMVOL 0x00000004 #define SD_F_PRIO_RD 0x10000000 #define SD_F_PRIO_WR 0x20000000 #define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR) diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c index fc148b961e..62071ba61b 100644 --- a/sys/dev/sound/pcm/vchan.c +++ b/sys/dev/sound/pcm/vchan.c @@ -23,15 +23,15 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/vchan.c,v 1.17.2.4 2006/04/04 17:43:49 ariff Exp $ - * $DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.6 2007/06/14 21:48:36 corecode Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/vchan.c,v 1.17.2.5 2007/02/04 06:17:14 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.7 2007/06/16 19:48:05 hasso Exp $ */ #include #include #include "feeder_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.6 2007/06/14 21:48:36 corecode Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.7 2007/06/16 19:48:05 hasso Exp $"); /* * Default speed @@ -177,6 +177,7 @@ vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, static int vchan_free(kobj_t obj, void *data) { + kfree(data, M_DEVBUF); return 0; } diff --git a/sys/dev/sound/usb/uaudio_pcm.c b/sys/dev/sound/usb/uaudio_pcm.c index bc49ce0671..bd79cbcb5d 100644 --- a/sys/dev/sound/usb/uaudio_pcm.c +++ b/sys/dev/sound/usb/uaudio_pcm.c @@ -1,5 +1,5 @@ -/* $FreeBSD: src/sys/dev/sound/usb/uaudio_pcm.c,v 1.15.2.1 2005/12/30 19:55:54 netchild Exp $ */ -/* $DragonFly: src/sys/dev/sound/usb/uaudio_pcm.c,v 1.7 2007/01/20 21:32:36 swildner Exp $ */ +/* $FreeBSD: src/sys/dev/sound/usb/uaudio_pcm.c,v 1.15.2.2 2007/05/13 20:53:40 ariff Exp $ */ +/* $DragonFly: src/sys/dev/sound/usb/uaudio_pcm.c,v 1.8 2007/06/16 19:48:05 hasso Exp $ */ /*- * Copyright (c) 2000-2002 Hiroyuki Aizu @@ -244,12 +244,20 @@ ua_mixer_init(struct snd_mixer *m) d = device_get_softc(ua->sc_dev); mask = uaudio_query_mix_info(pa_dev); - if (d && !(mask & SOUND_MIXER_PCM)) { - /* - * Emulate missing pcm mixer controller - * through FEEDER_VOLUME - */ - d->flags |= SD_F_SOFTVOL; + if (d != NULL) { + if (!(mask & SOUND_MASK_PCM)) { + /* + * Emulate missing pcm mixer controller + * through FEEDER_VOLUME + */ + d->flags |= SD_F_SOFTPCMVOL; + } + if (!(mask & SOUND_MASK_VOLUME)) { + mix_setparentchild(m, SOUND_MIXER_VOLUME, + SOUND_MASK_PCM); + mix_setrealdev(m, SOUND_MIXER_VOLUME, + SOUND_MIXER_NONE); + } } mix_setdevs(m, mask); -- 2.41.0