Home | History | Annotate | Line # | Download | only in pci
neo.c revision 1.19.2.8
      1  1.19.2.8     skrll /*	$NetBSD: neo.c,v 1.19.2.8 2005/11/10 14:06:02 skrll Exp $	*/
      2       1.1   thorpej 
      3       1.1   thorpej /*
      4       1.1   thorpej  * Copyright (c) 1999 Cameron Grant <gandalf (at) vilnya.demon.co.uk>
      5       1.1   thorpej  * All rights reserved.
      6       1.1   thorpej  *
      7       1.1   thorpej  * Derived from the public domain Linux driver
      8       1.1   thorpej  *
      9       1.1   thorpej  * Redistribution and use in source and binary forms, with or without
     10       1.1   thorpej  * modification, are permitted provided that the following conditions
     11       1.1   thorpej  * are met:
     12       1.1   thorpej  * 1. Redistributions of source code must retain the above copyright
     13       1.1   thorpej  *    notice, this list of conditions and the following disclaimer.
     14       1.1   thorpej  * 2. Redistributions in binary form must reproduce the above copyright
     15       1.1   thorpej  *    notice, this list of conditions and the following disclaimer in the
     16       1.1   thorpej  *    documentation and/or other materials provided with the distribution.
     17       1.1   thorpej  *
     18       1.1   thorpej  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     19       1.1   thorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     20       1.1   thorpej  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     21       1.1   thorpej  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     22       1.1   thorpej  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     23       1.1   thorpej  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     24       1.1   thorpej  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     25       1.1   thorpej  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
     26       1.1   thorpej  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     27       1.1   thorpej  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
     28       1.1   thorpej  * SUCH DAMAGE.
     29       1.1   thorpej  *
     30       1.1   thorpej  * FreeBSD: src/sys/dev/sound/pci/neomagic.c,v 1.8 2000/03/20 15:30:50 cg Exp
     31       1.1   thorpej  * OpenBSD: neo.c,v 1.4 2000/07/19 09:04:37 csapuntz Exp
     32       1.1   thorpej  */
     33      1.11     lukem 
     34      1.11     lukem #include <sys/cdefs.h>
     35  1.19.2.8     skrll __KERNEL_RCSID(0, "$NetBSD: neo.c,v 1.19.2.8 2005/11/10 14:06:02 skrll Exp $");
     36       1.1   thorpej 
     37       1.1   thorpej #include <sys/param.h>
     38       1.1   thorpej #include <sys/systm.h>
     39       1.1   thorpej #include <sys/kernel.h>
     40       1.1   thorpej #include <sys/malloc.h>
     41       1.1   thorpej #include <sys/device.h>
     42       1.1   thorpej 
     43       1.1   thorpej #include <machine/bus.h>
     44       1.1   thorpej 
     45       1.1   thorpej #include <dev/pci/pcidevs.h>
     46       1.1   thorpej #include <dev/pci/pcivar.h>
     47       1.1   thorpej 
     48       1.1   thorpej #include <dev/pci/neoreg.h>
     49       1.1   thorpej #include <dev/pci/neo-coeff.h>
     50       1.1   thorpej 
     51       1.1   thorpej #include <sys/audioio.h>
     52       1.1   thorpej #include <dev/audio_if.h>
     53       1.1   thorpej #include <dev/mulaw.h>
     54       1.1   thorpej #include <dev/auconv.h>
     55       1.1   thorpej 
     56       1.1   thorpej #include <dev/ic/ac97var.h>
     57       1.1   thorpej 
     58       1.1   thorpej 
     59       1.1   thorpej /* -------------------------------------------------------------------- */
     60  1.19.2.7     skrll /*
     61       1.1   thorpej  * As of 04/13/00, public documentation on the Neomagic 256 is not available.
     62       1.1   thorpej  * These comments were gleaned by looking at the driver carefully.
     63       1.1   thorpej  *
     64       1.1   thorpej  * The Neomagic 256 AV/ZX chips provide both video and audio capabilities
     65       1.1   thorpej  * on one chip. About 2-6 megabytes of memory are associated with
     66       1.1   thorpej  * the chip. Most of this goes to video frame buffers, but some is used for
     67       1.1   thorpej  * audio buffering
     68       1.1   thorpej  *
     69       1.1   thorpej  * Unlike most PCI audio chips, the Neomagic chip does not rely on DMA.
     70       1.1   thorpej  * Instead, the chip allows you to carve out two ring buffers out of its
     71       1.1   thorpej  * memory. However you carve this and how much you can carve seems to be
     72       1.1   thorpej  * voodoo. The algorithm is in nm_init.
     73       1.1   thorpej  *
     74  1.19.2.7     skrll  * Most Neomagic audio chips use the AC-97 codec interface. However, there
     75       1.1   thorpej  * seem to be a select few chips 256AV chips that do not support AC-97.
     76       1.1   thorpej  * This driver does not support them but there are rumors that it
     77      1.12    simonb  * might work with wss isa drivers. This might require some playing around
     78       1.1   thorpej  * with your BIOS.
     79       1.1   thorpej  *
     80       1.1   thorpej  * The Neomagic 256 AV/ZX have 2 PCI I/O region descriptors. Both of
     81       1.1   thorpej  * them describe a memory region. The frame buffer is the first region
     82       1.1   thorpej  * and the register set is the secodn region.
     83       1.1   thorpej  *
     84       1.1   thorpej  * The register manipulation logic is taken from the Linux driver,
     85       1.1   thorpej  * which is in the public domain.
     86       1.1   thorpej  *
     87       1.1   thorpej  * The Neomagic is even nice enough to map the AC-97 codec registers into
     88       1.1   thorpej  * the register space to allow direct manipulation. Watch out, accessing
     89       1.1   thorpej  * AC-97 registers on the Neomagic requires great delicateness, otherwise
     90       1.1   thorpej  * the thing will hang the PCI bus, rendering your system frozen.
     91       1.1   thorpej  *
     92       1.1   thorpej  * For one, it seems the Neomagic status register that reports AC-97
     93       1.1   thorpej  * readiness should NOT be polled more often than once each 1ms.
     94       1.1   thorpej  *
     95       1.1   thorpej  * Also, writes to the AC-97 register space may take order 40us to
     96       1.1   thorpej  * complete.
     97       1.1   thorpej  *
     98      1.12    simonb  * Unlike many sound engines, the Neomagic does not support (as far as
     99       1.1   thorpej  * we know :) the notion of interrupting every n bytes transferred,
    100       1.1   thorpej  * unlike many DMA engines.  Instead, it allows you to specify one
    101       1.1   thorpej  * location in each ring buffer (called the watermark). When the chip
    102       1.1   thorpej  * passes that location while playing, it signals an interrupt.
    103  1.19.2.7     skrll  *
    104       1.1   thorpej  * The ring buffer size is currently 16k. That is about 100ms of audio
    105      1.18   tsutsui  * at 44.1kHz/stero/16 bit. However, to keep the buffer full, interrupts
    106       1.1   thorpej  * are generated more often than that, so 20-40 interrupts per second
    107       1.1   thorpej  * should not be unexpected. Increasing BUFFSIZE should help minimize
    108       1.1   thorpej  * of glitches due to drivers that spend to much time looping at high
    109       1.1   thorpej  * privelege levels as well as the impact of badly written audio
    110       1.1   thorpej  * interface clients.
    111       1.1   thorpej  *
    112       1.1   thorpej  * TO-DO list:
    113       1.1   thorpej  *    Figure out interaction with video stuff (look at Xfree86 driver?)
    114       1.1   thorpej  *
    115       1.1   thorpej  *    Figure out how to shrink that huge table neo-coeff.h
    116       1.1   thorpej  */
    117       1.1   thorpej 
    118       1.1   thorpej #define	NM_BUFFSIZE	16384
    119       1.1   thorpej 
    120       1.1   thorpej /* device private data */
    121       1.1   thorpej struct neo_softc {
    122  1.19.2.6     skrll 	struct device	dev;
    123       1.1   thorpej 
    124       1.1   thorpej 	bus_space_tag_t bufiot;
    125  1.19.2.6     skrll 	bus_space_handle_t bufioh;
    126       1.1   thorpej 
    127       1.1   thorpej 	bus_space_tag_t regiot;
    128  1.19.2.6     skrll 	bus_space_handle_t regioh;
    129       1.1   thorpej 
    130  1.19.2.6     skrll 	uint32_t	type;
    131  1.19.2.6     skrll 	void		*ih;
    132       1.1   thorpej 
    133      1.19       wiz 	void	(*pintr)(void *);	/* DMA completion intr handler */
    134       1.1   thorpej 	void	*parg;		/* arg for intr() */
    135       1.1   thorpej 
    136      1.19       wiz 	void	(*rintr)(void *);	/* DMA completion intr handler */
    137       1.1   thorpej 	void	*rarg;		/* arg for intr() */
    138       1.1   thorpej 
    139       1.1   thorpej 	vaddr_t	buf_vaddr;
    140       1.1   thorpej 	vaddr_t	rbuf_vaddr;
    141       1.1   thorpej 	vaddr_t	pbuf_vaddr;
    142       1.1   thorpej 	int	pbuf_allocated;
    143       1.1   thorpej 	int	rbuf_allocated;
    144       1.1   thorpej 
    145       1.3   thorpej 	bus_addr_t buf_pciaddr;
    146       1.3   thorpej 	bus_addr_t rbuf_pciaddr;
    147       1.3   thorpej 	bus_addr_t pbuf_pciaddr;
    148       1.3   thorpej 
    149  1.19.2.6     skrll 	uint32_t	ac97_base, ac97_status, ac97_busy;
    150  1.19.2.6     skrll 	uint32_t	buftop, pbuf, rbuf, cbuf, acbuf;
    151  1.19.2.6     skrll 	uint32_t	playint, recint, misc1int, misc2int;
    152  1.19.2.6     skrll 	uint32_t	irsz, badintr;
    153       1.1   thorpej 
    154  1.19.2.6     skrll 	uint32_t	pbufsize;
    155  1.19.2.6     skrll 	uint32_t	rbufsize;
    156       1.1   thorpej 
    157  1.19.2.6     skrll 	uint32_t	pblksize;
    158  1.19.2.6     skrll 	uint32_t	rblksize;
    159       1.1   thorpej 
    160  1.19.2.6     skrll 	uint32_t	pwmark;
    161  1.19.2.6     skrll 	uint32_t	rwmark;
    162       1.1   thorpej 
    163       1.1   thorpej 	struct ac97_codec_if *codec_if;
    164       1.5  sommerfe 	struct ac97_host_if host_if;
    165       1.5  sommerfe 
    166       1.5  sommerfe 	void		*powerhook;
    167       1.1   thorpej };
    168       1.1   thorpej 
    169       1.1   thorpej /* -------------------------------------------------------------------- */
    170       1.1   thorpej 
    171       1.1   thorpej /*
    172       1.1   thorpej  * prototypes
    173       1.1   thorpej  */
    174       1.1   thorpej 
    175  1.19.2.6     skrll static int	nm_waitcd(struct neo_softc *);
    176  1.19.2.6     skrll static int	nm_loadcoeff(struct neo_softc *, int, int);
    177       1.1   thorpej static int	nm_init(struct neo_softc *);
    178       1.1   thorpej 
    179  1.19.2.8     skrll static int	neo_match(struct device *, struct cfdata *, void *);
    180  1.19.2.8     skrll static void	neo_attach(struct device *, struct device *, void *);
    181  1.19.2.8     skrll static int	neo_intr(void *);
    182  1.19.2.8     skrll 
    183  1.19.2.8     skrll static int	neo_query_encoding(void *, struct audio_encoding *);
    184  1.19.2.8     skrll static int	neo_set_params(void *, int, int, audio_params_t *,
    185  1.19.2.8     skrll 			       audio_params_t *, stream_filter_list_t *,
    186  1.19.2.8     skrll 			       stream_filter_list_t *);
    187  1.19.2.8     skrll static int	neo_round_blocksize(void *, int, int, const audio_params_t *);
    188  1.19.2.8     skrll static int	neo_trigger_output(void *, void *, void *, int,
    189  1.19.2.8     skrll 				   void (*)(void *), void *,
    190  1.19.2.8     skrll 				   const audio_params_t *);
    191  1.19.2.8     skrll static int	neo_trigger_input(void *, void *, void *, int,
    192  1.19.2.8     skrll 				  void (*)(void *), void *,
    193  1.19.2.8     skrll 				  const audio_params_t *);
    194  1.19.2.8     skrll static int	neo_halt_output(void *);
    195  1.19.2.8     skrll static int	neo_halt_input(void *);
    196  1.19.2.8     skrll static int	neo_getdev(void *, struct audio_device *);
    197  1.19.2.8     skrll static int	neo_mixer_set_port(void *, mixer_ctrl_t *);
    198  1.19.2.8     skrll static int	neo_mixer_get_port(void *, mixer_ctrl_t *);
    199  1.19.2.8     skrll static int	neo_attach_codec(void *, struct ac97_codec_if *);
    200  1.19.2.8     skrll static int	neo_read_codec(void *, uint8_t, uint16_t *);
    201  1.19.2.8     skrll static int	neo_write_codec(void *, uint8_t, uint16_t);
    202  1.19.2.8     skrll static int     neo_reset_codec(void *);
    203  1.19.2.8     skrll static enum ac97_host_flags neo_flags_codec(void *);
    204  1.19.2.8     skrll static int	neo_query_devinfo(void *, mixer_devinfo_t *);
    205  1.19.2.8     skrll static void *	neo_malloc(void *, int, size_t, struct malloc_type *, int);
    206  1.19.2.8     skrll static void	neo_free(void *, void *, struct malloc_type *);
    207  1.19.2.8     skrll static size_t	neo_round_buffersize(void *, int, size_t);
    208  1.19.2.8     skrll static paddr_t	neo_mappage(void *, void *, off_t, int);
    209  1.19.2.8     skrll static int	neo_get_props(void *);
    210  1.19.2.8     skrll static void	neo_power(int, void *);
    211       1.1   thorpej 
    212      1.15   thorpej CFATTACH_DECL(neo, sizeof(struct neo_softc),
    213      1.16   thorpej     neo_match, neo_attach, NULL, NULL);
    214       1.1   thorpej 
    215  1.19.2.8     skrll static struct audio_device neo_device = {
    216       1.1   thorpej 	"NeoMagic 256",
    217       1.1   thorpej 	"",
    218       1.1   thorpej 	"neo"
    219       1.1   thorpej };
    220       1.1   thorpej 
    221       1.1   thorpej /* The actual rates supported by the card. */
    222       1.1   thorpej static const int samplerates[9] = {
    223       1.1   thorpej 	8000,
    224       1.1   thorpej 	11025,
    225       1.1   thorpej 	16000,
    226       1.1   thorpej 	22050,
    227       1.1   thorpej 	24000,
    228       1.1   thorpej 	32000,
    229       1.1   thorpej 	44100,
    230       1.1   thorpej 	48000,
    231       1.1   thorpej 	99999999
    232       1.1   thorpej };
    233       1.1   thorpej 
    234  1.19.2.6     skrll #define NEO_NFORMATS	4
    235  1.19.2.6     skrll static const struct audio_format neo_formats[NEO_NFORMATS] = {
    236  1.19.2.6     skrll 	{NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16,
    237  1.19.2.6     skrll 	 2, AUFMT_STEREO, 8, {8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000}},
    238  1.19.2.6     skrll 	{NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16,
    239  1.19.2.6     skrll 	 1, AUFMT_MONAURAL, 8, {8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000}},
    240  1.19.2.6     skrll 	{NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_ULINEAR_LE, 8, 8,
    241  1.19.2.6     skrll 	 2, AUFMT_STEREO, 8, {8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000}},
    242  1.19.2.6     skrll 	{NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_ULINEAR_LE, 8, 8,
    243  1.19.2.6     skrll 	 1, AUFMT_MONAURAL, 8, {8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000}},
    244  1.19.2.6     skrll };
    245  1.19.2.6     skrll 
    246       1.1   thorpej /* -------------------------------------------------------------------- */
    247       1.1   thorpej 
    248  1.19.2.8     skrll static const struct audio_hw_if neo_hw_if = {
    249  1.19.2.6     skrll 	NULL,				/* open */
    250  1.19.2.6     skrll 	NULL,				/* close */
    251       1.1   thorpej 	NULL,				/* drain */
    252       1.1   thorpej 	neo_query_encoding,
    253       1.1   thorpej 	neo_set_params,
    254       1.1   thorpej 	neo_round_blocksize,
    255       1.1   thorpej 	NULL,				/* commit_setting */
    256       1.1   thorpej 	NULL,				/* init_output */
    257       1.1   thorpej 	NULL,				/* init_input */
    258       1.1   thorpej 	NULL,				/* start_output */
    259       1.1   thorpej 	NULL,				/* start_input */
    260       1.1   thorpej 	neo_halt_output,
    261       1.1   thorpej 	neo_halt_input,
    262       1.1   thorpej 	NULL,				/* speaker_ctl */
    263       1.1   thorpej 	neo_getdev,
    264       1.1   thorpej 	NULL,				/* getfd */
    265       1.1   thorpej 	neo_mixer_set_port,
    266       1.1   thorpej 	neo_mixer_get_port,
    267       1.1   thorpej 	neo_query_devinfo,
    268       1.1   thorpej 	neo_malloc,
    269       1.1   thorpej 	neo_free,
    270       1.1   thorpej 	neo_round_buffersize,
    271       1.3   thorpej 	neo_mappage,
    272       1.1   thorpej 	neo_get_props,
    273       1.1   thorpej 	neo_trigger_output,
    274       1.1   thorpej 	neo_trigger_input,
    275      1.10  augustss 	NULL,
    276       1.1   thorpej };
    277       1.1   thorpej 
    278       1.1   thorpej /* -------------------------------------------------------------------- */
    279       1.1   thorpej 
    280       1.1   thorpej #define	nm_rd_1(sc, regno)						\
    281       1.1   thorpej 	bus_space_read_1((sc)->regiot, (sc)->regioh, (regno))
    282       1.1   thorpej 
    283       1.1   thorpej #define	nm_rd_2(sc, regno)						\
    284       1.1   thorpej 	bus_space_read_2((sc)->regiot, (sc)->regioh, (regno))
    285       1.1   thorpej 
    286       1.1   thorpej #define	nm_rd_4(sc, regno)						\
    287       1.1   thorpej 	bus_space_read_4((sc)->regiot, (sc)->regioh, (regno))
    288       1.1   thorpej 
    289       1.1   thorpej #define	nm_wr_1(sc, regno, val)						\
    290       1.1   thorpej 	bus_space_write_1((sc)->regiot, (sc)->regioh, (regno), (val))
    291       1.1   thorpej 
    292       1.1   thorpej #define	nm_wr_2(sc, regno, val)						\
    293       1.1   thorpej 	bus_space_write_2((sc)->regiot, (sc)->regioh, (regno), (val))
    294       1.1   thorpej 
    295       1.1   thorpej #define	nm_wr_4(sc, regno, val)						\
    296       1.1   thorpej 	bus_space_write_4((sc)->regiot, (sc)->regioh, (regno), (val))
    297       1.1   thorpej 
    298       1.1   thorpej #define	nm_rdbuf_4(sc, regno)						\
    299       1.1   thorpej 	bus_space_read_4((sc)->bufiot, (sc)->bufioh, (regno))
    300       1.1   thorpej 
    301       1.1   thorpej #define	nm_wrbuf_1(sc, regno, val)					\
    302       1.1   thorpej 	bus_space_write_1((sc)->bufiot, (sc)->bufioh, (regno), (val))
    303       1.1   thorpej 
    304       1.1   thorpej /* ac97 codec */
    305       1.1   thorpej static int
    306       1.1   thorpej nm_waitcd(struct neo_softc *sc)
    307       1.1   thorpej {
    308  1.19.2.6     skrll 	int cnt;
    309  1.19.2.6     skrll 	int fail;
    310       1.1   thorpej 
    311  1.19.2.6     skrll 	cnt  = 10;
    312  1.19.2.6     skrll 	fail = 1;
    313       1.1   thorpej 	while (cnt-- > 0) {
    314       1.1   thorpej 		if (nm_rd_2(sc, sc->ac97_status) & sc->ac97_busy)
    315       1.1   thorpej 			DELAY(100);
    316       1.1   thorpej 		else {
    317  1.19.2.6     skrll 			fail = 0;
    318       1.1   thorpej 			break;
    319       1.1   thorpej 		}
    320       1.1   thorpej 	}
    321  1.19.2.6     skrll 	return fail;
    322       1.1   thorpej }
    323       1.1   thorpej 
    324       1.1   thorpej static void
    325  1.19.2.6     skrll nm_ackint(struct neo_softc *sc, uint32_t num)
    326       1.1   thorpej {
    327       1.1   thorpej 
    328       1.1   thorpej 	switch (sc->type) {
    329       1.1   thorpej 	case PCI_PRODUCT_NEOMAGIC_NMMM256AV_AU:
    330       1.1   thorpej 		nm_wr_2(sc, NM_INT_REG, num << 1);
    331       1.1   thorpej 		break;
    332       1.1   thorpej 
    333       1.1   thorpej 	case PCI_PRODUCT_NEOMAGIC_NMMM256ZX_AU:
    334       1.1   thorpej 		nm_wr_4(sc, NM_INT_REG, num);
    335       1.1   thorpej 		break;
    336       1.1   thorpej 	}
    337       1.1   thorpej }
    338       1.1   thorpej 
    339       1.1   thorpej static int
    340       1.1   thorpej nm_loadcoeff(struct neo_softc *sc, int dir, int num)
    341       1.1   thorpej {
    342       1.1   thorpej 	int ofs, sz, i;
    343  1.19.2.6     skrll 	uint32_t addr;
    344       1.1   thorpej 
    345       1.1   thorpej 	addr = (dir == AUMODE_PLAY)? 0x01c : 0x21c;
    346       1.1   thorpej 	if (dir == AUMODE_RECORD)
    347       1.1   thorpej 		num += 8;
    348       1.1   thorpej 	sz = coefficientSizes[num];
    349       1.1   thorpej 	ofs = 0;
    350       1.1   thorpej 	while (num-- > 0)
    351       1.1   thorpej 		ofs+= coefficientSizes[num];
    352       1.1   thorpej 	for (i = 0; i < sz; i++)
    353       1.1   thorpej 		nm_wrbuf_1(sc, sc->cbuf + i, coefficients[ofs + i]);
    354       1.1   thorpej 	nm_wr_4(sc, addr, sc->cbuf);
    355       1.1   thorpej 	if (dir == AUMODE_PLAY)
    356       1.1   thorpej 		sz--;
    357       1.1   thorpej 	nm_wr_4(sc, addr + 4, sc->cbuf + sz);
    358       1.1   thorpej 	return 0;
    359       1.1   thorpej }
    360       1.1   thorpej 
    361       1.1   thorpej /* The interrupt handler */
    362  1.19.2.8     skrll static int
    363       1.1   thorpej neo_intr(void *p)
    364       1.1   thorpej {
    365  1.19.2.6     skrll 	struct neo_softc *sc;
    366  1.19.2.1     skrll 	int status, x;
    367  1.19.2.6     skrll 	int rv;
    368       1.1   thorpej 
    369  1.19.2.6     skrll 	sc = (struct neo_softc *)p;
    370  1.19.2.6     skrll 	rv = 0;
    371       1.1   thorpej 	status = (sc->irsz == 2) ?
    372       1.1   thorpej 	    nm_rd_2(sc, NM_INT_REG) :
    373       1.1   thorpej 	    nm_rd_4(sc, NM_INT_REG);
    374       1.1   thorpej 
    375       1.1   thorpej 	if (status & sc->playint) {
    376       1.1   thorpej 		status &= ~sc->playint;
    377       1.1   thorpej 
    378       1.1   thorpej 		sc->pwmark += sc->pblksize;
    379       1.1   thorpej 		sc->pwmark %= sc->pbufsize;
    380       1.1   thorpej 
    381       1.1   thorpej 		nm_wr_4(sc, NM_PBUFFER_WMARK, sc->pbuf + sc->pwmark);
    382       1.1   thorpej 
    383       1.1   thorpej 		nm_ackint(sc, sc->playint);
    384       1.1   thorpej 
    385       1.1   thorpej 		if (sc->pintr)
    386       1.1   thorpej 			(*sc->pintr)(sc->parg);
    387       1.1   thorpej 
    388       1.1   thorpej 		rv = 1;
    389       1.1   thorpej 	}
    390       1.1   thorpej 	if (status & sc->recint) {
    391       1.1   thorpej 		status &= ~sc->recint;
    392       1.1   thorpej 
    393       1.1   thorpej 		sc->rwmark += sc->rblksize;
    394       1.1   thorpej 		sc->rwmark %= sc->rbufsize;
    395  1.19.2.8     skrll 		nm_wr_4(sc, NM_RBUFFER_WMARK, sc->rbuf + sc->rwmark);
    396       1.1   thorpej 		nm_ackint(sc, sc->recint);
    397       1.1   thorpej 		if (sc->rintr)
    398       1.1   thorpej 			(*sc->rintr)(sc->rarg);
    399       1.1   thorpej 
    400       1.1   thorpej 		rv = 1;
    401       1.1   thorpej 	}
    402       1.1   thorpej 	if (status & sc->misc1int) {
    403       1.1   thorpej 		status &= ~sc->misc1int;
    404       1.1   thorpej 		nm_ackint(sc, sc->misc1int);
    405       1.1   thorpej 		x = nm_rd_1(sc, 0x400);
    406       1.1   thorpej 		nm_wr_1(sc, 0x400, x | 2);
    407       1.1   thorpej 		printf("%s: misc int 1\n", sc->dev.dv_xname);
    408       1.1   thorpej 		rv = 1;
    409       1.1   thorpej 	}
    410       1.1   thorpej 	if (status & sc->misc2int) {
    411       1.1   thorpej 		status &= ~sc->misc2int;
    412       1.1   thorpej 		nm_ackint(sc, sc->misc2int);
    413       1.1   thorpej 		x = nm_rd_1(sc, 0x400);
    414       1.1   thorpej 		nm_wr_1(sc, 0x400, x & ~2);
    415       1.1   thorpej 		printf("%s: misc int 2\n", sc->dev.dv_xname);
    416       1.1   thorpej 		rv = 1;
    417       1.1   thorpej 	}
    418       1.1   thorpej 	if (status) {
    419       1.1   thorpej 		status &= ~sc->misc2int;
    420       1.1   thorpej 		nm_ackint(sc, sc->misc2int);
    421       1.1   thorpej 		printf("%s: unknown int\n", sc->dev.dv_xname);
    422       1.1   thorpej 		rv = 1;
    423       1.1   thorpej 	}
    424       1.1   thorpej 
    425  1.19.2.6     skrll 	return rv;
    426       1.1   thorpej }
    427       1.1   thorpej 
    428       1.1   thorpej /* -------------------------------------------------------------------- */
    429       1.1   thorpej 
    430       1.1   thorpej /*
    431       1.1   thorpej  * Probe and attach the card
    432       1.1   thorpej  */
    433       1.1   thorpej 
    434       1.1   thorpej static int
    435       1.1   thorpej nm_init(struct neo_softc *sc)
    436       1.1   thorpej {
    437  1.19.2.6     skrll 	uint32_t ofs, i;
    438       1.1   thorpej 
    439       1.1   thorpej 	switch (sc->type) {
    440       1.1   thorpej 	case PCI_PRODUCT_NEOMAGIC_NMMM256AV_AU:
    441       1.1   thorpej 		sc->ac97_base = NM_MIXER_OFFSET;
    442       1.1   thorpej 		sc->ac97_status = NM_MIXER_STATUS_OFFSET;
    443       1.1   thorpej 		sc->ac97_busy = NM_MIXER_READY_MASK;
    444       1.1   thorpej 
    445       1.1   thorpej 		sc->buftop = 2560 * 1024;
    446       1.1   thorpej 
    447       1.1   thorpej 		sc->irsz = 2;
    448       1.1   thorpej 		sc->playint = NM_PLAYBACK_INT;
    449       1.1   thorpej 		sc->recint = NM_RECORD_INT;
    450       1.1   thorpej 		sc->misc1int = NM_MISC_INT_1;
    451       1.1   thorpej 		sc->misc2int = NM_MISC_INT_2;
    452       1.1   thorpej 		break;
    453       1.1   thorpej 
    454       1.1   thorpej 	case PCI_PRODUCT_NEOMAGIC_NMMM256ZX_AU:
    455       1.1   thorpej 		sc->ac97_base = NM_MIXER_OFFSET;
    456       1.1   thorpej 		sc->ac97_status = NM2_MIXER_STATUS_OFFSET;
    457       1.1   thorpej 		sc->ac97_busy = NM2_MIXER_READY_MASK;
    458       1.1   thorpej 
    459       1.1   thorpej 		sc->buftop = (nm_rd_2(sc, 0xa0b) ? 6144 : 4096) * 1024;
    460       1.1   thorpej 
    461       1.1   thorpej 		sc->irsz = 4;
    462       1.1   thorpej 		sc->playint = NM2_PLAYBACK_INT;
    463       1.1   thorpej 		sc->recint = NM2_RECORD_INT;
    464       1.1   thorpej 		sc->misc1int = NM2_MISC_INT_1;
    465       1.1   thorpej 		sc->misc2int = NM2_MISC_INT_2;
    466       1.1   thorpej 		break;
    467       1.1   thorpej #ifdef DIAGNOSTIC
    468       1.1   thorpej 	default:
    469       1.1   thorpej 		panic("nm_init: impossible");
    470       1.1   thorpej #endif
    471       1.1   thorpej 	}
    472       1.1   thorpej 
    473       1.1   thorpej 	sc->badintr = 0;
    474       1.1   thorpej 	ofs = sc->buftop - 0x0400;
    475       1.1   thorpej 	sc->buftop -= 0x1400;
    476       1.1   thorpej 
    477  1.19.2.6     skrll 	if ((nm_rdbuf_4(sc, ofs) & NM_SIG_MASK) == NM_SIGNATURE) {
    478       1.1   thorpej 		i = nm_rdbuf_4(sc, ofs + 4);
    479       1.1   thorpej 		if (i != 0 && i != 0xffffffff)
    480       1.1   thorpej 			sc->buftop = i;
    481       1.1   thorpej 	}
    482       1.1   thorpej 
    483       1.1   thorpej 	sc->cbuf = sc->buftop - NM_MAX_COEFFICIENT;
    484       1.1   thorpej 	sc->rbuf = sc->cbuf - NM_BUFFSIZE;
    485       1.1   thorpej 	sc->pbuf = sc->rbuf - NM_BUFFSIZE;
    486       1.1   thorpej 	sc->acbuf = sc->pbuf - (NM_TOTAL_COEFF_COUNT * 4);
    487       1.1   thorpej 
    488       1.1   thorpej 	sc->buf_vaddr = (vaddr_t) bus_space_vaddr(sc->bufiot, sc->bufioh);
    489       1.1   thorpej 	sc->rbuf_vaddr = sc->buf_vaddr + sc->rbuf;
    490       1.1   thorpej 	sc->pbuf_vaddr = sc->buf_vaddr + sc->pbuf;
    491       1.1   thorpej 
    492       1.3   thorpej 	sc->rbuf_pciaddr = sc->buf_pciaddr + sc->rbuf;
    493       1.3   thorpej 	sc->pbuf_pciaddr = sc->buf_pciaddr + sc->pbuf;
    494       1.3   thorpej 
    495       1.1   thorpej 	nm_wr_1(sc, 0, 0x11);
    496       1.1   thorpej 	nm_wr_1(sc, NM_RECORD_ENABLE_REG, 0);
    497       1.1   thorpej 	nm_wr_2(sc, 0x214, 0);
    498       1.1   thorpej 
    499       1.1   thorpej 	return 0;
    500       1.1   thorpej }
    501       1.1   thorpej 
    502  1.19.2.8     skrll static int
    503       1.2   thorpej neo_match(struct device *parent, struct cfdata *match, void *aux)
    504       1.2   thorpej {
    505  1.19.2.6     skrll 	struct pci_attach_args *pa;
    506       1.2   thorpej 	pcireg_t subdev;
    507       1.2   thorpej 
    508  1.19.2.6     skrll 	pa = aux;
    509       1.2   thorpej 	if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_NEOMAGIC)
    510  1.19.2.6     skrll 		return 0;
    511       1.2   thorpej 
    512       1.2   thorpej 	subdev = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
    513       1.2   thorpej 
    514       1.2   thorpej 	switch (PCI_PRODUCT(pa->pa_id)) {
    515       1.2   thorpej 	case PCI_PRODUCT_NEOMAGIC_NMMM256AV_AU:
    516       1.2   thorpej 		/*
    517       1.2   thorpej 		 * We have to weed-out the non-AC'97 versions of
    518       1.2   thorpej 		 * the chip (i.e. the ones that are known to work
    519       1.2   thorpej 		 * in WSS emulation mode), as they won't work with
    520       1.2   thorpej 		 * this driver.
    521       1.2   thorpej 		 */
    522       1.2   thorpej 		switch (PCI_VENDOR(subdev)) {
    523       1.2   thorpej 		case PCI_VENDOR_DELL:
    524       1.2   thorpej 			switch (PCI_PRODUCT(subdev)) {
    525       1.2   thorpej 			case 0x008f:
    526  1.19.2.6     skrll 				return 0;
    527       1.2   thorpej 			}
    528       1.2   thorpej 			break;
    529       1.2   thorpej 
    530       1.2   thorpej 		case PCI_VENDOR_HP:
    531       1.2   thorpej 			switch (PCI_PRODUCT(subdev)) {
    532       1.2   thorpej 			case 0x0007:
    533  1.19.2.6     skrll 				return 0;
    534       1.2   thorpej 			}
    535       1.8  drochner 			break;
    536       1.8  drochner 
    537       1.8  drochner 		case PCI_VENDOR_IBM:
    538       1.8  drochner 			switch (PCI_PRODUCT(subdev)) {
    539       1.8  drochner 			case 0x00dd:
    540  1.19.2.6     skrll 				return 0;
    541       1.8  drochner 			}
    542       1.8  drochner 			break;
    543       1.2   thorpej 		}
    544  1.19.2.6     skrll 		return 1;
    545       1.2   thorpej 
    546       1.2   thorpej 	case PCI_PRODUCT_NEOMAGIC_NMMM256ZX_AU:
    547  1.19.2.6     skrll 		return 1;
    548       1.2   thorpej 	}
    549       1.2   thorpej 
    550  1.19.2.6     skrll 	return 0;
    551       1.2   thorpej }
    552       1.2   thorpej 
    553  1.19.2.8     skrll static void
    554       1.5  sommerfe neo_power(int why, void *addr)
    555       1.5  sommerfe {
    556  1.19.2.6     skrll 	struct neo_softc *sc;
    557       1.5  sommerfe 
    558  1.19.2.6     skrll 	sc = (struct neo_softc *)addr;
    559       1.5  sommerfe 	if (why == PWR_RESUME) {
    560       1.5  sommerfe 		nm_init(sc);
    561  1.19.2.6     skrll 		sc->codec_if->vtbl->restore_ports(sc->codec_if);
    562       1.5  sommerfe 	}
    563       1.5  sommerfe }
    564       1.5  sommerfe 
    565  1.19.2.8     skrll static void
    566       1.1   thorpej neo_attach(struct device *parent, struct device *self, void *aux)
    567       1.1   thorpej {
    568  1.19.2.6     skrll 	struct neo_softc *sc;
    569  1.19.2.6     skrll 	struct pci_attach_args *pa;
    570  1.19.2.6     skrll 	pci_chipset_tag_t pc;
    571       1.1   thorpej 	char const *intrstr;
    572       1.1   thorpej 	pci_intr_handle_t ih;
    573       1.1   thorpej 	pcireg_t csr;
    574       1.1   thorpej 
    575  1.19.2.6     skrll 	sc = (struct neo_softc *)self;
    576  1.19.2.6     skrll 	pa = (struct pci_attach_args *)aux;
    577  1.19.2.6     skrll 	pc = pa->pa_pc;
    578  1.19.2.6     skrll 
    579       1.1   thorpej 	sc->type = PCI_PRODUCT(pa->pa_id);
    580       1.1   thorpej 
    581       1.1   thorpej 	printf(": NeoMagic 256%s audio\n",
    582       1.1   thorpej 	    sc->type == PCI_PRODUCT_NEOMAGIC_NMMM256AV_AU ? "AV" : "ZX");
    583       1.1   thorpej 
    584       1.1   thorpej 	/* Map I/O register */
    585       1.1   thorpej 	if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM, 0,
    586       1.3   thorpej 			   &sc->bufiot, &sc->bufioh, &sc->buf_pciaddr, NULL)) {
    587       1.1   thorpej 		printf("%s: can't map buffer\n", sc->dev.dv_xname);
    588       1.1   thorpej 		return;
    589       1.1   thorpej 	}
    590       1.1   thorpej 
    591      1.13   thorpej 	if (pci_mapreg_map(pa, PCI_MAPREG_START + 4, PCI_MAPREG_TYPE_MEM,
    592      1.13   thorpej 	    BUS_SPACE_MAP_LINEAR, &sc->regiot, &sc->regioh, NULL, NULL)) {
    593       1.1   thorpej 		printf("%s: can't map registers\n", sc->dev.dv_xname);
    594       1.1   thorpej 		return;
    595       1.1   thorpej 	}
    596       1.1   thorpej 
    597       1.1   thorpej 	/* Map and establish the interrupt. */
    598       1.7  sommerfe 	if (pci_intr_map(pa, &ih)) {
    599       1.1   thorpej 		printf("%s: couldn't map interrupt\n", sc->dev.dv_xname);
    600       1.1   thorpej 		return;
    601       1.1   thorpej 	}
    602       1.1   thorpej 
    603       1.1   thorpej 	intrstr = pci_intr_string(pc, ih);
    604       1.1   thorpej 	sc->ih = pci_intr_establish(pc, ih, IPL_AUDIO, neo_intr, sc);
    605       1.1   thorpej 
    606       1.1   thorpej 	if (sc->ih == NULL) {
    607       1.1   thorpej 		printf("%s: couldn't establish interrupt",
    608       1.1   thorpej 		       sc->dev.dv_xname);
    609       1.1   thorpej 		if (intrstr != NULL)
    610       1.1   thorpej 			printf(" at %s", intrstr);
    611       1.1   thorpej 		printf("\n");
    612       1.1   thorpej 		return;
    613       1.1   thorpej 	}
    614       1.1   thorpej 	printf("%s: interruping at %s\n", sc->dev.dv_xname, intrstr);
    615       1.1   thorpej 
    616  1.19.2.1     skrll 	if (nm_init(sc) != 0)
    617       1.1   thorpej 		return;
    618       1.1   thorpej 
    619       1.1   thorpej 	/* Enable the device. */
    620       1.1   thorpej 	csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
    621       1.1   thorpej 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
    622       1.1   thorpej 		       csr | PCI_COMMAND_MASTER_ENABLE);
    623       1.1   thorpej 
    624       1.1   thorpej 	sc->host_if.arg = sc;
    625       1.1   thorpej 
    626       1.1   thorpej 	sc->host_if.attach = neo_attach_codec;
    627       1.1   thorpej 	sc->host_if.read   = neo_read_codec;
    628       1.1   thorpej 	sc->host_if.write  = neo_write_codec;
    629       1.1   thorpej 	sc->host_if.reset  = neo_reset_codec;
    630       1.1   thorpej 	sc->host_if.flags  = neo_flags_codec;
    631       1.1   thorpej 
    632  1.19.2.6     skrll 	if (ac97_attach(&sc->host_if, self) != 0)
    633       1.1   thorpej 		return;
    634       1.5  sommerfe 
    635       1.5  sommerfe 	sc->powerhook = powerhook_establish(neo_power, sc);
    636       1.1   thorpej 
    637       1.1   thorpej 	audio_attach_mi(&neo_hw_if, sc, &sc->dev);
    638       1.1   thorpej }
    639       1.1   thorpej 
    640  1.19.2.8     skrll static int
    641  1.19.2.6     skrll neo_read_codec(void *v, uint8_t a, uint16_t *d)
    642       1.1   thorpej {
    643  1.19.2.6     skrll 	struct neo_softc *sc;
    644  1.19.2.6     skrll 
    645  1.19.2.6     skrll 	sc = v;
    646       1.1   thorpej 	if (!nm_waitcd(sc)) {
    647       1.1   thorpej 		*d = nm_rd_2(sc, sc->ac97_base + a);
    648       1.1   thorpej 		DELAY(1000);
    649       1.1   thorpej 		return 0;
    650       1.1   thorpej 	}
    651       1.1   thorpej 
    652  1.19.2.6     skrll 	return ENXIO;
    653       1.1   thorpej }
    654       1.1   thorpej 
    655       1.1   thorpej 
    656  1.19.2.8     skrll static int
    657       1.1   thorpej neo_write_codec(void *v, u_int8_t a, u_int16_t d)
    658       1.1   thorpej {
    659  1.19.2.6     skrll 	struct neo_softc *sc;
    660  1.19.2.6     skrll 	int cnt;
    661       1.1   thorpej 
    662  1.19.2.6     skrll 	sc = v;
    663  1.19.2.6     skrll 	cnt = 3;
    664       1.1   thorpej 	if (!nm_waitcd(sc)) {
    665       1.1   thorpej 		while (cnt-- > 0) {
    666       1.1   thorpej 			nm_wr_2(sc, sc->ac97_base + a, d);
    667       1.1   thorpej 			if (!nm_waitcd(sc)) {
    668       1.1   thorpej 				DELAY(1000);
    669  1.19.2.6     skrll 				return 0;
    670       1.1   thorpej 			}
    671       1.1   thorpej 		}
    672       1.1   thorpej 	}
    673       1.1   thorpej 
    674  1.19.2.6     skrll 	return ENXIO;
    675       1.1   thorpej }
    676       1.1   thorpej 
    677  1.19.2.8     skrll static int
    678       1.1   thorpej neo_attach_codec(void *v, struct ac97_codec_if *codec_if)
    679       1.1   thorpej {
    680  1.19.2.6     skrll 	struct neo_softc *sc;
    681       1.1   thorpej 
    682  1.19.2.6     skrll 	sc = v;
    683       1.1   thorpej 	sc->codec_if = codec_if;
    684  1.19.2.6     skrll 	return 0;
    685       1.1   thorpej }
    686       1.1   thorpej 
    687  1.19.2.8     skrll static int
    688       1.1   thorpej neo_reset_codec(void *v)
    689       1.1   thorpej {
    690  1.19.2.6     skrll 	struct neo_softc *sc;
    691       1.1   thorpej 
    692  1.19.2.6     skrll 	sc = v;
    693       1.1   thorpej 	nm_wr_1(sc, 0x6c0, 0x01);
    694       1.1   thorpej 	nm_wr_1(sc, 0x6cc, 0x87);
    695       1.1   thorpej 	nm_wr_1(sc, 0x6cc, 0x80);
    696       1.1   thorpej 	nm_wr_1(sc, 0x6cc, 0x00);
    697  1.19.2.4     skrll 	return 0;
    698       1.1   thorpej }
    699       1.1   thorpej 
    700  1.19.2.8     skrll static enum ac97_host_flags
    701       1.1   thorpej neo_flags_codec(void *v)
    702       1.1   thorpej {
    703       1.1   thorpej 
    704  1.19.2.6     skrll 	return AC97_HOST_DONT_READ;
    705       1.1   thorpej }
    706       1.1   thorpej 
    707  1.19.2.8     skrll static int
    708       1.1   thorpej neo_query_encoding(void *addr, struct audio_encoding *fp)
    709       1.1   thorpej {
    710       1.1   thorpej 
    711       1.1   thorpej 	switch (fp->index) {
    712       1.1   thorpej 	case 0:
    713       1.1   thorpej 		strcpy(fp->name, AudioEulinear);
    714       1.1   thorpej 		fp->encoding = AUDIO_ENCODING_ULINEAR;
    715       1.1   thorpej 		fp->precision = 8;
    716       1.1   thorpej 		fp->flags = 0;
    717  1.19.2.6     skrll 		return 0;
    718       1.1   thorpej 	case 1:
    719       1.1   thorpej 		strcpy(fp->name, AudioEmulaw);
    720       1.1   thorpej 		fp->encoding = AUDIO_ENCODING_ULAW;
    721       1.1   thorpej 		fp->precision = 8;
    722       1.1   thorpej 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
    723  1.19.2.6     skrll 		return 0;
    724       1.1   thorpej 	case 2:
    725       1.1   thorpej 		strcpy(fp->name, AudioEalaw);
    726       1.1   thorpej 		fp->encoding = AUDIO_ENCODING_ALAW;
    727       1.1   thorpej 		fp->precision = 8;
    728       1.1   thorpej 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
    729  1.19.2.6     skrll 		return 0;
    730       1.1   thorpej 	case 3:
    731       1.1   thorpej 		strcpy(fp->name, AudioEslinear);
    732       1.1   thorpej 		fp->encoding = AUDIO_ENCODING_SLINEAR;
    733       1.1   thorpej 		fp->precision = 8;
    734       1.1   thorpej 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
    735       1.1   thorpej 		return (0);
    736       1.1   thorpej 	case 4:
    737       1.1   thorpej 		strcpy(fp->name, AudioEslinear_le);
    738       1.1   thorpej 		fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
    739       1.1   thorpej 		fp->precision = 16;
    740       1.1   thorpej 		fp->flags = 0;
    741  1.19.2.6     skrll 		return 0;
    742       1.1   thorpej 	case 5:
    743       1.1   thorpej 		strcpy(fp->name, AudioEulinear_le);
    744       1.1   thorpej 		fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
    745       1.1   thorpej 		fp->precision = 16;
    746       1.1   thorpej 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
    747  1.19.2.6     skrll 		return 0;
    748       1.1   thorpej 	case 6:
    749       1.1   thorpej 		strcpy(fp->name, AudioEslinear_be);
    750       1.1   thorpej 		fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
    751       1.1   thorpej 		fp->precision = 16;
    752       1.1   thorpej 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
    753  1.19.2.6     skrll 		return 0;
    754       1.1   thorpej 	case 7:
    755       1.1   thorpej 		strcpy(fp->name, AudioEulinear_be);
    756       1.1   thorpej 		fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
    757       1.1   thorpej 		fp->precision = 16;
    758       1.1   thorpej 		fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
    759  1.19.2.6     skrll 		return 0;
    760       1.1   thorpej 	default:
    761  1.19.2.6     skrll 		return EINVAL;
    762       1.1   thorpej 	}
    763       1.1   thorpej }
    764       1.1   thorpej 
    765       1.1   thorpej /* Todo: don't commit settings to card until we've verified all parameters */
    766  1.19.2.8     skrll static int
    767  1.19.2.6     skrll neo_set_params(void *addr, int setmode, int usemode, audio_params_t *play,
    768  1.19.2.6     skrll     audio_params_t *rec, stream_filter_list_t *pfil, stream_filter_list_t *rfil)
    769       1.1   thorpej {
    770  1.19.2.6     skrll 	struct neo_softc *sc;
    771  1.19.2.6     skrll 	audio_params_t *p;
    772  1.19.2.6     skrll 	stream_filter_list_t *fil;
    773  1.19.2.6     skrll 	uint32_t base;
    774  1.19.2.6     skrll 	uint8_t x;
    775  1.19.2.6     skrll 	int mode, i;
    776       1.1   thorpej 
    777  1.19.2.6     skrll 	sc = addr;
    778  1.19.2.6     skrll 	for (mode = AUMODE_RECORD; mode != -1;
    779       1.1   thorpej 	     mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
    780       1.1   thorpej 		if ((setmode & mode) == 0)
    781       1.1   thorpej 			continue;
    782       1.1   thorpej 
    783       1.1   thorpej 		p = mode == AUMODE_PLAY ? play : rec;
    784       1.1   thorpej 
    785       1.1   thorpej 		if (p == NULL) continue;
    786       1.1   thorpej 
    787       1.1   thorpej 		for (x = 0; x < 8; x++) {
    788       1.1   thorpej 			if (p->sample_rate <
    789       1.1   thorpej 			    (samplerates[x] + samplerates[x + 1]) / 2)
    790       1.1   thorpej 				break;
    791       1.1   thorpej 		}
    792       1.1   thorpej 		if (x == 8)
    793  1.19.2.6     skrll 			return EINVAL;
    794       1.1   thorpej 
    795       1.1   thorpej 		p->sample_rate = samplerates[x];
    796       1.1   thorpej 		nm_loadcoeff(sc, mode, x);
    797       1.1   thorpej 
    798       1.1   thorpej 		x <<= 4;
    799       1.1   thorpej 		x &= NM_RATE_MASK;
    800       1.1   thorpej 		if (p->precision == 16)
    801       1.1   thorpej 			x |= NM_RATE_BITS_16;
    802       1.1   thorpej 		if (p->channels == 2)
    803       1.1   thorpej 			x |= NM_RATE_STEREO;
    804       1.1   thorpej 
    805  1.19.2.6     skrll 		base = (mode == AUMODE_PLAY)?
    806       1.1   thorpej 		    NM_PLAYBACK_REG_OFFSET : NM_RECORD_REG_OFFSET;
    807       1.1   thorpej 		nm_wr_1(sc, base + NM_RATE_REG_OFFSET, x);
    808       1.1   thorpej 
    809  1.19.2.6     skrll 		fil = mode == AUMODE_PLAY ? pfil : rfil;
    810  1.19.2.6     skrll 		i = auconv_set_converter(neo_formats, NEO_NFORMATS,
    811  1.19.2.6     skrll 					 mode, p, FALSE, fil);
    812  1.19.2.6     skrll 		if (i < 0)
    813  1.19.2.6     skrll 			return EINVAL;
    814  1.19.2.6     skrll 	}
    815       1.1   thorpej 
    816  1.19.2.6     skrll 	return 0;
    817       1.1   thorpej }
    818       1.1   thorpej 
    819  1.19.2.8     skrll static int
    820  1.19.2.6     skrll neo_round_blocksize(void *addr, int blk, int mode, const audio_params_t *param)
    821       1.1   thorpej {
    822       1.1   thorpej 
    823  1.19.2.6     skrll 	return NM_BUFFSIZE / 2;
    824       1.1   thorpej }
    825       1.1   thorpej 
    826  1.19.2.8     skrll static int
    827       1.1   thorpej neo_trigger_output(void *addr, void *start, void *end, int blksize,
    828  1.19.2.6     skrll     void (*intr)(void *), void *arg, const audio_params_t *param)
    829       1.1   thorpej {
    830  1.19.2.6     skrll 	struct neo_softc *sc;
    831       1.1   thorpej 	int ssz;
    832       1.1   thorpej 
    833  1.19.2.6     skrll 	sc = addr;
    834       1.1   thorpej 	sc->pintr = intr;
    835       1.1   thorpej 	sc->parg = arg;
    836       1.1   thorpej 
    837  1.19.2.6     skrll 	ssz = (param->precision == 16) ? 2 : 1;
    838       1.1   thorpej 	if (param->channels == 2)
    839       1.1   thorpej 		ssz <<= 1;
    840       1.1   thorpej 
    841       1.1   thorpej 	sc->pbufsize = ((char*)end - (char *)start);
    842       1.1   thorpej 	sc->pblksize = blksize;
    843       1.1   thorpej 	sc->pwmark = blksize;
    844       1.1   thorpej 
    845       1.1   thorpej 	nm_wr_4(sc, NM_PBUFFER_START, sc->pbuf);
    846  1.19.2.6     skrll 	nm_wr_4(sc, NM_PBUFFER_END, sc->pbuf + sc->pbufsize - ssz);
    847       1.1   thorpej 	nm_wr_4(sc, NM_PBUFFER_CURRP, sc->pbuf);
    848       1.1   thorpej 	nm_wr_4(sc, NM_PBUFFER_WMARK, sc->pbuf + sc->pwmark);
    849       1.1   thorpej 	nm_wr_1(sc, NM_PLAYBACK_ENABLE_REG, NM_PLAYBACK_FREERUN |
    850       1.1   thorpej 	    NM_PLAYBACK_ENABLE_FLAG);
    851       1.1   thorpej 	nm_wr_2(sc, NM_AUDIO_MUTE_REG, 0);
    852       1.1   thorpej 
    853  1.19.2.6     skrll 	return 0;
    854       1.1   thorpej }
    855       1.1   thorpej 
    856  1.19.2.8     skrll static int
    857       1.1   thorpej neo_trigger_input(void *addr, void *start, void *end, int blksize,
    858  1.19.2.6     skrll     void (*intr)(void *), void *arg, const audio_params_t *param)
    859       1.1   thorpej {
    860  1.19.2.6     skrll 	struct neo_softc *sc;
    861       1.1   thorpej 	int ssz;
    862       1.1   thorpej 
    863  1.19.2.6     skrll 	sc = addr;
    864       1.1   thorpej 	sc->rintr = intr;
    865       1.1   thorpej 	sc->rarg = arg;
    866       1.1   thorpej 
    867  1.19.2.6     skrll 	ssz = (param->precision == 16) ? 2 : 1;
    868       1.1   thorpej 	if (param->channels == 2)
    869       1.1   thorpej 		ssz <<= 1;
    870       1.1   thorpej 
    871       1.1   thorpej 	sc->rbufsize = ((char*)end - (char *)start);
    872       1.1   thorpej 	sc->rblksize = blksize;
    873       1.1   thorpej 	sc->rwmark = blksize;
    874       1.1   thorpej 
    875       1.1   thorpej 	nm_wr_4(sc, NM_RBUFFER_START, sc->rbuf);
    876       1.1   thorpej 	nm_wr_4(sc, NM_RBUFFER_END, sc->rbuf + sc->rbufsize);
    877       1.1   thorpej 	nm_wr_4(sc, NM_RBUFFER_CURRP, sc->rbuf);
    878       1.1   thorpej 	nm_wr_4(sc, NM_RBUFFER_WMARK, sc->rbuf + sc->rwmark);
    879       1.1   thorpej 	nm_wr_1(sc, NM_RECORD_ENABLE_REG, NM_RECORD_FREERUN |
    880       1.1   thorpej 	    NM_RECORD_ENABLE_FLAG);
    881       1.1   thorpej 
    882  1.19.2.6     skrll 	return 0;
    883       1.1   thorpej }
    884       1.1   thorpej 
    885  1.19.2.8     skrll static int
    886       1.1   thorpej neo_halt_output(void *addr)
    887       1.1   thorpej {
    888  1.19.2.6     skrll 	struct neo_softc *sc;
    889       1.1   thorpej 
    890  1.19.2.6     skrll 	sc = (struct neo_softc *)addr;
    891       1.1   thorpej 	nm_wr_1(sc, NM_PLAYBACK_ENABLE_REG, 0);
    892       1.1   thorpej 	nm_wr_2(sc, NM_AUDIO_MUTE_REG, NM_AUDIO_MUTE_BOTH);
    893  1.19.2.1     skrll 	sc->pintr = 0;
    894       1.1   thorpej 
    895  1.19.2.6     skrll 	return 0;
    896       1.1   thorpej }
    897       1.1   thorpej 
    898  1.19.2.8     skrll static int
    899       1.1   thorpej neo_halt_input(void *addr)
    900       1.1   thorpej {
    901  1.19.2.6     skrll 	struct neo_softc *sc;
    902       1.1   thorpej 
    903  1.19.2.6     skrll 	sc = (struct neo_softc *)addr;
    904       1.1   thorpej 	nm_wr_1(sc, NM_RECORD_ENABLE_REG, 0);
    905  1.19.2.1     skrll 	sc->rintr = 0;
    906       1.1   thorpej 
    907  1.19.2.6     skrll 	return 0;
    908       1.1   thorpej }
    909       1.1   thorpej 
    910  1.19.2.8     skrll static int
    911       1.1   thorpej neo_getdev(void *addr, struct audio_device *retp)
    912       1.1   thorpej {
    913       1.1   thorpej 
    914       1.1   thorpej 	*retp = neo_device;
    915  1.19.2.6     skrll 	return 0;
    916       1.1   thorpej }
    917       1.1   thorpej 
    918  1.19.2.8     skrll static int
    919       1.1   thorpej neo_mixer_set_port(void *addr, mixer_ctrl_t *cp)
    920       1.1   thorpej {
    921  1.19.2.6     skrll 	struct neo_softc *sc;
    922       1.1   thorpej 
    923  1.19.2.6     skrll 	sc = addr;
    924  1.19.2.6     skrll 	return sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp);
    925       1.1   thorpej }
    926       1.1   thorpej 
    927  1.19.2.8     skrll static int
    928       1.1   thorpej neo_mixer_get_port(void *addr, mixer_ctrl_t *cp)
    929       1.1   thorpej {
    930  1.19.2.6     skrll 	struct neo_softc *sc;
    931       1.1   thorpej 
    932  1.19.2.6     skrll 	sc = addr;
    933  1.19.2.6     skrll 	return sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp);
    934       1.1   thorpej }
    935       1.1   thorpej 
    936  1.19.2.8     skrll static int
    937       1.1   thorpej neo_query_devinfo(void *addr, mixer_devinfo_t *dip)
    938       1.1   thorpej {
    939  1.19.2.6     skrll 	struct neo_softc *sc;
    940       1.1   thorpej 
    941  1.19.2.6     skrll 	sc = addr;
    942  1.19.2.6     skrll 	return sc->codec_if->vtbl->query_devinfo(sc->codec_if, dip);
    943       1.1   thorpej }
    944       1.1   thorpej 
    945  1.19.2.8     skrll static void *
    946      1.17   thorpej neo_malloc(void *addr, int direction, size_t size, struct malloc_type *pool,
    947      1.17   thorpej     int flags)
    948       1.1   thorpej {
    949  1.19.2.6     skrll 	struct neo_softc *sc;
    950  1.19.2.6     skrll 	void *rv;
    951       1.1   thorpej 
    952  1.19.2.6     skrll 	sc = addr;
    953  1.19.2.6     skrll 	rv = NULL;
    954       1.1   thorpej 	switch (direction) {
    955       1.1   thorpej 	case AUMODE_PLAY:
    956       1.1   thorpej 		if (sc->pbuf_allocated == 0) {
    957       1.1   thorpej 			rv = (void *) sc->pbuf_vaddr;
    958       1.1   thorpej 			sc->pbuf_allocated = 1;
    959       1.1   thorpej 		}
    960       1.1   thorpej 		break;
    961       1.1   thorpej 
    962       1.1   thorpej 	case AUMODE_RECORD:
    963       1.1   thorpej 		if (sc->rbuf_allocated == 0) {
    964       1.1   thorpej 			rv = (void *) sc->rbuf_vaddr;
    965       1.1   thorpej 			sc->rbuf_allocated = 1;
    966       1.1   thorpej 		}
    967       1.1   thorpej 		break;
    968       1.1   thorpej 	}
    969       1.1   thorpej 
    970  1.19.2.6     skrll 	return rv;
    971       1.1   thorpej }
    972       1.1   thorpej 
    973  1.19.2.8     skrll static void
    974      1.17   thorpej neo_free(void *addr, void *ptr, struct malloc_type *pool)
    975       1.1   thorpej {
    976  1.19.2.6     skrll 	struct neo_softc *sc;
    977  1.19.2.6     skrll 	vaddr_t v;
    978       1.1   thorpej 
    979  1.19.2.6     skrll 	sc = addr;
    980  1.19.2.6     skrll 	v = (vaddr_t)ptr;
    981       1.1   thorpej 	if (v == sc->pbuf_vaddr)
    982       1.1   thorpej 		sc->pbuf_allocated = 0;
    983       1.1   thorpej 	else if (v == sc->rbuf_vaddr)
    984       1.1   thorpej 		sc->rbuf_allocated = 0;
    985       1.1   thorpej 	else
    986       1.1   thorpej 		printf("neo_free: bad address %p\n", ptr);
    987       1.1   thorpej }
    988       1.1   thorpej 
    989  1.19.2.8     skrll static size_t
    990       1.1   thorpej neo_round_buffersize(void *addr, int direction, size_t size)
    991       1.1   thorpej {
    992       1.1   thorpej 
    993  1.19.2.6     skrll 	return NM_BUFFSIZE;
    994       1.1   thorpej }
    995       1.1   thorpej 
    996  1.19.2.8     skrll static paddr_t
    997       1.3   thorpej neo_mappage(void *addr, void *mem, off_t off, int prot)
    998       1.3   thorpej {
    999  1.19.2.6     skrll 	struct neo_softc *sc;
   1000  1.19.2.6     skrll 	vaddr_t v;
   1001       1.3   thorpej 	bus_addr_t pciaddr;
   1002       1.3   thorpej 
   1003  1.19.2.6     skrll 	sc = addr;
   1004  1.19.2.6     skrll 	v = (vaddr_t)mem;
   1005       1.3   thorpej 	if (v == sc->pbuf_vaddr)
   1006       1.3   thorpej 		pciaddr = sc->pbuf_pciaddr;
   1007       1.3   thorpej 	else if (v == sc->rbuf_vaddr)
   1008       1.3   thorpej 		pciaddr = sc->rbuf_pciaddr;
   1009       1.3   thorpej 	else
   1010  1.19.2.6     skrll 		return -1;
   1011       1.3   thorpej 
   1012  1.19.2.6     skrll 	return bus_space_mmap(sc->bufiot, pciaddr, off, prot,
   1013  1.19.2.6     skrll 	    BUS_SPACE_MAP_LINEAR);
   1014       1.3   thorpej }
   1015       1.1   thorpej 
   1016  1.19.2.8     skrll static int
   1017       1.1   thorpej neo_get_props(void *addr)
   1018       1.1   thorpej {
   1019       1.1   thorpej 
   1020  1.19.2.6     skrll 	return AUDIO_PROP_INDEPENDENT | AUDIO_PROP_MMAP |
   1021  1.19.2.6     skrll 	    AUDIO_PROP_FULLDUPLEX;
   1022       1.1   thorpej }
   1023