156746Sroberto/*-
256746Sroberto * Copyright 2008 by Marco Trillo. All rights reserved.
356746Sroberto *
456746Sroberto * Redistribution and use in source and binary forms, with or without
556746Sroberto * modification, are permitted provided that the following conditions
656746Sroberto * are met:
756746Sroberto * 1. Redistributions of source code must retain the above copyright
856746Sroberto *    notice, this list of conditions and the following disclaimer.
956746Sroberto * 2. Redistributions in binary form must reproduce the above copyright
1056746Sroberto *    notice, this list of conditions and the following disclaimer in the
1156746Sroberto *    documentation and/or other materials provided with the distribution.
1256746Sroberto *
1356746Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1456746Sroberto * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1556746Sroberto * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1656746Sroberto * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1782498Sroberto * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1882498Sroberto * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
1982498Sroberto * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2082498Sroberto * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2182498Sroberto * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2282498Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2382498Sroberto * SUCH DAMAGE.
24132451Sroberto *
2556746Sroberto * $FreeBSD: releng/10.2/sys/dev/sound/macio/davbus.c 193640 2009-06-07 19:12:08Z ariff $
2656746Sroberto */
2756746Sroberto
2856746Sroberto/*
2956746Sroberto *	Apple DAVbus audio controller.
3056746Sroberto */
3156746Sroberto
3256746Sroberto#include <sys/param.h>
3356746Sroberto#include <sys/systm.h>
3456746Sroberto#include <sys/bus.h>
35132451Sroberto#include <sys/kernel.h>
36182007Sroberto#include <sys/lock.h>
37182007Sroberto#include <sys/malloc.h>
38182007Sroberto#include <sys/module.h>
39182007Sroberto#include <sys/mutex.h>
40182007Sroberto#include <sys/rman.h>
4156746Sroberto
42285612Sdelphij#include <dev/ofw/ofw_bus.h>
43285612Sdelphij
44285612Sdelphij#ifdef HAVE_KERNEL_OPTION_HEADERS
45285612Sdelphij#include "opt_snd.h"
46285612Sdelphij#endif
47285612Sdelphij
4856746Sroberto#include <dev/sound/pcm/sound.h>
4956746Sroberto
5056746Sroberto#include <dev/sound/macio/aoa.h>
5156746Sroberto#include <dev/sound/macio/davbusreg.h>
5256746Sroberto
53132451Sroberto#include <machine/intr_machdep.h>
54182007Sroberto#include <machine/resource.h>
5556746Sroberto#include <machine/bus.h>
5656746Sroberto
5756746Sroberto#include "mixer_if.h"
5856746Sroberto
5956746Srobertostruct davbus_softc {
6056746Sroberto	struct aoa_softc 	 aoa;
6156746Sroberto	phandle_t 		 node;
6256746Sroberto	phandle_t 		 soundnode;
63182007Sroberto	struct resource 	*reg;
64182007Sroberto	struct mtx 		 mutex;
65182007Sroberto	int 			 device_id;
66285612Sdelphij	u_int 			 output_mask;
67182007Sroberto	u_int 			(*read_status)(struct davbus_softc *, u_int);
68182007Sroberto	void			(*set_outputs)(struct davbus_softc *, u_int);
69182007Sroberto};
70182007Sroberto
71182007Srobertostatic int 	davbus_probe(device_t);
72285612Sdelphijstatic int 	davbus_attach(device_t);
73285612Sdelphijstatic void	davbus_cint(void *);
74285612Sdelphij
75285612Sdelphijstatic device_method_t pcm_davbus_methods[] = {
7656746Sroberto	/* Device interface. */
7756746Sroberto	DEVMETHOD(device_probe,		davbus_probe),
78182007Sroberto	DEVMETHOD(device_attach, 	davbus_attach),
7956746Sroberto
8082498Sroberto	{ 0, 0 }
81132451Sroberto};
8256746Sroberto
8356746Srobertostatic driver_t pcm_davbus_driver = {
84285612Sdelphij	"pcm",
85285612Sdelphij	pcm_davbus_methods,
8656746Sroberto	PCM_SOFTC_SIZE
8756746Sroberto};
88182007Sroberto
89132451SrobertoDRIVER_MODULE(pcm_davbus, macio, pcm_davbus_driver, pcm_devclass, 0, 0);
90182007SrobertoMODULE_DEPEND(pcm_davbus, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
91182007Sroberto
92182007Sroberto/*****************************************************************************
93182007Sroberto			Probe and attachment routines.
94182007Sroberto *****************************************************************************/
95182007Srobertostatic int
96182007Srobertodavbus_probe(device_t self)
97182007Sroberto{
98132451Sroberto	const char 		*name;
99132451Sroberto
100285612Sdelphij	name = ofw_bus_get_name(self);
10156746Sroberto	if (!name)
10256746Sroberto		return (ENXIO);
103182007Sroberto
104182007Sroberto	if (strcmp(name, "davbus") != 0)
105182007Sroberto		return (ENXIO);
106182007Sroberto
107182007Sroberto	device_set_desc(self, "Apple DAVBus Audio Controller");
108182007Sroberto
109182007Sroberto	return (0);
110182007Sroberto}
111285612Sdelphij
112182007Sroberto/*
113182007Sroberto * Burgundy codec control
114182007Sroberto */
115182007Sroberto
116182007Srobertostatic int	burgundy_init(struct snd_mixer *m);
11756746Srobertostatic int	burgundy_uninit(struct snd_mixer *m);
11856746Srobertostatic int	burgundy_reinit(struct snd_mixer *m);
119182007Srobertostatic void 	burgundy_write_locked(struct davbus_softc *, u_int, u_int);
120132451Srobertostatic void	burgundy_set_outputs(struct davbus_softc *d, u_int mask);
121132451Srobertostatic u_int	burgundy_read_status(struct davbus_softc *d, u_int status);
122182007Srobertostatic int	burgundy_set(struct snd_mixer *m, unsigned dev, unsigned left,
123182007Sroberto		    unsigned right);
124182007Srobertostatic u_int32_t	burgundy_setrecsrc(struct snd_mixer *m, u_int32_t src);
125182007Sroberto
12656746Srobertostatic kobj_method_t burgundy_mixer_methods[] = {
127182007Sroberto	KOBJMETHOD(mixer_init, 		burgundy_init),
128182007Sroberto	KOBJMETHOD(mixer_uninit, 	burgundy_uninit),
129182007Sroberto	KOBJMETHOD(mixer_reinit, 	burgundy_reinit),
130182007Sroberto	KOBJMETHOD(mixer_set, 		burgundy_set),
131182007Sroberto	KOBJMETHOD(mixer_setrecsrc,	burgundy_setrecsrc),
13256746Sroberto	KOBJMETHOD_END
13356746Sroberto};
13456746Sroberto
13556746SrobertoMIXER_DECLARE(burgundy_mixer);
13656746Sroberto
137132451Srobertostatic int
138182007Srobertoburgundy_init(struct snd_mixer *m)
139182007Sroberto{
140285612Sdelphij	struct davbus_softc *d;
141182007Sroberto
14256746Sroberto	d = mix_getdevinfo(m);
14356746Sroberto
144132451Sroberto	d->read_status = burgundy_read_status;
14556746Sroberto	d->set_outputs = burgundy_set_outputs;
14656746Sroberto
14756746Sroberto	/*
14856746Sroberto	 * We configure the Burgundy codec as follows:
14956746Sroberto	 *
15056746Sroberto	 * 	o Input subframe 0 is connected to input digital
15156746Sroberto	 *	  stream A (ISA).
15256746Sroberto	 *	o Stream A (ISA) is mixed in mixer 2 (MIX2).
15356746Sroberto	 *	o Output of mixer 2 (MIX2) is routed to output sources
15456746Sroberto	 *	  OS0 and OS1 which can be converted to analog.
15556746Sroberto	 *
156182007Sroberto	 */
15756746Sroberto	mtx_lock(&d->mutex);
158285612Sdelphij
159285612Sdelphij	burgundy_write_locked(d, 0x16700, 0x40);
160285612Sdelphij
161285612Sdelphij	burgundy_write_locked(d, BURGUNDY_MIX0_REG, 0);
16256746Sroberto	burgundy_write_locked(d, BURGUNDY_MIX1_REG, 0);
16356746Sroberto	burgundy_write_locked(d, BURGUNDY_MIX2_REG, BURGUNDY_MIX_ISA);
164132451Sroberto	burgundy_write_locked(d, BURGUNDY_MIX3_REG, 0);
16556746Sroberto
16656746Sroberto	burgundy_write_locked(d, BURGUNDY_OS_REG, BURGUNDY_OS0_MIX2 |
16756746Sroberto	    BURGUNDY_OS1_MIX2);
16856746Sroberto
16956746Sroberto	burgundy_write_locked(d, BURGUNDY_SDIN_REG, BURGUNDY_ISA_SF0);
170182007Sroberto
171182007Sroberto	/* Set several digital scalers to unity gain. */
172182007Sroberto	burgundy_write_locked(d, BURGUNDY_MXS2L_REG, BURGUNDY_MXS_UNITY);
173132451Sroberto	burgundy_write_locked(d, BURGUNDY_MXS2R_REG, BURGUNDY_MXS_UNITY);
17456746Sroberto	burgundy_write_locked(d, BURGUNDY_OSS0L_REG, BURGUNDY_OSS_UNITY);
17556746Sroberto	burgundy_write_locked(d, BURGUNDY_OSS0R_REG, BURGUNDY_OSS_UNITY);
17656746Sroberto	burgundy_write_locked(d, BURGUNDY_OSS1L_REG, BURGUNDY_OSS_UNITY);
17756746Sroberto	burgundy_write_locked(d, BURGUNDY_OSS1R_REG, BURGUNDY_OSS_UNITY);
178132451Sroberto	burgundy_write_locked(d, BURGUNDY_ISSAL_REG, BURGUNDY_ISS_UNITY);
17956746Sroberto	burgundy_write_locked(d, BURGUNDY_ISSAR_REG, BURGUNDY_ISS_UNITY);
18056746Sroberto
181182007Sroberto	burgundy_set_outputs(d, burgundy_read_status(d,
182182007Sroberto	    bus_read_4(d->reg, DAVBUS_CODEC_STATUS)));
183182007Sroberto
184182007Sroberto	mtx_unlock(&d->mutex);
185182007Sroberto
186182007Sroberto	mix_setdevs(m, SOUND_MASK_VOLUME);
187182007Sroberto
188182007Sroberto	return (0);
189182007Sroberto}
190182007Sroberto
191182007Srobertostatic int
192182007Srobertoburgundy_uninit(struct snd_mixer *m)
193132451Sroberto{
194132451Sroberto	return (0);
195182007Sroberto}
196132451Sroberto
197182007Srobertostatic int
198182007Srobertoburgundy_reinit(struct snd_mixer *m)
19956746Sroberto{
20056746Sroberto	return (0);
201132451Sroberto}
202132451Sroberto
20356746Srobertostatic void
204285612Sdelphijburgundy_write_locked(struct davbus_softc *d, u_int reg, u_int val)
205285612Sdelphij{
206285612Sdelphij	u_int size, addr, offset, data, i;
207285612Sdelphij
20856746Sroberto	size = (reg & 0x00FF0000) >> 16;
20956746Sroberto	addr = (reg & 0x0000FF00) >> 8;
210182007Sroberto	offset = reg & 0xFF;
21156746Sroberto
212182007Sroberto	for (i = offset; i < offset + size; ++i) {
213182007Sroberto		data = BURGUNDY_CTRL_WRITE | (addr << 12) |
214182007Sroberto		    ((size + offset - 1) << 10) | (i << 8) | (val & 0xFF);
215132451Sroberto		if (i == offset)
21656746Sroberto			data |= BURGUNDY_CTRL_RESET;
21756746Sroberto
21856746Sroberto		bus_write_4(d->reg, DAVBUS_CODEC_CTRL, data);
21956746Sroberto
22056746Sroberto		while (bus_read_4(d->reg, DAVBUS_CODEC_CTRL) &
22156746Sroberto		    DAVBUS_CODEC_BUSY)
22256746Sroberto			DELAY(1);
22356746Sroberto
22456746Sroberto		val >>= 8; /* next byte. */
22556746Sroberto	}
22656746Sroberto}
22756746Sroberto
228132451Sroberto/* Must be called with d->mutex held. */
229132451Srobertostatic void
23056746Srobertoburgundy_set_outputs(struct davbus_softc *d, u_int mask)
23156746Sroberto{
23256746Sroberto	u_int	x = 0;
233285612Sdelphij
234285612Sdelphij	if (mask == d->output_mask)
235285612Sdelphij		return;
236285612Sdelphij
237285612Sdelphij	/*
238285612Sdelphij	 *	Bordeaux card wirings:
239285612Sdelphij	 *		Port 15:	RCA out
240285612Sdelphij	 *		Port 16:	Minijack out
241285612Sdelphij	 *		Port 17:	Internal speaker
242285612Sdelphij	 *
243285612Sdelphij	 *	B&W G3 wirings:
244285612Sdelphij	 *		Port 14:	Minijack out
245285612Sdelphij	 *		Port 17:	Internal speaker
24656746Sroberto	 */
247285612Sdelphij
24856746Sroberto	DPRINTF(("Enabled outputs:"));
24956746Sroberto	if (mask & (1 << 0)) {
25056746Sroberto		DPRINTF((" SPEAKER"));
251182007Sroberto		x |= BURGUNDY_P17M_EN;
25256746Sroberto	}
25356746Sroberto	if (mask & (1 << 1)) {
254182007Sroberto		DPRINTF((" HEADPHONES"));
255182007Sroberto		x |= BURGUNDY_P14L_EN | BURGUNDY_P14R_EN;
256182007Sroberto	}
257182007Sroberto	DPRINTF(("\n"));
258182007Sroberto
259182007Sroberto	burgundy_write_locked(d, BURGUNDY_MUTE_REG, x);
260182007Sroberto	d->output_mask = mask;
261182007Sroberto}
262182007Sroberto
263182007Srobertostatic u_int
264182007Srobertoburgundy_read_status(struct davbus_softc *d, u_int status)
265182007Sroberto{
266182007Sroberto	if (status & 0x4)
267182007Sroberto		return (1 << 1);
268182007Sroberto	else
269182007Sroberto		return (1 << 0);
270182007Sroberto}
271182007Sroberto
272182007Srobertostatic int
273182007Srobertoburgundy_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
27456746Sroberto{
27556746Sroberto	struct davbus_softc *d;
27656746Sroberto	int lval, rval;
27756746Sroberto
27856746Sroberto	lval = ((100 - left) * 15 / 100) & 0xf;
27956746Sroberto	rval = ((100 - right) * 15 / 100) & 0xf;
28056746Sroberto	DPRINTF(("volume %d %d\n", lval, rval));
28156746Sroberto
28256746Sroberto	d = mix_getdevinfo(m);
28356746Sroberto
28456746Sroberto	switch (dev) {
28556746Sroberto	case SOUND_MIXER_VOLUME:
28656746Sroberto		mtx_lock(&d->mutex);
28756746Sroberto
28856746Sroberto		burgundy_write_locked(d, BURGUNDY_OL13_REG, lval);
28956746Sroberto		burgundy_write_locked(d, BURGUNDY_OL14_REG, (rval << 4) | lval);
29056746Sroberto		burgundy_write_locked(d, BURGUNDY_OL15_REG, (rval << 4) | lval);
291132451Sroberto		burgundy_write_locked(d, BURGUNDY_OL16_REG, (rval << 4) | lval);
292182007Sroberto		burgundy_write_locked(d, BURGUNDY_OL17_REG, lval);
293182007Sroberto
294182007Sroberto		mtx_unlock(&d->mutex);
295182007Sroberto
296182007Sroberto		return (left | (right << 8));
297182007Sroberto	}
298182007Sroberto
299182007Sroberto	return (0);
300182007Sroberto}
301182007Sroberto
302182007Srobertostatic u_int32_t
303182007Srobertoburgundy_setrecsrc(struct snd_mixer *m, u_int32_t src)
304182007Sroberto{
30556746Sroberto	return (0);
30656746Sroberto}
30756746Sroberto
30856746Sroberto/*
30956746Sroberto * Screamer Codec Control
31056746Sroberto */
31156746Sroberto
31256746Srobertostatic int	screamer_init(struct snd_mixer *m);
31356746Srobertostatic int	screamer_uninit(struct snd_mixer *m);
31456746Srobertostatic int	screamer_reinit(struct snd_mixer *m);
315182007Srobertostatic void 	screamer_write_locked(struct davbus_softc *, u_int, u_int);
316182007Srobertostatic void	screamer_set_outputs(struct davbus_softc *d, u_int mask);
31756746Srobertostatic u_int	screamer_read_status(struct davbus_softc *d, u_int status);
31856746Srobertostatic int	screamer_set(struct snd_mixer *m, unsigned dev, unsigned left,
31956746Sroberto		    unsigned right);
32056746Srobertostatic u_int32_t	screamer_setrecsrc(struct snd_mixer *m, u_int32_t src);
32156746Sroberto
32256746Srobertostatic kobj_method_t screamer_mixer_methods[] = {
32356746Sroberto	KOBJMETHOD(mixer_init, 		screamer_init),
32456746Sroberto	KOBJMETHOD(mixer_uninit, 	screamer_uninit),
325182007Sroberto	KOBJMETHOD(mixer_reinit, 	screamer_reinit),
326182007Sroberto	KOBJMETHOD(mixer_set, 		screamer_set),
327182007Sroberto	KOBJMETHOD(mixer_setrecsrc,	screamer_setrecsrc),
328182007Sroberto	KOBJMETHOD_END
32956746Sroberto};
33056746Sroberto
33156746SrobertoMIXER_DECLARE(screamer_mixer);
33256746Sroberto
33356746Srobertostatic int
33456746Srobertoscreamer_init(struct snd_mixer *m)
33556746Sroberto{
33656746Sroberto	struct davbus_softc *d;
33756746Sroberto
33856746Sroberto	d = mix_getdevinfo(m);
33956746Sroberto
34056746Sroberto	d->read_status = screamer_read_status;
34156746Sroberto	d->set_outputs = screamer_set_outputs;
34256746Sroberto
34356746Sroberto	mtx_lock(&d->mutex);
34456746Sroberto
34556746Sroberto	screamer_write_locked(d, SCREAMER_CODEC_ADDR0, SCREAMER_INPUT_CD |
34656746Sroberto	    SCREAMER_DEFAULT_CD_GAIN);
34756746Sroberto
34856746Sroberto	screamer_set_outputs(d, screamer_read_status(d,
34956746Sroberto	    bus_read_4(d->reg, DAVBUS_CODEC_STATUS)));
35056746Sroberto
35156746Sroberto	screamer_write_locked(d, SCREAMER_CODEC_ADDR2, 0);
35256746Sroberto	screamer_write_locked(d, SCREAMER_CODEC_ADDR4, 0);
35356746Sroberto	screamer_write_locked(d, SCREAMER_CODEC_ADDR5, 0);
35456746Sroberto	screamer_write_locked(d, SCREAMER_CODEC_ADDR6, 0);
35556746Sroberto
35656746Sroberto	mtx_unlock(&d->mutex);
35756746Sroberto
35856746Sroberto	mix_setdevs(m, SOUND_MASK_VOLUME);
35956746Sroberto
36056746Sroberto	return (0);
36156746Sroberto}
36256746Sroberto
36356746Srobertostatic int
36456746Srobertoscreamer_uninit(struct snd_mixer *m)
36556746Sroberto{
36656746Sroberto	return (0);
36756746Sroberto}
36856746Sroberto
36956746Srobertostatic int
37056746Srobertoscreamer_reinit(struct snd_mixer *m)
37156746Sroberto{
37256746Sroberto	return (0);
37356746Sroberto}
374182007Sroberto
37556746Sroberto
37656746Srobertostatic void
37756746Srobertoscreamer_write_locked(struct davbus_softc *d, u_int reg, u_int val)
37856746Sroberto{
379285612Sdelphij	u_int 		x;
38056746Sroberto
38156746Sroberto	KASSERT(val == (val & 0xfff), ("bad val"));
38256746Sroberto
38356746Sroberto	while (bus_read_4(d->reg, DAVBUS_CODEC_CTRL) & DAVBUS_CODEC_BUSY)
38456746Sroberto		DELAY(100);
38556746Sroberto
38656746Sroberto	x = reg;
38756746Sroberto	x |= SCREAMER_CODEC_EMSEL0;
38856746Sroberto	x |= val;
38956746Sroberto	bus_write_4(d->reg, DAVBUS_CODEC_CTRL, x);
39056746Sroberto
39156746Sroberto	while (bus_read_4(d->reg, DAVBUS_CODEC_CTRL) & DAVBUS_CODEC_BUSY)
39256746Sroberto		DELAY(100);
39356746Sroberto}
39456746Sroberto
39556746Sroberto/* Must be called with d->mutex held. */
39656746Srobertostatic void
39756746Srobertoscreamer_set_outputs(struct davbus_softc *d, u_int mask)
39856746Sroberto{
39956746Sroberto	u_int 	x;
40056746Sroberto
40156746Sroberto	if (mask == d->output_mask) {
40256746Sroberto		return;
40356746Sroberto	}
40456746Sroberto
40556746Sroberto	x = SCREAMER_MUTE_SPEAKER | SCREAMER_MUTE_HEADPHONES;
40656746Sroberto
40756746Sroberto	DPRINTF(("Enabled outputs: "));
40856746Sroberto
40956746Sroberto	if (mask & (1 << 0)) {
41056746Sroberto		DPRINTF(("SPEAKER "));
41156746Sroberto		x &= ~SCREAMER_MUTE_SPEAKER;
41256746Sroberto	}
41356746Sroberto	if (mask & (1 << 1)) {
41456746Sroberto		DPRINTF(("HEADPHONES "));
41556746Sroberto		x &= ~SCREAMER_MUTE_HEADPHONES;
41656746Sroberto	}
41756746Sroberto
41856746Sroberto	DPRINTF(("\n"));
41956746Sroberto
42056746Sroberto	if (d->device_id == 5 || d->device_id == 11) {
42156746Sroberto		DPRINTF(("Enabling programmable output.\n"));
42256746Sroberto		x |= SCREAMER_PROG_OUTPUT0;
42356746Sroberto	}
42456746Sroberto	if (d->device_id == 8 || d->device_id == 11) {
42556746Sroberto		x &= ~SCREAMER_MUTE_SPEAKER;
42656746Sroberto
42756746Sroberto		if (mask & (1 << 0))
42856746Sroberto			x |= SCREAMER_PROG_OUTPUT1; /* enable speaker. */
42956746Sroberto	}
43056746Sroberto
43156746Sroberto	screamer_write_locked(d, SCREAMER_CODEC_ADDR1, x);
43256746Sroberto	d->output_mask = mask;
43356746Sroberto}
43456746Sroberto
43556746Srobertostatic u_int
43656746Srobertoscreamer_read_status(struct davbus_softc *d, u_int status)
43756746Sroberto{
43856746Sroberto	int 	headphones;
43956746Sroberto
44056746Sroberto	switch (d->device_id) {
44156746Sroberto	case 5: /* Sawtooth */
44256746Sroberto		headphones = (status & 0x4);
44356746Sroberto		break;
44456746Sroberto
44556746Sroberto	case 8:
44656746Sroberto	case 11: /* iMac DV */
44756746Sroberto		/* The iMac DV has 2 headphone outputs. */
44856746Sroberto		headphones = (status & 0x7);
44956746Sroberto		break;
45056746Sroberto
45156746Sroberto	default:
45256746Sroberto		headphones = (status & 0x8);
453132451Sroberto	}
454132451Sroberto
45556746Sroberto	if (headphones)
45656746Sroberto		return (1 << 1);
45756746Sroberto	else
45856746Sroberto		return (1 << 0);
45956746Sroberto}
46056746Sroberto
461285612Sdelphijstatic int
462285612Sdelphijscreamer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
46356746Sroberto{
46456746Sroberto	struct davbus_softc *d;
46556746Sroberto	int lval, rval;
46656746Sroberto
46756746Sroberto	lval = ((100 - left) * 15 / 100) & 0xf;
46856746Sroberto	rval = ((100 - right) * 15 / 100) & 0xf;
46956746Sroberto	DPRINTF(("volume %d %d\n", lval, rval));
47056746Sroberto
47156746Sroberto	d = mix_getdevinfo(m);
47256746Sroberto
47356746Sroberto	switch (dev) {
47456746Sroberto	case SOUND_MIXER_VOLUME:
475182007Sroberto		mtx_lock(&d->mutex);
476182007Sroberto		screamer_write_locked(d, SCREAMER_CODEC_ADDR2, (lval << 6) |
477182007Sroberto		    rval);
478182007Sroberto		screamer_write_locked(d, SCREAMER_CODEC_ADDR4, (lval << 6) |
47956746Sroberto		    rval);
48056746Sroberto		mtx_unlock(&d->mutex);
481132451Sroberto
482182007Sroberto		return (left | (right << 8));
483182007Sroberto	}
484132451Sroberto
485132451Sroberto	return (0);
486132451Sroberto}
487132451Sroberto
488182007Srobertostatic u_int32_t
489182007Srobertoscreamer_setrecsrc(struct snd_mixer *m, u_int32_t src)
490182007Sroberto{
49156746Sroberto	return (0);
492182007Sroberto}
493182007Sroberto
494132451Srobertostatic int
495182007Srobertodavbus_attach(device_t self)
49656746Sroberto{
49756746Sroberto	struct davbus_softc 	*sc;
49856746Sroberto	struct resource 	*dbdma_irq, *cintr;
49956746Sroberto	void 			*cookie;
500182007Sroberto	char			 compat[64];
50156746Sroberto	int 			 rid, oirq, err;
50256746Sroberto
50356746Sroberto	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
50456746Sroberto
50556746Sroberto	sc->aoa.sc_dev = self;
50656746Sroberto	sc->node = ofw_bus_get_node(self);
50756746Sroberto	sc->soundnode = OF_child(sc->node);
50856746Sroberto
509182007Sroberto	/* Map the controller register space. */
51056746Sroberto	rid = 0;
51156746Sroberto	sc->reg = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE);
51256746Sroberto	if (sc->reg == NULL)
51356746Sroberto		return (ENXIO);
51456746Sroberto
51556746Sroberto	/* Map the DBDMA channel register space. */
516285612Sdelphij	rid = 1;
517182007Sroberto	sc->aoa.sc_odma = bus_alloc_resource_any(self, SYS_RES_MEMORY,
51856746Sroberto	    &rid, RF_ACTIVE);
519182007Sroberto	if (sc->aoa.sc_odma == NULL)
52056746Sroberto		return (ENXIO);
521132451Sroberto
522132451Sroberto	/* Establish the DBDMA channel edge-triggered interrupt. */
523132451Sroberto	rid = 1;
524132451Sroberto	dbdma_irq = bus_alloc_resource_any(self, SYS_RES_IRQ,
525132451Sroberto	    &rid, RF_SHAREABLE | RF_ACTIVE);
526132451Sroberto	if (dbdma_irq == NULL)
527285612Sdelphij		return (ENXIO);
52856746Sroberto
529132451Sroberto	oirq = rman_get_start(dbdma_irq);
53056746Sroberto
53156746Sroberto	DPRINTF(("interrupting at irq %d\n", oirq));
53256746Sroberto
53356746Sroberto	err = powerpc_config_intr(oirq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW);
53456746Sroberto	if (err != 0)
535132451Sroberto		return (err);
536132451Sroberto
537132451Sroberto	snd_setup_intr(self, dbdma_irq, INTR_MPSAFE, aoa_interrupt,
53856746Sroberto	    sc, &cookie);
539132451Sroberto
54056746Sroberto	/* Now initialize the controller. */
54156746Sroberto
54256746Sroberto	bzero(compat, sizeof(compat));
54356746Sroberto	OF_getprop(sc->soundnode, "compatible", compat, sizeof(compat));
544132451Sroberto	OF_getprop(sc->soundnode, "device-id", &sc->device_id, sizeof(u_int));
54556746Sroberto
54656746Sroberto	mtx_init(&sc->mutex, "DAVbus", NULL, MTX_DEF);
54756746Sroberto
54856746Sroberto	device_printf(self, "codec: <%s>\n", compat);
54956746Sroberto
55056746Sroberto	/* Setup the control interrupt. */
55156746Sroberto	rid = 0;
55256746Sroberto	cintr = bus_alloc_resource_any(self, SYS_RES_IRQ,
55356746Sroberto	     &rid, RF_SHAREABLE | RF_ACTIVE);
55456746Sroberto	if (cintr != NULL)
55556746Sroberto		bus_setup_intr(self, cintr, INTR_TYPE_MISC | INTR_MPSAFE,
55656746Sroberto		    NULL, davbus_cint, sc, &cookie);
55756746Sroberto
55856746Sroberto	/* Initialize controller registers. */
55956746Sroberto        bus_write_4(sc->reg, DAVBUS_SOUND_CTRL, DAVBUS_INPUT_SUBFRAME0 |
560132451Sroberto	    DAVBUS_OUTPUT_SUBFRAME0 | DAVBUS_RATE_44100 | DAVBUS_INTR_PORTCHG);
56156746Sroberto
56256746Sroberto	/* Attach DBDMA engine and PCM layer */
56356746Sroberto	err = aoa_attach(sc);
56456746Sroberto	if (err)
56556746Sroberto		return (err);
56656746Sroberto
567182007Sroberto	/* Install codec module */
568182007Sroberto	if (strcmp(compat, "screamer") == 0)
56956746Sroberto		mixer_init(self, &screamer_mixer_class, sc);
57056746Sroberto	else if (strcmp(compat, "burgundy") == 0)
57156746Sroberto		mixer_init(self, &burgundy_mixer_class, sc);
57256746Sroberto
57356746Sroberto	return (0);
57456746Sroberto}
57556746Sroberto
57656746Srobertostatic void
57756746Srobertodavbus_cint(void *ptr)
57856746Sroberto{
57956746Sroberto	struct davbus_softc *d = ptr;
58056746Sroberto	u_int	reg, status, mask;
58156746Sroberto
582285612Sdelphij	mtx_lock(&d->mutex);
583285612Sdelphij
584285612Sdelphij	reg = bus_read_4(d->reg, DAVBUS_SOUND_CTRL);
585285612Sdelphij	if (reg & DAVBUS_PORTCHG) {
58656746Sroberto
58756746Sroberto		status = bus_read_4(d->reg, DAVBUS_CODEC_STATUS);
58856746Sroberto
58956746Sroberto		if (d->read_status && d->set_outputs) {
590285612Sdelphij
591285612Sdelphij			mask = (*d->read_status)(d, status);
592285612Sdelphij			(*d->set_outputs)(d, mask);
593285612Sdelphij		}
594285612Sdelphij
595285612Sdelphij		/* Clear the interrupt. */
596285612Sdelphij		bus_write_4(d->reg, DAVBUS_SOUND_CTRL, reg);
597285612Sdelphij	}
598285612Sdelphij
599285612Sdelphij	mtx_unlock(&d->mutex);
600285612Sdelphij}
601285612Sdelphij
602285612Sdelphij