xref: /openbsd/sys/dev/pci/auglx.c (revision 8529ddd3)
1 /*      $OpenBSD: auglx.c,v 1.13 2015/05/11 06:46:21 ratchov Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
5  * All rights reserved.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
16  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /*
21  * AMD CS5536 series AC'97 audio driver.
22  *
23  * The following datasheets were helpful in the development of this
24  * driver:
25  *
26  * AMD Geode LX Processors Data Book
27  * http://www.amd.com/files/connectivitysolutions/geode/geode_lx/\
28  *     33234F_LX_databook.pdf
29  *
30  * AMD Geode CS5536 Companion Device Data Book
31  * http://www.amd.com/files/connectivitysolutions/geode/geode_lx/\
32  *     33238G_cs5536_db.pdf
33  *
34  * Realtek ALC203 Two-Channel AC'97 2.3 Audio Codec
35  * ftp://202.65.194.211/pc/audio/ALC203_DataSheet_1.6.pdf
36  *
37  * This driver is inspired by the auich(4) and auixp(4) drivers, some
38  * of the hardware-independent functionality has been derived from them
39  * (e.g. memory allocation for the upper level, parameter setting).
40  */
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/device.h>
45 #include <sys/malloc.h>
46 #include <sys/audioio.h>
47 
48 #include <machine/bus.h>
49 
50 #include <dev/pci/pcireg.h>
51 #include <dev/pci/pcivar.h>
52 #include <dev/pci/pcidevs.h>
53 #include <dev/audio_if.h>
54 
55 #include <dev/ic/ac97.h>
56 
57 #define AUGLX_ACC_BAR		0x10
58 
59 /* ACC Native Registers */
60 #define ACC_GPIO_STATUS		0x00
61 #define ACC_GPIO_CNTL		0x04
62 #define ACC_CODEC_STATUS	0x08
63 #define ACC_CODEC_CNTL		0x0c
64 #define ACC_IRQ_STATUS		0x12
65 #define ACC_ENGINE_CNTL		0x14
66 #define ACC_BM0_CMD		0x20		/* Bus Master 0 Command */
67 #define ACC_BM0_STATUS		0x21		/* Bus Master 0 IRQ Status */
68 #define ACC_BM0_PRD		0x24		/* BM0 PRD Table Address */
69 #define ACC_BM1_CMD		0x28		/* Bus Master 1 Command */
70 #define ACC_BM1_STATUS		0x29		/* Bus Master 1 IRQ Status */
71 #define ACC_BM1_PRD		0x2c		/* BM1 PRD Table Address */
72 #define ACC_BM2_CMD		0x30		/* Bus Master 2 Command */
73 #define ACC_BM2_STATUS		0x31		/* Bus Master 2 IRQ Status */
74 #define ACC_BM2_PRD		0x34		/* BM2 PRD Table Address */
75 #define ACC_BM3_CMD		0x38		/* Bus Master 3 Command */
76 #define ACC_BM3_STATUS		0x39		/* Bus Master 3 IRQ Status */
77 #define ACC_BM3_PRD		0x3c		/* BM3 PRD Table Address */
78 #define ACC_BM4_CMD		0x40		/* Bus Master 4 Command */
79 #define ACC_BM4_STATUS		0x41		/* Bus Master 4 IRQ Status */
80 #define ACC_BM4_PRD		0x44		/* BM4 PRD Table Address */
81 #define ACC_BM5_CMD		0x48		/* Bus Master 5 Command */
82 #define ACC_BM5_STATUS		0x49		/* Bus Master 5 IRQ Status */
83 #define ACC_BM5_PRD		0x4c		/* BM5 PRD Table Address */
84 #define ACC_BM6_CMD		0x50		/* Bus Master 6 Command */
85 #define ACC_BM6_STATUS		0x51		/* Bus Master 6 IRQ Status */
86 #define ACC_BM6_PRD		0x54		/* BM6 PRD Table Address */
87 #define ACC_BM7_CMD		0x58		/* Bus Master 7 Command */
88 #define ACC_BM7_STATUS		0x59		/* Bus Master 7 IRQ Status */
89 #define ACC_BM7_PRD		0x5c		/* BM7 PRD Table Address */
90 #define ACC_BM0_PNTR		0x60		/* Bus Master 0 DMA Pointer */
91 #define ACC_BM1_PNTR		0x64		/* Bus Master 1 DMA Pointer */
92 #define ACC_BM2_PNTR		0x68		/* Bus Master 2 DMA Pointer */
93 #define ACC_BM3_PNTR		0x6c		/* Bus Master 3 DMA Pointer */
94 #define ACC_BM4_PNTR		0x70		/* Bus Master 4 DMA Pointer */
95 #define ACC_BM5_PNTR		0x74		/* Bus Master 5 DMA Pointer */
96 #define ACC_BM6_PNTR		0x78		/* Bus Master 6 DMA Pointer */
97 #define ACC_BM7_PNTR		0x7c		/* Bus Master 7 DMA Pointer */
98 
99 /* ACC_IRQ_STATUS Bit Definitions */
100 #define BM7_IRQ_STS	0x0200	/* Audio Bus Master 7 IRQ Status */
101 #define BM6_IRQ_STS	0x0100	/* Audio Bus Master 6 IRQ Status */
102 #define BM5_IRQ_STS	0x0080	/* Audio Bus Master 5 IRQ Status */
103 #define BM4_IRQ_STS	0x0040	/* Audio Bus Master 4 IRQ Status */
104 #define BM3_IRQ_STS	0x0020	/* Audio Bus Master 3 IRQ Status */
105 #define BM2_IRQ_STS	0x0010	/* Audio Bus Master 2 IRQ Status */
106 #define BM1_IRQ_STS	0x0008	/* Audio Bus Master 1 IRQ Status */
107 #define BM0_IRQ_STS	0x0004	/* Audio Bus Master 0 IRQ Status */
108 #define WU_IRQ_STS	0x0002	/* Codec GPIO Wakeup IRQ Status */
109 #define IRQ_STS		0x0001	/* Codec GPIO IRQ Status */
110 
111 /* ACC_ENGINE_CNTL Bit Definitions */
112 #define SSND_MODE	0x00000001	/* Surround Sound (5.1) Sync. Mode */
113 
114 /* ACC_BM[x]_CMD Bit Descriptions */
115 #define BMx_CMD_RW		0x08	/* 0: Mem to codec, 1: codec to mem */
116 #define BMx_CMD_BYTE_ORD	0x04	/* 0: Little Endian, 1: Big Endian */
117 #define BMx_CMD_BM_CTL_DIS	0x00	/* Disable bus master */
118 #define BMx_CMD_BM_CTL_EN	0x01	/* Enable bus master */
119 #define BMx_CMD_BM_CTL_PAUSE	0x03	/* Pause bus master */
120 
121 /* ACC_BM[x]_STATUS Bit Definitions */
122 #define BMx_BM_EOP_ERR		0x02	/* Bus master error */
123 #define BMx_BM_EOP		0x01	/* End of page */
124 
125 /* ACC_CODEC_CNTL Bit Definitions */
126 #define RW_CMD			0x80000000
127 #define PD_PRIM			0x00200000
128 #define PD_SEC			0x00100000
129 #define LNK_SHTDOWN		0x00040000
130 #define LNK_WRM_RST		0x00020000
131 #define CMD_NEW			0x00010000
132 
133 /* ACC_CODEC_STATUS Bit Definitions */
134 #define PRM_RDY_STS		0x00800000
135 #define SEC_RDY_STS		0x00400000
136 #define SDATAIN2_EN		0x00200000
137 #define BM5_SEL			0x00100000
138 #define BM4_SEL			0x00080000
139 #define STS_NEW			0x00020000
140 
141 #define AUGLX_TOUT		1000	/* uSec */
142 
143 #define	AUGLX_DMALIST_MAX	1
144 #define	AUGLX_DMASEG_MAX	65536
145 
146 struct auglx_prd {
147 	u_int32_t	base;
148 	u_int32_t	size;
149 #define AUGLX_PRD_EOT	0x80000000
150 #define AUGLX_PRD_EOP	0x40000000
151 #define AUGLX_PRD_JMP	0x20000000
152 };
153 
154 #define	AUGLX_FIXED_RATE 48000
155 
156 struct auglx_dma {
157 	bus_dmamap_t		 map;
158 	caddr_t			 addr;
159 	bus_dma_segment_t	 segs[AUGLX_DMALIST_MAX];
160 	int			 nsegs;
161 	size_t			 size;
162 	struct auglx_dma	*next;
163 };
164 
165 struct auglx_softc {
166 	struct device		 sc_dev;
167 	void			*sc_ih;
168 
169 	audio_device_t		 sc_audev;
170 
171 	bus_space_tag_t		 sc_iot;
172 	bus_space_handle_t	 sc_ioh;
173 	bus_dma_tag_t		 sc_dmat;
174 
175 	/*
176 	 * The CS5536 ACC has eight bus masters to support 5.1 audio.
177 	 * This driver, however, only supports main playback and recording
178 	 * since I only have a Realtek ALC203 codec available for testing.
179 	 */
180 	struct auglx_ring {
181 		bus_dmamap_t		 sc_prd;
182 		struct auglx_prd	*sc_vprd;
183 		int			 sc_nprd;
184 
185 		size_t			 sc_size;
186 		int			 nsegs;
187 		bus_dma_segment_t	 seg;
188 
189 		void			(*intr)(void *);
190 		void			*arg;
191 	} bm0, bm1;	/* bm0: output, bm1: input */
192 
193 	struct auglx_dma	*sc_dmas;
194 
195 	struct ac97_codec_if	*codec_if;
196 	struct ac97_host_if	 host_if;
197 
198 	int			 sc_dmamap_flags;
199 };
200 
201 #ifdef AUGLX_DEBUG
202 #define	DPRINTF(l,x)	do { if (auglx_debug & (l)) printf x; } while(0)
203 int auglx_debug = 0;
204 #define AUGLX_DBG_ACC	0x0001
205 #define AUGLX_DBG_DMA	0x0002
206 #define AUGLX_DBG_IRQ	0x0004
207 #else
208 #define	DPRINTF(x,y)	/* nothing */
209 #endif
210 
211 struct cfdriver auglx_cd = {
212 	NULL, "auglx", DV_DULL
213 };
214 
215 int auglx_open(void *, int);
216 void auglx_close(void *);
217 int auglx_query_encoding(void *, struct audio_encoding *);
218 int auglx_set_params(void *, int, int, struct audio_params *,
219     struct audio_params *);
220 int auglx_round_blocksize(void *, int);
221 int auglx_halt_output(void *);
222 int auglx_halt_input(void *);
223 int auglx_getdev(void *, struct audio_device *);
224 int auglx_set_port(void *, mixer_ctrl_t *);
225 int auglx_get_port(void *, mixer_ctrl_t *);
226 int auglx_query_devinfo(void *, mixer_devinfo_t *);
227 void *auglx_allocm(void *, int, size_t, int, int);
228 void auglx_freem(void *, void *, int);
229 size_t auglx_round_buffersize(void *, int, size_t);
230 paddr_t auglx_mappage(void *, void *, off_t, int);
231 int auglx_get_props(void *);
232 int auglx_trigger_output(void *, void *, void *, int, void (*)(void *),
233     void *, struct audio_params *);
234 int auglx_trigger_input(void *, void *, void *, int, void (*)(void *),
235     void *, struct audio_params *);
236 int auglx_alloc_cdata(struct auglx_softc *);
237 int auglx_alloc_prd(struct auglx_softc *, size_t, struct auglx_ring *);
238 void auglx_free_prd(struct auglx_softc *sc, struct auglx_ring *bm);
239 int auglx_allocmem(struct auglx_softc *, size_t, size_t, struct auglx_dma *);
240 void auglx_freemem(struct auglx_softc *, struct auglx_dma *);
241 void auglx_get_default_params(void *, int, struct audio_params *);
242 
243 struct audio_hw_if auglx_hw_if = {
244 	auglx_open,
245 	auglx_close,
246 	NULL,			/* drain */
247 	auglx_query_encoding,
248 	auglx_set_params,
249 	auglx_round_blocksize,
250 	NULL,			/* commit_setting */
251 	NULL,			/* init_output */
252 	NULL,			/* init_input */
253 	NULL,			/* start_output */
254 	NULL,			/* start_input */
255 	auglx_halt_output,
256 	auglx_halt_input,
257 	NULL,			/* speaker_ctl */
258 	auglx_getdev,
259 	NULL,			/* getfd */
260 	auglx_set_port,
261 	auglx_get_port,
262 	auglx_query_devinfo,
263 	auglx_allocm,
264 	auglx_freem,
265 	auglx_round_buffersize,
266 	auglx_mappage,
267 	auglx_get_props,
268 	auglx_trigger_output,
269 	auglx_trigger_input,
270 	auglx_get_default_params
271 };
272 
273 int	auglx_match(struct device *, void *, void *);
274 void	auglx_attach(struct device *, struct device *, void *);
275 int	auglx_activate(struct device *, int);
276 int	auglx_intr(void *);
277 
278 int	auglx_attach_codec(void *, struct ac97_codec_if *);
279 int	auglx_read_codec(void *, u_int8_t, u_int16_t *);
280 int	auglx_write_codec(void *, u_int8_t, u_int16_t);
281 void	auglx_reset_codec(void *);
282 enum ac97_host_flags	auglx_flags_codec(void *);
283 
284 struct cfattach auglx_ca = {
285 	sizeof(struct auglx_softc), auglx_match, auglx_attach, NULL,
286 	auglx_activate
287 };
288 
289 const struct pci_matchid auglx_devices[] = {
290 	{ PCI_VENDOR_AMD,	PCI_PRODUCT_AMD_CS5536_AUDIO }
291 };
292 
293 int
294 auglx_match(struct device *parent, void *match, void *aux)
295 {
296 	return (pci_matchbyid((struct pci_attach_args *)aux, auglx_devices,
297 	    sizeof(auglx_devices) / sizeof(auglx_devices[0])));
298 }
299 
300 void
301 auglx_attach(struct device *parent, struct device *self, void *aux)
302 {
303 	struct auglx_softc *sc = (struct auglx_softc *)self;
304 	struct pci_attach_args *pa = aux;
305 	bus_size_t bar_size;
306 	pci_intr_handle_t ih;
307 	const char *intrstr;
308 
309 	if (pci_mapreg_map(pa, AUGLX_ACC_BAR, PCI_MAPREG_TYPE_IO, 0,
310 	    &sc->sc_iot, &sc->sc_ioh, NULL, &bar_size, 0)) {
311 		printf(": can't map ACC I/O space\n");
312 		return;
313 	}
314 
315 	sc->sc_dmat = pa->pa_dmat;
316 	sc->sc_dmamap_flags = BUS_DMA_COHERENT;
317 
318 	if (pci_intr_map(pa, &ih)) {
319 		printf(": can't map interrupt");
320 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size);
321 		return;
322 	}
323 	intrstr = pci_intr_string(pa->pa_pc, ih);
324 	sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO | IPL_MPSAFE,
325 	    auglx_intr, sc, sc->sc_dev.dv_xname);
326 	if (!sc->sc_ih) {
327 		printf(": can't establish interrupt");
328 		if (intrstr)
329 			printf(" at %s", intrstr);
330 		printf("\n");
331 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size);
332 		return;
333 	}
334 
335 	strlcpy(sc->sc_audev.name, "CS5536 AC97", sizeof sc->sc_audev.name);
336 	snprintf(sc->sc_audev.version, sizeof sc->sc_audev.version, "0x%02x",
337 		 PCI_REVISION(pa->pa_class));
338 	strlcpy(sc->sc_audev.config, sc->sc_dev.dv_xname,
339 		sizeof sc->sc_audev.config);
340 
341 	printf(": %s, %s\n", intrstr, sc->sc_audev.name);
342 
343 	sc->host_if.arg = sc;
344 	sc->host_if.attach = auglx_attach_codec;
345 	sc->host_if.read = auglx_read_codec;
346 	sc->host_if.write = auglx_write_codec;
347 	sc->host_if.reset = auglx_reset_codec;
348 	sc->host_if.flags = auglx_flags_codec;
349 
350 	if (ac97_attach(&sc->host_if) != 0) {
351 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size);
352 		return;
353 	}
354 	audio_attach_mi(&auglx_hw_if, sc, &sc->sc_dev);
355 }
356 
357 /* Functions to communicate with the AC97 Codec via the ACC */
358 int
359 auglx_read_codec(void *v, u_int8_t reg, u_int16_t *val)
360 {
361 	struct auglx_softc *sc = v;
362 	u_int32_t codec_cntl, codec_status;
363 	int i;
364 
365 	codec_cntl = RW_CMD | ((u_int32_t)reg << 24) | CMD_NEW;
366 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl);
367 
368 	for (i = AUGLX_TOUT; i; i--) {
369 		codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
370 		    ACC_CODEC_CNTL);
371 		if (!(codec_cntl & CMD_NEW))
372 			break;
373 		delay(1);
374 	}
375 	if (codec_cntl & CMD_NEW) {
376 		printf("%s: codec read timeout after write\n",
377 		    sc->sc_dev.dv_xname);
378 		return -1;
379 	}
380 
381 	for (i = AUGLX_TOUT; i; i--) {
382 		codec_status = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
383 		    ACC_CODEC_STATUS);
384 		if ((codec_status & STS_NEW) && (codec_status >> 24 == reg))
385 			break;
386 		delay(10);
387 	}
388 	if (i == 0) {
389 		printf("%s: codec status read timeout, 0x%08x\n",
390 		    sc->sc_dev.dv_xname, codec_status);
391 		return -1;
392 	}
393 
394 	*val = codec_status & 0xffff;
395 	DPRINTF(AUGLX_DBG_ACC, ("%s: read codec register 0x%02x: 0x%04x\n",
396 	    sc->sc_dev.dv_xname, reg, *val));
397 	return 0;
398 }
399 
400 int
401 auglx_write_codec(void *v, u_int8_t reg, u_int16_t val)
402 {
403 	struct auglx_softc *sc = v;
404 	u_int32_t codec_cntl;
405 	int i;
406 
407 	DPRINTF(AUGLX_DBG_ACC, ("%s: write codec register 0x%02x: 0x%04x\n",
408 	    sc->sc_dev.dv_xname, reg, val));
409 
410 
411 	codec_cntl = ((u_int32_t)reg << 24) | CMD_NEW | val;
412 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl);
413 
414 	for (i = AUGLX_TOUT; i; i--) {
415 		codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
416 		    ACC_CODEC_CNTL);
417 		if (!(codec_cntl & CMD_NEW))
418 			break;
419 		delay(1);
420 	}
421 	if (codec_cntl & CMD_NEW) {
422 		printf("%s: codec write timeout\n", sc->sc_dev.dv_xname);
423 		return -1;
424 	}
425 
426 	return 0;
427 }
428 
429 int
430 auglx_attach_codec(void *v, struct ac97_codec_if *cif)
431 {
432 	struct auglx_softc *sc = v;
433 
434 	sc->codec_if = cif;
435 	return 0;
436 }
437 
438 void
439 auglx_reset_codec(void *v)
440 {
441 	struct auglx_softc *sc = v;
442 	u_int32_t codec_cntl;
443 	int i;
444 
445 	codec_cntl = LNK_WRM_RST | CMD_NEW;
446 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl);
447 
448 	for (i = AUGLX_TOUT; i; i--) {
449 		codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
450 		    ACC_CODEC_CNTL);
451 		if (!(codec_cntl & CMD_NEW))
452 			continue;
453 		delay(1);
454 	}
455 	if (codec_cntl & CMD_NEW)
456 		printf("%s: codec reset timeout\n", sc->sc_dev.dv_xname);
457 }
458 
459 enum ac97_host_flags
460 auglx_flags_codec(void *v)
461 {
462 	return 0;
463 }
464 
465 /*
466  * Audio functions
467  */
468 int
469 auglx_open(void *v, int flags)
470 {
471 	return 0;
472 }
473 
474 void
475 auglx_close(void *v)
476 {
477 }
478 
479 
480 int
481 auglx_query_encoding(void *v, struct audio_encoding *aep)
482 {
483 	switch (aep->index) {
484 	case 0:
485 		strlcpy(aep->name, AudioEslinear_le, sizeof aep->name);
486 		aep->encoding = AUDIO_ENCODING_SLINEAR_LE;
487 		aep->precision = 16;
488 		aep->flags = 0;
489 		break;
490 	default:
491 		return EINVAL;
492 	}
493 	aep->bps = AUDIO_BPS(aep->precision);
494 	aep->msb = 1;
495 
496 	return 0;
497 }
498 
499 
500 int
501 auglx_set_params(void *v, int setmode, int usemode, struct audio_params *play,
502     struct audio_params *rec)
503 {
504 	struct auglx_softc *sc = v;
505 	int error;
506 	u_int orate;
507 
508 	if (setmode & AUMODE_PLAY) {
509 		play->precision = 16;
510 		play->channels = 2;
511 		play->encoding = AUDIO_ENCODING_SLINEAR_LE;
512 		play->bps = AUDIO_BPS(play->precision);
513 		play->msb = 1;
514 
515 		orate = play->sample_rate;
516 
517 		play->sample_rate = orate;
518 		error = ac97_set_rate(sc->codec_if,
519 		    AC97_REG_PCM_LFE_DAC_RATE, &play->sample_rate);
520 		if (error)
521 			return error;
522 
523 		play->sample_rate = orate;
524 		error = ac97_set_rate(sc->codec_if,
525 		    AC97_REG_PCM_SURR_DAC_RATE, &play->sample_rate);
526 		if (error)
527 			return error;
528 
529 		play->sample_rate = orate;
530 		error = ac97_set_rate(sc->codec_if,
531 		    AC97_REG_PCM_FRONT_DAC_RATE, &play->sample_rate);
532 		if (error)
533 			return error;
534 	}
535 
536 	if (setmode & AUMODE_RECORD) {
537 		rec->precision = 16;
538 		rec->channels = 2;
539 		rec->encoding = AUDIO_ENCODING_ULINEAR_LE;
540 		rec->bps = AUDIO_BPS(rec->precision);
541 		rec->msb = 1;
542 
543 		error = ac97_set_rate(sc->codec_if, AC97_REG_PCM_LR_ADC_RATE,
544 		    &rec->sample_rate);
545 		if (error)
546 			return error;
547 	}
548 
549 	return 0;
550 }
551 
552 int
553 auglx_round_blocksize(void *v, int blk)
554 {
555 	return (blk + 0x3f) & ~0x3f;
556 }
557 
558 int
559 auglx_halt_output(void *v)
560 {
561 	struct auglx_softc *sc = v;
562 
563 	DPRINTF(AUGLX_DBG_DMA, ("%s: halt_output\n", sc->sc_dev.dv_xname));
564 
565 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD, 0x00);
566 	sc->bm0.intr = NULL;
567 	return 0;
568 }
569 
570 int
571 auglx_halt_input(void *v)
572 {
573 	struct auglx_softc *sc = v;
574 
575 	DPRINTF(AUGLX_DBG_DMA,
576 	    ("%s: halt_input\n", sc->sc_dev.dv_xname));
577 
578 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD, 0x00);
579 	sc->bm1.intr = NULL;
580 	return 0;
581 }
582 
583 int
584 auglx_getdev(void *v, struct audio_device *adp)
585 {
586 	struct auglx_softc *sc = v;
587 	*adp = sc->sc_audev;
588 	return 0;
589 }
590 
591 int
592 auglx_set_port(void *v, mixer_ctrl_t *cp)
593 {
594 	struct auglx_softc *sc = v;
595 	return sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp);
596 }
597 
598 int
599 auglx_get_port(void *v, mixer_ctrl_t *cp)
600 {
601 	struct auglx_softc *sc = v;
602 	return sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp);
603 }
604 
605 int
606 auglx_query_devinfo(void *v, mixer_devinfo_t *dp)
607 {
608 	struct auglx_softc *sc = v;
609 	return sc->codec_if->vtbl->query_devinfo(sc->codec_if, dp);
610 }
611 
612 void *
613 auglx_allocm(void *v, int direction, size_t size, int pool, int flags)
614 {
615 	struct auglx_softc *sc = v;
616 	struct auglx_dma *p;
617 	int error;
618 
619 	DPRINTF(AUGLX_DBG_DMA, ("%s: request buffer of size %ld, dir %d\n",
620 	    sc->sc_dev.dv_xname, size, direction));
621 
622 	/* can only use 1 segment */
623 	if (size > AUGLX_DMASEG_MAX) {
624 		DPRINTF(AUGLX_DBG_DMA,
625 		    ("%s: requested buffer size too large: %d", \
626 		    sc->sc_dev.dv_xname, size));
627 		return NULL;
628 	}
629 
630 	p = malloc(sizeof(*p), pool, flags | M_ZERO);
631 	if (!p)
632 		return NULL;
633 
634 	error = auglx_allocmem(sc, size, PAGE_SIZE, p);
635 	if (error) {
636 		free(p, pool, 0);
637 		return NULL;
638 	}
639 
640 	p->next = sc->sc_dmas;
641 	sc->sc_dmas = p;
642 
643 	return p->addr;
644 }
645 
646 void
647 auglx_freem(void *v, void *ptr, int pool)
648 {
649 	struct auglx_softc *sc;
650 	struct auglx_dma *p, **pp;
651 
652 	sc = v;
653 	for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->next) {
654 		if (p->addr == ptr) {
655 			auglx_freemem(sc, p);
656 			*pp = p->next;
657 			free(p, pool, 0);
658 			return;
659 		}
660 	}
661 }
662 
663 
664 size_t
665 auglx_round_buffersize(void *v, int direction, size_t size)
666 {
667 	if (size > AUGLX_DMASEG_MAX)
668 		size = AUGLX_DMASEG_MAX;
669 
670 	return size;
671 }
672 
673 paddr_t
674 auglx_mappage(void *v, void *mem, off_t off, int prot)
675 {
676 	struct auglx_softc *sc = v;
677 	struct auglx_dma *p;
678 
679 	if (off < 0)
680 		return -1;
681 
682 	for (p = sc->sc_dmas; p && p->addr != mem; p = p->next);
683 	if (!p)
684 		return -1;
685 
686 	return bus_dmamem_mmap(sc->sc_dmat, p->segs, p->nsegs,
687 	    off, prot, BUS_DMA_WAITOK);
688 }
689 
690 int
691 auglx_get_props(void *v)
692 {
693 	return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
694 }
695 
696 int
697 auglx_intr(void *v)
698 {
699 	struct auglx_softc *sc = v;
700 	u_int16_t irq_sts;
701 	u_int8_t bm_sts;
702 
703 	mtx_enter(&audio_lock);
704 	irq_sts = bus_space_read_2(sc->sc_iot, sc->sc_ioh, ACC_IRQ_STATUS);
705 	if (irq_sts == 0) {
706 		mtx_leave(&audio_lock);
707 		return 0;
708 	}
709 
710 	if (irq_sts & BM0_IRQ_STS) {
711 		bm_sts = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
712 		    ACC_BM0_STATUS);
713 		if (sc->bm0.intr) {
714 			sc->bm0.intr(sc->bm0.arg);
715 			bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD,
716 			    BMx_CMD_BM_CTL_EN);
717 		}
718 	} else if (irq_sts & BM1_IRQ_STS) {
719 		bm_sts = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
720 		    ACC_BM1_STATUS);
721 		if (sc->bm1.intr) {
722 			sc->bm1.intr(sc->bm1.arg);
723 			bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD,
724 			    BMx_CMD_RW | BMx_CMD_BM_CTL_EN);
725 		}
726 	} else {
727 		DPRINTF(AUGLX_DBG_IRQ, ("%s: stray intr, status = 0x%04x\n",
728 		    sc->sc_dev.dv_xname, irq_sts));
729 		mtx_leave(&audio_lock);
730 		return -1;
731 	}
732 	mtx_leave(&audio_lock);
733 	return 1;
734 }
735 
736 int
737 auglx_trigger_output(void *v, void *start, void *end, int blksize,
738     void (*intr)(void *), void *arg, struct audio_params *param)
739 {
740 	struct auglx_softc *sc = v;
741 	struct auglx_dma *p;
742 	size_t size;
743 	u_int32_t addr;
744 	int i, nprd;
745 
746 	size = (size_t)((caddr_t)end - (caddr_t)start);
747 	DPRINTF(AUGLX_DBG_DMA, ("%s: trigger_output, %p 0x%08x bytes, "
748 	    "blksize 0x%04x\n", sc->sc_dev.dv_xname, start, size, blksize));
749 
750 	for (p = sc->sc_dmas; p && p->addr != start; p = p->next);
751 	if (!p) {
752 		DPRINTF(AUGLX_DBG_DMA, ("%s dma reg not found\n",
753 		    sc->sc_dev.dv_xname));
754 		return -1;
755 	}
756 
757 	/* set up the PRDs */
758 	nprd = size / blksize;
759 	if (sc->bm0.sc_nprd != nprd + 1) {
760 		if (sc->bm0.sc_nprd > 0)
761 			auglx_free_prd(sc, &sc->bm0);
762 		sc->bm0.sc_nprd = nprd + 1;
763 		auglx_alloc_prd(sc,
764 		    sc->bm0.sc_nprd * sizeof(struct auglx_prd), &sc->bm0);
765 	}
766 	DPRINTF(AUGLX_DBG_DMA, ("%s: nprd = %d\n", sc->sc_dev.dv_xname,
767 	    nprd));
768 	addr = p->segs->ds_addr;
769 	for (i = 0; i < nprd; i++) {
770 		sc->bm0.sc_vprd[i].base = addr;
771 		sc->bm0.sc_vprd[i].size = blksize | AUGLX_PRD_EOP;
772 		addr += blksize;
773 	}
774 	sc->bm0.sc_vprd[i].base = sc->bm0.sc_prd->dm_segs[0].ds_addr;
775 	sc->bm0.sc_vprd[i].size = AUGLX_PRD_JMP;
776 
777 #ifdef AUGLX_DEBUG
778 	for (i = 0; i < sc->bm0.sc_nprd; i++)
779 		DPRINTF(AUGLX_DBG_DMA, ("%s: PRD[%d].base = %p, size %p\n",
780 		    sc->sc_dev.dv_xname, i, sc->bm0.sc_vprd[i].base,
781 		    sc->bm0.sc_vprd[i].size));
782 #endif
783 	sc->bm0.intr = intr;
784 	sc->bm0.arg = arg;
785 
786 	mtx_enter(&audio_lock);
787 	/* Program the BM0 PRD register */
788 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_BM0_PRD,
789 	    sc->bm0.sc_prd->dm_segs[0].ds_addr);
790 	/* Start Audio Bus Master 0 */
791 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD,
792 	    BMx_CMD_BM_CTL_EN);
793 	mtx_leave(&audio_lock);
794 	return 0;
795 }
796 
797 int
798 auglx_trigger_input(void *v, void *start, void *end, int blksize,
799     void (*intr)(void *), void * arg, struct audio_params *param)
800 {
801 	struct auglx_softc *sc = v;
802 	struct auglx_dma *p;
803 	size_t size;
804 	u_int32_t addr;
805 	int i, nprd;
806 
807 	size = (size_t)((caddr_t)end - (caddr_t)start);
808 	DPRINTF(AUGLX_DBG_DMA, ("%s: trigger_input, %p 0x%08x bytes, "
809 	    "blksize 0x%04x\n", sc->sc_dev.dv_xname, start, size, blksize));
810 
811 	for (p = sc->sc_dmas; p && p->addr != start; p = p->next);
812 	if (!p) {
813 		DPRINTF(AUGLX_DBG_DMA, ("%s dma reg not found\n",
814 		    sc->sc_dev.dv_xname));
815 		return -1;
816 	}
817 
818 	/* set up the PRDs */
819 	nprd = size / blksize;
820 	if (sc->bm1.sc_nprd != nprd + 1) {
821 		if (sc->bm1.sc_nprd > 0)
822 			auglx_free_prd(sc, &sc->bm1);
823 		sc->bm1.sc_nprd = nprd + 1;
824 		auglx_alloc_prd(sc,
825 		    sc->bm1.sc_nprd * sizeof(struct auglx_prd), &sc->bm1);
826 	}
827 	DPRINTF(AUGLX_DBG_DMA, ("%s: nprd = %d\n", sc->sc_dev.dv_xname,
828 	    nprd));
829 	addr = p->segs->ds_addr;
830 	for (i = 0; i < nprd; i++) {
831 		sc->bm1.sc_vprd[i].base = addr;
832 		sc->bm1.sc_vprd[i].size = blksize | AUGLX_PRD_EOP;
833 		addr += blksize;
834 	}
835 	sc->bm1.sc_vprd[i].base = sc->bm1.sc_prd->dm_segs[0].ds_addr;
836 	sc->bm1.sc_vprd[i].size = AUGLX_PRD_JMP;
837 
838 #ifdef AUGLX_DEBUG
839 	for (i = 0; i < sc->bm1.sc_nprd; i++)
840 		DPRINTF(AUGLX_DBG_DMA, ("%s: PRD[%d].base = %p, size %p\n",
841 		    sc->sc_dev.dv_xname, i, sc->bm1.sc_vprd[i].base,
842 		    sc->bm1.sc_vprd[i].size));
843 #endif
844 	sc->bm1.intr = intr;
845 	sc->bm1.arg = arg;
846 
847 	mtx_enter(&audio_lock);
848 	/* Program the BM1 PRD register */
849 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_BM1_PRD,
850 	    sc->bm1.sc_prd->dm_segs[0].ds_addr);
851 	/* Start Audio Bus Master 0 */
852 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD,
853 	    BMx_CMD_RW | BMx_CMD_BM_CTL_EN);
854 	mtx_leave(&audio_lock);
855 	return 0;
856 }
857 
858 int
859 auglx_allocmem(struct auglx_softc *sc, size_t size, size_t align,
860     struct auglx_dma *p)
861 {
862 	int error;
863 
864 	p->size = size;
865 	error = bus_dmamem_alloc(sc->sc_dmat, p->size, align, 0, p->segs, 1,
866 	    &p->nsegs, BUS_DMA_NOWAIT);
867 	if (error) {
868 		DPRINTF(AUGLX_DBG_DMA,
869 		    ("%s: bus_dmamem_alloc failed: error %d\n",
870 		    sc->sc_dev.dv_xname, error));
871 		return error;
872 	}
873 
874 	error = bus_dmamem_map(sc->sc_dmat, p->segs, 1, p->size, &p->addr,
875 	    BUS_DMA_NOWAIT | sc->sc_dmamap_flags);
876 	if (error) {
877 		DPRINTF(AUGLX_DBG_DMA,
878 		    ("%s: bus_dmamem_map failed: error %d\n",
879 		    sc->sc_dev.dv_xname, error));
880 		goto free;
881 	}
882 
883 	error = bus_dmamap_create(sc->sc_dmat, p->size, 1, p->size, 0,
884 	    BUS_DMA_NOWAIT, &p->map);
885 	if (error) {
886 		DPRINTF(AUGLX_DBG_DMA,
887 		    ("%s: bus_dmamap_create failed: error %d\n",
888 		    sc->sc_dev.dv_xname, error));
889 		goto unmap;
890 	}
891 
892 	error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, p->size, NULL,
893 	    BUS_DMA_NOWAIT);
894 	if (error) {
895 		DPRINTF(AUGLX_DBG_DMA,
896 		    ("%s: bus_dmamap_load failed: error %d\n",
897 		    sc->sc_dev.dv_xname, error));
898 		goto destroy;
899 	}
900 	return 0;
901 
902  destroy:
903 	bus_dmamap_destroy(sc->sc_dmat, p->map);
904  unmap:
905 	bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size);
906  free:
907 	bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs);
908 	return error;
909 }
910 
911 void
912 auglx_freemem(struct auglx_softc *sc, struct auglx_dma *p)
913 {
914 	bus_dmamap_unload(sc->sc_dmat, p->map);
915 	bus_dmamap_destroy(sc->sc_dmat, p->map);
916 	bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size);
917 	bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs);
918 }
919 
920 void
921 auglx_get_default_params(void *addr, int mode, struct audio_params *params)
922 {
923 	ac97_get_default_params(params);
924 }
925 
926 int
927 auglx_alloc_prd(struct auglx_softc *sc, size_t size, struct auglx_ring *bm)
928 {
929 	int error, rseg;
930 
931 	/*
932 	 * Allocate PRD table structure, and create and load the
933 	 * DMA map for it.
934 	 */
935 	if ((error = bus_dmamem_alloc(sc->sc_dmat, size,
936 	    PAGE_SIZE, 0, &bm->seg, 1, &rseg, 0)) != 0) {
937 		printf("%s: unable to allocate PRD, error = %d\n",
938 		    sc->sc_dev.dv_xname, error);
939 		goto fail_0;
940 	}
941 
942 	if ((error = bus_dmamem_map(sc->sc_dmat, &bm->seg, rseg,
943 	    size, (caddr_t *)&bm->sc_vprd,
944 	    sc->sc_dmamap_flags)) != 0) {
945 		printf("%s: unable to map PRD, error = %d\n",
946 		    sc->sc_dev.dv_xname, error);
947 		goto fail_1;
948 	}
949 
950 	if ((error = bus_dmamap_create(sc->sc_dmat, size,
951 	    1, size, 0, 0, &bm->sc_prd)) != 0) {
952 		printf("%s: unable to create PRD DMA map, "
953 		    "error = %d\n", sc->sc_dev.dv_xname, error);
954 		goto fail_2;
955 	}
956 
957 	if ((error = bus_dmamap_load(sc->sc_dmat, bm->sc_prd, bm->sc_vprd,
958 	    size, NULL, 0)) != 0) {
959 		printf("%s: unable tp load control data DMA map, "
960 		    "error = %d\n", sc->sc_dev.dv_xname, error);
961 		goto fail_3;
962 	}
963 
964 	return 0;
965 
966  fail_3:
967 	bus_dmamap_destroy(sc->sc_dmat, bm->sc_prd);
968  fail_2:
969 	bus_dmamem_unmap(sc->sc_dmat, (caddr_t)bm->sc_vprd,
970 	    sizeof(struct auglx_prd));
971  fail_1:
972 	bus_dmamem_free(sc->sc_dmat, &bm->seg, rseg);
973  fail_0:
974 	return error;
975 }
976 
977 void
978 auglx_free_prd(struct auglx_softc *sc, struct auglx_ring *bm)
979 {
980 	bus_dmamap_unload(sc->sc_dmat, bm->sc_prd);
981 	bus_dmamap_destroy(sc->sc_dmat, bm->sc_prd);
982 	bus_dmamem_unmap(sc->sc_dmat, (caddr_t)bm->sc_vprd, bm->sc_size);
983 	bus_dmamem_free(sc->sc_dmat, &bm->seg, bm->nsegs);
984 }
985 
986 int
987 auglx_activate(struct device *self, int act)
988 {
989 	struct auglx_softc *sc = (struct auglx_softc *)self;
990 	int rv = 0;
991 
992 	switch (act) {
993 	case DVACT_RESUME:
994 		ac97_resume(&sc->host_if, sc->codec_if);
995 		rv = config_activate_children(self, act);
996 		break;
997 	default:
998 		rv = config_activate_children(self, act);
999 		break;
1000 	}
1001 	return (rv);
1002 }
1003