xref: /openbsd-src/sys/dev/pci/envy.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: envy.c,v 1.15 2009/03/29 21:53:52 sthen Exp $	*/
2 /*
3  * Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 #include <sys/ioctl.h>
22 #include <sys/audioio.h>
23 #include <sys/malloc.h>
24 #include <dev/pci/pcivar.h>
25 #include <dev/pci/pcidevs.h>
26 #include <dev/pci/envyvar.h>
27 #include <dev/pci/envyreg.h>
28 #include <dev/audio_if.h>
29 #include <machine/bus.h>
30 
31 #ifdef ENVY_DEBUG
32 #define DPRINTF(...) do { if (envydebug) printf(__VA_ARGS__); } while(0)
33 #define DPRINTFN(n, ...) do { if (envydebug > (n)) printf(__VA_ARGS__); } while(0)
34 int envydebug = 1;
35 #else
36 #define DPRINTF(...) do {} while(0)
37 #define DPRINTFN(n, ...) do {} while(0)
38 #endif
39 #define DEVNAME(sc) ((sc)->dev.dv_xname)
40 
41 int  envymatch(struct device *, void *, void *);
42 void envyattach(struct device *, struct device *, void *);
43 int  envydetach(struct device *, int);
44 
45 int  envy_ccs_read(struct envy_softc *, int);
46 void envy_ccs_write(struct envy_softc *, int, int);
47 int  envy_cci_read(struct envy_softc *, int);
48 void envy_cci_write(struct envy_softc *, int, int);
49 void envy_i2c_wait(struct envy_softc *);
50 int  envy_i2c_read(struct envy_softc *, int, int);
51 void envy_i2c_write(struct envy_softc *, int, int, int);
52 int  envy_gpio_read(struct envy_softc *);
53 void envy_gpio_write(struct envy_softc *, int);
54 void envy_eeprom_read(struct envy_softc *, unsigned char *);
55 void envy_reset(struct envy_softc *);
56 int  envy_ak_read(struct envy_softc *, int, int);
57 void envy_ak_write(struct envy_softc *, int, int, int);
58 int  envy_intr(void *);
59 
60 int envy_lineout_getsrc(struct envy_softc *, int);
61 void envy_lineout_setsrc(struct envy_softc *, int, int);
62 int envy_spdout_getsrc(struct envy_softc *, int);
63 void envy_spdout_setsrc(struct envy_softc *, int, int);
64 void envy_mon_getvol(struct envy_softc *, int, int, int *);
65 void envy_mon_setvol(struct envy_softc *, int, int, int);
66 
67 int envy_open(void *, int);
68 void envy_close(void *);
69 void *envy_allocm(void *, int, size_t, int, int);
70 void envy_freem(void *, void *, int);
71 int envy_query_encoding(void *, struct audio_encoding *);
72 int envy_set_params(void *, int, int, struct audio_params *,
73     struct audio_params *);
74 int envy_round_blocksize(void *, int);
75 size_t envy_round_buffersize(void *, int, size_t);
76 int envy_trigger_output(void *, void *, void *, int,
77     void (*)(void *), void *, struct audio_params *);
78 int envy_trigger_input(void *, void *, void *, int,
79     void (*)(void *), void *, struct audio_params *);
80 int envy_halt_output(void *);
81 int envy_halt_input(void *);
82 int envy_getdev(void *, struct audio_device *);
83 int envy_query_devinfo(void *, struct mixer_devinfo *);
84 int envy_get_port(void *, struct mixer_ctrl *);
85 int envy_set_port(void *, struct mixer_ctrl *);
86 int envy_get_props(void *);
87 
88 struct cfattach envy_ca = {
89 	sizeof(struct envy_softc), envymatch, envyattach, envydetach
90 };
91 
92 struct cfdriver envy_cd = {
93 	NULL, "envy", DV_DULL
94 };
95 
96 struct audio_hw_if envy_hw_if = {
97 	envy_open,		/* open */
98 	envy_close,		/* close */
99 	NULL,			/* drain */
100 	envy_query_encoding,	/* query_encoding */
101 	envy_set_params,	/* set_params */
102 	envy_round_blocksize,	/* round_blocksize */
103 	NULL,			/* commit_settings */
104 	NULL,			/* init_output */
105 	NULL,			/* init_input */
106 	NULL,			/* start_output */
107 	NULL,			/* start_input */
108 	envy_halt_output,	/* halt_output */
109 	envy_halt_input,	/* halt_input */
110 	NULL,			/* speaker_ctl */
111 	envy_getdev,		/* getdev */
112 	NULL,			/* setfd */
113 	envy_set_port,		/* set_port */
114 	envy_get_port,		/* get_port */
115 	envy_query_devinfo,	/* query_devinfo */
116 	envy_allocm,		/* malloc */
117 	envy_freem,		/* free */
118 	envy_round_buffersize,	/* round_buffersize */
119 	NULL,			/* mappage */
120 	envy_get_props,		/* get_props */
121 	envy_trigger_output,	/* trigger_output */
122 	envy_trigger_input,	/* trigger_input */
123 	NULL
124 };
125 
126 /*
127  * correspondence between rates (in frames per second)
128  * and values of rate register
129  */
130 struct {
131 	int rate, reg;
132 } envy_rates[] = {
133 	{ 8000, 0x6}, { 9600, 0x3}, {11025, 0xa}, {12000, 2}, {16000, 5},
134 	{22050, 0x9}, {24000, 0x1}, {32000, 0x4}, {44100, 8}, {48000, 0},
135 	{64000, 0xf}, {88200, 0xb}, {96000, 0x7}, {-1, -1}
136 };
137 
138 int
139 envy_ccs_read(struct envy_softc *sc, int reg)
140 {
141 	return bus_space_read_1(sc->ccs_iot, sc->ccs_ioh, reg);
142 }
143 
144 void
145 envy_ccs_write(struct envy_softc *sc, int reg, int val)
146 {
147 	bus_space_write_1(sc->ccs_iot, sc->ccs_ioh, reg, val);
148 }
149 
150 int
151 envy_cci_read(struct envy_softc *sc, int index)
152 {
153 	int val;
154 	envy_ccs_write(sc, ENVY_CCI_INDEX, index);
155 	val = envy_ccs_read(sc, ENVY_CCI_DATA);
156 	return val;
157 }
158 
159 void
160 envy_cci_write(struct envy_softc *sc, int index, int data)
161 {
162 	envy_ccs_write(sc, ENVY_CCI_INDEX, index);
163 	envy_ccs_write(sc, ENVY_CCI_DATA, data);
164 }
165 
166 void
167 envy_i2c_wait(struct envy_softc *sc)
168 {
169 	int timeout = 50, st;
170 
171         for (;;) {
172 		st = envy_ccs_read(sc, ENVY_I2C_CTL);
173 		if (!(st & ENVY_I2C_CTL_BUSY))
174 			break;
175 		if (timeout == 0) {
176 			printf("%s: i2c busy timeout\n", DEVNAME(sc));
177 			break;
178 		}
179 		delay(50);
180 		timeout--;
181 	}
182 }
183 
184 int
185 envy_i2c_read(struct envy_softc *sc, int dev, int addr)
186 {
187 	envy_i2c_wait(sc);
188 	envy_ccs_write(sc, ENVY_I2C_ADDR, addr);
189 	envy_i2c_wait(sc);
190 	envy_ccs_write(sc, ENVY_I2C_DEV, dev << 1);
191 	envy_i2c_wait(sc);
192 	return envy_ccs_read(sc, ENVY_I2C_DATA);
193 }
194 
195 void
196 envy_i2c_write(struct envy_softc *sc, int dev, int addr, int data)
197 {
198 	if (dev == 0x50) {
199 		printf("%s: writing on eeprom is evil...\n", DEVNAME(sc));
200 		return;
201 	}
202 	envy_i2c_wait(sc);
203 	envy_ccs_write(sc, ENVY_I2C_ADDR, addr);
204 	envy_i2c_wait(sc);
205 	envy_ccs_write(sc, ENVY_I2C_DATA, data);
206 	envy_i2c_wait(sc);
207 	envy_ccs_write(sc, ENVY_I2C_DEV, (dev << 1) | 1);
208 }
209 
210 void
211 envy_eeprom_read(struct envy_softc *sc, unsigned char *eeprom)
212 {
213 	int i;
214 
215 	for (i = 0; i < ENVY_EEPROM_MAXSZ; i++) {
216 		eeprom[i] = envy_i2c_read(sc, ENVY_I2C_DEV_EEPROM, i);
217 	}
218 #ifdef ENVY_DEBUG
219 	printf("%s: eeprom: ", DEVNAME(sc));
220 	for (i = 0; i < ENVY_EEPROM_MAXSZ; i++) {
221 		printf(" %02x", (unsigned)eeprom[i]);
222 	}
223 	printf("\n");
224 #endif
225 }
226 
227 int
228 envy_ak_read(struct envy_softc *sc, int dev, int addr) {
229 	return sc->ak[dev].reg[addr];
230 }
231 
232 void
233 envy_ak_write(struct envy_softc *sc, int dev, int addr, int data)
234 {
235 	int bits, i, reg;
236 
237 	DPRINTFN(2, "envy_ak_write: %d, %d, 0x%x\n", dev, addr, data);
238 	sc->ak[dev].reg[addr] = data;
239 
240 	reg = envy_cci_read(sc, ENVY_GPIO_DATA);
241 	reg &= ~ENVY_GPIO_CSMASK;
242 	reg |=  ENVY_GPIO_CS(dev);
243 	envy_cci_write(sc, ENVY_GPIO_DATA, reg);
244 	delay(1);
245 
246 	bits  = 0xa000 | (addr << 8) | data;
247 	for (i = 0; i < 16; i++) {
248 		reg &= ~(ENVY_GPIO_CLK | ENVY_GPIO_DOUT);
249 		reg |= (bits & 0x8000) ? ENVY_GPIO_DOUT : 0;
250 		envy_cci_write(sc, ENVY_GPIO_DATA, reg);
251 		delay(1);
252 
253 		reg |= ENVY_GPIO_CLK;
254 		envy_cci_write(sc, ENVY_GPIO_DATA, reg);
255 		delay(1);
256 		bits <<= 1;
257 	}
258 
259 	reg |= ENVY_GPIO_CSMASK;
260 	envy_cci_write(sc, ENVY_GPIO_DATA, reg);
261 	delay(1);
262 }
263 
264 void
265 envy_reset(struct envy_softc *sc)
266 {
267 	char eeprom[ENVY_EEPROM_MAXSZ];
268 	int dev;
269 
270 	/*
271 	 * full reset
272 	 */
273 	envy_ccs_write(sc, ENVY_CTL, ENVY_CTL_RESET | ENVY_CTL_NATIVE);
274 	delay(200);
275 	envy_ccs_write(sc, ENVY_CTL, ENVY_CTL_NATIVE);
276 	delay(200);
277 
278 	/*
279 	 * read config from eprom and write it to registers
280 	 */
281 	envy_eeprom_read(sc, eeprom);
282 	pci_conf_write(sc->pci_pc, sc->pci_tag, ENVY_CONF,
283 	    eeprom[ENVY_EEPROM_CONF] |
284 	    (eeprom[ENVY_EEPROM_ACLINK] << 8) |
285 	    (eeprom[ENVY_EEPROM_I2S] << 16) |
286 	    (eeprom[ENVY_EEPROM_SPDIF] << 24));
287 	envy_cci_write(sc, ENVY_GPIO_MASK, eeprom[ENVY_EEPROM_GPIOMASK]);
288 	envy_cci_write(sc, ENVY_GPIO_DIR,  eeprom[ENVY_EEPROM_GPIODIR]);
289 	envy_cci_write(sc, ENVY_GPIO_DATA, eeprom[ENVY_EEPROM_GPIOST]);
290 
291 	DPRINTF("%s: gpio_mask = %02x\n", DEVNAME(sc),
292 		envy_cci_read(sc, ENVY_GPIO_MASK));
293 	DPRINTF("%s: gpio_dir = %02x\n", DEVNAME(sc),
294 		envy_cci_read(sc, ENVY_GPIO_DIR));
295 	DPRINTF("%s: gpio_state = %02x\n", DEVNAME(sc),
296 		envy_cci_read(sc, ENVY_GPIO_DATA));
297 
298 	/*
299 	 * reset ak4524 codecs
300 	 */
301 	for (dev = 0; dev < 4; dev++) {
302 		envy_ak_write(sc, dev, AK_RST, 0x0);
303 		delay(300);
304 		envy_ak_write(sc, dev, AK_RST, AK_RST_AD | AK_RST_DA);
305 		envy_ak_write(sc, dev, AK_FMT, AK_FMT_IIS24);
306 		sc->ak[dev].reg[AK_DEEMVOL] = AK_DEEM_OFF;
307 		sc->ak[dev].reg[AK_ADC_GAIN0] = 0x7f;
308 		sc->ak[dev].reg[AK_ADC_GAIN1] = 0x7f;
309 		sc->ak[dev].reg[AK_DAC_GAIN0] = 0x7f;
310 		sc->ak[dev].reg[AK_DAC_GAIN1] = 0x7f;
311 	}
312 
313 	/*
314 	 * clear all interrupts and unmask used ones
315 	 */
316 	envy_ccs_write(sc, ENVY_CCS_INTSTAT, 0xff);
317 	envy_ccs_write(sc, ENVY_CCS_INTMASK, ~ENVY_CCS_INT_MT);
318 }
319 
320 int
321 envy_intr(void *self)
322 {
323 	struct envy_softc *sc = (struct envy_softc *)self;
324 	int st;
325 
326 	st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR);
327 	if (!(st & (ENVY_MT_INTR_PACK | ENVY_MT_INTR_RACK))) {
328 		return 0;
329 	}
330 	if (st & ENVY_MT_INTR_PACK) {
331 		st = ENVY_MT_INTR_PACK;
332 		bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st);
333 		sc->ointr(sc->oarg);
334 	}
335 	if (st & ENVY_MT_INTR_RACK) {
336 		st = ENVY_MT_INTR_RACK;
337 		bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st);
338 		sc->iintr(sc->iarg);
339 	}
340 	return 1;
341 }
342 
343 int
344 envy_lineout_getsrc(struct envy_softc *sc, int out) {
345 	int reg, shift, src;
346 
347 	reg = bus_space_read_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_OUTSRC);
348 	DPRINTF("%s: outsrc=%x\n", DEVNAME(sc), reg);
349 	shift = (out  & 1) ? (out & ~1) + 8 : out;
350 	src = (reg >> shift) & 3;
351 	if (src == ENVY_MT_OUTSRC_DMA) {
352 		return ENVY_MIX_OUTSRC_DMA;
353 	} else if (src == ENVY_MT_OUTSRC_MON) {
354 		return ENVY_MIX_OUTSRC_MON;
355 	}
356 	reg = bus_space_read_4(sc->mt_iot, sc->mt_ioh, ENVY_MT_INSEL);
357 	DPRINTF("%s: insel=%x\n", DEVNAME(sc), reg);
358 	reg = (reg >> (out * 4)) & 0xf;
359 	if (src == ENVY_MT_OUTSRC_LINE)
360 		return ENVY_MIX_OUTSRC_LINEIN + (reg & 7);
361 	else
362 		return ENVY_MIX_OUTSRC_SPDIN + (reg >> 3);
363 }
364 
365 void
366 envy_lineout_setsrc(struct envy_softc *sc, int out, int src) {
367 	int reg, shift, mask, sel;
368 
369 	if (src < ENVY_MIX_OUTSRC_DMA) {
370 		/*
371 		 * linein and spdin are used as output source so we
372 		 * must select the input source channel number
373 		 */
374 		if (src < ENVY_MIX_OUTSRC_SPDIN)
375 			sel = src - ENVY_MIX_OUTSRC_LINEIN;
376 		else
377 			sel = (src - ENVY_MIX_OUTSRC_SPDIN) << 3;
378 
379 		shift = out * ENVY_MT_INSEL_BITS;
380 		mask = ENVY_MT_INSEL_MASK << shift;
381 		reg = bus_space_read_4(sc->mt_iot, sc->mt_ioh, ENVY_MT_INSEL);
382 		reg = (reg & ~mask) | (sel << shift);
383 		bus_space_write_4(sc->mt_iot, sc->mt_ioh, ENVY_MT_INSEL, reg);
384 		DPRINTF("%s: insel <- %x\n", DEVNAME(sc), reg);
385 	}
386 
387 	/*
388 	 * set the lineout route register
389 	 */
390 	if (src < ENVY_MIX_OUTSRC_SPDIN) {
391 		sel = ENVY_MT_OUTSRC_LINE;
392 	} else if (src < ENVY_MIX_OUTSRC_DMA) {
393 		sel = ENVY_MT_OUTSRC_SPD;
394 	} else if (src == ENVY_MIX_OUTSRC_DMA) {
395 		sel = ENVY_MT_OUTSRC_DMA;
396 	} else {
397 		sel = ENVY_MT_OUTSRC_MON;
398 	}
399 	shift = (out  & 1) ? (out & ~1) + 8 : out;
400 	mask = ENVY_MT_OUTSRC_MASK << shift;
401 	reg = bus_space_read_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_OUTSRC);
402 	reg = (reg & ~mask) | (sel << shift);
403 	bus_space_write_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_OUTSRC, reg);
404 	DPRINTF("%s: outsrc <- %x\n", DEVNAME(sc), reg);
405 }
406 
407 
408 int
409 envy_spdout_getsrc(struct envy_softc *sc, int out) {
410 	int reg, src, sel;
411 
412 	reg = bus_space_read_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_SPDROUTE);
413 	DPRINTF("%s: spdroute=%x\n", DEVNAME(sc), reg);
414 	src = (out == 0) ? reg : reg >> 2;
415 	src &= ENVY_MT_SPDSRC_MASK;
416 	if (src == ENVY_MT_SPDSRC_DMA) {
417 		return ENVY_MIX_OUTSRC_DMA;
418 	} else if (src == ENVY_MT_SPDSRC_MON) {
419 		return ENVY_MIX_OUTSRC_MON;
420 	}
421 
422 	sel = (out == 0) ? reg >> 8 : reg >> 12;
423 	sel &= ENVY_MT_SPDSEL_MASK;
424 	if (src == ENVY_MT_SPDSRC_LINE)
425 		return ENVY_MIX_OUTSRC_LINEIN + (sel & 7);
426 	else
427 		return ENVY_MIX_OUTSRC_SPDIN + (sel >> 3);
428 }
429 
430 void
431 envy_spdout_setsrc(struct envy_softc *sc, int out, int src) {
432 	int reg, shift, mask, sel;
433 
434 	reg = bus_space_read_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_SPDROUTE);
435 	if (src < ENVY_MIX_OUTSRC_DMA) {
436 		/*
437 		 * linein and spdin are used as output source so we
438 		 * must select the input source channel number
439 		 */
440 		if (src < ENVY_MIX_OUTSRC_SPDIN)
441 			sel = src - ENVY_MIX_OUTSRC_LINEIN;
442 		else
443 			sel = (src - ENVY_MIX_OUTSRC_SPDIN) << 3;
444 
445 		shift = 8 + out * ENVY_MT_SPDSEL_BITS;
446 		mask = ENVY_MT_SPDSEL_MASK << shift;
447 		reg = (reg & ~mask) | (sel << shift);
448 	}
449 
450 	/*
451 	 * set the lineout route register
452 	 */
453 	if (src < ENVY_MIX_OUTSRC_SPDIN) {
454 		sel = ENVY_MT_OUTSRC_LINE;
455 	} else if (src < ENVY_MIX_OUTSRC_DMA) {
456 		sel = ENVY_MT_OUTSRC_SPD;
457 	} else if (src == ENVY_MIX_OUTSRC_DMA) {
458 		sel = ENVY_MT_OUTSRC_DMA;
459 	} else {
460 		sel = ENVY_MT_OUTSRC_MON;
461 	}
462 	shift = out * 2;
463 	mask = ENVY_MT_SPDSRC_MASK << shift;
464 	reg = (reg & ~mask) | (sel << shift);
465 	bus_space_write_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_SPDROUTE, reg);
466 	DPRINTF("%s: spdroute <- %x\n", DEVNAME(sc), reg);
467 }
468 
469 void
470 envy_mon_getvol(struct envy_softc *sc, int idx, int ch, int *val) {
471 	int reg;
472 
473 	bus_space_write_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_MONIDX, idx);
474 	reg = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_MONDATA + ch);
475 	*val = 0x7f - (reg & 0x7f);
476 }
477 
478 void
479 envy_mon_setvol(struct envy_softc *sc, int idx, int ch, int val) {
480 	int reg;
481 
482 	bus_space_write_2(sc->mt_iot, sc->mt_ioh, ENVY_MT_MONIDX, idx);
483 	reg = 0x7f - val;
484 	DPRINTF("%s: mon=%d/%d <- %d\n", DEVNAME(sc), reg, ch, val);
485 	bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_MONDATA + ch, reg);
486 }
487 
488 int
489 envymatch(struct device *parent, void *match, void *aux) {
490 	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
491 
492 	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ICENSEMBLE &&
493 	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ICENSEMBLE_ICE1712) {
494 		return 1;
495 	}
496 	return 0;
497 }
498 
499 void
500 envyattach(struct device *parent, struct device *self, void *aux)
501 {
502 	struct envy_softc *sc = (struct envy_softc *)self;
503 	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
504 	pci_intr_handle_t ih;
505 	const char *intrstr;
506 
507 	sc->pci_tag = pa->pa_tag;
508 	sc->pci_pc = pa->pa_pc;
509 	sc->pci_dmat = pa->pa_dmat;
510 	sc->pci_ih = NULL;
511 	sc->ibuf.addr = sc->obuf.addr = NULL;
512 	sc->ccs_iosz = 0;
513 	sc->mt_iosz = 0;
514 
515 	if (pci_mapreg_map(pa, ENVY_CTL_BAR, PCI_MAPREG_TYPE_IO, 0,
516 			   &sc->ccs_iot, &sc->ccs_ioh, NULL, &sc->ccs_iosz, 0)) {
517 		printf(": can't map ctl i/o space\n");
518 		sc->ccs_iosz = 0;
519 		return;
520         }
521 	if (pci_mapreg_map(pa, ENVY_MT_BAR, PCI_MAPREG_TYPE_IO, 0,
522 			   &sc->mt_iot, &sc->mt_ioh, NULL, &sc->mt_iosz, 0)) {
523 		printf(": can't map mt i/o space\n");
524 		sc->mt_iosz = 0;
525 		return;
526         }
527 	if (pci_intr_map(pa, &ih)) {
528 		printf(": can't map interrupt\n");
529 	}
530 	intrstr = pci_intr_string(sc->pci_pc, ih);
531 	sc->pci_ih = pci_intr_establish(sc->pci_pc, ih, IPL_AUDIO,
532 	    envy_intr, sc, sc->dev.dv_xname);
533 	if (sc->pci_ih == NULL) {
534 		printf(": can't establish interrupt");
535 		if (intrstr)
536 			printf(" at %s", intrstr);
537 		printf("\n");
538 		return;
539 	}
540 	printf(": %s\n", intrstr);
541 	envy_reset(sc);
542 	sc->audio = audio_attach_mi(&envy_hw_if, sc, &sc->dev);
543 }
544 
545 int
546 envydetach(struct device *self, int flags)
547 {
548 	struct envy_softc *sc = (struct envy_softc *)self;
549 
550 	if (sc->pci_ih != NULL) {
551 		pci_intr_disestablish(sc->pci_pc, sc->pci_ih);
552 		sc->pci_ih = NULL;
553 	}
554 	if (sc->ccs_iosz) {
555 		bus_space_unmap(sc->ccs_iot, sc->ccs_ioh, sc->ccs_iosz);
556 	}
557 	if (sc->mt_iosz) {
558 		bus_space_unmap(sc->mt_iot, sc->mt_ioh, sc->mt_iosz);
559 	}
560 	return 0;
561 }
562 
563 int
564 envy_open(void *self, int flags)
565 {
566 	return 0;
567 }
568 
569 void
570 envy_close(void *self)
571 {
572 }
573 
574 void *
575 envy_allocm(void *self, int dir, size_t size, int type, int flags)
576 {
577 	struct envy_softc *sc = (struct envy_softc *)self;
578 	int err, rsegs, basereg, wait;
579 	struct envy_buf *buf;
580 
581 	if (dir == AUMODE_RECORD) {
582 		buf = &sc->ibuf;
583 		basereg = ENVY_MT_RADDR;
584 	} else {
585 		buf = &sc->obuf;
586 		basereg = ENVY_MT_PADDR;
587 	}
588 	if (buf->addr != NULL) {
589 		DPRINTF("%s: multiple alloc, dir = %d\n", DEVNAME(sc), dir);
590 		return NULL;
591 	}
592 	buf->size = size;
593 	wait = (flags & M_NOWAIT) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK;
594 
595 #define ENVY_ALIGN	4
596 #define ENVY_BOUNDARY	0
597 
598 	err = bus_dmamem_alloc(sc->pci_dmat, buf->size, ENVY_ALIGN,
599 	    ENVY_BOUNDARY, &buf->seg, 1, &rsegs, wait);
600 	if (err) {
601 		DPRINTF("%s: dmamem_alloc: failed %d\n", DEVNAME(sc), err);
602 		goto err_ret;
603 	}
604 
605 	err = bus_dmamem_map(sc->pci_dmat, &buf->seg, rsegs, buf->size,
606             &buf->addr, wait | BUS_DMA_COHERENT);
607 	if (err) {
608 		DPRINTF("%s: dmamem_map: failed %d\n", DEVNAME(sc), err);
609 		goto err_free;
610 	}
611 
612 	err = bus_dmamap_create(sc->pci_dmat, buf->size, 1, buf->size, 0,
613 	    wait, &buf->map);
614 	if (err) {
615 		DPRINTF("%s: dmamap_create: failed %d\n", DEVNAME(sc), err);
616 		goto err_unmap;
617 	}
618 
619 	err = bus_dmamap_load(sc->pci_dmat, buf->map, buf->addr,
620             buf->size, NULL, wait);
621 	if (err) {
622 		DPRINTF("%s: dmamap_load: failed %d\n", DEVNAME(sc), err);
623 		goto err_destroy;
624 	}
625 	bus_space_write_4(sc->mt_iot, sc->mt_ioh, basereg, buf->seg.ds_addr);
626 	DPRINTF("%s: allocated %d bytes dir=%d, ka=%p, da=%p\n",
627 		DEVNAME(sc), buf->size, dir, buf->addr, buf->seg.ds_addr);
628 	return buf->addr;
629 
630  err_destroy:
631 	bus_dmamap_destroy(sc->pci_dmat, buf->map);
632  err_unmap:
633 	bus_dmamem_unmap(sc->pci_dmat, buf->addr, buf->size);
634  err_free:
635 	bus_dmamem_free(sc->pci_dmat, &buf->seg, 1);
636  err_ret:
637 	return NULL;
638 }
639 
640 void
641 envy_freem(void *self, void *addr, int type)
642 {
643 	struct envy_buf *buf;
644 	struct envy_softc *sc = (struct envy_softc *)self;
645 	int dir;
646 
647 	if (sc->ibuf.addr == addr) {
648 		buf = &sc->ibuf;
649 		dir = AUMODE_RECORD;
650 	} else if (sc->obuf.addr == addr) {
651 		buf = &sc->obuf;
652 		dir = AUMODE_PLAY;
653 	} else {
654 		DPRINTF("%s: no buf to free\n", DEVNAME(sc));
655 		return;
656 	}
657 	bus_dmamap_destroy(sc->pci_dmat, buf->map);
658 	bus_dmamem_unmap(sc->pci_dmat, buf->addr, buf->size);
659 	bus_dmamem_free(sc->pci_dmat, &buf->seg, 1);
660 	buf->addr = NULL;
661 	DPRINTF("%s: freed buffer (mode=%d)\n", DEVNAME(sc), dir);
662 }
663 
664 int
665 envy_query_encoding(void *self, struct audio_encoding *enc)
666 {
667 	if (enc->index == 0) {
668 		strlcpy(enc->name, AudioEslinear_le, sizeof(enc->name));
669 		enc->encoding = AUDIO_ENCODING_SLINEAR_LE;
670 		enc->precision = 24;
671 		enc->flags = 0;
672 		return 0;
673 	}
674 	return EINVAL;
675 }
676 
677 int
678 envy_set_params(void *self, int setmode, int usemode,
679     struct audio_params *p, struct audio_params *r)
680 {
681 	struct envy_softc *sc = (struct envy_softc *)self;
682 	int i, rate, reg;
683 
684 	if (setmode == 0) {
685 		DPRINTF("%s: no params to set\n", DEVNAME(sc));
686 		return 0;
687 	}
688 	if (setmode == (AUMODE_PLAY | AUMODE_RECORD) &&
689 	    p->sample_rate != r->sample_rate) {
690 		DPRINTF("%s: play/rec rates mismatch\n", DEVNAME(sc));
691 		r->sample_rate = p->sample_rate;
692 	}
693 	rate = (setmode & AUMODE_PLAY) ? p->sample_rate : r->sample_rate;
694 	for (i = 0; envy_rates[i].rate < rate; i++) {
695 		if (envy_rates[i].rate == -1) {
696 			i--;
697 			DPRINTF("%s: rate: %d -> %d\n", DEVNAME(sc), rate, i);
698 			break;
699 		}
700 	}
701 	reg = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_RATE);
702 	reg &= ~ENVY_MT_RATEMASK;
703 	reg |= envy_rates[i].reg;
704 	bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_RATE, reg);
705 	if (setmode & AUMODE_PLAY) {
706 		p->encoding = AUDIO_ENCODING_SLINEAR;
707 		p->precision = 24;
708 		p->channels = ENVY_PCHANS;
709 	}
710 	if (setmode & AUMODE_RECORD) {
711 		r->encoding = AUDIO_ENCODING_SLINEAR;
712 		r->precision = 24;
713 		r->channels = ENVY_RCHANS;
714 	}
715 	return 0;
716 }
717 
718 int
719 envy_round_blocksize(void *self, int blksz)
720 {
721 	/*
722 	 * XXX: sizes depend on the mode but we don't have
723 	 * access to the mode here; So we use the greatest
724 	 * common divisor of input and output blocksizes, until
725 	 * upper layer is fixed
726 	 */
727 #define ENVY_GCD (6 * 5 * 4)
728 	return (blksz / ENVY_GCD) * ENVY_GCD;
729 }
730 
731 size_t
732 envy_round_buffersize(void *self, int dir, size_t bufsz)
733 {
734 	/*
735 	 * XXX: same remark as above
736 	 */
737 	return (bufsz / ENVY_GCD) * ENVY_GCD;
738 }
739 
740 int
741 envy_trigger_output(void *self, void *start, void *end, int blksz,
742     void (*intr)(void *), void *arg, struct audio_params *param)
743 {
744 	struct envy_softc *sc = (struct envy_softc *)self;
745 	size_t bufsz;
746 	int st;
747 
748 	bufsz = end - start;
749 	if (bufsz % (ENVY_PCHANS * 4) != 0) {
750 		DPRINTF("%s: %d: bad output bufsz\n", DEVNAME(sc), bufsz);
751 		return EINVAL;
752 	}
753 	if (blksz % (ENVY_PCHANS * 4) != 0) {
754 		DPRINTF("%s: %d: bad output blksz\n", DEVNAME(sc), blksz);
755 		return EINVAL;
756 	}
757 	bus_space_write_2(sc->mt_iot, sc->mt_ioh,
758 	    ENVY_MT_PBUFSZ, bufsz / 4 - 1);
759 	bus_space_write_2(sc->mt_iot, sc->mt_ioh,
760 	    ENVY_MT_PBLKSZ, blksz / 4 - 1);
761 
762 	sc->ointr = intr;
763 	sc->oarg = arg;
764 
765 	st = ENVY_MT_INTR_PACK;
766 	bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st);
767 
768 	st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL);
769 	st |= ENVY_MT_CTL_PSTART;
770 	bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL, st);
771 	return 0;
772 }
773 
774 int
775 envy_trigger_input(void *self, void *start, void *end, int blksz,
776     void (*intr)(void *), void *arg, struct audio_params *param)
777 {
778 	struct envy_softc *sc = (struct envy_softc *)self;
779 	size_t bufsz;
780 	int st;
781 
782 	bufsz = end - start;
783 	if (bufsz % (ENVY_RCHANS * 4) != 0) {
784 		DPRINTF("%s: %d: bad input bufsz\n", DEVNAME(sc), bufsz);
785 		return EINVAL;
786 	}
787 	if (blksz % (ENVY_RCHANS * 4) != 0) {
788 		DPRINTF("%s: %d: bad input blksz\n", DEVNAME(sc), blksz);
789 		return EINVAL;
790 	}
791 	bus_space_write_2(sc->mt_iot, sc->mt_ioh,
792 	    ENVY_MT_RBUFSZ, bufsz / 4 - 1);
793 	bus_space_write_2(sc->mt_iot, sc->mt_ioh,
794 	    ENVY_MT_RBLKSZ, blksz / 4 - 1);
795 
796 	sc->iintr = intr;
797 	sc->iarg = arg;
798 
799 	st = ENVY_MT_INTR_RACK;
800 	bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_INTR, st);
801 
802 	st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL);
803 	st |= ENVY_MT_CTL_RSTART;
804 	bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL, st);
805 	return 0;
806 }
807 
808 int
809 envy_halt_output(void *self)
810 {
811 	struct envy_softc *sc = (struct envy_softc *)self;
812 	int st;
813 
814 	st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL);
815 	st &= ~ENVY_MT_CTL_PSTART;
816 	bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL, st);
817 	return 0;
818 }
819 
820 int
821 envy_halt_input(void *self)
822 {
823 	struct envy_softc *sc = (struct envy_softc *)self;
824 	int st;
825 
826 	st = bus_space_read_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL);
827 	st &= ~ENVY_MT_CTL_RSTART;
828 	bus_space_write_1(sc->mt_iot, sc->mt_ioh, ENVY_MT_CTL, st);
829 	return 0;
830 }
831 
832 int
833 envy_getdev(void *self, struct audio_device *dev)
834 {
835 	strlcpy(dev->name, "Envy24", MAX_AUDIO_DEV_LEN);
836 	strlcpy(dev->version, "-", MAX_AUDIO_DEV_LEN);	/* XXX eeprom version */
837 	strlcpy(dev->config, "envy", MAX_AUDIO_DEV_LEN);
838 	return 0;
839 }
840 
841 int
842 envy_query_devinfo(void *self, struct mixer_devinfo *dev)
843 {
844 	int i, n, out;
845 	char *classes[] = {
846 		AudioCinputs, AudioCoutputs, AudioCmonitor
847 	};
848 
849 	if (dev->index < 0)
850 		return ENXIO;
851 
852 	dev->prev = dev->next = AUDIO_MIXER_LAST;
853 	if (dev->index < ENVY_MIX_OUTSRC) {
854 		dev->type = AUDIO_MIXER_CLASS;
855 		dev->mixer_class = dev->index - ENVY_MIX_CLASSIN;
856 		strlcpy(dev->label.name,
857 		    classes[dev->index - ENVY_MIX_CLASSIN], MAX_AUDIO_DEV_LEN);
858 		return 0;
859 	}
860 	if (dev->index < ENVY_MIX_MONITOR) {
861 		n = 0;
862 		out = dev->index - ENVY_MIX_OUTSRC;
863 		dev->type = AUDIO_MIXER_ENUM;
864 		dev->mixer_class = ENVY_MIX_CLASSOUT;
865 		dev->prev = ENVY_MIX_OLVL(4) + out;
866 		for (i = 0; i < 10; i++) {
867 			dev->un.e.member[n].ord = n;
868 			snprintf(dev->un.e.member[n++].label.name,
869 			    MAX_AUDIO_DEV_LEN, AudioNline "%d", i);
870 		}
871 		dev->un.e.member[n].ord = n;
872 		snprintf(dev->un.e.member[n++].label.name,
873 			 MAX_AUDIO_DEV_LEN, "play%d", out);
874 		if (out < 2) {
875 			dev->un.e.member[n].ord = n;
876 			snprintf(dev->un.e.member[n++].label.name,
877 			    MAX_AUDIO_DEV_LEN, "mon%d", out);
878 		}
879 		snprintf(dev->label.name, MAX_AUDIO_DEV_LEN,
880 		    AudioNsource, out);
881 		dev->un.s.num_mem = n;
882 		return 0;
883 	}
884 	if (dev->index < ENVY_MIX_ILVL(4)) {
885 		out = dev->index - ENVY_MIX_MONITOR;
886 		dev->type = AUDIO_MIXER_VALUE;
887 		dev->mixer_class = ENVY_MIX_CLASSMON;
888 		dev->un.v.delta = 2;
889 		dev->un.v.num_channels = 1;
890 		snprintf(dev->label.name, MAX_AUDIO_DEV_LEN,
891 			 "%s%d", out < 10 ? "play" : "rec", out % 10);
892 		strlcpy(dev->un.v.units.name, AudioNvolume, MAX_AUDIO_DEV_LEN);
893 		return 0;
894 	}
895 	if (dev->index < ENVY_MIX_OLVL(4)) {	/* inputs.line */
896 		out = dev->index - ENVY_MIX_ILVL(4);
897 		dev->type = AUDIO_MIXER_VALUE;
898 		dev->mixer_class = ENVY_MIX_CLASSIN;
899 		dev->un.v.delta = 2;
900 		dev->un.v.num_channels = 1;
901 		snprintf(dev->label.name, MAX_AUDIO_DEV_LEN,
902 		    AudioNline "%d", out);
903 		strlcpy(dev->un.v.units.name, AudioNvolume, MAX_AUDIO_DEV_LEN);
904 		return 0;
905 	}
906 	if (dev->index < ENVY_MIX_OMUTE(4)) {	/* outputs.line */
907 		out = dev->index - ENVY_MIX_OLVL(4);
908 		dev->type = AUDIO_MIXER_VALUE;
909 		dev->mixer_class = ENVY_MIX_CLASSOUT;
910 		dev->next = ENVY_MIX_OUTSRC + out;
911 		dev->un.v.delta = 2;
912 		dev->un.v.num_channels = 1;
913 		snprintf(dev->label.name, MAX_AUDIO_DEV_LEN,
914 		    AudioNline "%d", out);
915 		strlcpy(dev->un.v.units.name, AudioNvolume, MAX_AUDIO_DEV_LEN);
916 		return 0;
917 	}
918 	if (dev->index < ENVY_MIX_INVAL(4)) {	/* outputs.mute */
919 		out = dev->index - ENVY_MIX_OMUTE(4);
920 		dev->type = AUDIO_MIXER_ENUM;
921 		dev->mixer_class = ENVY_MIX_CLASSOUT;
922 		dev->un.e.member[0].ord = 0;
923 		strlcpy(dev->un.e.member[0].label.name, AudioNoff,
924 		    MAX_AUDIO_DEV_LEN);
925 		dev->un.e.member[1].ord = 1;
926 		strlcpy(dev->un.e.member[1].label.name, AudioNon,
927 		    MAX_AUDIO_DEV_LEN);
928 		dev->un.s.num_mem = 2;
929 		snprintf(dev->label.name, MAX_AUDIO_DEV_LEN,
930 		    AudioNmute "%d-%d", 2 * out, 2 * out + 1);
931 		return 0;
932 	}
933 	return ENXIO;
934 }
935 
936 int
937 envy_get_port(void *self, struct mixer_ctrl *ctl)
938 {
939 	struct envy_softc *sc = (struct envy_softc *)self;
940 	int out, val;
941 
942 	if (ctl->dev < ENVY_MIX_OUTSRC) {
943 		return EINVAL;
944 	}
945 	if (ctl->dev <  ENVY_MIX_OUTSRC + 8) {
946 		out = ctl->dev - ENVY_MIX_OUTSRC;
947 		ctl->un.ord = envy_lineout_getsrc(sc, out);
948 		return 0;
949 	}
950 	if (ctl->dev <  ENVY_MIX_MONITOR) {
951 		out = ctl->dev - (ENVY_MIX_OUTSRC + 8);
952 		ctl->un.ord = envy_spdout_getsrc(sc, out);
953 		return 0;
954 	}
955 	if (ctl->dev <  ENVY_MIX_ILVL(4)) {
956 		out = ctl->dev - ENVY_MIX_MONITOR;
957 		envy_mon_getvol(sc, out / 2, out % 2, &val);
958 		ctl->un.value.num_channels = 1;
959 		ctl->un.value.level[0] = 2 * val;
960 		return 0;
961 	}
962 	if (ctl->dev < ENVY_MIX_OLVL(4)) {
963 		out = ctl->dev - ENVY_MIX_ILVL(4);
964 		val = envy_ak_read(sc, out / 2, (out % 2) + AK_ADC_GAIN0);
965 		ctl->un.value.num_channels = 1;
966 		ctl->un.value.level[0] = 2 * val;
967 		return 0;
968 	}
969 	if (ctl->dev < ENVY_MIX_OMUTE(4)) {
970 		out = ctl->dev - ENVY_MIX_OLVL(4);
971 		val = envy_ak_read(sc, out / 2, (out % 2) + AK_DAC_GAIN0);
972 		ctl->un.value.num_channels = 1;
973 		ctl->un.value.level[0] = 2 * val;
974 		return 0;
975 	}
976 	if (ctl->dev < ENVY_MIX_INVAL(4)) {
977 		out = ctl->dev - ENVY_MIX_OMUTE(4);
978 		val = envy_ak_read(sc, out, AK_DEEMVOL);
979 		ctl->un.ord = (val & AK_MUTE) ? 1 : 0;
980 		return 0;
981 	}
982 	return ENXIO;
983 }
984 
985 int
986 envy_set_port(void *self, struct mixer_ctrl *ctl)
987 {
988 	struct envy_softc *sc = (struct envy_softc *)self;
989 	int out, maxsrc, val;
990 
991 	if (ctl->dev < ENVY_MIX_OUTSRC) {
992 		return EINVAL;
993 	}
994 	if (ctl->dev < ENVY_MIX_OUTSRC + 8) {
995 		out = ctl->dev - ENVY_MIX_OUTSRC;
996 		maxsrc = (out < 2 || out >= 8) ? 12 : 11;
997 		if (ctl->un.ord < 0 || ctl->un.ord >= maxsrc)
998 			return EINVAL;
999 		envy_lineout_setsrc(sc, out, ctl->un.ord);
1000 		return 0;
1001 	}
1002 	if (ctl->dev <  ENVY_MIX_MONITOR) {
1003 		out = ctl->dev - (ENVY_MIX_OUTSRC + 8);
1004 		if (ctl->un.ord < 0 || ctl->un.ord >= 12)
1005 			return EINVAL;
1006 		envy_spdout_setsrc(sc, out, ctl->un.ord);
1007 		return 0;
1008 	}
1009 	if (ctl->dev <  ENVY_MIX_ILVL(4)) {
1010 		out = ctl->dev - ENVY_MIX_MONITOR;
1011 		if (ctl->un.value.num_channels != 1) {
1012 			return EINVAL;
1013 		}
1014 		val = ctl->un.value.level[0] / 2;
1015 		envy_mon_setvol(sc, out / 2, out % 2, val);
1016 		return 0;
1017 	}
1018 	if (ctl->dev < ENVY_MIX_OLVL(4)) {
1019 		if (ctl->un.value.num_channels != 1)
1020 			return EINVAL;
1021 		out = ctl->dev - ENVY_MIX_ILVL(4);
1022 		val = ctl->un.value.level[0] / 2;
1023 		envy_ak_write(sc, out / 2, (out % 2) + AK_ADC_GAIN0, val);
1024 		return 0;
1025 	}
1026 	if (ctl->dev < ENVY_MIX_OMUTE(4)) {
1027 		if (ctl->un.value.num_channels != 1)
1028 			return EINVAL;
1029 		out = ctl->dev - ENVY_MIX_OLVL(4);
1030 		val = ctl->un.value.level[0] / 2;
1031 		envy_ak_write(sc, out / 2, (out % 2) + AK_DAC_GAIN0, val);
1032 		return 0;
1033 	}
1034 	if (ctl->dev < ENVY_MIX_INVAL(4)) {
1035 		if (ctl->un.ord >= 2)
1036 			return EINVAL;
1037 		out = ctl->dev - ENVY_MIX_OMUTE(4);
1038 		val = AK_DEEM_OFF | (ctl->un.ord ? AK_MUTE : 0);
1039 		envy_ak_write(sc, out, AK_DEEMVOL, val);
1040 		return 0;
1041 	}
1042 	return ENXIO;
1043 }
1044 
1045 int
1046 envy_get_props(void *self)
1047 {
1048 	return AUDIO_PROP_FULLDUPLEX | AUDIO_PROP_INDEPENDENT;
1049 }
1050