auacer.c revision 1.3
1/*	$NetBSD: auacer.c,v 1.3 2004/11/10 04:20:26 kent Exp $	*/
2
3/*-
4 * Copyright (c) 2004 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *	This product includes software developed by the NetBSD
21 *	Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 *    contributors may be used to endorse or promote products derived
24 *    from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39/*
40 * Acer Labs M5455 audio driver
41 *
42 * Acer provides data sheets after signing an NDA.
43 * The chip behaves somewhat like the Intel i8x0, so this driver
44 * is loosely based on the auich driver.  Additional information taken from
45 * the ALSA intel8x0.c driver (which handles M5455 as well).
46 *
47 * As an historical note one can observe that the auich driver borrows
48 * lot from the first NetBSD PCI audio driver, the eap driver.  But this
49 * is not attributed anywhere.
50 */
51
52
53#include <sys/cdefs.h>
54__KERNEL_RCSID(0, "$NetBSD: auacer.c,v 1.3 2004/11/10 04:20:26 kent Exp $");
55
56#include <sys/param.h>
57#include <sys/systm.h>
58#include <sys/kernel.h>
59#include <sys/malloc.h>
60#include <sys/device.h>
61#include <sys/fcntl.h>
62#include <sys/proc.h>
63
64#include <uvm/uvm_extern.h>	/* for PAGE_SIZE */
65
66#include <dev/pci/pcidevs.h>
67#include <dev/pci/pcivar.h>
68#include <dev/pci/auacerreg.h>
69
70#include <sys/audioio.h>
71#include <dev/audio_if.h>
72#include <dev/mulaw.h>
73#include <dev/auconv.h>
74
75#include <machine/bus.h>
76
77#include <dev/ic/ac97reg.h>
78#include <dev/ic/ac97var.h>
79
80struct auacer_dma {
81	bus_dmamap_t map;
82	caddr_t addr;
83	bus_dma_segment_t segs[1];
84	int nsegs;
85	size_t size;
86	struct auacer_dma *next;
87};
88
89#define	DMAADDR(p)	((p)->map->dm_segs[0].ds_addr)
90#define	KERNADDR(p)	((void *)((p)->addr))
91
92struct auacer_cdata {
93	struct auacer_dmalist ic_dmalist_pcmo[ALI_DMALIST_MAX];
94};
95
96struct auacer_chan {
97	uint32_t ptr;
98	uint32_t start, p, end;
99	uint32_t blksize, fifoe;
100	uint32_t ack;
101	uint32_t port;
102	struct auacer_dmalist *dmalist;
103	void (*intr)(void *);
104	void *arg;
105};
106
107struct auacer_softc {
108	struct device sc_dev;
109	void *sc_ih;
110
111	audio_device_t sc_audev;
112
113	bus_space_tag_t iot;
114	bus_space_handle_t mix_ioh;
115	bus_space_handle_t aud_ioh;
116	bus_dma_tag_t dmat;
117
118	struct ac97_codec_if *codec_if;
119	struct ac97_host_if host_if;
120
121	/* DMA scatter-gather lists. */
122	bus_dmamap_t sc_cddmamap;
123#define	sc_cddma	sc_cddmamap->dm_segs[0].ds_addr
124
125	struct auacer_cdata *sc_cdata;
126
127	struct auacer_chan sc_pcmo;
128
129	struct auacer_dma *sc_dmas;
130
131	pci_chipset_tag_t sc_pc;
132	pcitag_t sc_pt;
133
134	int  sc_dmamap_flags;
135
136	/* Power Management */
137	void *sc_powerhook;
138	int sc_suspend;
139};
140
141#define READ1(sc, a) bus_space_read_1(sc->iot, sc->aud_ioh, a)
142#define READ2(sc, a) bus_space_read_2(sc->iot, sc->aud_ioh, a)
143#define READ4(sc, a) bus_space_read_4(sc->iot, sc->aud_ioh, a)
144#define WRITE1(sc, a, v) bus_space_write_1(sc->iot, sc->aud_ioh, a, v)
145#define WRITE2(sc, a, v) bus_space_write_2(sc->iot, sc->aud_ioh, a, v)
146#define WRITE4(sc, a, v) bus_space_write_4(sc->iot, sc->aud_ioh, a, v)
147
148/* Debug */
149#ifdef AUACER_DEBUG
150#define	DPRINTF(l,x)	do { if (auacer_debug & (l)) printf x; } while(0)
151int auacer_debug = 0;
152#define	ALI_DEBUG_CODECIO	0x0001
153#define	ALI_DEBUG_DMA		0x0002
154#define	ALI_DEBUG_INTR		0x0004
155#define ALI_DEBUG_API		0x0008
156#define ALI_DEBUG_MIXERAPI	0x0010
157#else
158#define	DPRINTF(x,y)	/* nothing */
159#endif
160
161int	auacer_match(struct device *, struct cfdata *, void *);
162void	auacer_attach(struct device *, struct device *, void *);
163int	auacer_intr(void *);
164
165CFATTACH_DECL(auacer, sizeof(struct auacer_softc),
166    auacer_match, auacer_attach, NULL, NULL);
167
168int	auacer_open(void *, int);
169void	auacer_close(void *);
170int	auacer_query_encoding(void *, struct audio_encoding *);
171int	auacer_set_params(void *, int, int, struct audio_params *,
172	    struct audio_params *);
173int	auacer_round_blocksize(void *, int);
174int	auacer_halt_output(void *);
175int	auacer_halt_input(void *);
176int	auacer_getdev(void *, struct audio_device *);
177int	auacer_set_port(void *, mixer_ctrl_t *);
178int	auacer_get_port(void *, mixer_ctrl_t *);
179int	auacer_query_devinfo(void *, mixer_devinfo_t *);
180void	*auacer_allocm(void *, int, size_t, struct malloc_type *, int);
181void	auacer_freem(void *, void *, struct malloc_type *);
182size_t	auacer_round_buffersize(void *, int, size_t);
183paddr_t	auacer_mappage(void *, void *, off_t, int);
184int	auacer_get_props(void *);
185int	auacer_trigger_output(void *, void *, void *, int, void (*)(void *),
186	    void *, struct audio_params *);
187int	auacer_trigger_input(void *, void *, void *, int, void (*)(void *),
188	    void *, struct audio_params *);
189
190int	auacer_alloc_cdata(struct auacer_softc *);
191
192int	auacer_allocmem(struct auacer_softc *, size_t, size_t,
193	    struct auacer_dma *);
194int	auacer_freemem(struct auacer_softc *, struct auacer_dma *);
195
196void	auacer_powerhook(int, void *);
197int	auacer_set_rate(struct auacer_softc *, int, u_long);
198void	auacer_finish_attach(struct device *);
199
200static void auacer_reset(struct auacer_softc *sc);
201
202struct audio_hw_if auacer_hw_if = {
203	auacer_open,
204	auacer_close,
205	NULL,			/* drain */
206	auacer_query_encoding,
207	auacer_set_params,
208	auacer_round_blocksize,
209	NULL,			/* commit_setting */
210	NULL,			/* init_output */
211	NULL,			/* init_input */
212	NULL,			/* start_output */
213	NULL,			/* start_input */
214	auacer_halt_output,
215	auacer_halt_input,
216	NULL,			/* speaker_ctl */
217	auacer_getdev,
218	NULL,			/* getfd */
219	auacer_set_port,
220	auacer_get_port,
221	auacer_query_devinfo,
222	auacer_allocm,
223	auacer_freem,
224	auacer_round_buffersize,
225	auacer_mappage,
226	auacer_get_props,
227	auacer_trigger_output,
228	auacer_trigger_input,
229	NULL,			/* dev_ioctl */
230};
231
232int	auacer_attach_codec(void *, struct ac97_codec_if *);
233int	auacer_read_codec(void *, u_int8_t, u_int16_t *);
234int	auacer_write_codec(void *, u_int8_t, u_int16_t);
235int	auacer_reset_codec(void *);
236
237int
238auacer_match(struct device *parent, struct cfdata *match, void *aux)
239{
240	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
241
242	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ALI &&
243	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ALI_M5455)
244		return 1;
245	return 0;
246}
247
248void
249auacer_attach(struct device *parent, struct device *self, void *aux)
250{
251	struct auacer_softc *sc = (struct auacer_softc *)self;
252	struct pci_attach_args *pa = aux;
253	pci_intr_handle_t ih;
254	bus_size_t aud_size;
255	pcireg_t v;
256	const char *intrstr;
257
258	aprint_normal(": Acer Labs M5455 Audio controller\n");
259
260	if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_IO, 0, &sc->iot,
261		&sc->aud_ioh, NULL, &aud_size)) {
262		aprint_error(": can't map i/o space\n");
263		return;
264	}
265
266	sc->sc_pc = pa->pa_pc;
267	sc->sc_pt = pa->pa_tag;
268	sc->dmat = pa->pa_dmat;
269
270	sc->sc_dmamap_flags = BUS_DMA_COHERENT;	/* XXX remove */
271
272	/* enable bus mastering */
273	v = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
274	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
275	    v | PCI_COMMAND_MASTER_ENABLE);
276
277	/* Map and establish the interrupt. */
278	if (pci_intr_map(pa, &ih)) {
279		aprint_error("%s: can't map interrupt\n", sc->sc_dev.dv_xname);
280		return;
281	}
282	intrstr = pci_intr_string(pa->pa_pc, ih);
283	sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO,
284	    auacer_intr, sc);
285	if (sc->sc_ih == NULL) {
286		aprint_error("%s: can't establish interrupt",
287		    sc->sc_dev.dv_xname);
288		if (intrstr != NULL)
289			aprint_normal(" at %s", intrstr);
290		aprint_normal("\n");
291		return;
292	}
293	aprint_normal("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr);
294
295	strlcpy(sc->sc_audev.name, "M5455 AC97", MAX_AUDIO_DEV_LEN);
296	snprintf(sc->sc_audev.version, MAX_AUDIO_DEV_LEN,
297		 "0x%02x", PCI_REVISION(pa->pa_class));
298	strlcpy(sc->sc_audev.config, sc->sc_dev.dv_xname, MAX_AUDIO_DEV_LEN);
299
300	/* Set up DMA lists. */
301	auacer_alloc_cdata(sc);
302	sc->sc_pcmo.dmalist = sc->sc_cdata->ic_dmalist_pcmo;
303	sc->sc_pcmo.ptr = 0;
304	sc->sc_pcmo.port = ALI_BASE_PO;
305
306	DPRINTF(ALI_DEBUG_DMA, ("auacer_attach: lists %p\n",
307	    sc->sc_pcmo.dmalist));
308
309	sc->host_if.arg = sc;
310	sc->host_if.attach = auacer_attach_codec;
311	sc->host_if.read = auacer_read_codec;
312	sc->host_if.write = auacer_write_codec;
313	sc->host_if.reset = auacer_reset_codec;
314
315	if (ac97_attach(&sc->host_if) != 0)
316		return;
317
318	/* Watch for power change */
319	sc->sc_suspend = PWR_RESUME;
320	sc->sc_powerhook = powerhook_establish(auacer_powerhook, sc);
321
322	audio_attach_mi(&auacer_hw_if, sc, &sc->sc_dev);
323
324	auacer_reset(sc);
325}
326
327static int
328auacer_ready_codec(struct auacer_softc *sc, int mask)
329{
330	int count = 0;
331
332	for (count = 0; count < 0x7f; count++) {
333		int val = READ1(sc, ALI_CSPSR);
334		if (val & mask)
335			return 0;
336	}
337
338	aprint_normal("auacer_ready_codec: AC97 codec ready timeout.\n");
339	return EBUSY;
340}
341
342static int
343auacer_sema_codec(struct auacer_softc *sc)
344{
345	int time = 100;
346
347	while (time-- && (READ4(sc, ALI_CAS) & ALI_CAS_SEM_BUSY))
348		delay(1);
349	if (!time)
350		aprint_normal("auacer_sema_codec: timeout\n");
351	return auacer_ready_codec(sc, ALI_CSPSR_CODEC_READY);
352}
353
354int
355auacer_read_codec(void *v, u_int8_t reg, u_int16_t *val)
356{
357	struct auacer_softc *sc = v;
358
359	if (auacer_sema_codec(sc))
360		return EIO;
361
362	reg |= ALI_CPR_ADDR_READ;
363#if 0
364	if (ac97->num)
365		reg |= ALI_CPR_ADDR_SECONDARY;
366#endif
367	WRITE2(sc, ALI_CPR_ADDR, reg);
368	if (auacer_ready_codec(sc, ALI_CSPSR_READ_OK))
369		return EIO;
370	*val = READ2(sc, ALI_SPR);
371
372	DPRINTF(ALI_DEBUG_CODECIO, ("auacer_read_codec: reg=0x%x val=0x%x\n",
373				    reg, *val));
374
375	return 0;
376}
377
378int
379auacer_write_codec(void *v, u_int8_t reg, u_int16_t val)
380{
381	struct auacer_softc *sc = v;
382
383	DPRINTF(ALI_DEBUG_CODECIO, ("auacer_write_codec: reg=0x%x val=0x%x\n",
384				    reg, val));
385
386	if (auacer_sema_codec(sc))
387		return EIO;
388	WRITE2(sc, ALI_CPR, val);
389#if 0
390	if (ac97->num)
391		reg |= ALI_CPR_ADDR_SECONDARY;
392#endif
393	WRITE2(sc, ALI_CPR_ADDR, reg);
394	auacer_ready_codec(sc, ALI_CSPSR_WRITE_OK);
395	return 0;
396}
397
398int
399auacer_attach_codec(void *v, struct ac97_codec_if *cif)
400{
401	struct auacer_softc *sc = v;
402
403	sc->codec_if = cif;
404	return 0;
405}
406
407int
408auacer_reset_codec(void *v)
409{
410	struct auacer_softc *sc = v;
411	u_int32_t reg;
412	int i = 0;
413
414	reg = READ4(sc, ALI_SCR);
415	if ((reg & 2) == 0)	/* Cold required */
416		reg |= 2;
417	else
418		reg |= 1;	/* Warm */
419	reg &= ~0x80000000;	/* ACLink on */
420	WRITE4(sc, ALI_SCR, reg);
421
422	while (i < 10) {
423		if ((READ4(sc, ALI_INTERRUPTSR) & ALI_INT_GPIO) == 0)
424			break;
425		delay(50000);	/* XXX */
426		i++;
427	}
428	if (i == 10) {
429		return EIO;
430	}
431
432	for (i = 0; i < 10; i++) {
433		reg = READ4(sc, ALI_RTSR);
434		if (reg & 0x80) /* primary codec */
435			break;
436		WRITE4(sc, ALI_RTSR, reg | 0x80);
437		delay(50000);	/* XXX */
438	}
439
440	return 0;
441}
442
443static void
444auacer_reset(struct auacer_softc *sc)
445{
446	WRITE4(sc, ALI_SCR, ALI_SCR_RESET);
447	WRITE4(sc, ALI_FIFOCR1, 0x83838383);
448	WRITE4(sc, ALI_FIFOCR2, 0x83838383);
449	WRITE4(sc, ALI_FIFOCR3, 0x83838383);
450	WRITE4(sc, ALI_INTERFACECR, ALI_IF_PO); /* XXX pcm out only */
451	WRITE4(sc, ALI_INTERRUPTCR, 0x00000000);
452	WRITE4(sc, ALI_INTERRUPTSR, 0x00000000);
453}
454
455int
456auacer_open(void *v, int flags)
457{
458	DPRINTF(ALI_DEBUG_API, ("auacer_open: flags=%d\n", flags));
459	return 0;
460}
461
462void
463auacer_close(void *v)
464{
465	DPRINTF(ALI_DEBUG_API, ("auacer_close\n"));
466}
467
468int
469auacer_query_encoding(void *v, struct audio_encoding *aep)
470{
471	DPRINTF(ALI_DEBUG_API, ("auacer_query_encoding\n"));
472
473	switch (aep->index) {
474	case 0:
475		strcpy(aep->name, AudioEulinear);
476		aep->encoding = AUDIO_ENCODING_ULINEAR;
477		aep->precision = 8;
478		aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
479		return (0);
480	case 1:
481		strcpy(aep->name, AudioEmulaw);
482		aep->encoding = AUDIO_ENCODING_ULAW;
483		aep->precision = 8;
484		aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
485		return (0);
486	case 2:
487		strcpy(aep->name, AudioEalaw);
488		aep->encoding = AUDIO_ENCODING_ALAW;
489		aep->precision = 8;
490		aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
491		return (0);
492	case 3:
493		strcpy(aep->name, AudioEslinear);
494		aep->encoding = AUDIO_ENCODING_SLINEAR;
495		aep->precision = 8;
496		aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
497		return (0);
498	case 4:
499		strcpy(aep->name, AudioEslinear_le);
500		aep->encoding = AUDIO_ENCODING_SLINEAR_LE;
501		aep->precision = 16;
502		aep->flags = 0;
503		return (0);
504	case 5:
505		strcpy(aep->name, AudioEulinear_le);
506		aep->encoding = AUDIO_ENCODING_ULINEAR_LE;
507		aep->precision = 16;
508		aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
509		return (0);
510	case 6:
511		strcpy(aep->name, AudioEslinear_be);
512		aep->encoding = AUDIO_ENCODING_SLINEAR_BE;
513		aep->precision = 16;
514		aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
515		return (0);
516	case 7:
517		strcpy(aep->name, AudioEulinear_be);
518		aep->encoding = AUDIO_ENCODING_ULINEAR_BE;
519		aep->precision = 16;
520		aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
521		return (0);
522	default:
523		return (EINVAL);
524	}
525}
526
527int
528auacer_set_rate(struct auacer_softc *sc, int mode, u_long srate)
529{
530	int ret;
531	u_long ratetmp;
532
533	DPRINTF(ALI_DEBUG_API, ("auacer_set_rate: srate=%lu\n", srate));
534
535	ratetmp = srate;
536	if (mode == AUMODE_RECORD)
537		return sc->codec_if->vtbl->set_rate(sc->codec_if,
538		    AC97_REG_PCM_LR_ADC_RATE, &ratetmp);
539	ret = sc->codec_if->vtbl->set_rate(sc->codec_if,
540	    AC97_REG_PCM_FRONT_DAC_RATE, &ratetmp);
541	if (ret)
542		return ret;
543	ratetmp = srate;
544	ret = sc->codec_if->vtbl->set_rate(sc->codec_if,
545	    AC97_REG_PCM_SURR_DAC_RATE, &ratetmp);
546	if (ret)
547		return ret;
548	ratetmp = srate;
549	ret = sc->codec_if->vtbl->set_rate(sc->codec_if,
550	    AC97_REG_PCM_LFE_DAC_RATE, &ratetmp);
551	return ret;
552}
553
554int
555auacer_set_params(void *v, int setmode, int usemode, struct audio_params *play,
556    struct audio_params *rec)
557{
558	struct auacer_softc *sc = v;
559	struct audio_params *p;
560	uint32_t control;
561	int mode;
562
563	DPRINTF(ALI_DEBUG_API, ("auacer_set_params\n"));
564
565	for (mode = AUMODE_RECORD; mode != -1;
566	     mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
567		if ((setmode & mode) == 0)
568			continue;
569
570		p = mode == AUMODE_PLAY ? play : rec;
571		if (p == NULL)
572			continue;
573
574		if ((p->sample_rate !=  8000) &&
575		    (p->sample_rate != 11025) &&
576		    (p->sample_rate != 12000) &&
577		    (p->sample_rate != 16000) &&
578		    (p->sample_rate != 22050) &&
579		    (p->sample_rate != 24000) &&
580		    (p->sample_rate != 32000) &&
581		    (p->sample_rate != 44100) &&
582		    (p->sample_rate != 48000))
583			return (EINVAL);
584
585		p->factor = 1;
586		if (p->precision == 8)
587			p->factor *= 2;
588
589		p->sw_code = NULL;
590		/* setup hardware formats */
591		p->hw_encoding = AUDIO_ENCODING_SLINEAR_LE;
592		p->hw_precision = 16;
593
594		if (mode == AUMODE_RECORD) {
595			if (p->channels < 1 || p->channels > 2)
596				return EINVAL;
597		} else {
598			switch (p->channels) {
599			case 1:
600				break;
601			case 2:
602				break;
603			case 4:
604				if (!AC97_IS_4CH(sc->codec_if))
605					return EINVAL;
606				break;
607			case 6:
608				if (!AC97_IS_6CH(sc->codec_if))
609					return EINVAL;
610				break;
611			default:
612				return EINVAL;
613			}
614		}
615		/* If monaural is requested, aurateconv expands a monaural
616		 * stream to stereo. */
617		if (p->channels == 1)
618			p->hw_channels = 2;
619
620		switch (p->encoding) {
621		case AUDIO_ENCODING_SLINEAR_BE:
622			if (p->precision == 16) {
623				p->sw_code = swap_bytes;
624			} else {
625				if (mode == AUMODE_PLAY)
626					p->sw_code = linear8_to_linear16_le;
627				else
628					p->sw_code = linear16_to_linear8_le;
629			}
630			break;
631
632		case AUDIO_ENCODING_SLINEAR_LE:
633			if (p->precision != 16) {
634				if (mode == AUMODE_PLAY)
635					p->sw_code = linear8_to_linear16_le;
636				else
637					p->sw_code = linear16_to_linear8_le;
638			}
639			break;
640
641		case AUDIO_ENCODING_ULINEAR_BE:
642			if (p->precision == 16) {
643				if (mode == AUMODE_PLAY)
644					p->sw_code =
645					    swap_bytes_change_sign16_le;
646				else
647					p->sw_code =
648					    change_sign16_swap_bytes_le;
649			} else {
650				if (mode == AUMODE_PLAY)
651					p->sw_code =
652					    ulinear8_to_slinear16_le;
653				else
654					p->sw_code =
655					    slinear16_to_ulinear8_le;
656			}
657			break;
658
659		case AUDIO_ENCODING_ULINEAR_LE:
660			if (p->precision == 16) {
661				p->sw_code = change_sign16_le;
662			} else {
663				if (mode == AUMODE_PLAY)
664					p->sw_code =
665					    ulinear8_to_slinear16_le;
666				else
667					p->sw_code =
668					    slinear16_to_ulinear8_le;
669			}
670			break;
671
672		case AUDIO_ENCODING_ULAW:
673			if (mode == AUMODE_PLAY) {
674				p->sw_code = mulaw_to_slinear16_le;
675			} else {
676				p->sw_code = slinear16_to_mulaw_le;
677			}
678			break;
679
680		case AUDIO_ENCODING_ALAW:
681			if (mode == AUMODE_PLAY) {
682				p->sw_code = alaw_to_slinear16_le;
683			} else {
684				p->sw_code = slinear16_to_alaw_le;
685			}
686			break;
687
688		default:
689			return (EINVAL);
690		}
691
692		if (AC97_IS_FIXED_RATE(sc->codec_if)) {
693			p->hw_sample_rate = AC97_SINGLE_RATE;
694			/* If hw_sample_rate is changed, aurateconv works. */
695		} else {
696			if (auacer_set_rate(sc, mode, p->sample_rate))
697				return EINVAL;
698		}
699
700		if (mode == AUMODE_PLAY) {
701			control = READ4(sc, ALI_SCR);
702			control &= ~ALI_SCR_PCM_246_MASK;
703			if (p->channels == 4)
704				control |= ALI_SCR_PCM_4;
705			else if (p->channels == 6)
706				control |= ALI_SCR_PCM_6;
707			WRITE4(sc, ALI_SCR, control);
708		}
709	}
710
711	return (0);
712}
713
714int
715auacer_round_blocksize(void *v, int blk)
716{
717
718	return (blk & ~0x3f);		/* keep good alignment */
719}
720
721static void
722auacer_halt(struct auacer_softc *sc, struct auacer_chan *chan)
723{
724	uint32_t val;
725	uint8_t port = chan->port;
726	uint32_t slot;
727
728	DPRINTF(ALI_DEBUG_API, ("auacer_halt: port=0x%x\n", port));
729
730	chan->intr = 0;
731
732	slot = ALI_PORT2SLOT(port);
733
734	val = READ4(sc, ALI_DMACR);
735	val |= 1 << (slot+16); /* pause */
736	val &= ~(1 << slot); /* no start */
737	WRITE4(sc, ALI_DMACR, val);
738	WRITE1(sc, port + ALI_OFF_CR, 0);
739	while (READ1(sc, port + ALI_OFF_CR))
740		;
741	/* reset whole DMA things */
742	WRITE1(sc, port + ALI_OFF_CR, ALI_CR_RR);
743	/* clear interrupts */
744	WRITE1(sc, port + ALI_OFF_SR, READ1(sc, port+ALI_OFF_SR) | ALI_SR_W1TC);
745	WRITE4(sc, ALI_INTERRUPTSR, ALI_PORT2INTR(port));
746}
747
748int
749auacer_halt_output(void *v)
750{
751	struct auacer_softc *sc = v;
752
753	DPRINTF(ALI_DEBUG_DMA, ("auacer_halt_output\n"));
754
755	auacer_halt(sc, &sc->sc_pcmo);
756
757	return (0);
758}
759
760int
761auacer_halt_input(void *v)
762{
763	/*struct auacer_softc *sc = v;*/
764
765	DPRINTF(ALI_DEBUG_DMA, ("auacer_halt_input\n"));
766
767	return (0);
768}
769
770int
771auacer_getdev(void *v, struct audio_device *adp)
772{
773	struct auacer_softc *sc = v;
774
775	DPRINTF(ALI_DEBUG_API, ("auacer_getdev\n"));
776
777	*adp = sc->sc_audev;
778	return (0);
779}
780
781int
782auacer_set_port(void *v, mixer_ctrl_t *cp)
783{
784	struct auacer_softc *sc = v;
785
786	DPRINTF(ALI_DEBUG_MIXERAPI, ("auacer_set_port\n"));
787
788	return (sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp));
789}
790
791int
792auacer_get_port(void *v, mixer_ctrl_t *cp)
793{
794	struct auacer_softc *sc = v;
795
796	DPRINTF(ALI_DEBUG_MIXERAPI, ("auacer_get_port\n"));
797
798	return (sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp));
799}
800
801int
802auacer_query_devinfo(void *v, mixer_devinfo_t *dp)
803{
804	struct auacer_softc *sc = v;
805
806	DPRINTF(ALI_DEBUG_MIXERAPI, ("auacer_query_devinfo\n"));
807
808	return (sc->codec_if->vtbl->query_devinfo(sc->codec_if, dp));
809}
810
811void *
812auacer_allocm(void *v, int direction, size_t size, struct malloc_type *pool,
813    int flags)
814{
815	struct auacer_softc *sc = v;
816	struct auacer_dma *p;
817	int error;
818
819	if (size > (ALI_DMALIST_MAX * ALI_DMASEG_MAX))
820		return (NULL);
821
822	p = malloc(sizeof(*p), pool, flags | M_ZERO);
823	if (p == NULL)
824		return (NULL);
825
826	error = auacer_allocmem(sc, size, 0, p);
827	if (error) {
828		free(p, pool);
829		return (NULL);
830	}
831
832	p->next = sc->sc_dmas;
833	sc->sc_dmas = p;
834
835	return (KERNADDR(p));
836}
837
838void
839auacer_freem(void *v, void *ptr, struct malloc_type *pool)
840{
841	struct auacer_softc *sc = v;
842	struct auacer_dma *p, **pp;
843
844	for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->next) {
845		if (KERNADDR(p) == ptr) {
846			auacer_freemem(sc, p);
847			*pp = p->next;
848			free(p, pool);
849			return;
850		}
851	}
852}
853
854size_t
855auacer_round_buffersize(void *v, int direction, size_t size)
856{
857
858	if (size > (ALI_DMALIST_MAX * ALI_DMASEG_MAX))
859		size = ALI_DMALIST_MAX * ALI_DMASEG_MAX;
860
861	return size;
862}
863
864paddr_t
865auacer_mappage(void *v, void *mem, off_t off, int prot)
866{
867	struct auacer_softc *sc = v;
868	struct auacer_dma *p;
869
870	if (off < 0)
871		return (-1);
872
873	for (p = sc->sc_dmas; p && KERNADDR(p) != mem; p = p->next)
874		;
875	if (!p)
876		return (-1);
877	return (bus_dmamem_mmap(sc->dmat, p->segs, p->nsegs,
878	    off, prot, BUS_DMA_WAITOK));
879}
880
881int
882auacer_get_props(void *v)
883{
884	struct auacer_softc *sc = v;
885	int props;
886
887	props = AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
888	/*
889	 * Even if the codec is fixed-rate, set_param() succeeds for any sample
890	 * rate because of aurateconv.  Applications can't know what rate the
891	 * device can process in the case of mmap().
892	 */
893	if (!AC97_IS_FIXED_RATE(sc->codec_if))
894		props |= AUDIO_PROP_MMAP;
895	return props;
896}
897
898static void
899auacer_add_entry(struct auacer_chan *chan)
900{
901	struct auacer_dmalist *q;
902
903	q = &chan->dmalist[chan->ptr];
904
905	DPRINTF(ALI_DEBUG_INTR,
906		("auacer_add_entry: %p = %x @ 0x%x\n",
907		 q, chan->blksize / 2, chan->p));
908
909	q->base = htole32(chan->p);
910	q->len = htole32((chan->blksize / ALI_SAMPLE_SIZE) | ALI_DMAF_IOC);
911	chan->p += chan->blksize;
912	if (chan->p >= chan->end)
913		chan->p = chan->start;
914
915	if (++chan->ptr >= ALI_DMALIST_MAX)
916		chan->ptr = 0;
917}
918
919static void
920auacer_upd_chan(struct auacer_softc *sc, struct auacer_chan *chan)
921{
922	uint32_t sts;
923	uint32_t civ;
924
925	sts = READ2(sc, chan->port + ALI_OFF_SR);
926	/* intr ack */
927	WRITE2(sc, chan->port + ALI_OFF_SR, sts & ALI_SR_W1TC);
928	WRITE4(sc, ALI_INTERRUPTSR, ALI_PORT2INTR(chan->port));
929
930	DPRINTF(ALI_DEBUG_INTR, ("auacer_upd_chan: sts=0x%x\n", sts));
931
932	if (sts & ALI_SR_DMA_INT_FIFO) {
933		printf("%s: fifo underrun # %u\n",
934		       sc->sc_dev.dv_xname, ++chan->fifoe);
935	}
936
937	civ = READ1(sc, chan->port + ALI_OFF_CIV);
938
939	DPRINTF(ALI_DEBUG_INTR,("auacer_intr: civ=%u ptr=%u\n",civ,chan->ptr));
940
941	/* XXX */
942	while (chan->ptr != civ) {
943		auacer_add_entry(chan);
944	}
945
946	WRITE1(sc, chan->port + ALI_OFF_LVI, (chan->ptr - 1) & ALI_LVI_MASK);
947
948	while (chan->ack != civ) {
949		if (chan->intr) {
950			DPRINTF(ALI_DEBUG_INTR,("auacer_upd_chan: callback\n"));
951			chan->intr(chan->arg);
952		}
953		chan->ack++;
954		if (chan->ack >= ALI_DMALIST_MAX)
955			chan->ack = 0;
956	}
957}
958
959int
960auacer_intr(void *v)
961{
962	struct auacer_softc *sc = v;
963	int ret, intrs;
964
965	intrs = READ4(sc, ALI_INTERRUPTSR);
966	DPRINTF(ALI_DEBUG_INTR, ("auacer_intr: intrs=0x%x\n", intrs));
967
968	ret = 0;
969	if (intrs & ALI_INT_PCMOUT) {
970		auacer_upd_chan(sc, &sc->sc_pcmo);
971		ret++;
972	}
973
974	return ret != 0;
975}
976
977static void
978auacer_setup_chan(struct auacer_softc *sc, struct auacer_chan *chan,
979		  uint32_t start, uint32_t size, uint32_t blksize,
980		  void (*intr)(void *), void *arg)
981{
982	uint32_t port, slot;
983	uint32_t offs, val;
984
985	chan->start = start;
986	chan->ptr = 0;
987	chan->p = chan->start;
988	chan->end = chan->start + size;
989	chan->blksize = blksize;
990	chan->ack = 0;
991	chan->intr = intr;
992	chan->arg = arg;
993
994	auacer_add_entry(chan);
995	auacer_add_entry(chan);
996
997	port = chan->port;
998	slot = ALI_PORT2SLOT(port);
999
1000	WRITE1(sc, port + ALI_OFF_CIV, 0);
1001	WRITE1(sc, port + ALI_OFF_LVI, (chan->ptr - 1) & ALI_LVI_MASK);
1002	offs = (char *)chan->dmalist - (char *)sc->sc_cdata;
1003	WRITE4(sc, port + ALI_OFF_BDBAR, sc->sc_cddma + offs);
1004	WRITE1(sc, port + ALI_OFF_CR,
1005	       ALI_CR_IOCE | ALI_CR_FEIE | ALI_CR_LVBIE | ALI_CR_RPBM);
1006	val = READ4(sc, ALI_DMACR);
1007	val &= ~(1 << (slot+16)); /* no pause */
1008	val |= 1 << slot;	/* start */
1009	WRITE4(sc, ALI_DMACR, val);
1010}
1011
1012int
1013auacer_trigger_output(void *v, void *start, void *end, int blksize,
1014    void (*intr)(void *), void *arg, struct audio_params *param)
1015{
1016	struct auacer_softc *sc = v;
1017	struct auacer_dma *p;
1018	uint32_t size;
1019
1020	DPRINTF(ALI_DEBUG_DMA,
1021		("auacer_trigger_output(%p, %p, %d, %p, %p, %p)\n",
1022		 start, end, blksize, intr, arg, param));
1023
1024	for (p = sc->sc_dmas; p && KERNADDR(p) != start; p = p->next)
1025		;
1026	if (!p) {
1027		printf("auacer_trigger_output: bad addr %p\n", start);
1028		return (EINVAL);
1029	}
1030
1031	size = (char *)end - (char *)start;
1032	auacer_setup_chan(sc, &sc->sc_pcmo, DMAADDR(p), size, blksize,
1033			  intr, arg);
1034
1035	return 0;
1036}
1037
1038int
1039auacer_trigger_input(void *v, void *start, void *end, int blksize,
1040		     void (*intr)(void *), void *arg,
1041		     struct audio_params *param)
1042{
1043	return (EINVAL);
1044}
1045
1046int
1047auacer_allocmem(struct auacer_softc *sc, size_t size, size_t align,
1048    struct auacer_dma *p)
1049{
1050	int error;
1051
1052	p->size = size;
1053	error = bus_dmamem_alloc(sc->dmat, p->size, align, 0,
1054				 p->segs, sizeof(p->segs)/sizeof(p->segs[0]),
1055				 &p->nsegs, BUS_DMA_NOWAIT);
1056	if (error)
1057		return (error);
1058
1059	error = bus_dmamem_map(sc->dmat, p->segs, p->nsegs, p->size,
1060			       &p->addr, BUS_DMA_NOWAIT|sc->sc_dmamap_flags);
1061	if (error)
1062		goto free;
1063
1064	error = bus_dmamap_create(sc->dmat, p->size, 1, p->size,
1065				  0, BUS_DMA_NOWAIT, &p->map);
1066	if (error)
1067		goto unmap;
1068
1069	error = bus_dmamap_load(sc->dmat, p->map, p->addr, p->size, NULL,
1070				BUS_DMA_NOWAIT);
1071	if (error)
1072		goto destroy;
1073	return (0);
1074
1075 destroy:
1076	bus_dmamap_destroy(sc->dmat, p->map);
1077 unmap:
1078	bus_dmamem_unmap(sc->dmat, p->addr, p->size);
1079 free:
1080	bus_dmamem_free(sc->dmat, p->segs, p->nsegs);
1081	return (error);
1082}
1083
1084int
1085auacer_freemem(struct auacer_softc *sc, struct auacer_dma *p)
1086{
1087
1088	bus_dmamap_unload(sc->dmat, p->map);
1089	bus_dmamap_destroy(sc->dmat, p->map);
1090	bus_dmamem_unmap(sc->dmat, p->addr, p->size);
1091	bus_dmamem_free(sc->dmat, p->segs, p->nsegs);
1092	return (0);
1093}
1094
1095int
1096auacer_alloc_cdata(struct auacer_softc *sc)
1097{
1098	bus_dma_segment_t seg;
1099	int error, rseg;
1100
1101	/*
1102	 * Allocate the control data structure, and create and load the
1103	 * DMA map for it.
1104	 */
1105	if ((error = bus_dmamem_alloc(sc->dmat,
1106				      sizeof(struct auacer_cdata),
1107				      PAGE_SIZE, 0, &seg, 1, &rseg, 0)) != 0) {
1108		printf("%s: unable to allocate control data, error = %d\n",
1109		    sc->sc_dev.dv_xname, error);
1110		goto fail_0;
1111	}
1112
1113	if ((error = bus_dmamem_map(sc->dmat, &seg, rseg,
1114				    sizeof(struct auacer_cdata),
1115				    (caddr_t *) &sc->sc_cdata,
1116				    sc->sc_dmamap_flags)) != 0) {
1117		printf("%s: unable to map control data, error = %d\n",
1118		    sc->sc_dev.dv_xname, error);
1119		goto fail_1;
1120	}
1121
1122	if ((error = bus_dmamap_create(sc->dmat, sizeof(struct auacer_cdata), 1,
1123				       sizeof(struct auacer_cdata), 0, 0,
1124				       &sc->sc_cddmamap)) != 0) {
1125		printf("%s: unable to create control data DMA map, "
1126		    "error = %d\n", sc->sc_dev.dv_xname, error);
1127		goto fail_2;
1128	}
1129
1130	if ((error = bus_dmamap_load(sc->dmat, sc->sc_cddmamap,
1131				     sc->sc_cdata, sizeof(struct auacer_cdata),
1132				     NULL, 0)) != 0) {
1133		printf("%s: unable tp load control data DMA map, "
1134		    "error = %d\n", sc->sc_dev.dv_xname, error);
1135		goto fail_3;
1136	}
1137
1138	return (0);
1139
1140 fail_3:
1141	bus_dmamap_destroy(sc->dmat, sc->sc_cddmamap);
1142 fail_2:
1143	bus_dmamem_unmap(sc->dmat, (caddr_t) sc->sc_cdata,
1144	    sizeof(struct auacer_cdata));
1145 fail_1:
1146	bus_dmamem_free(sc->dmat, &seg, rseg);
1147 fail_0:
1148	return (error);
1149}
1150
1151void
1152auacer_powerhook(int why, void *addr)
1153{
1154	struct auacer_softc *sc = (struct auacer_softc *)addr;
1155
1156	switch (why) {
1157	case PWR_SUSPEND:
1158	case PWR_STANDBY:
1159		/* Power down */
1160		DPRINTF(1, ("%s: power down\n", sc->sc_dev.dv_xname));
1161		sc->sc_suspend = why;
1162		break;
1163
1164	case PWR_RESUME:
1165		/* Wake up */
1166		DPRINTF(1, ("%s: power resume\n", sc->sc_dev.dv_xname));
1167		if (sc->sc_suspend == PWR_RESUME) {
1168			printf("%s: resume without suspend.\n",
1169			    sc->sc_dev.dv_xname);
1170			sc->sc_suspend = why;
1171			return;
1172		}
1173		sc->sc_suspend = why;
1174		auacer_reset_codec(sc);
1175		delay(1000);
1176		sc->codec_if->vtbl->restore_ports(sc->codec_if);
1177		break;
1178
1179	case PWR_SOFTSUSPEND:
1180	case PWR_SOFTSTANDBY:
1181	case PWR_SOFTRESUME:
1182		break;
1183	}
1184}
1185