cs4231.c revision 193667
1139749Simp/*-
2136944Syongari * Copyright (c) 1999 Jason L. Wright (jason@thought.net)
3136944Syongari * Copyright (c) 2004 Pyun YongHyeon
4136944Syongari * All rights reserved.
5136944Syongari *
6136944Syongari * Redistribution and use in source and binary forms, with or without
7136944Syongari * modification, are permitted provided that the following conditions
8136944Syongari * are met:
9136944Syongari * 1. Redistributions of source code must retain the above copyright
10136944Syongari *    notice, this list of conditions and the following disclaimer.
11136944Syongari * 2. Redistributions in binary form must reproduce the above copyright
12136944Syongari *    notice, this list of conditions and the following disclaimer in the
13136944Syongari *    documentation and/or other materials provided with the distribution.
14136944Syongari *
15136944Syongari * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16136944Syongari * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17136944Syongari * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18136944Syongari * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19136944Syongari * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20136944Syongari * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21136944Syongari * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22136944Syongari * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23136944Syongari * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24136944Syongari * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25136944Syongari * POSSIBILITY OF SUCH DAMAGE.
26136944Syongari *
27136944Syongari * Effort sponsored in part by the Defense Advanced Research Projects
28136944Syongari * Agency (DARPA) and Air Force Research Laboratory, Air Force
29136944Syongari * Materiel Command, USAF, under agreement number F30602-01-2-0537.
30136944Syongari *
31136944Syongari *	from: OpenBSD: cs4231.c,v 1.21 2003/07/03 20:36:07 jason Exp
32136944Syongari */
33136944Syongari
34136944Syongari/*
35136944Syongari * Driver for CS4231 based audio found in some sun4m systems (cs4231)
36136944Syongari * based on ideas from the S/Linux project and the NetBSD project.
37136944Syongari */
38136944Syongari
39136944Syongari#include <sys/cdefs.h>
40136944Syongari__FBSDID("$FreeBSD: head/sys/dev/sound/sbus/cs4231.c 193667 2009-06-07 23:38:16Z ariff $");
41136944Syongari
42136944Syongari#include <sys/param.h>
43136944Syongari#include <sys/systm.h>
44136944Syongari#include <sys/bus.h>
45136944Syongari#include <sys/kernel.h>
46136944Syongari#include <sys/resource.h>
47136944Syongari
48136944Syongari#include <dev/ofw/ofw_bus.h>
49136944Syongari#include <dev/ofw/openfirm.h>
50136944Syongari#include <machine/bus.h>
51136944Syongari#include <machine/ofw_machdep.h>
52136944Syongari
53193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS
54193640Sariff#include "opt_snd.h"
55193640Sariff#endif
56193640Sariff
57136944Syongari#include <dev/sound/pcm/sound.h>
58136944Syongari#include <dev/sound/sbus/apcdmareg.h>
59136944Syongari#include <dev/sound/sbus/cs4231.h>
60136944Syongari
61136944Syongari#include <sparc64/sbus/sbusvar.h>
62136944Syongari#include <sparc64/ebus/ebusreg.h>
63136944Syongari
64136944Syongari#include "mixer_if.h"
65136944Syongari
66136944Syongari/*
67136944Syongari * The driver supports CS4231A audio chips found on Sbus/Ebus based
68136944Syongari * UltraSPARCs. Though, CS4231A says it supports full-duplex mode, I
69136944Syongari * doubt it due to the lack of independent sampling frequency register
70136944Syongari * for playback/capture.
71136944Syongari * Since I couldn't find any documentation for APCDMA programming
72136944Syongari * information, I guessed the usage of APCDMA from that of OpenBSD's
73136944Syongari * driver. The EBDMA infomation of PCIO can be obtained from
74136944Syongari *  http://solutions.sun.com/embedded/databook/web/microprocessors/pcio.html
75136944Syongari * And CS4231A datasheet can also be obtained from
76136944Syongari *  ftp://ftp.alsa-project.org/pub/manuals/cirrus/4231a.pdf
77136944Syongari *
78136944Syongari * Audio capture(recording) was not tested at all and may have bugs.
79136944Syongari * Sorry, I don't have microphone. Don't try to use full-duplex mode.
80136944Syongari * It wouldn't work.
81136944Syongari */
82136944Syongari#define CS_TIMEOUT		90000
83136944Syongari
84136944Syongari#define CS4231_MIN_BUF_SZ	(16*1024)
85136944Syongari#define CS4231_DEFAULT_BUF_SZ	(32*1024)
86136944Syongari#define CS4231_MAX_BUF_SZ	(64*1024)
87136944Syongari#define CS4231_MAX_BLK_SZ	(8*1024)
88136944Syongari#define CS4231_MAX_APC_DMA_SZ	(8*1024)
89136944Syongari
90136944Syongari
91136944Syongari#undef CS4231_DEBUG
92136944Syongari#ifdef CS4231_DEBUG
93136944Syongari#define DPRINTF(x)		printf x
94136944Syongari#else
95136944Syongari#define DPRINTF(x)
96136944Syongari#endif
97136944Syongari#define CS4231_AUTO_CALIBRATION
98136944Syongari
99136944Syongaristruct cs4231_softc;
100136944Syongari
101136944Syongaristruct cs4231_channel {
102136944Syongari	struct cs4231_softc	*parent;
103136944Syongari	struct pcm_channel	*channel;
104136944Syongari	struct snd_dbuf		*buffer;
105136944Syongari	u_int32_t		format;
106136944Syongari	u_int32_t		speed;
107136944Syongari	u_int32_t		nextaddr;
108136944Syongari	u_int32_t		togo;
109136944Syongari	int			dir;
110136944Syongari	int			locked;
111136944Syongari};
112136944Syongari
113136944Syongari#define CS4231_RES_MEM_MAX	4
114136944Syongari#define CS4231_RES_IRQ_MAX	2
115136944Syongaristruct cs4231_softc {
116136944Syongari	struct device		*sc_dev;
117136944Syongari	int			sc_rid[CS4231_RES_MEM_MAX];
118136944Syongari	struct resource		*sc_res[CS4231_RES_MEM_MAX];
119136944Syongari	bus_space_handle_t	sc_regh[CS4231_RES_MEM_MAX];
120136944Syongari	bus_space_tag_t		sc_regt[CS4231_RES_MEM_MAX];
121136944Syongari
122136944Syongari	int			sc_irqrid[CS4231_RES_IRQ_MAX];
123136944Syongari	struct resource		*sc_irqres[CS4231_RES_IRQ_MAX];
124136944Syongari	void			*sc_ih[CS4231_RES_IRQ_MAX];
125136944Syongari	bus_dma_tag_t		sc_dmat[CS4231_RES_IRQ_MAX];
126136944Syongari	int			sc_burst;
127136944Syongari
128136944Syongari	u_int32_t		sc_bufsz;
129136944Syongari	struct cs4231_channel	sc_pch;
130136944Syongari	struct cs4231_channel	sc_rch;
131136944Syongari	int			sc_enabled;
132136944Syongari	int			sc_nmres;
133136944Syongari	int			sc_nires;
134136944Syongari	int			sc_codecv;
135136944Syongari	int			sc_chipvid;
136136944Syongari	int			sc_flags;
137136944Syongari#define CS4231_SBUS		0x01
138136944Syongari#define CS4231_EBUS		0x02
139136944Syongari
140136944Syongari	struct mtx		*sc_lock;
141136944Syongari};
142136944Syongari
143136944Syongaristruct mix_table {
144136944Syongari	u_int32_t	reg:8;
145136944Syongari	u_int32_t	bits:8;
146136944Syongari	u_int32_t	mute:8;
147136944Syongari	u_int32_t	shift:4;
148136944Syongari	u_int32_t	neg:1;
149136944Syongari	u_int32_t	avail:1;
150136944Syongari	u_int32_t	recdev:1;
151136944Syongari};
152136944Syongari
153136944Syongaristatic int	cs4231_bus_probe(device_t);
154136944Syongaristatic int	cs4231_sbus_attach(device_t);
155136944Syongaristatic int	cs4231_ebus_attach(device_t);
156136944Syongaristatic int	cs4231_attach_common(struct cs4231_softc *);
157136944Syongaristatic int	cs4231_bus_detach(device_t);
158136944Syongaristatic int	cs4231_bus_suspend(device_t);
159136944Syongaristatic int	cs4231_bus_resume(device_t);
160136944Syongaristatic void	cs4231_getversion(struct cs4231_softc *);
161136944Syongaristatic void	cs4231_free_resource(struct cs4231_softc *);
162136944Syongaristatic void	cs4231_ebdma_reset(struct cs4231_softc *);
163136944Syongaristatic void	cs4231_power_reset(struct cs4231_softc *, int);
164136944Syongaristatic int	cs4231_enable(struct cs4231_softc *, int);
165136944Syongaristatic void	cs4231_disable(struct cs4231_softc *);
166136944Syongaristatic void	cs4231_write(struct cs4231_softc *, u_int8_t, u_int8_t);
167136944Syongaristatic u_int8_t cs4231_read(struct cs4231_softc *, u_int8_t);
168136944Syongaristatic void	cs4231_sbus_intr(void *);
169136944Syongaristatic void	cs4231_ebus_pintr(void *arg);
170136944Syongaristatic void	cs4231_ebus_cintr(void *arg);
171136944Syongaristatic int	cs4231_mixer_init(struct snd_mixer *);
172136944Syongaristatic void	cs4231_mixer_set_value(struct cs4231_softc *,
173136944Syongari    const struct mix_table *, u_int8_t);
174136944Syongaristatic int	cs4231_mixer_set(struct snd_mixer *, u_int32_t, u_int32_t,
175136944Syongari    u_int32_t);
176136944Syongaristatic int	cs4231_mixer_setrecsrc(struct snd_mixer *, u_int32_t);
177136944Syongaristatic void	*cs4231_chan_init(kobj_t, void *, struct snd_dbuf *,
178136944Syongari    struct pcm_channel *, int);
179136944Syongaristatic int	cs4231_chan_setformat(kobj_t, void *, u_int32_t);
180136944Syongaristatic int	cs4231_chan_setspeed(kobj_t, void *, u_int32_t);
181136944Syongaristatic void	cs4231_chan_fs(struct cs4231_softc *, int, u_int8_t);
182136944Syongaristatic int	cs4231_chan_setblocksize(kobj_t, void *, u_int32_t);
183136944Syongaristatic int	cs4231_chan_trigger(kobj_t, void *, int);
184136944Syongaristatic int	cs4231_chan_getptr(kobj_t, void *);
185136944Syongaristatic struct pcmchan_caps *
186136944Syongari    cs4231_chan_getcaps(kobj_t, void *);
187136944Syongaristatic void	cs4231_trigger(struct cs4231_channel *);
188136944Syongaristatic void	cs4231_apcdma_trigger(struct cs4231_softc *,
189136944Syongari    struct cs4231_channel *);
190136944Syongaristatic void	cs4231_ebdma_trigger(struct cs4231_softc *,
191136944Syongari    struct cs4231_channel *);
192136944Syongaristatic void	cs4231_halt(struct cs4231_channel *);
193136944Syongari
194136944Syongari#define CS4231_LOCK(sc)		snd_mtxlock(sc->sc_lock)
195136944Syongari#define CS4231_UNLOCK(sc)	snd_mtxunlock(sc->sc_lock)
196136944Syongari#define CS4231_LOCK_ASSERT(sc)	snd_mtxassert(sc->sc_lock)
197136944Syongari
198136944Syongari#define CS_WRITE(sc,r,v)	\
199136944Syongari    bus_space_write_1((sc)->sc_regt[0], (sc)->sc_regh[0], (r) << 2, (v))
200136944Syongari#define CS_READ(sc,r)		\
201136944Syongari    bus_space_read_1((sc)->sc_regt[0], (sc)->sc_regh[0], (r) << 2)
202136944Syongari
203136944Syongari#define APC_WRITE(sc,r,v)	\
204136944Syongari    bus_space_write_4(sc->sc_regt[0], sc->sc_regh[0], r, v)
205136944Syongari#define APC_READ(sc,r)		\
206136944Syongari    bus_space_read_4(sc->sc_regt[0], sc->sc_regh[0], r)
207136944Syongari
208136944Syongari#define EBDMA_P_WRITE(sc,r,v)	\
209136944Syongari    bus_space_write_4((sc)->sc_regt[1], (sc)->sc_regh[1], (r), (v))
210136944Syongari#define EBDMA_P_READ(sc,r)	\
211136944Syongari    bus_space_read_4((sc)->sc_regt[1], (sc)->sc_regh[1], (r))
212136944Syongari
213136944Syongari#define EBDMA_C_WRITE(sc,r,v)	\
214136944Syongari    bus_space_write_4((sc)->sc_regt[2], (sc)->sc_regh[2], (r), (v))
215136944Syongari#define EBDMA_C_READ(sc,r)	\
216136944Syongari    bus_space_read_4((sc)->sc_regt[2], (sc)->sc_regh[2], (r))
217136944Syongari
218136944Syongari#define AUXIO_CODEC		0x00
219136944Syongari#define AUXIO_WRITE(sc,r,v)	\
220136944Syongari    bus_space_write_4((sc)->sc_regt[3], (sc)->sc_regh[3], (r), (v))
221136944Syongari#define AUXIO_READ(sc,r)	\
222136944Syongari    bus_space_read_4((sc)->sc_regt[3], (sc)->sc_regh[3], (r))
223136944Syongari
224136944Syongari#define CODEC_WARM_RESET	0
225136944Syongari#define CODEC_COLD_RESET	1
226136944Syongari
227136944Syongari/* SBus */
228136944Syongaristatic device_method_t cs4231_sbus_methods[] = {
229136944Syongari	DEVMETHOD(device_probe,		cs4231_bus_probe),
230136944Syongari	DEVMETHOD(device_attach,	cs4231_sbus_attach),
231136944Syongari	DEVMETHOD(device_detach,	cs4231_bus_detach),
232136944Syongari	DEVMETHOD(device_suspend,	cs4231_bus_suspend),
233136944Syongari	DEVMETHOD(device_resume,	cs4231_bus_resume),
234136944Syongari	{0, 0}
235136944Syongari};
236136944Syongari
237136944Syongaristatic driver_t cs4231_sbus_driver = {
238136944Syongari	"pcm",
239136944Syongari	cs4231_sbus_methods,
240136944Syongari	PCM_SOFTC_SIZE
241136944Syongari};
242136944Syongari
243136944SyongariDRIVER_MODULE(snd_audiocs, sbus, cs4231_sbus_driver, pcm_devclass, 0, 0);
244136944Syongari
245136944Syongari/* EBus */
246136944Syongaristatic device_method_t cs4231_ebus_methods[] = {
247136944Syongari	DEVMETHOD(device_probe,		cs4231_bus_probe),
248136944Syongari	DEVMETHOD(device_attach,	cs4231_ebus_attach),
249136944Syongari	DEVMETHOD(device_detach,	cs4231_bus_detach),
250136944Syongari	DEVMETHOD(device_suspend,	cs4231_bus_suspend),
251136944Syongari	DEVMETHOD(device_resume,	cs4231_bus_resume),
252136944Syongari	{0, 0}
253136944Syongari};
254136944Syongari
255136944Syongaristatic driver_t cs4231_ebus_driver = {
256136944Syongari	"pcm",
257136944Syongari	cs4231_ebus_methods,
258136944Syongari	PCM_SOFTC_SIZE
259136944Syongari};
260136944Syongari
261136944SyongariDRIVER_MODULE(snd_audiocs, ebus, cs4231_ebus_driver, pcm_devclass, 0, 0);
262136944SyongariMODULE_DEPEND(snd_audiocs, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
263136944SyongariMODULE_VERSION(snd_audiocs, 1);
264136944Syongari
265136944Syongari
266136944Syongaristatic u_int32_t cs4231_fmt[] = {
267193640Sariff	SND_FORMAT(AFMT_U8, 1, 0),
268193640Sariff	SND_FORMAT(AFMT_U8, 2, 0),
269193640Sariff	SND_FORMAT(AFMT_MU_LAW, 1, 0),
270193640Sariff	SND_FORMAT(AFMT_MU_LAW, 2, 0),
271193640Sariff	SND_FORMAT(AFMT_A_LAW, 1, 0),
272193640Sariff	SND_FORMAT(AFMT_A_LAW, 2, 0),
273193640Sariff	SND_FORMAT(AFMT_IMA_ADPCM, 1, 0),
274193640Sariff	SND_FORMAT(AFMT_IMA_ADPCM, 2, 0),
275193640Sariff	SND_FORMAT(AFMT_S16_LE, 1, 0),
276193667Sariff	SND_FORMAT(AFMT_S16_LE, 2, 0),
277193640Sariff	SND_FORMAT(AFMT_S16_BE, 1, 0),
278193640Sariff	SND_FORMAT(AFMT_S16_BE, 2, 0),
279136944Syongari	0
280136944Syongari};
281136944Syongari
282136944Syongaristatic struct pcmchan_caps cs4231_caps = {5510, 48000, cs4231_fmt, 0};
283136944Syongari
284136944Syongari/*
285136944Syongari * sound(4) channel interface
286136944Syongari */
287136944Syongaristatic kobj_method_t cs4231_chan_methods[] = {
288136944Syongari	KOBJMETHOD(channel_init,		cs4231_chan_init),
289136944Syongari	KOBJMETHOD(channel_setformat,		cs4231_chan_setformat),
290136944Syongari	KOBJMETHOD(channel_setspeed,		cs4231_chan_setspeed),
291136944Syongari	KOBJMETHOD(channel_setblocksize,	cs4231_chan_setblocksize),
292136944Syongari	KOBJMETHOD(channel_trigger,		cs4231_chan_trigger),
293136944Syongari	KOBJMETHOD(channel_getptr,		cs4231_chan_getptr),
294136944Syongari	KOBJMETHOD(channel_getcaps,		cs4231_chan_getcaps),
295193640Sariff	KOBJMETHOD_END
296136944Syongari};
297136944SyongariCHANNEL_DECLARE(cs4231_chan);
298136944Syongari
299136944Syongari/*
300136944Syongari * sound(4) mixer interface
301136944Syongari */
302136944Syongaristatic kobj_method_t cs4231_mixer_methods[] = {
303136944Syongari	KOBJMETHOD(mixer_init,		cs4231_mixer_init),
304136944Syongari	KOBJMETHOD(mixer_set,		cs4231_mixer_set),
305136944Syongari	KOBJMETHOD(mixer_setrecsrc,	cs4231_mixer_setrecsrc),
306193640Sariff	KOBJMETHOD_END
307136944Syongari};
308136944SyongariMIXER_DECLARE(cs4231_mixer);
309136944Syongari
310136944Syongaristatic int
311136944Syongarics4231_bus_probe(device_t dev)
312136944Syongari{
313166098Smarius	const char *compat, *name;
314136944Syongari
315166098Smarius	compat = ofw_bus_get_compat(dev);
316136944Syongari	name = ofw_bus_get_name(dev);
317166098Smarius	if (strcmp("SUNW,CS4231", name) == 0 ||
318166098Smarius	    (compat != NULL && strcmp("SUNW,CS4231", compat) == 0)) {
319136944Syongari		device_set_desc(dev, "Sun Audiocs");
320142890Simp		return (BUS_PROBE_DEFAULT);
321136944Syongari	}
322136944Syongari	return (ENXIO);
323136944Syongari}
324136944Syongari
325136944Syongaristatic int
326136944Syongarics4231_sbus_attach(device_t dev)
327136944Syongari{
328136944Syongari	struct cs4231_softc *sc;
329136944Syongari	int burst;
330136944Syongari
331170873Sariff	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
332136944Syongari	sc->sc_dev = dev;
333136944Syongari	/*
334136944Syongari	 * XXX
335136944Syongari	 * No public documentation exists on programming burst size of APCDMA.
336136944Syongari	 */
337136944Syongari	burst = sbus_get_burstsz(sc->sc_dev);
338136944Syongari	if ((burst & SBUS_BURST_64))
339136944Syongari		sc->sc_burst = 64;
340136944Syongari	else if ((burst & SBUS_BURST_32))
341136944Syongari		sc->sc_burst = 32;
342136944Syongari	else if ((burst & SBUS_BURST_16))
343136944Syongari		sc->sc_burst = 16;
344136944Syongari	else
345136944Syongari		sc->sc_burst = 0;
346136944Syongari	sc->sc_flags = CS4231_SBUS;
347136944Syongari	sc->sc_nmres = 1;
348136944Syongari	sc->sc_nires = 1;
349136944Syongari	return cs4231_attach_common(sc);
350136944Syongari}
351136944Syongari
352136944Syongaristatic int
353136944Syongarics4231_ebus_attach(device_t dev)
354136944Syongari{
355136944Syongari	struct cs4231_softc *sc;
356136944Syongari
357136944Syongari	sc = malloc(sizeof(struct cs4231_softc), M_DEVBUF, M_NOWAIT | M_ZERO);
358136944Syongari	if (sc == NULL) {
359136944Syongari		device_printf(dev, "cannot allocate softc\n");
360136944Syongari		return (ENOMEM);
361136944Syongari	}
362136944Syongari	sc->sc_dev = dev;
363136944Syongari	sc->sc_burst = EBDCSR_BURST_1;
364136944Syongari	sc->sc_nmres = CS4231_RES_MEM_MAX;
365136944Syongari	sc->sc_nires = CS4231_RES_IRQ_MAX;
366136944Syongari	sc->sc_flags = CS4231_EBUS;
367136944Syongari	return cs4231_attach_common(sc);
368136944Syongari}
369136944Syongari
370136944Syongaristatic int
371136944Syongarics4231_attach_common(struct cs4231_softc *sc)
372136944Syongari{
373136944Syongari	char status[SND_STATUSLEN];
374136944Syongari	driver_intr_t *ihandler;
375136944Syongari	int i;
376136944Syongari
377136944Syongari	sc->sc_lock = snd_mtxcreate(device_get_nameunit(sc->sc_dev),
378167608Sariff	    "snd_cs4231 softc");
379136944Syongari
380136944Syongari	for (i = 0; i < sc->sc_nmres; i++) {
381136944Syongari		sc->sc_rid[i] = i;
382136944Syongari		if ((sc->sc_res[i] = bus_alloc_resource_any(sc->sc_dev,
383146410Smarius		    SYS_RES_MEMORY, &sc->sc_rid[i], RF_ACTIVE)) == NULL) {
384136944Syongari			device_printf(sc->sc_dev,
385136944Syongari			    "cannot map register %d\n", i);
386136944Syongari			goto fail;
387136944Syongari		}
388136944Syongari		sc->sc_regt[i] = rman_get_bustag(sc->sc_res[i]);
389136944Syongari		sc->sc_regh[i] = rman_get_bushandle(sc->sc_res[i]);
390136944Syongari	}
391136944Syongari	for (i = 0; i < sc->sc_nires; i++) {
392136944Syongari		sc->sc_irqrid[i] = i;
393136944Syongari		if ((sc->sc_irqres[i] = bus_alloc_resource_any(sc->sc_dev,
394136944Syongari		    SYS_RES_IRQ, &sc->sc_irqrid[i], RF_SHAREABLE | RF_ACTIVE))
395136944Syongari		    == NULL) {
396136944Syongari			if ((sc->sc_flags & CS4231_SBUS) != 0)
397136944Syongari				device_printf(sc->sc_dev,
398136944Syongari				    "cannot allocate interrupt\n");
399136944Syongari			else
400136944Syongari				device_printf(sc->sc_dev, "cannot allocate %s "
401136944Syongari				    "interrupt\n", i == 0 ? "capture" :
402136944Syongari				    "playback");
403136944Syongari			goto fail;
404136944Syongari		}
405136944Syongari	}
406136944Syongari
407136944Syongari	ihandler = cs4231_sbus_intr;
408136944Syongari	for (i = 0; i < sc->sc_nires; i++) {
409136944Syongari		if ((sc->sc_flags & CS4231_EBUS) != 0) {
410136944Syongari			if (i == 0)
411136944Syongari				ihandler = cs4231_ebus_cintr;
412136944Syongari			else
413136944Syongari				ihandler = cs4231_ebus_pintr;
414136944Syongari		}
415136944Syongari		if (snd_setup_intr(sc->sc_dev, sc->sc_irqres[i], INTR_MPSAFE,
416136944Syongari		    ihandler, sc, &sc->sc_ih[i])) {
417136944Syongari			if ((sc->sc_flags & CS4231_SBUS) != 0)
418136944Syongari				device_printf(sc->sc_dev,
419136944Syongari				    "cannot set up interrupt\n");
420136944Syongari			else
421136944Syongari				device_printf(sc->sc_dev, "cannot set up %s "
422136944Syongari				    " interrupt\n", i == 0 ? "capture" :
423136944Syongari				    "playback");
424136944Syongari			goto fail;
425136944Syongari		}
426136944Syongari	}
427136944Syongari
428136944Syongari	sc->sc_bufsz = pcm_getbuffersize(sc->sc_dev, CS4231_MIN_BUF_SZ,
429136944Syongari	    CS4231_DEFAULT_BUF_SZ, CS4231_MAX_BUF_SZ);
430136944Syongari	for (i = 0; i < sc->sc_nires; i++) {
431136944Syongari		if (bus_dma_tag_create(
432166165Smarius		    bus_get_dma_tag(sc->sc_dev),/* parent */
433136944Syongari		    64, 0,			/* alignment, boundary */
434136944Syongari		    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
435136944Syongari		    BUS_SPACE_MAXADDR,		/* highaddr */
436136944Syongari		    NULL, NULL,			/* filtfunc, filtfuncarg */
437136944Syongari		    sc->sc_bufsz,		/* maxsize */
438136944Syongari		    1,				/* nsegments */
439136944Syongari		    sc->sc_bufsz,		/* maxsegsz */
440136944Syongari		    BUS_DMA_ALLOCNOW,		/* flags */
441136944Syongari		    NULL,			/* lockfunc */
442136944Syongari		    NULL,			/* lockfuncarg */
443136944Syongari		    &sc->sc_dmat[i])) {
444136944Syongari			if ((sc->sc_flags & CS4231_SBUS) != 0)
445136944Syongari				device_printf(sc->sc_dev,
446136944Syongari				    "cannot allocate DMA tag\n");
447136944Syongari			else
448136944Syongari				device_printf(sc->sc_dev, "cannot allocate %s "
449136944Syongari				    "DMA tag\n", i == 0 ? "capture" :
450136944Syongari				    "playback");
451136944Syongari			goto fail;
452136944Syongari		}
453136944Syongari	}
454136944Syongari	cs4231_enable(sc, CODEC_WARM_RESET);
455136944Syongari	cs4231_getversion(sc);
456136944Syongari	if (mixer_init(sc->sc_dev, &cs4231_mixer_class, sc) != 0)
457136944Syongari		goto fail;
458136944Syongari	if (pcm_register(sc->sc_dev, sc, 1, 1)) {
459136944Syongari		device_printf(sc->sc_dev, "cannot register to pcm\n");
460136944Syongari		goto fail;
461136944Syongari	}
462136944Syongari	if (pcm_addchan(sc->sc_dev, PCMDIR_REC, &cs4231_chan_class, sc) != 0)
463136944Syongari		goto chan_fail;
464136944Syongari	if (pcm_addchan(sc->sc_dev, PCMDIR_PLAY, &cs4231_chan_class, sc) != 0)
465136944Syongari		goto chan_fail;
466136944Syongari	if ((sc->sc_flags & CS4231_SBUS) != 0)
467136944Syongari		snprintf(status, SND_STATUSLEN, "at mem 0x%lx irq %ld bufsz %u",
468136944Syongari		    rman_get_start(sc->sc_res[0]),
469136944Syongari		    rman_get_start(sc->sc_irqres[0]), sc->sc_bufsz);
470136944Syongari	else
471136944Syongari		snprintf(status, SND_STATUSLEN, "at io 0x%lx 0x%lx 0x%lx 0x%lx "
472136944Syongari		    "irq %ld %ld bufsz %u", rman_get_start(sc->sc_res[0]),
473136944Syongari		    rman_get_start(sc->sc_res[1]),
474136944Syongari		    rman_get_start(sc->sc_res[2]),
475136944Syongari		    rman_get_start(sc->sc_res[3]),
476136944Syongari		    rman_get_start(sc->sc_irqres[0]),
477136944Syongari		    rman_get_start(sc->sc_irqres[1]), sc->sc_bufsz);
478136944Syongari	pcm_setstatus(sc->sc_dev, status);
479136944Syongari	return (0);
480136944Syongari
481136944Syongarichan_fail:
482136944Syongari	pcm_unregister(sc->sc_dev);
483136944Syongarifail:
484136944Syongari	cs4231_free_resource(sc);
485136944Syongari	return (ENXIO);
486136944Syongari}
487136944Syongari
488136944Syongaristatic int
489136944Syongarics4231_bus_detach(device_t dev)
490136944Syongari{
491136944Syongari	struct cs4231_softc *sc;
492136944Syongari	struct cs4231_channel *pch, *rch;
493136944Syongari	int error;
494136944Syongari
495136944Syongari	sc = pcm_getdevinfo(dev);
496136944Syongari	CS4231_LOCK(sc);
497136944Syongari	pch = &sc->sc_pch;
498136944Syongari	rch = &sc->sc_pch;
499136944Syongari	if (pch->locked || rch->locked) {
500136944Syongari		CS4231_UNLOCK(sc);
501136944Syongari		return (EBUSY);
502136944Syongari	}
503136944Syongari	/*
504136944Syongari	 * Since EBDMA requires valid DMA buffer to drain its FIFO, we need
505136944Syongari	 * real DMA buffer for draining.
506136944Syongari	 */
507136944Syongari	if ((sc->sc_flags & CS4231_EBUS) != 0)
508136944Syongari		cs4231_ebdma_reset(sc);
509136944Syongari	CS4231_UNLOCK(sc);
510136944Syongari	error = pcm_unregister(dev);
511136944Syongari	if (error)
512136944Syongari		return (error);
513136944Syongari	cs4231_free_resource(sc);
514136944Syongari	return (0);
515136944Syongari}
516136944Syongari
517136944Syongaristatic int
518136944Syongarics4231_bus_suspend(device_t dev)
519136944Syongari{
520136944Syongari
521136944Syongari	return (ENXIO);
522136944Syongari}
523136944Syongari
524136944Syongaristatic int
525136944Syongarics4231_bus_resume(device_t dev)
526136944Syongari{
527136944Syongari
528136944Syongari	return (ENXIO);
529136944Syongari}
530136944Syongari
531136944Syongaristatic void
532136944Syongarics4231_getversion(struct cs4231_softc *sc)
533136944Syongari{
534136944Syongari	u_int8_t v;
535136944Syongari
536136944Syongari	v = cs4231_read(sc, CS_MISC_INFO);
537136944Syongari	sc->sc_codecv = v & CS_CODEC_ID_MASK;
538136944Syongari	v = cs4231_read(sc, CS_VERSION_ID);
539136944Syongari	v &= (CS_VERSION_NUMBER | CS_VERSION_CHIPID);
540136944Syongari	sc->sc_chipvid = v;
541136944Syongari	switch(v) {
542136944Syongari		case 0x80:
543136944Syongari			device_printf(sc->sc_dev, "<CS4231 Codec Id. %d>\n",
544136944Syongari			    sc->sc_codecv);
545136944Syongari			break;
546136944Syongari		case 0xa0:
547136944Syongari			device_printf(sc->sc_dev, "<CS4231A Codec Id. %d>\n",
548136944Syongari			    sc->sc_codecv);
549136944Syongari			break;
550136944Syongari		case 0x82:
551136944Syongari			device_printf(sc->sc_dev, "<CS4232 Codec Id. %d>\n",
552136944Syongari			    sc->sc_codecv);
553136944Syongari			break;
554136944Syongari		default:
555136944Syongari			device_printf(sc->sc_dev,
556136944Syongari			    "<Unknown 0x%x Codec Id. %d\n", v, sc->sc_codecv);
557136944Syongari			break;
558136944Syongari	}
559136944Syongari}
560136944Syongari
561136944Syongaristatic void
562136944Syongarics4231_ebdma_reset(struct cs4231_softc *sc)
563136944Syongari{
564136944Syongari	int i;
565136944Syongari
566136944Syongari	/* playback */
567136944Syongari	EBDMA_P_WRITE(sc, EBDMA_DCSR,
568136944Syongari	    EBDMA_P_READ(sc, EBDMA_DCSR) & ~(EBDCSR_INTEN | EBDCSR_NEXTEN));
569136944Syongari	EBDMA_P_WRITE(sc, EBDMA_DCSR, EBDCSR_RESET);
570136944Syongari	for (i = CS_TIMEOUT;
571136944Syongari	    i && EBDMA_P_READ(sc, EBDMA_DCSR) & EBDCSR_DRAIN; i--)
572136944Syongari		DELAY(1);
573136944Syongari	if (i == 0)
574136944Syongari		device_printf(sc->sc_dev,
575136944Syongari		    "timeout waiting for playback DMA reset\n");
576136944Syongari	EBDMA_P_WRITE(sc, EBDMA_DCSR, sc->sc_burst);
577136944Syongari	/* capture */
578136944Syongari	EBDMA_C_WRITE(sc, EBDMA_DCSR,
579136944Syongari	    EBDMA_C_READ(sc, EBDMA_DCSR) & ~(EBDCSR_INTEN | EBDCSR_NEXTEN));
580136944Syongari	EBDMA_C_WRITE(sc, EBDMA_DCSR, EBDCSR_RESET);
581136944Syongari	for (i = CS_TIMEOUT;
582136944Syongari	    i && EBDMA_C_READ(sc, EBDMA_DCSR) & EBDCSR_DRAIN; i--)
583136944Syongari		DELAY(1);
584136944Syongari	if (i == 0)
585136944Syongari		device_printf(sc->sc_dev,
586136944Syongari		    "timeout waiting for capture DMA reset\n");
587136944Syongari	EBDMA_C_WRITE(sc, EBDMA_DCSR, sc->sc_burst);
588136944Syongari}
589136944Syongari
590136944Syongaristatic void
591136944Syongarics4231_power_reset(struct cs4231_softc *sc, int how)
592136944Syongari{
593136944Syongari	u_int32_t v;
594136944Syongari	int i;
595136944Syongari
596136944Syongari	if ((sc->sc_flags & CS4231_SBUS) != 0) {
597136944Syongari		APC_WRITE(sc, APC_CSR, APC_CSR_RESET);
598136944Syongari		DELAY(10);
599136944Syongari		APC_WRITE(sc, APC_CSR, 0);
600136944Syongari		DELAY(10);
601136944Syongari		APC_WRITE(sc,
602136944Syongari		    APC_CSR, APC_READ(sc, APC_CSR) | APC_CSR_CODEC_RESET);
603136944Syongari		DELAY(20);
604136944Syongari		APC_WRITE(sc,
605136944Syongari		    APC_CSR, APC_READ(sc, APC_CSR) & (~APC_CSR_CODEC_RESET));
606136944Syongari	} else {
607136944Syongari		v = AUXIO_READ(sc, AUXIO_CODEC);
608136944Syongari		if (how == CODEC_WARM_RESET && v != 0) {
609136944Syongari			AUXIO_WRITE(sc, AUXIO_CODEC, 0);
610136944Syongari			DELAY(20);
611136944Syongari		} else if (how == CODEC_COLD_RESET){
612136944Syongari			AUXIO_WRITE(sc, AUXIO_CODEC, 1);
613136944Syongari			DELAY(20);
614136944Syongari			AUXIO_WRITE(sc, AUXIO_CODEC, 0);
615136944Syongari			DELAY(20);
616136944Syongari		}
617136944Syongari		cs4231_ebdma_reset(sc);
618136944Syongari	}
619136944Syongari
620136944Syongari	for (i = CS_TIMEOUT;
621136944Syongari	    i && CS_READ(sc, CS4231_IADDR) == CS_IN_INIT; i--)
622136944Syongari		DELAY(10);
623136944Syongari	if (i == 0)
624136944Syongari		device_printf(sc->sc_dev, "timeout waiting for reset\n");
625136944Syongari
626136944Syongari	/* turn on cs4231 mode */
627136944Syongari	cs4231_write(sc, CS_MISC_INFO,
628136944Syongari	    cs4231_read(sc, CS_MISC_INFO) | CS_MODE2);
629172568Skevlo	/* enable interrupts & clear CSR */
630136944Syongari        cs4231_write(sc, CS_PIN_CONTROL,
631136944Syongari            cs4231_read(sc, CS_PIN_CONTROL) | INTERRUPT_ENABLE);
632136944Syongari	CS_WRITE(sc, CS4231_STATUS, 0);
633136944Syongari	/* enable DAC output */
634136944Syongari	cs4231_write(sc, CS_LEFT_OUTPUT_CONTROL,
635136944Syongari	    cs4231_read(sc, CS_LEFT_OUTPUT_CONTROL) & ~OUTPUT_MUTE);
636136944Syongari	cs4231_write(sc, CS_RIGHT_OUTPUT_CONTROL,
637136944Syongari	    cs4231_read(sc, CS_RIGHT_OUTPUT_CONTROL) & ~OUTPUT_MUTE);
638136944Syongari	/* mute AUX1 since it generates noises */
639136944Syongari	cs4231_write(sc, CS_LEFT_AUX1_CONTROL,
640136944Syongari	    cs4231_read(sc, CS_LEFT_AUX1_CONTROL) | AUX_INPUT_MUTE);
641136944Syongari	cs4231_write(sc, CS_RIGHT_AUX1_CONTROL,
642136944Syongari	    cs4231_read(sc, CS_RIGHT_AUX1_CONTROL) | AUX_INPUT_MUTE);
643136944Syongari	/* protect buffer underrun and set output level to 0dB */
644136944Syongari	cs4231_write(sc, CS_ALT_FEATURE1,
645136944Syongari	    cs4231_read(sc, CS_ALT_FEATURE1) | CS_DAC_ZERO | CS_OUTPUT_LVL);
646136944Syongari	/* enable high pass filter, dual xtal was disabled due to noises */
647136944Syongari	cs4231_write(sc, CS_ALT_FEATURE2,
648136944Syongari	    cs4231_read(sc, CS_ALT_FEATURE2) | CS_HPF_ENABLE);
649136944Syongari}
650136944Syongari
651136944Syongaristatic int
652136944Syongarics4231_enable(struct cs4231_softc *sc, int how)
653136944Syongari{
654136944Syongari	cs4231_power_reset(sc, how);
655136944Syongari	sc->sc_enabled = 1;
656136944Syongari        return (0);
657136944Syongari}
658136944Syongari
659136944Syongaristatic void
660136944Syongarics4231_disable(struct cs4231_softc *sc)
661136944Syongari{
662136944Syongari	u_int8_t v;
663136944Syongari
664136944Syongari	CS4231_LOCK_ASSERT(sc);
665136944Syongari
666136944Syongari	if (sc->sc_enabled == 0)
667136944Syongari		return;
668136944Syongari	sc->sc_enabled = 0;
669136944Syongari	CS4231_UNLOCK(sc);
670136944Syongari	cs4231_halt(&sc->sc_pch);
671136944Syongari	cs4231_halt(&sc->sc_rch);
672136944Syongari	CS4231_LOCK(sc);
673136944Syongari	v = cs4231_read(sc, CS_PIN_CONTROL) & ~INTERRUPT_ENABLE;
674136944Syongari	cs4231_write(sc, CS_PIN_CONTROL, v);
675136944Syongari
676136944Syongari	if ((sc->sc_flags & CS4231_SBUS) != 0) {
677136944Syongari		APC_WRITE(sc, APC_CSR, APC_CSR_RESET);
678136944Syongari		DELAY(10);
679136944Syongari		APC_WRITE(sc, APC_CSR, 0);
680136944Syongari		DELAY(10);
681136944Syongari	} else
682136944Syongari		cs4231_ebdma_reset(sc);
683136944Syongari}
684136944Syongari
685136944Syongaristatic void
686136944Syongarics4231_free_resource(struct cs4231_softc *sc)
687136944Syongari{
688136944Syongari	int i;
689136944Syongari
690136944Syongari	CS4231_LOCK(sc);
691136944Syongari	cs4231_disable(sc);
692136944Syongari	CS4231_UNLOCK(sc);
693136944Syongari	for (i = 0; i < sc->sc_nires; i++) {
694136944Syongari		if (sc->sc_irqres[i]) {
695136944Syongari			if (sc->sc_ih[i]) {
696136944Syongari				bus_teardown_intr(sc->sc_dev, sc->sc_irqres[i],
697136944Syongari				    sc->sc_ih[i]);
698136944Syongari				sc->sc_ih[i] = NULL;
699136944Syongari			}
700136944Syongari			bus_release_resource(sc->sc_dev, SYS_RES_IRQ,
701136944Syongari			    sc->sc_irqrid[i], sc->sc_irqres[i]);
702136944Syongari			sc->sc_irqres[i] = NULL;
703136944Syongari		}
704136944Syongari	}
705136944Syongari	for (i = 0; i < sc->sc_nires; i++) {
706136944Syongari		if (sc->sc_dmat[i])
707136944Syongari			bus_dma_tag_destroy(sc->sc_dmat[i]);
708136944Syongari	}
709136944Syongari	for (i = 0; i < sc->sc_nmres; i++) {
710136944Syongari		if (sc->sc_res[i])
711146410Smarius			bus_release_resource(sc->sc_dev, SYS_RES_MEMORY,
712136944Syongari			    sc->sc_rid[i], sc->sc_res[i]);
713136944Syongari	}
714136944Syongari	snd_mtxfree(sc->sc_lock);
715136944Syongari	free(sc, M_DEVBUF);
716136944Syongari}
717136944Syongari
718136944Syongaristatic void
719136944Syongarics4231_write(struct cs4231_softc *sc, u_int8_t r, u_int8_t v)
720136944Syongari{
721136944Syongari	CS_WRITE(sc, CS4231_IADDR, r);
722136944Syongari	CS_WRITE(sc, CS4231_IDATA, v);
723136944Syongari}
724136944Syongari
725136944Syongaristatic u_int8_t
726136944Syongarics4231_read(struct cs4231_softc *sc, u_int8_t r)
727136944Syongari{
728136944Syongari	CS_WRITE(sc, CS4231_IADDR, r);
729136944Syongari	return (CS_READ(sc, CS4231_IDATA));
730136944Syongari}
731136944Syongari
732136944Syongaristatic void
733136944Syongarics4231_sbus_intr(void *arg)
734136944Syongari{
735136944Syongari	struct cs4231_softc *sc;
736136944Syongari	struct cs4231_channel *pch, *rch;
737136944Syongari	u_int32_t csr;
738136944Syongari	u_int8_t status;
739136944Syongari
740136944Syongari	sc = arg;
741136944Syongari	CS4231_LOCK(sc);
742136944Syongari
743136944Syongari	csr = APC_READ(sc, APC_CSR);
744136944Syongari	if ((csr & APC_CSR_GI) == 0) {
745136944Syongari		CS4231_UNLOCK(sc);
746136944Syongari		return;
747136944Syongari	}
748136944Syongari	APC_WRITE(sc, APC_CSR, csr);
749136944Syongari
750136944Syongari	if ((csr & APC_CSR_EIE) && (csr & APC_CSR_EI)) {
751136944Syongari		status = cs4231_read(sc, CS_TEST_AND_INIT);
752136944Syongari		device_printf(sc->sc_dev,
753136944Syongari		    "apc error interrupt : stat = 0x%x\n", status);
754136944Syongari	}
755136944Syongari
756136944Syongari	pch = rch = NULL;
757136944Syongari	if ((csr & APC_CSR_PMIE) && (csr & APC_CSR_PMI)) {
758136944Syongari		u_long nextaddr, saddr;
759136944Syongari		u_int32_t togo;
760136944Syongari
761136944Syongari		pch = &sc->sc_pch;
762136944Syongari		togo = pch->togo;
763136944Syongari		saddr = sndbuf_getbufaddr(pch->buffer);
764136944Syongari		nextaddr = pch->nextaddr + togo;
765136944Syongari		if (nextaddr >=  saddr + sndbuf_getsize(pch->buffer))
766136944Syongari			nextaddr = saddr;
767136944Syongari		APC_WRITE(sc, APC_PNVA, nextaddr);
768136944Syongari		APC_WRITE(sc, APC_PNC, togo);
769136944Syongari		pch->nextaddr = nextaddr;
770136944Syongari	}
771136944Syongari
772136944Syongari	if ((csr & APC_CSR_CIE) && (csr & APC_CSR_CI) && (csr & APC_CSR_CD)) {
773136944Syongari		u_long nextaddr, saddr;
774136944Syongari		u_int32_t togo;
775136944Syongari
776136944Syongari		rch = &sc->sc_rch;
777136944Syongari		togo = rch->togo;
778136944Syongari		saddr = sndbuf_getbufaddr(rch->buffer);
779136944Syongari		nextaddr = rch->nextaddr + togo;
780136944Syongari		if (nextaddr >= saddr + sndbuf_getsize(rch->buffer))
781136944Syongari			nextaddr = saddr;
782136944Syongari		APC_WRITE(sc, APC_CNVA, nextaddr);
783136944Syongari		APC_WRITE(sc, APC_CNC, togo);
784136944Syongari		rch->nextaddr = nextaddr;
785136944Syongari	}
786136944Syongari	CS4231_UNLOCK(sc);
787136944Syongari	if (pch)
788136944Syongari		chn_intr(pch->channel);
789136944Syongari	if (rch)
790136944Syongari		chn_intr(rch->channel);
791136944Syongari}
792136944Syongari
793136944Syongari/* playback interrupt handler */
794136944Syongaristatic void
795136944Syongarics4231_ebus_pintr(void *arg)
796136944Syongari{
797136944Syongari	struct cs4231_softc *sc;
798136944Syongari	struct cs4231_channel *ch;
799136944Syongari	u_int32_t csr;
800136944Syongari	u_int8_t status;
801136944Syongari
802136944Syongari	sc = arg;
803136944Syongari	CS4231_LOCK(sc);
804136944Syongari
805136944Syongari	csr = EBDMA_P_READ(sc, EBDMA_DCSR);
806136944Syongari	if ((csr & EBDCSR_INT) == 0) {
807136944Syongari		CS4231_UNLOCK(sc);
808136944Syongari		return;
809136944Syongari	}
810136944Syongari
811136944Syongari	if ((csr & EBDCSR_ERR)) {
812136944Syongari		status = cs4231_read(sc, CS_TEST_AND_INIT);
813136944Syongari		device_printf(sc->sc_dev,
814136944Syongari		    "ebdma error interrupt : stat = 0x%x\n", status);
815136944Syongari	}
816136944Syongari	EBDMA_P_WRITE(sc, EBDMA_DCSR, csr | EBDCSR_TC);
817136944Syongari
818136944Syongari	ch = NULL;
819136944Syongari	if (csr & EBDCSR_TC) {
820136944Syongari		u_long nextaddr, saddr;
821136944Syongari		u_int32_t togo;
822136944Syongari
823136944Syongari		ch = &sc->sc_pch;
824136944Syongari		togo = ch->togo;
825136944Syongari		saddr = sndbuf_getbufaddr(ch->buffer);
826136944Syongari		nextaddr = ch->nextaddr + togo;
827136944Syongari		if (nextaddr >=  saddr + sndbuf_getsize(ch->buffer))
828136944Syongari			nextaddr = saddr;
829136944Syongari		/*
830136944Syongari		 * EBDMA_DCNT is loaded automatically
831136944Syongari		 * EBDMA_P_WRITE(sc, EBDMA_DCNT, togo);
832136944Syongari		 */
833136944Syongari		EBDMA_P_WRITE(sc, EBDMA_DADDR, nextaddr);
834136944Syongari		ch->nextaddr = nextaddr;
835136944Syongari	}
836136944Syongari	CS4231_UNLOCK(sc);
837136944Syongari	if (ch)
838136944Syongari		chn_intr(ch->channel);
839136944Syongari}
840136944Syongari
841136944Syongari/* capture interrupt handler */
842136944Syongaristatic void
843136944Syongarics4231_ebus_cintr(void *arg)
844136944Syongari{
845136944Syongari	struct cs4231_softc *sc;
846136944Syongari	struct cs4231_channel *ch;
847136944Syongari	u_int32_t csr;
848136944Syongari	u_int8_t status;
849136944Syongari
850136944Syongari	sc = arg;
851136944Syongari	CS4231_LOCK(sc);
852136944Syongari
853136944Syongari	csr = EBDMA_C_READ(sc, EBDMA_DCSR);
854136944Syongari	if ((csr & EBDCSR_INT) == 0) {
855136944Syongari		CS4231_UNLOCK(sc);
856136944Syongari		return;
857136944Syongari	}
858136944Syongari	if ((csr & EBDCSR_ERR)) {
859136944Syongari		status = cs4231_read(sc, CS_TEST_AND_INIT);
860136944Syongari		device_printf(sc->sc_dev,
861136944Syongari		    "dma error interrupt : stat = 0x%x\n", status);
862136944Syongari	}
863136944Syongari	EBDMA_C_WRITE(sc, EBDMA_DCSR, csr | EBDCSR_TC);
864136944Syongari
865136944Syongari	ch = NULL;
866136944Syongari	if (csr & EBDCSR_TC) {
867136944Syongari		u_long nextaddr, saddr;
868136944Syongari		u_int32_t togo;
869136944Syongari
870136944Syongari		ch = &sc->sc_rch;
871136944Syongari		togo = ch->togo;
872136944Syongari		saddr = sndbuf_getbufaddr(ch->buffer);
873136944Syongari		nextaddr = ch->nextaddr + togo;
874136944Syongari		if (nextaddr >= saddr + sndbuf_getblksz(ch->buffer))
875136944Syongari			nextaddr = saddr;
876136944Syongari		/*
877136944Syongari		 * EBDMA_DCNT is loaded automatically
878136944Syongari		 * EBDMA_C_WRITE(sc, EBDMA_DCNT, togo);
879136944Syongari		 */
880136944Syongari		EBDMA_C_WRITE(sc, EBDMA_DADDR, nextaddr);
881136944Syongari		ch->nextaddr = nextaddr;
882136944Syongari	}
883136944Syongari	CS4231_UNLOCK(sc);
884136944Syongari	if (ch)
885136944Syongari		chn_intr(ch->channel);
886136944Syongari}
887136944Syongari
888136944Syongaristatic const struct mix_table cs4231_mix_table[SOUND_MIXER_NRDEVICES][2] = {
889136944Syongari	[SOUND_MIXER_PCM] = {
890136944Syongari		{ CS_LEFT_OUTPUT_CONTROL,	6, OUTPUT_MUTE, 0, 1, 1, 0 },
891136944Syongari		{ CS_RIGHT_OUTPUT_CONTROL,	6, OUTPUT_MUTE, 0, 1, 1, 0 }
892136944Syongari	},
893136944Syongari	[SOUND_MIXER_SPEAKER] = {
894136944Syongari		{ CS_MONO_IO_CONTROL,		4, MONO_OUTPUT_MUTE, 0, 1, 1, 0 },
895136944Syongari		{ CS_REG_NONE,			0, 0, 0, 0, 1, 0 }
896136944Syongari	},
897136944Syongari	[SOUND_MIXER_LINE] = {
898136944Syongari		{ CS_LEFT_LINE_CONTROL,		5, LINE_INPUT_MUTE, 0, 1, 1, 1 },
899136944Syongari		{ CS_RIGHT_LINE_CONTROL,	5, LINE_INPUT_MUTE, 0, 1, 1, 1 }
900136944Syongari	},
901136944Syongari	/*
902136944Syongari	 * AUX1 : removed intentionally since it generates noises
903136944Syongari	 * AUX2 : Ultra1/Ultra2 has no internal CD-ROM audio in
904136944Syongari	 */
905136944Syongari	[SOUND_MIXER_CD] = {
906136944Syongari		{ CS_LEFT_AUX2_CONTROL,		5, LINE_INPUT_MUTE, 0, 1, 1, 1 },
907136944Syongari		{ CS_RIGHT_AUX2_CONTROL,	5, LINE_INPUT_MUTE, 0, 1, 1, 1 }
908136944Syongari	},
909136944Syongari	[SOUND_MIXER_MIC] = {
910136944Syongari		{ CS_LEFT_INPUT_CONTROL,	4, 0, 0, 0, 1, 1 },
911136944Syongari		{ CS_RIGHT_INPUT_CONTROL,	4, 0, 0, 0, 1, 1 }
912136944Syongari	},
913136944Syongari	[SOUND_MIXER_IGAIN] = {
914136944Syongari		{ CS_LEFT_INPUT_CONTROL,	4, 0, 0, 1, 0 },
915136944Syongari		{ CS_RIGHT_INPUT_CONTROL,	4, 0, 0, 1, 0 }
916136944Syongari	}
917136944Syongari};
918136944Syongari
919136944Syongaristatic int
920136944Syongarics4231_mixer_init(struct snd_mixer *m)
921136944Syongari{
922136944Syongari	u_int32_t v;
923136944Syongari	int i;
924136944Syongari
925136944Syongari	v = 0;
926136944Syongari	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
927136944Syongari		if (cs4231_mix_table[i][0].avail != 0)
928136944Syongari			v |= (1 << i);
929136944Syongari	mix_setdevs(m, v);
930136944Syongari	v = 0;
931136944Syongari	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
932136944Syongari		if (cs4231_mix_table[i][0].recdev != 0)
933136944Syongari			v |= (1 << i);
934136944Syongari	mix_setrecdevs(m, v);
935136944Syongari	return (0);
936136944Syongari}
937136944Syongari
938136944Syongaristatic void
939136944Syongarics4231_mixer_set_value(struct cs4231_softc *sc,  const struct mix_table *mt,
940136944Syongari    u_int8_t v)
941136944Syongari{
942136944Syongari	u_int8_t mask, reg;
943136944Syongari	u_int8_t old, shift, val;
944136944Syongari
945136944Syongari	if (mt->avail == 0 || mt->reg == CS_REG_NONE)
946136944Syongari		return;
947136944Syongari	reg = mt->reg;
948136944Syongari	if (mt->neg != 0)
949136944Syongari		val = 100 - v;
950136944Syongari	else
951136944Syongari		val = v;
952136944Syongari	mask = (1 << mt->bits) - 1;
953136944Syongari	val = ((val * mask) + 50) / 100;
954136944Syongari	shift = mt->shift;
955136944Syongari	val <<= shift;
956136944Syongari	if (v == 0)
957136944Syongari		val |= mt->mute;
958136944Syongari	old = cs4231_read(sc, reg);
959136944Syongari	old &= ~(mt->mute | (mask << shift));
960136944Syongari	val |= old;
961136944Syongari	if (reg == CS_LEFT_INPUT_CONTROL || reg == CS_RIGHT_INPUT_CONTROL) {
962136944Syongari		if ((val & (mask << shift)) != 0)
963136944Syongari			val |= ADC_INPUT_GAIN_ENABLE;
964136944Syongari		else
965136944Syongari			val &= ~ADC_INPUT_GAIN_ENABLE;
966136944Syongari	}
967136944Syongari	cs4231_write(sc, reg, val);
968136944Syongari}
969136944Syongari
970136944Syongaristatic int
971136944Syongarics4231_mixer_set(struct snd_mixer *m, u_int32_t dev, u_int32_t left,
972136944Syongari    u_int32_t right)
973136944Syongari{
974136944Syongari	struct cs4231_softc *sc;
975136944Syongari
976136944Syongari	sc = mix_getdevinfo(m);
977136944Syongari	CS4231_LOCK(sc);
978136944Syongari	cs4231_mixer_set_value(sc, &cs4231_mix_table[dev][0], left);
979136944Syongari	cs4231_mixer_set_value(sc, &cs4231_mix_table[dev][1], right);
980136944Syongari	CS4231_UNLOCK(sc);
981136944Syongari
982136944Syongari	return (left | (right << 8));
983136944Syongari}
984136944Syongari
985136944Syongaristatic int
986136944Syongarics4231_mixer_setrecsrc(struct snd_mixer *m, u_int32_t src)
987136944Syongari{
988136944Syongari	struct cs4231_softc *sc;
989136944Syongari	u_int8_t	v;
990136944Syongari
991136944Syongari	sc = mix_getdevinfo(m);
992136944Syongari	switch (src) {
993136944Syongari	case SOUND_MASK_LINE:
994136944Syongari		v = CS_IN_LINE;
995136944Syongari		break;
996136944Syongari
997136944Syongari	case SOUND_MASK_CD:
998136944Syongari		v = CS_IN_DAC;
999136944Syongari		break;
1000136944Syongari
1001136944Syongari	case SOUND_MASK_MIC:
1002136944Syongari	default:
1003136944Syongari		v = CS_IN_MIC;
1004136944Syongari		src = SOUND_MASK_MIC;
1005136944Syongari		break;
1006136944Syongari	}
1007136944Syongari	CS4231_LOCK(sc);
1008136944Syongari	cs4231_write(sc, CS_LEFT_INPUT_CONTROL,
1009136944Syongari	    (cs4231_read(sc, CS_LEFT_INPUT_CONTROL) & CS_IN_MASK) | v);
1010136944Syongari	cs4231_write(sc, CS_RIGHT_INPUT_CONTROL,
1011136944Syongari	    (cs4231_read(sc, CS_RIGHT_INPUT_CONTROL) & CS_IN_MASK) | v);
1012136944Syongari	CS4231_UNLOCK(sc);
1013136944Syongari
1014136944Syongari	return (src);
1015136944Syongari}
1016136944Syongari
1017136944Syongaristatic void *
1018136944Syongarics4231_chan_init(kobj_t obj, void *dev, struct snd_dbuf *b,
1019136944Syongari    struct pcm_channel *c, int dir)
1020136944Syongari{
1021136944Syongari	struct cs4231_softc *sc;
1022136944Syongari	struct cs4231_channel *ch;
1023136944Syongari	bus_dma_tag_t dmat;
1024136944Syongari
1025136944Syongari	sc = dev;
1026136944Syongari	ch = (dir == PCMDIR_PLAY) ? &sc->sc_pch : &sc->sc_rch;
1027136944Syongari	ch->parent = sc;
1028136944Syongari	ch->channel = c;
1029136944Syongari	ch->dir = dir;
1030136944Syongari	ch->buffer = b;
1031136944Syongari	if ((sc->sc_flags & CS4231_SBUS) != 0)
1032136944Syongari		dmat = sc->sc_dmat[0];
1033136944Syongari	else {
1034136944Syongari		if (dir == PCMDIR_PLAY)
1035136944Syongari			dmat = sc->sc_dmat[1];
1036136944Syongari		else
1037136944Syongari			dmat = sc->sc_dmat[0];
1038136944Syongari	}
1039168847Sariff	if (sndbuf_alloc(ch->buffer, dmat, 0, sc->sc_bufsz) != 0)
1040136944Syongari		return (NULL);
1041136944Syongari	DPRINTF(("%s channel addr: 0x%lx\n", dir == PCMDIR_PLAY ? "playback" :
1042136944Syongari	    "capture", sndbuf_getbufaddr(ch->buffer)));
1043136944Syongari
1044136944Syongari	return (ch);
1045136944Syongari}
1046136944Syongari
1047136944Syongaristatic int
1048136944Syongarics4231_chan_setformat(kobj_t obj, void *data, u_int32_t format)
1049136944Syongari{
1050136944Syongari	struct cs4231_softc *sc;
1051136944Syongari	struct cs4231_channel *ch;
1052136944Syongari	u_int32_t encoding;
1053136944Syongari	u_int8_t fs, v;
1054136944Syongari
1055136944Syongari	ch = data;
1056136944Syongari	sc = ch->parent;
1057136944Syongari
1058136944Syongari	CS4231_LOCK(sc);
1059136944Syongari	if (ch->format == format) {
1060136944Syongari		CS4231_UNLOCK(sc);
1061136944Syongari		return (0);
1062136944Syongari	}
1063136944Syongari
1064193640Sariff	encoding = AFMT_ENCODING(format);
1065136944Syongari	fs = 0;
1066136944Syongari	switch (encoding) {
1067136944Syongari	case AFMT_U8:
1068136944Syongari		fs = CS_AFMT_U8;
1069136944Syongari		break;
1070136944Syongari	case AFMT_MU_LAW:
1071136944Syongari		fs = CS_AFMT_MU_LAW;
1072136944Syongari		break;
1073136944Syongari	case AFMT_S16_LE:
1074136944Syongari		fs = CS_AFMT_S16_LE;
1075136944Syongari		break;
1076136944Syongari	case AFMT_A_LAW:
1077136944Syongari		fs = CS_AFMT_A_LAW;
1078136944Syongari		break;
1079136944Syongari	case AFMT_IMA_ADPCM:
1080136944Syongari		fs = CS_AFMT_IMA_ADPCM;
1081136944Syongari		break;
1082136944Syongari	case AFMT_S16_BE:
1083136944Syongari		fs = CS_AFMT_S16_BE;
1084136944Syongari		break;
1085136944Syongari	default:
1086136944Syongari		fs = CS_AFMT_U8;
1087136944Syongari		format = AFMT_U8;
1088136944Syongari		break;
1089136944Syongari	}
1090136944Syongari
1091193640Sariff	if (AFMT_CHANNEL(format) > 1)
1092136944Syongari		fs |= CS_AFMT_STEREO;
1093136944Syongari
1094136944Syongari	DPRINTF(("FORMAT: %s : 0x%x\n", ch->dir == PCMDIR_PLAY ? "playback" :
1095136944Syongari	    "capture", format));
1096136944Syongari	v = cs4231_read(sc, CS_CLOCK_DATA_FORMAT);
1097136944Syongari	v &= CS_CLOCK_DATA_FORMAT_MASK;
1098136944Syongari	fs |= v;
1099136944Syongari	cs4231_chan_fs(sc, ch->dir, fs);
1100136944Syongari	ch->format = format;
1101136944Syongari	CS4231_UNLOCK(sc);
1102136944Syongari
1103136944Syongari	return (0);
1104136944Syongari}
1105136944Syongari
1106136944Syongaristatic int
1107136944Syongarics4231_chan_setspeed(kobj_t obj, void *data, u_int32_t speed)
1108136944Syongari{
1109136944Syongari	typedef struct {
1110136944Syongari		u_int32_t speed;
1111136944Syongari		u_int8_t bits;
1112136944Syongari	} speed_struct;
1113136944Syongari
1114136944Syongari	const static speed_struct speed_table[] = {
1115136944Syongari		{5510,  (0 << 1) | CLOCK_XTAL2},
1116136944Syongari		{5510,  (0 << 1) | CLOCK_XTAL2},
1117136944Syongari		{6620,  (7 << 1) | CLOCK_XTAL2},
1118136944Syongari		{8000,  (0 << 1) | CLOCK_XTAL1},
1119136944Syongari		{9600,  (7 << 1) | CLOCK_XTAL1},
1120136944Syongari		{11025, (1 << 1) | CLOCK_XTAL2},
1121136944Syongari		{16000, (1 << 1) | CLOCK_XTAL1},
1122136944Syongari		{18900, (2 << 1) | CLOCK_XTAL2},
1123136944Syongari		{22050, (3 << 1) | CLOCK_XTAL2},
1124136944Syongari		{27420, (2 << 1) | CLOCK_XTAL1},
1125136944Syongari		{32000, (3 << 1) | CLOCK_XTAL1},
1126136944Syongari		{33075, (6 << 1) | CLOCK_XTAL2},
1127136944Syongari		{33075, (4 << 1) | CLOCK_XTAL2},
1128136944Syongari		{44100, (5 << 1) | CLOCK_XTAL2},
1129136944Syongari		{48000, (6 << 1) | CLOCK_XTAL1},
1130136944Syongari	};
1131136944Syongari
1132136944Syongari	struct cs4231_softc *sc;
1133136944Syongari	struct cs4231_channel *ch;
1134136944Syongari	int i, n, sel;
1135136944Syongari	u_int8_t fs;
1136136944Syongari
1137136944Syongari	ch = data;
1138136944Syongari	sc = ch->parent;
1139136944Syongari	CS4231_LOCK(sc);
1140136944Syongari	if (ch->speed == speed) {
1141136944Syongari		CS4231_UNLOCK(sc);
1142136944Syongari		return (speed);
1143136944Syongari	}
1144136944Syongari	n = sizeof(speed_table) / sizeof(speed_struct);
1145136944Syongari
1146136944Syongari	for (i = 1, sel =0; i < n - 1; i++)
1147136944Syongari		if (abs(speed - speed_table[i].speed) <
1148136944Syongari		    abs(speed - speed_table[sel].speed))
1149136944Syongari			sel = i;
1150136944Syongari	DPRINTF(("SPEED: %s : %dHz -> %dHz\n", ch->dir == PCMDIR_PLAY ?
1151136944Syongari	    "playback" : "capture", speed, speed_table[sel].speed));
1152136944Syongari	speed = speed_table[sel].speed;
1153136944Syongari
1154136944Syongari	fs = cs4231_read(sc, CS_CLOCK_DATA_FORMAT);
1155136944Syongari	fs &= ~CS_CLOCK_DATA_FORMAT_MASK;
1156136944Syongari	fs |= speed_table[sel].bits;
1157136944Syongari	cs4231_chan_fs(sc, ch->dir, fs);
1158136944Syongari	ch->speed = speed;
1159136944Syongari	CS4231_UNLOCK(sc);
1160136944Syongari
1161136944Syongari	return (speed);
1162136944Syongari}
1163136944Syongari
1164136944Syongaristatic void
1165136944Syongarics4231_chan_fs(struct cs4231_softc *sc, int dir, u_int8_t fs)
1166136944Syongari{
1167136944Syongari	int i, doreset;
1168136944Syongari#ifdef CS4231_AUTO_CALIBRATION
1169136944Syongari	u_int8_t v;
1170136944Syongari#endif
1171136944Syongari
1172136944Syongari	CS4231_LOCK_ASSERT(sc);
1173136944Syongari
1174136944Syongari	/* set autocalibration */
1175136944Syongari	doreset = 0;
1176136944Syongari#ifdef CS4231_AUTO_CALIBRATION
1177136944Syongari	v = cs4231_read(sc, CS_INTERFACE_CONFIG) | AUTO_CAL_ENABLE;
1178136944Syongari	CS_WRITE(sc, CS4231_IADDR, MODE_CHANGE_ENABLE);
1179136944Syongari	CS_WRITE(sc, CS4231_IADDR, MODE_CHANGE_ENABLE | CS_INTERFACE_CONFIG);
1180136944Syongari	CS_WRITE(sc, CS4231_IDATA, v);
1181136944Syongari#endif
1182136944Syongari
1183136944Syongari	/*
1184136944Syongari	 * We always need to write CS_CLOCK_DATA_FORMAT register since
1185136944Syongari	 * the clock frequency is shared with playback/capture.
1186136944Syongari	 */
1187136944Syongari	CS_WRITE(sc, CS4231_IADDR, MODE_CHANGE_ENABLE | CS_CLOCK_DATA_FORMAT);
1188136944Syongari	CS_WRITE(sc, CS4231_IDATA, fs);
1189136944Syongari	CS_READ(sc, CS4231_IDATA);
1190136944Syongari	CS_READ(sc, CS4231_IDATA);
1191136944Syongari	for (i = CS_TIMEOUT;
1192136944Syongari	    i && CS_READ(sc, CS4231_IADDR) == CS_IN_INIT; i--)
1193136944Syongari		DELAY(10);
1194136944Syongari	if (i == 0) {
1195136944Syongari		device_printf(sc->sc_dev, "timeout setting playback speed\n");
1196136944Syongari		doreset++;
1197136944Syongari	}
1198136944Syongari
1199136944Syongari	/*
1200136944Syongari	 * capture channel
1201136944Syongari	 * cs4231 doesn't allow seperate fs setup for playback/capture.
1202136944Syongari	 * I believe this will break full-duplex operation.
1203136944Syongari	 */
1204136944Syongari	if (dir == PCMDIR_REC) {
1205136944Syongari		CS_WRITE(sc, CS4231_IADDR, MODE_CHANGE_ENABLE | CS_REC_FORMAT);
1206136944Syongari		CS_WRITE(sc, CS4231_IDATA, fs);
1207136944Syongari		CS_READ(sc, CS4231_IDATA);
1208136944Syongari		CS_READ(sc, CS4231_IDATA);
1209136944Syongari		for (i = CS_TIMEOUT;
1210136944Syongari		    i && CS_READ(sc, CS4231_IADDR) == CS_IN_INIT; i--)
1211136944Syongari			DELAY(10);
1212136944Syongari		if (i == 0) {
1213136944Syongari			device_printf(sc->sc_dev,
1214136944Syongari			    "timeout setting capture format\n");
1215136944Syongari			doreset++;
1216136944Syongari		}
1217136944Syongari	}
1218136944Syongari
1219136944Syongari	CS_WRITE(sc, CS4231_IADDR, 0);
1220136944Syongari	for (i = CS_TIMEOUT;
1221136944Syongari	    i && CS_READ(sc, CS4231_IADDR) == CS_IN_INIT; i--)
1222136944Syongari		DELAY(10);
1223136944Syongari	if (i == 0) {
1224136944Syongari		device_printf(sc->sc_dev, "timeout waiting for !MCE\n");
1225136944Syongari		doreset++;
1226136944Syongari	}
1227136944Syongari
1228136944Syongari#ifdef CS4231_AUTO_CALIBRATION
1229136944Syongari	CS_WRITE(sc, CS4231_IADDR, CS_TEST_AND_INIT);
1230136944Syongari	for (i = CS_TIMEOUT;
1231136944Syongari	    i && CS_READ(sc, CS4231_IDATA) & AUTO_CAL_IN_PROG; i--)
1232136944Syongari		DELAY(10);
1233136944Syongari	if (i == 0) {
1234136944Syongari		device_printf(sc->sc_dev,
1235136944Syongari		    "timeout waiting for autocalibration\n");
1236136944Syongari		doreset++;
1237136944Syongari	}
1238136944Syongari#endif
1239136944Syongari	if (doreset) {
1240136944Syongari		/*
1241136944Syongari		 * Maybe the last resort to avoid a dreadful message like
1242136944Syongari		 * "pcm0:play:0: play interrupt timeout, channel dead" would
1243136944Syongari		 * be hardware reset.
1244136944Syongari		 */
1245136944Syongari		device_printf(sc->sc_dev, "trying to hardware reset\n");
1246136944Syongari		cs4231_disable(sc);
1247136944Syongari		cs4231_enable(sc, CODEC_COLD_RESET);
1248136944Syongari		CS4231_UNLOCK(sc); /* XXX */
1249136944Syongari		if (mixer_reinit(sc->sc_dev) != 0)
1250136944Syongari			device_printf(sc->sc_dev,
1251136944Syongari			    "unable to reinitialize the mixer\n");
1252136944Syongari		CS4231_LOCK(sc);
1253136944Syongari	}
1254136944Syongari}
1255136944Syongari
1256136944Syongaristatic int
1257136944Syongarics4231_chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
1258136944Syongari{
1259136944Syongari	struct cs4231_softc *sc;
1260136944Syongari	struct cs4231_channel *ch;
1261136944Syongari	int nblks, error;
1262136944Syongari
1263136944Syongari	ch = data;
1264136944Syongari	sc = ch->parent;
1265136944Syongari
1266136944Syongari	if (blocksize > CS4231_MAX_BLK_SZ)
1267136944Syongari		blocksize = CS4231_MAX_BLK_SZ;
1268136944Syongari	nblks = sc->sc_bufsz / blocksize;
1269136944Syongari	error = sndbuf_resize(ch->buffer, nblks, blocksize);
1270136944Syongari	if (error != 0)
1271136944Syongari		device_printf(sc->sc_dev,
1272136944Syongari		    "unable to block size, blksz = %d, error = %d\n",
1273136944Syongari		    blocksize, error);
1274136944Syongari
1275136944Syongari        return (blocksize);
1276136944Syongari}
1277136944Syongari
1278136944Syongaristatic int
1279136944Syongarics4231_chan_trigger(kobj_t obj, void *data, int go)
1280136944Syongari{
1281136944Syongari	struct cs4231_channel *ch;
1282136944Syongari
1283136944Syongari	ch = data;
1284136944Syongari	switch (go) {
1285136944Syongari	case PCMTRIG_EMLDMAWR:
1286136944Syongari	case PCMTRIG_EMLDMARD:
1287136944Syongari		break;
1288136944Syongari	case PCMTRIG_START:
1289136944Syongari		cs4231_trigger(ch);
1290136944Syongari		break;
1291136944Syongari	case PCMTRIG_ABORT:
1292136944Syongari	case PCMTRIG_STOP:
1293136944Syongari		cs4231_halt(ch);
1294136944Syongari		break;
1295136944Syongari	default:
1296136944Syongari		break;
1297136944Syongari	}
1298136944Syongari
1299136944Syongari	return (0);
1300136944Syongari}
1301136944Syongari
1302136944Syongaristatic int
1303136944Syongarics4231_chan_getptr(kobj_t obj, void *data)
1304136944Syongari{
1305136944Syongari	struct cs4231_softc *sc;
1306136944Syongari	struct cs4231_channel *ch;
1307136944Syongari	u_int32_t cur;
1308136944Syongari	int ptr, sz;
1309136944Syongari
1310136944Syongari	ch = data;
1311136944Syongari	sc = ch->parent;
1312136944Syongari
1313136944Syongari	CS4231_LOCK(sc);
1314136944Syongari	if ((sc->sc_flags & CS4231_SBUS) != 0)
1315136944Syongari		cur = (ch->dir == PCMDIR_PLAY) ? APC_READ(sc, APC_PVA) :
1316136944Syongari		    APC_READ(sc, APC_CVA);
1317136944Syongari	else
1318136944Syongari		cur = (ch->dir == PCMDIR_PLAY) ? EBDMA_P_READ(sc, EBDMA_DADDR) :
1319136944Syongari			EBDMA_C_READ(sc, EBDMA_DADDR);
1320136944Syongari	sz = sndbuf_getsize(ch->buffer);
1321136944Syongari	ptr = cur - sndbuf_getbufaddr(ch->buffer) + sz;
1322136944Syongari	CS4231_UNLOCK(sc);
1323136944Syongari
1324136944Syongari	ptr %= sz;
1325136944Syongari	return (ptr);
1326136944Syongari}
1327136944Syongari
1328136944Syongaristatic struct pcmchan_caps *
1329136944Syongarics4231_chan_getcaps(kobj_t obj, void *data)
1330136944Syongari{
1331136944Syongari
1332136944Syongari	return (&cs4231_caps);
1333136944Syongari}
1334136944Syongari
1335136944Syongaristatic void
1336136944Syongarics4231_trigger(struct cs4231_channel *ch)
1337136944Syongari{
1338136944Syongari	struct cs4231_softc *sc;
1339136944Syongari
1340136944Syongari	sc = ch->parent;
1341136944Syongari	if ((sc->sc_flags & CS4231_SBUS) != 0)
1342136944Syongari		cs4231_apcdma_trigger(sc, ch);
1343136944Syongari	else
1344136944Syongari		cs4231_ebdma_trigger(sc, ch);
1345136944Syongari}
1346136944Syongari
1347136944Syongaristatic void
1348136944Syongarics4231_apcdma_trigger(struct cs4231_softc *sc, struct cs4231_channel *ch)
1349136944Syongari{
1350136944Syongari	u_int32_t csr, togo;
1351136944Syongari	u_int32_t nextaddr;
1352136944Syongari
1353136944Syongari	CS4231_LOCK(sc);
1354136944Syongari	if (ch->locked) {
1355136944Syongari		device_printf(sc->sc_dev, "%s channel already triggered\n",
1356136944Syongari		    ch->dir == PCMDIR_PLAY ? "playback" : "capture");
1357136944Syongari		CS4231_UNLOCK(sc);
1358136944Syongari		return;
1359136944Syongari	}
1360136944Syongari
1361136944Syongari	nextaddr = sndbuf_getbufaddr(ch->buffer);
1362136944Syongari	togo = sndbuf_getsize(ch->buffer) / 2;
1363136944Syongari	if (togo > CS4231_MAX_APC_DMA_SZ)
1364136944Syongari		togo = CS4231_MAX_APC_DMA_SZ;
1365136944Syongari	ch->togo = togo;
1366136944Syongari	if (ch->dir == PCMDIR_PLAY) {
1367136944Syongari		DPRINTF(("TRG: PNVA = 0x%x, togo = 0x%x\n", nextaddr, togo));
1368136944Syongari
1369136944Syongari		cs4231_read(sc, CS_TEST_AND_INIT); /* clear pending error */
1370136944Syongari		csr = APC_READ(sc, APC_CSR);
1371136944Syongari		APC_WRITE(sc, APC_PNVA, nextaddr);
1372136944Syongari		APC_WRITE(sc, APC_PNC, togo);
1373136944Syongari
1374136944Syongari		if ((csr & APC_CSR_PDMA_GO) == 0 ||
1375136944Syongari		    (csr & APC_CSR_PPAUSE) != 0) {
1376136944Syongari			APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) &
1377136944Syongari			    ~(APC_CSR_PIE | APC_CSR_PPAUSE));
1378136944Syongari			APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) |
1379136944Syongari			    APC_CSR_GIE | APC_CSR_PIE | APC_CSR_EIE |
1380136944Syongari			    APC_CSR_EI | APC_CSR_PMIE | APC_CSR_PDMA_GO);
1381136944Syongari			cs4231_write(sc, CS_INTERFACE_CONFIG,
1382136944Syongari			    cs4231_read(sc, CS_INTERFACE_CONFIG) |
1383136944Syongari			    PLAYBACK_ENABLE);
1384136944Syongari		}
1385136944Syongari		/* load next address */
1386136944Syongari		if (APC_READ(sc, APC_CSR) & APC_CSR_PD) {
1387136944Syongari			nextaddr += togo;
1388136944Syongari			APC_WRITE(sc, APC_PNVA, nextaddr);
1389136944Syongari			APC_WRITE(sc, APC_PNC, togo);
1390136944Syongari		}
1391136944Syongari	} else {
1392136944Syongari		DPRINTF(("TRG: CNVA = 0x%x, togo = 0x%x\n", nextaddr, togo));
1393136944Syongari
1394136944Syongari		cs4231_read(sc, CS_TEST_AND_INIT); /* clear pending error */
1395136944Syongari		APC_WRITE(sc, APC_CNVA, nextaddr);
1396136944Syongari		APC_WRITE(sc, APC_CNC, togo);
1397136944Syongari		csr = APC_READ(sc, APC_CSR);
1398136944Syongari		if ((csr & APC_CSR_CDMA_GO) == 0 ||
1399136944Syongari		    (csr & APC_CSR_CPAUSE) != 0) {
1400136944Syongari			csr &= APC_CSR_CPAUSE;
1401136944Syongari			csr |= APC_CSR_GIE | APC_CSR_CMIE | APC_CSR_CIE |
1402136944Syongari			    APC_CSR_EI | APC_CSR_CDMA_GO;
1403136944Syongari			APC_WRITE(sc, APC_CSR, csr);
1404136944Syongari			cs4231_write(sc, CS_INTERFACE_CONFIG,
1405136944Syongari			    cs4231_read(sc, CS_INTERFACE_CONFIG) |
1406136944Syongari			    CAPTURE_ENABLE);
1407136944Syongari		}
1408136944Syongari		/* load next address */
1409136944Syongari		if (APC_READ(sc, APC_CSR) & APC_CSR_CD) {
1410136944Syongari			nextaddr += togo;
1411136944Syongari			APC_WRITE(sc, APC_CNVA, nextaddr);
1412136944Syongari			APC_WRITE(sc, APC_CNC, togo);
1413136944Syongari		}
1414136944Syongari	}
1415136944Syongari	ch->nextaddr = nextaddr;
1416136944Syongari	ch->locked = 1;
1417136944Syongari	CS4231_UNLOCK(sc);
1418136944Syongari}
1419136944Syongari
1420136944Syongaristatic void
1421136944Syongarics4231_ebdma_trigger(struct cs4231_softc *sc, struct cs4231_channel *ch)
1422136944Syongari{
1423136944Syongari	u_int32_t csr, togo;
1424136944Syongari	u_int32_t nextaddr;
1425136944Syongari
1426136944Syongari	CS4231_LOCK(sc);
1427136944Syongari	if (ch->locked) {
1428136944Syongari		device_printf(sc->sc_dev, "%s channel already triggered\n",
1429136944Syongari		    ch->dir == PCMDIR_PLAY ? "playback" : "capture");
1430136944Syongari		CS4231_UNLOCK(sc);
1431136944Syongari		return;
1432136944Syongari	}
1433136944Syongari
1434136944Syongari	nextaddr = sndbuf_getbufaddr(ch->buffer);
1435136944Syongari	togo = sndbuf_getsize(ch->buffer) / 2;
1436136944Syongari	if (togo % 64 == 0)
1437136944Syongari		sc->sc_burst = EBDCSR_BURST_16;
1438136944Syongari	else if (togo % 32 == 0)
1439136944Syongari		sc->sc_burst = EBDCSR_BURST_8;
1440136944Syongari	else if (togo % 16 == 0)
1441136944Syongari		sc->sc_burst = EBDCSR_BURST_4;
1442136944Syongari	else
1443136944Syongari		sc->sc_burst = EBDCSR_BURST_1;
1444136944Syongari	ch->togo = togo;
1445136944Syongari	DPRINTF(("TRG: DNAR = 0x%x, togo = 0x%x\n", nextaddr, togo));
1446136944Syongari	if (ch->dir == PCMDIR_PLAY) {
1447136944Syongari		cs4231_read(sc, CS_TEST_AND_INIT); /* clear pending error */
1448136944Syongari		csr = EBDMA_P_READ(sc, EBDMA_DCSR);
1449136944Syongari
1450136944Syongari		if (csr & EBDCSR_DMAEN) {
1451136944Syongari			EBDMA_P_WRITE(sc, EBDMA_DCNT, togo);
1452136944Syongari			EBDMA_P_WRITE(sc, EBDMA_DADDR, nextaddr);
1453136944Syongari		} else {
1454136944Syongari			EBDMA_P_WRITE(sc, EBDMA_DCSR, EBDCSR_RESET);
1455136944Syongari			EBDMA_P_WRITE(sc, EBDMA_DCSR, sc->sc_burst);
1456136944Syongari			EBDMA_P_WRITE(sc, EBDMA_DCNT, togo);
1457136944Syongari			EBDMA_P_WRITE(sc, EBDMA_DADDR, nextaddr);
1458136944Syongari
1459136944Syongari			EBDMA_P_WRITE(sc, EBDMA_DCSR, sc->sc_burst |
1460136944Syongari			    EBDCSR_DMAEN | EBDCSR_INTEN | EBDCSR_CNTEN |
1461136944Syongari			    EBDCSR_NEXTEN);
1462136944Syongari			cs4231_write(sc, CS_INTERFACE_CONFIG,
1463136944Syongari			    cs4231_read(sc, CS_INTERFACE_CONFIG) |
1464136944Syongari			    PLAYBACK_ENABLE);
1465136944Syongari		}
1466136944Syongari		/* load next address */
1467136944Syongari		if (EBDMA_P_READ(sc, EBDMA_DCSR) & EBDCSR_A_LOADED) {
1468136944Syongari			nextaddr += togo;
1469136944Syongari			EBDMA_P_WRITE(sc, EBDMA_DCNT, togo);
1470136944Syongari			EBDMA_P_WRITE(sc, EBDMA_DADDR, nextaddr);
1471136944Syongari		}
1472136944Syongari	} else {
1473136944Syongari		cs4231_read(sc, CS_TEST_AND_INIT); /* clear pending error */
1474136944Syongari		csr = EBDMA_C_READ(sc, EBDMA_DCSR);
1475136944Syongari
1476136944Syongari		if (csr & EBDCSR_DMAEN) {
1477136944Syongari			EBDMA_C_WRITE(sc, EBDMA_DCNT, togo);
1478136944Syongari			EBDMA_C_WRITE(sc, EBDMA_DADDR, nextaddr);
1479136944Syongari		} else {
1480136944Syongari			EBDMA_C_WRITE(sc, EBDMA_DCSR, EBDCSR_RESET);
1481136944Syongari			EBDMA_C_WRITE(sc, EBDMA_DCSR, sc->sc_burst);
1482136944Syongari			EBDMA_C_WRITE(sc, EBDMA_DCNT, togo);
1483136944Syongari			EBDMA_C_WRITE(sc, EBDMA_DADDR, nextaddr);
1484136944Syongari
1485136944Syongari			EBDMA_C_WRITE(sc, EBDMA_DCSR, sc->sc_burst |
1486136944Syongari			    EBDCSR_WRITE | EBDCSR_DMAEN | EBDCSR_INTEN |
1487136944Syongari			    EBDCSR_CNTEN | EBDCSR_NEXTEN);
1488136944Syongari			cs4231_write(sc, CS_INTERFACE_CONFIG,
1489136944Syongari			    cs4231_read(sc, CS_INTERFACE_CONFIG) |
1490136944Syongari			    CAPTURE_ENABLE);
1491136944Syongari		}
1492136944Syongari		/* load next address */
1493136944Syongari		if (EBDMA_C_READ(sc, EBDMA_DCSR) & EBDCSR_A_LOADED) {
1494136944Syongari			nextaddr += togo;
1495136944Syongari			EBDMA_C_WRITE(sc, EBDMA_DCNT, togo);
1496136944Syongari			EBDMA_C_WRITE(sc, EBDMA_DADDR, nextaddr);
1497136944Syongari		}
1498136944Syongari	}
1499136944Syongari	ch->nextaddr = nextaddr;
1500136944Syongari	ch->locked = 1;
1501136944Syongari	CS4231_UNLOCK(sc);
1502136944Syongari}
1503136944Syongari
1504136944Syongaristatic void
1505136944Syongarics4231_halt(struct cs4231_channel *ch)
1506136944Syongari{
1507136944Syongari	struct cs4231_softc *sc;
1508136944Syongari	u_int8_t status;
1509136944Syongari	int i;
1510136944Syongari
1511136944Syongari	sc = ch->parent;
1512136944Syongari	CS4231_LOCK(sc);
1513136944Syongari	if (ch->locked == 0) {
1514136944Syongari		CS4231_UNLOCK(sc);
1515136944Syongari		return;
1516136944Syongari	}
1517136944Syongari
1518136944Syongari	if (ch->dir == PCMDIR_PLAY ) {
1519136944Syongari		if ((sc->sc_flags & CS4231_SBUS) != 0) {
1520136944Syongari			/* XXX Kills some capture bits */
1521136944Syongari			APC_WRITE(sc, APC_CSR, APC_READ(sc, APC_CSR) &
1522136944Syongari			    ~(APC_CSR_EI | APC_CSR_GIE | APC_CSR_PIE |
1523136944Syongari			    APC_CSR_EIE | APC_CSR_PDMA_GO | APC_CSR_PMIE));
1524136944Syongari		} else {
1525136944Syongari			EBDMA_P_WRITE(sc, EBDMA_DCSR,
1526136944Syongari			    EBDMA_P_READ(sc, EBDMA_DCSR) & ~EBDCSR_DMAEN);
1527136944Syongari		}
1528136944Syongari		/* Waiting for playback FIFO to empty */
1529136944Syongari		status = cs4231_read(sc, CS_TEST_AND_INIT);
1530136944Syongari		for (i = CS_TIMEOUT;
1531136944Syongari		    i && (status & PLAYBACK_UNDERRUN) == 0; i--) {
1532136944Syongari			DELAY(5);
1533136944Syongari			status = cs4231_read(sc, CS_TEST_AND_INIT);
1534136944Syongari		}
1535136944Syongari		if (i == 0)
1536136944Syongari			device_printf(sc->sc_dev, "timeout waiting for "
1537136944Syongari			    "playback FIFO drain\n");
1538136944Syongari		cs4231_write(sc, CS_INTERFACE_CONFIG,
1539136944Syongari		    cs4231_read(sc, CS_INTERFACE_CONFIG) & (~PLAYBACK_ENABLE));
1540136944Syongari	} else {
1541136944Syongari		if ((sc->sc_flags & CS4231_SBUS) != 0) {
1542136944Syongari			/* XXX Kills some playback bits */
1543136944Syongari			APC_WRITE(sc, APC_CSR, APC_CSR_CAPTURE_PAUSE);
1544136944Syongari		} else {
1545136944Syongari			EBDMA_C_WRITE(sc, EBDMA_DCSR,
1546136944Syongari			    EBDMA_C_READ(sc, EBDMA_DCSR) & ~EBDCSR_DMAEN);
1547136944Syongari		}
1548136944Syongari		/* Waiting for capture FIFO to empty */
1549136944Syongari		status = cs4231_read(sc, CS_TEST_AND_INIT);
1550136944Syongari		for (i = CS_TIMEOUT;
1551136944Syongari		    i && (status & CAPTURE_OVERRUN) == 0; i--) {
1552136944Syongari			DELAY(5);
1553136944Syongari			status = cs4231_read(sc, CS_TEST_AND_INIT);
1554136944Syongari		}
1555136944Syongari		if (i == 0)
1556136944Syongari			device_printf(sc->sc_dev, "timeout waiting for "
1557136944Syongari			    "capture FIFO drain\n");
1558136944Syongari		cs4231_write(sc, CS_INTERFACE_CONFIG,
1559136944Syongari		    cs4231_read(sc, CS_INTERFACE_CONFIG) & (~CAPTURE_ENABLE));
1560136944Syongari	}
1561136944Syongari	ch->locked = 0;
1562136944Syongari	CS4231_UNLOCK(sc);
1563136944Syongari}
1564