18486eb86SNathan Whitehorn /*- 28486eb86SNathan Whitehorn * Copyright 2008 by Marco Trillo. All rights reserved. 38486eb86SNathan Whitehorn * 48486eb86SNathan Whitehorn * Redistribution and use in source and binary forms, with or without 58486eb86SNathan Whitehorn * modification, are permitted provided that the following conditions 68486eb86SNathan Whitehorn * are met: 78486eb86SNathan Whitehorn * 1. Redistributions of source code must retain the above copyright 88486eb86SNathan Whitehorn * notice, this list of conditions and the following disclaimer. 98486eb86SNathan Whitehorn * 2. Redistributions in binary form must reproduce the above copyright 108486eb86SNathan Whitehorn * notice, this list of conditions and the following disclaimer in the 118486eb86SNathan Whitehorn * documentation and/or other materials provided with the distribution. 128486eb86SNathan Whitehorn * 138486eb86SNathan Whitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 148486eb86SNathan Whitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 158486eb86SNathan Whitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 168486eb86SNathan Whitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 178486eb86SNathan Whitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 188486eb86SNathan Whitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 198486eb86SNathan Whitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 208486eb86SNathan Whitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 218486eb86SNathan Whitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 228486eb86SNathan Whitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 238486eb86SNathan Whitehorn * SUCH DAMAGE. 248486eb86SNathan Whitehorn * 258486eb86SNathan Whitehorn * $FreeBSD$ 268486eb86SNathan Whitehorn */ 278486eb86SNathan Whitehorn 288486eb86SNathan Whitehorn /* 298486eb86SNathan Whitehorn * Apple DAVbus audio controller. 308486eb86SNathan Whitehorn */ 318486eb86SNathan Whitehorn 328486eb86SNathan Whitehorn #include <sys/param.h> 338486eb86SNathan Whitehorn #include <sys/systm.h> 348486eb86SNathan Whitehorn #include <sys/bus.h> 358486eb86SNathan Whitehorn #include <sys/kernel.h> 368486eb86SNathan Whitehorn #include <sys/lock.h> 378486eb86SNathan Whitehorn #include <sys/malloc.h> 388486eb86SNathan Whitehorn #include <sys/module.h> 398486eb86SNathan Whitehorn #include <sys/mutex.h> 408486eb86SNathan Whitehorn #include <sys/rman.h> 418486eb86SNathan Whitehorn 428486eb86SNathan Whitehorn #include <dev/ofw/ofw_bus.h> 4390da2b28SAriff Abdullah 4490da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 4590da2b28SAriff Abdullah #include "opt_snd.h" 4690da2b28SAriff Abdullah #endif 4790da2b28SAriff Abdullah 488486eb86SNathan Whitehorn #include <dev/sound/pcm/sound.h> 4990da2b28SAriff Abdullah 508486eb86SNathan Whitehorn #include <dev/sound/macio/aoa.h> 518486eb86SNathan Whitehorn #include <dev/sound/macio/davbusreg.h> 528486eb86SNathan Whitehorn 538486eb86SNathan Whitehorn #include <machine/intr_machdep.h> 548486eb86SNathan Whitehorn #include <machine/resource.h> 558486eb86SNathan Whitehorn #include <machine/bus.h> 568486eb86SNathan Whitehorn 578486eb86SNathan Whitehorn #include "mixer_if.h" 588486eb86SNathan Whitehorn 598486eb86SNathan Whitehorn struct davbus_softc { 608486eb86SNathan Whitehorn struct aoa_softc aoa; 618486eb86SNathan Whitehorn phandle_t node; 628486eb86SNathan Whitehorn phandle_t soundnode; 638486eb86SNathan Whitehorn struct resource *reg; 648486eb86SNathan Whitehorn struct mtx mutex; 658486eb86SNathan Whitehorn int device_id; 668486eb86SNathan Whitehorn u_int output_mask; 678486eb86SNathan Whitehorn u_int (*read_status)(struct davbus_softc *, u_int); 688486eb86SNathan Whitehorn void (*set_outputs)(struct davbus_softc *, u_int); 698486eb86SNathan Whitehorn }; 708486eb86SNathan Whitehorn 718486eb86SNathan Whitehorn static int davbus_probe(device_t); 728486eb86SNathan Whitehorn static int davbus_attach(device_t); 738486eb86SNathan Whitehorn static void davbus_cint(void *); 748486eb86SNathan Whitehorn 758486eb86SNathan Whitehorn static device_method_t pcm_davbus_methods[] = { 768486eb86SNathan Whitehorn /* Device interface. */ 778486eb86SNathan Whitehorn DEVMETHOD(device_probe, davbus_probe), 788486eb86SNathan Whitehorn DEVMETHOD(device_attach, davbus_attach), 798486eb86SNathan Whitehorn 808486eb86SNathan Whitehorn { 0, 0 } 818486eb86SNathan Whitehorn }; 828486eb86SNathan Whitehorn 838486eb86SNathan Whitehorn static driver_t pcm_davbus_driver = { 848486eb86SNathan Whitehorn "pcm", 858486eb86SNathan Whitehorn pcm_davbus_methods, 86a808d017SNathan Whitehorn PCM_SOFTC_SIZE 878486eb86SNathan Whitehorn }; 888486eb86SNathan Whitehorn 898486eb86SNathan Whitehorn DRIVER_MODULE(pcm_davbus, macio, pcm_davbus_driver, pcm_devclass, 0, 0); 908486eb86SNathan Whitehorn MODULE_DEPEND(pcm_davbus, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 918486eb86SNathan Whitehorn 928486eb86SNathan Whitehorn /***************************************************************************** 938486eb86SNathan Whitehorn Probe and attachment routines. 948486eb86SNathan Whitehorn *****************************************************************************/ 958486eb86SNathan Whitehorn static int 968486eb86SNathan Whitehorn davbus_probe(device_t self) 978486eb86SNathan Whitehorn { 988486eb86SNathan Whitehorn const char *name; 998486eb86SNathan Whitehorn 1008486eb86SNathan Whitehorn name = ofw_bus_get_name(self); 1018486eb86SNathan Whitehorn if (!name) 1028486eb86SNathan Whitehorn return (ENXIO); 1038486eb86SNathan Whitehorn 1048486eb86SNathan Whitehorn if (strcmp(name, "davbus") != 0) 1058486eb86SNathan Whitehorn return (ENXIO); 1068486eb86SNathan Whitehorn 1078486eb86SNathan Whitehorn device_set_desc(self, "Apple DAVBus Audio Controller"); 1088486eb86SNathan Whitehorn 1098486eb86SNathan Whitehorn return (0); 1108486eb86SNathan Whitehorn } 1118486eb86SNathan Whitehorn 1128486eb86SNathan Whitehorn /* 1138486eb86SNathan Whitehorn * Burgundy codec control 1148486eb86SNathan Whitehorn */ 1158486eb86SNathan Whitehorn 1168486eb86SNathan Whitehorn static int burgundy_init(struct snd_mixer *m); 1178486eb86SNathan Whitehorn static int burgundy_uninit(struct snd_mixer *m); 1188486eb86SNathan Whitehorn static int burgundy_reinit(struct snd_mixer *m); 1198486eb86SNathan Whitehorn static void burgundy_write_locked(struct davbus_softc *, u_int, u_int); 1208486eb86SNathan Whitehorn static void burgundy_set_outputs(struct davbus_softc *d, u_int mask); 1218486eb86SNathan Whitehorn static u_int burgundy_read_status(struct davbus_softc *d, u_int status); 1228486eb86SNathan Whitehorn static int burgundy_set(struct snd_mixer *m, unsigned dev, unsigned left, 1238486eb86SNathan Whitehorn unsigned right); 12490da2b28SAriff Abdullah static u_int32_t burgundy_setrecsrc(struct snd_mixer *m, u_int32_t src); 1258486eb86SNathan Whitehorn 1268486eb86SNathan Whitehorn static kobj_method_t burgundy_mixer_methods[] = { 1278486eb86SNathan Whitehorn KOBJMETHOD(mixer_init, burgundy_init), 1288486eb86SNathan Whitehorn KOBJMETHOD(mixer_uninit, burgundy_uninit), 1298486eb86SNathan Whitehorn KOBJMETHOD(mixer_reinit, burgundy_reinit), 1308486eb86SNathan Whitehorn KOBJMETHOD(mixer_set, burgundy_set), 1318486eb86SNathan Whitehorn KOBJMETHOD(mixer_setrecsrc, burgundy_setrecsrc), 13290da2b28SAriff Abdullah KOBJMETHOD_END 1338486eb86SNathan Whitehorn }; 1348486eb86SNathan Whitehorn 1358486eb86SNathan Whitehorn MIXER_DECLARE(burgundy_mixer); 1368486eb86SNathan Whitehorn 1378486eb86SNathan Whitehorn static int 1388486eb86SNathan Whitehorn burgundy_init(struct snd_mixer *m) 1398486eb86SNathan Whitehorn { 1408486eb86SNathan Whitehorn struct davbus_softc *d; 1418486eb86SNathan Whitehorn 1428486eb86SNathan Whitehorn d = mix_getdevinfo(m); 1438486eb86SNathan Whitehorn 1448486eb86SNathan Whitehorn d->read_status = burgundy_read_status; 1458486eb86SNathan Whitehorn d->set_outputs = burgundy_set_outputs; 1468486eb86SNathan Whitehorn 1478486eb86SNathan Whitehorn /* 1488486eb86SNathan Whitehorn * We configure the Burgundy codec as follows: 1498486eb86SNathan Whitehorn * 1508486eb86SNathan Whitehorn * o Input subframe 0 is connected to input digital 1518486eb86SNathan Whitehorn * stream A (ISA). 1528486eb86SNathan Whitehorn * o Stream A (ISA) is mixed in mixer 2 (MIX2). 1538486eb86SNathan Whitehorn * o Output of mixer 2 (MIX2) is routed to output sources 1548486eb86SNathan Whitehorn * OS0 and OS1 which can be converted to analog. 1558486eb86SNathan Whitehorn * 1568486eb86SNathan Whitehorn */ 1578486eb86SNathan Whitehorn mtx_lock(&d->mutex); 1588486eb86SNathan Whitehorn 1598486eb86SNathan Whitehorn burgundy_write_locked(d, 0x16700, 0x40); 1608486eb86SNathan Whitehorn 1618486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_MIX0_REG, 0); 1628486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_MIX1_REG, 0); 1638486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_MIX2_REG, BURGUNDY_MIX_ISA); 1648486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_MIX3_REG, 0); 1658486eb86SNathan Whitehorn 1668486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_OS_REG, BURGUNDY_OS0_MIX2 | 1678486eb86SNathan Whitehorn BURGUNDY_OS1_MIX2); 1688486eb86SNathan Whitehorn 1698486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_SDIN_REG, BURGUNDY_ISA_SF0); 1708486eb86SNathan Whitehorn 1718486eb86SNathan Whitehorn /* Set several digital scalers to unity gain. */ 1728486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_MXS2L_REG, BURGUNDY_MXS_UNITY); 1738486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_MXS2R_REG, BURGUNDY_MXS_UNITY); 1748486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_OSS0L_REG, BURGUNDY_OSS_UNITY); 1758486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_OSS0R_REG, BURGUNDY_OSS_UNITY); 1768486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_OSS1L_REG, BURGUNDY_OSS_UNITY); 1778486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_OSS1R_REG, BURGUNDY_OSS_UNITY); 1788486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_ISSAL_REG, BURGUNDY_ISS_UNITY); 1798486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_ISSAR_REG, BURGUNDY_ISS_UNITY); 1808486eb86SNathan Whitehorn 1818486eb86SNathan Whitehorn burgundy_set_outputs(d, burgundy_read_status(d, 1828486eb86SNathan Whitehorn bus_read_4(d->reg, DAVBUS_CODEC_STATUS))); 1838486eb86SNathan Whitehorn 1848486eb86SNathan Whitehorn mtx_unlock(&d->mutex); 1858486eb86SNathan Whitehorn 1868486eb86SNathan Whitehorn mix_setdevs(m, SOUND_MASK_VOLUME); 1878486eb86SNathan Whitehorn 1888486eb86SNathan Whitehorn return (0); 1898486eb86SNathan Whitehorn } 1908486eb86SNathan Whitehorn 1918486eb86SNathan Whitehorn static int 1928486eb86SNathan Whitehorn burgundy_uninit(struct snd_mixer *m) 1938486eb86SNathan Whitehorn { 1948486eb86SNathan Whitehorn return (0); 1958486eb86SNathan Whitehorn } 1968486eb86SNathan Whitehorn 1978486eb86SNathan Whitehorn static int 1988486eb86SNathan Whitehorn burgundy_reinit(struct snd_mixer *m) 1998486eb86SNathan Whitehorn { 2008486eb86SNathan Whitehorn return (0); 2018486eb86SNathan Whitehorn } 2028486eb86SNathan Whitehorn 2038486eb86SNathan Whitehorn static void 2048486eb86SNathan Whitehorn burgundy_write_locked(struct davbus_softc *d, u_int reg, u_int val) 2058486eb86SNathan Whitehorn { 2068486eb86SNathan Whitehorn u_int size, addr, offset, data, i; 2078486eb86SNathan Whitehorn 2088486eb86SNathan Whitehorn size = (reg & 0x00FF0000) >> 16; 2098486eb86SNathan Whitehorn addr = (reg & 0x0000FF00) >> 8; 2108486eb86SNathan Whitehorn offset = reg & 0xFF; 2118486eb86SNathan Whitehorn 2128486eb86SNathan Whitehorn for (i = offset; i < offset + size; ++i) { 2138486eb86SNathan Whitehorn data = BURGUNDY_CTRL_WRITE | (addr << 12) | 2148486eb86SNathan Whitehorn ((size + offset - 1) << 10) | (i << 8) | (val & 0xFF); 2158486eb86SNathan Whitehorn if (i == offset) 2168486eb86SNathan Whitehorn data |= BURGUNDY_CTRL_RESET; 2178486eb86SNathan Whitehorn 2188486eb86SNathan Whitehorn bus_write_4(d->reg, DAVBUS_CODEC_CTRL, data); 2198486eb86SNathan Whitehorn 2208486eb86SNathan Whitehorn while (bus_read_4(d->reg, DAVBUS_CODEC_CTRL) & 2218486eb86SNathan Whitehorn DAVBUS_CODEC_BUSY) 2228486eb86SNathan Whitehorn DELAY(1); 2238486eb86SNathan Whitehorn 2248486eb86SNathan Whitehorn val >>= 8; /* next byte. */ 2258486eb86SNathan Whitehorn } 2268486eb86SNathan Whitehorn } 2278486eb86SNathan Whitehorn 2288486eb86SNathan Whitehorn /* Must be called with d->mutex held. */ 2298486eb86SNathan Whitehorn static void 2308486eb86SNathan Whitehorn burgundy_set_outputs(struct davbus_softc *d, u_int mask) 2318486eb86SNathan Whitehorn { 2328486eb86SNathan Whitehorn u_int x = 0; 2338486eb86SNathan Whitehorn 2348486eb86SNathan Whitehorn if (mask == d->output_mask) 2358486eb86SNathan Whitehorn return; 2368486eb86SNathan Whitehorn 2378486eb86SNathan Whitehorn /* 2388486eb86SNathan Whitehorn * Bordeaux card wirings: 2398486eb86SNathan Whitehorn * Port 15: RCA out 2408486eb86SNathan Whitehorn * Port 16: Minijack out 2418486eb86SNathan Whitehorn * Port 17: Internal speaker 2428486eb86SNathan Whitehorn * 2438486eb86SNathan Whitehorn * B&W G3 wirings: 2448486eb86SNathan Whitehorn * Port 14: Minijack out 2458486eb86SNathan Whitehorn * Port 17: Internal speaker 2468486eb86SNathan Whitehorn */ 2478486eb86SNathan Whitehorn 2488486eb86SNathan Whitehorn DPRINTF(("Enabled outputs:")); 2498486eb86SNathan Whitehorn if (mask & (1 << 0)) { 2508486eb86SNathan Whitehorn DPRINTF((" SPEAKER")); 2518486eb86SNathan Whitehorn x |= BURGUNDY_P17M_EN; 2528486eb86SNathan Whitehorn } 2538486eb86SNathan Whitehorn if (mask & (1 << 1)) { 2548486eb86SNathan Whitehorn DPRINTF((" HEADPHONES")); 2558486eb86SNathan Whitehorn x |= BURGUNDY_P14L_EN | BURGUNDY_P14R_EN; 2568486eb86SNathan Whitehorn } 2578486eb86SNathan Whitehorn DPRINTF(("\n")); 2588486eb86SNathan Whitehorn 2598486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_MUTE_REG, x); 2608486eb86SNathan Whitehorn d->output_mask = mask; 2618486eb86SNathan Whitehorn } 2628486eb86SNathan Whitehorn 2638486eb86SNathan Whitehorn static u_int 2648486eb86SNathan Whitehorn burgundy_read_status(struct davbus_softc *d, u_int status) 2658486eb86SNathan Whitehorn { 2668486eb86SNathan Whitehorn if (status & 0x4) 2678486eb86SNathan Whitehorn return (1 << 1); 2688486eb86SNathan Whitehorn else 2698486eb86SNathan Whitehorn return (1 << 0); 2708486eb86SNathan Whitehorn } 2718486eb86SNathan Whitehorn 2728486eb86SNathan Whitehorn static int 2738486eb86SNathan Whitehorn burgundy_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 2748486eb86SNathan Whitehorn { 2758486eb86SNathan Whitehorn struct davbus_softc *d; 2768486eb86SNathan Whitehorn int lval, rval; 2778486eb86SNathan Whitehorn 2788486eb86SNathan Whitehorn lval = ((100 - left) * 15 / 100) & 0xf; 2798486eb86SNathan Whitehorn rval = ((100 - right) * 15 / 100) & 0xf; 2808486eb86SNathan Whitehorn DPRINTF(("volume %d %d\n", lval, rval)); 2818486eb86SNathan Whitehorn 2828486eb86SNathan Whitehorn d = mix_getdevinfo(m); 2838486eb86SNathan Whitehorn 2848486eb86SNathan Whitehorn switch (dev) { 2858486eb86SNathan Whitehorn case SOUND_MIXER_VOLUME: 2868486eb86SNathan Whitehorn mtx_lock(&d->mutex); 2878486eb86SNathan Whitehorn 2888486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_OL13_REG, lval); 2898486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_OL14_REG, (rval << 4) | lval); 2908486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_OL15_REG, (rval << 4) | lval); 2918486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_OL16_REG, (rval << 4) | lval); 2928486eb86SNathan Whitehorn burgundy_write_locked(d, BURGUNDY_OL17_REG, lval); 2938486eb86SNathan Whitehorn 2948486eb86SNathan Whitehorn mtx_unlock(&d->mutex); 2958486eb86SNathan Whitehorn 2968486eb86SNathan Whitehorn return (left | (right << 8)); 2978486eb86SNathan Whitehorn } 2988486eb86SNathan Whitehorn 2998486eb86SNathan Whitehorn return (0); 3008486eb86SNathan Whitehorn } 3018486eb86SNathan Whitehorn 30290da2b28SAriff Abdullah static u_int32_t 3038486eb86SNathan Whitehorn burgundy_setrecsrc(struct snd_mixer *m, u_int32_t src) 3048486eb86SNathan Whitehorn { 3058486eb86SNathan Whitehorn return (0); 3068486eb86SNathan Whitehorn } 3078486eb86SNathan Whitehorn 3088486eb86SNathan Whitehorn /* 3098486eb86SNathan Whitehorn * Screamer Codec Control 3108486eb86SNathan Whitehorn */ 3118486eb86SNathan Whitehorn 3128486eb86SNathan Whitehorn static int screamer_init(struct snd_mixer *m); 3138486eb86SNathan Whitehorn static int screamer_uninit(struct snd_mixer *m); 3148486eb86SNathan Whitehorn static int screamer_reinit(struct snd_mixer *m); 3158486eb86SNathan Whitehorn static void screamer_write_locked(struct davbus_softc *, u_int, u_int); 3168486eb86SNathan Whitehorn static void screamer_set_outputs(struct davbus_softc *d, u_int mask); 3178486eb86SNathan Whitehorn static u_int screamer_read_status(struct davbus_softc *d, u_int status); 3188486eb86SNathan Whitehorn static int screamer_set(struct snd_mixer *m, unsigned dev, unsigned left, 3198486eb86SNathan Whitehorn unsigned right); 32090da2b28SAriff Abdullah static u_int32_t screamer_setrecsrc(struct snd_mixer *m, u_int32_t src); 3218486eb86SNathan Whitehorn 3228486eb86SNathan Whitehorn static kobj_method_t screamer_mixer_methods[] = { 3238486eb86SNathan Whitehorn KOBJMETHOD(mixer_init, screamer_init), 3248486eb86SNathan Whitehorn KOBJMETHOD(mixer_uninit, screamer_uninit), 3258486eb86SNathan Whitehorn KOBJMETHOD(mixer_reinit, screamer_reinit), 3268486eb86SNathan Whitehorn KOBJMETHOD(mixer_set, screamer_set), 3278486eb86SNathan Whitehorn KOBJMETHOD(mixer_setrecsrc, screamer_setrecsrc), 32890da2b28SAriff Abdullah KOBJMETHOD_END 3298486eb86SNathan Whitehorn }; 3308486eb86SNathan Whitehorn 3318486eb86SNathan Whitehorn MIXER_DECLARE(screamer_mixer); 3328486eb86SNathan Whitehorn 3338486eb86SNathan Whitehorn static int 3348486eb86SNathan Whitehorn screamer_init(struct snd_mixer *m) 3358486eb86SNathan Whitehorn { 3368486eb86SNathan Whitehorn struct davbus_softc *d; 3378486eb86SNathan Whitehorn 3388486eb86SNathan Whitehorn d = mix_getdevinfo(m); 3398486eb86SNathan Whitehorn 3408486eb86SNathan Whitehorn d->read_status = screamer_read_status; 3418486eb86SNathan Whitehorn d->set_outputs = screamer_set_outputs; 3428486eb86SNathan Whitehorn 3438486eb86SNathan Whitehorn mtx_lock(&d->mutex); 3448486eb86SNathan Whitehorn 3458486eb86SNathan Whitehorn screamer_write_locked(d, SCREAMER_CODEC_ADDR0, SCREAMER_INPUT_CD | 3468486eb86SNathan Whitehorn SCREAMER_DEFAULT_CD_GAIN); 3478486eb86SNathan Whitehorn 3488486eb86SNathan Whitehorn screamer_set_outputs(d, screamer_read_status(d, 3498486eb86SNathan Whitehorn bus_read_4(d->reg, DAVBUS_CODEC_STATUS))); 3508486eb86SNathan Whitehorn 3518486eb86SNathan Whitehorn screamer_write_locked(d, SCREAMER_CODEC_ADDR2, 0); 3528486eb86SNathan Whitehorn screamer_write_locked(d, SCREAMER_CODEC_ADDR4, 0); 3538486eb86SNathan Whitehorn screamer_write_locked(d, SCREAMER_CODEC_ADDR5, 0); 3548486eb86SNathan Whitehorn screamer_write_locked(d, SCREAMER_CODEC_ADDR6, 0); 3558486eb86SNathan Whitehorn 3568486eb86SNathan Whitehorn mtx_unlock(&d->mutex); 3578486eb86SNathan Whitehorn 3588486eb86SNathan Whitehorn mix_setdevs(m, SOUND_MASK_VOLUME); 3598486eb86SNathan Whitehorn 3608486eb86SNathan Whitehorn return (0); 3618486eb86SNathan Whitehorn } 3628486eb86SNathan Whitehorn 3638486eb86SNathan Whitehorn static int 3648486eb86SNathan Whitehorn screamer_uninit(struct snd_mixer *m) 3658486eb86SNathan Whitehorn { 3668486eb86SNathan Whitehorn return (0); 3678486eb86SNathan Whitehorn } 3688486eb86SNathan Whitehorn 3698486eb86SNathan Whitehorn static int 3708486eb86SNathan Whitehorn screamer_reinit(struct snd_mixer *m) 3718486eb86SNathan Whitehorn { 3728486eb86SNathan Whitehorn return (0); 3738486eb86SNathan Whitehorn } 3748486eb86SNathan Whitehorn 3758486eb86SNathan Whitehorn 3768486eb86SNathan Whitehorn static void 3778486eb86SNathan Whitehorn screamer_write_locked(struct davbus_softc *d, u_int reg, u_int val) 3788486eb86SNathan Whitehorn { 3798486eb86SNathan Whitehorn u_int x; 3808486eb86SNathan Whitehorn 3818486eb86SNathan Whitehorn KASSERT(val == (val & 0xfff), ("bad val")); 3828486eb86SNathan Whitehorn 3838486eb86SNathan Whitehorn while (bus_read_4(d->reg, DAVBUS_CODEC_CTRL) & DAVBUS_CODEC_BUSY) 3848486eb86SNathan Whitehorn DELAY(100); 3858486eb86SNathan Whitehorn 3868486eb86SNathan Whitehorn x = reg; 3878486eb86SNathan Whitehorn x |= SCREAMER_CODEC_EMSEL0; 3888486eb86SNathan Whitehorn x |= val; 3898486eb86SNathan Whitehorn bus_write_4(d->reg, DAVBUS_CODEC_CTRL, x); 3908486eb86SNathan Whitehorn 3918486eb86SNathan Whitehorn while (bus_read_4(d->reg, DAVBUS_CODEC_CTRL) & DAVBUS_CODEC_BUSY) 3928486eb86SNathan Whitehorn DELAY(100); 3938486eb86SNathan Whitehorn } 3948486eb86SNathan Whitehorn 3958486eb86SNathan Whitehorn /* Must be called with d->mutex held. */ 3968486eb86SNathan Whitehorn static void 3978486eb86SNathan Whitehorn screamer_set_outputs(struct davbus_softc *d, u_int mask) 3988486eb86SNathan Whitehorn { 3998486eb86SNathan Whitehorn u_int x; 4008486eb86SNathan Whitehorn 4018486eb86SNathan Whitehorn if (mask == d->output_mask) { 4028486eb86SNathan Whitehorn return; 4038486eb86SNathan Whitehorn } 4048486eb86SNathan Whitehorn 4058486eb86SNathan Whitehorn x = SCREAMER_MUTE_SPEAKER | SCREAMER_MUTE_HEADPHONES; 4068486eb86SNathan Whitehorn 4078486eb86SNathan Whitehorn DPRINTF(("Enabled outputs: ")); 4088486eb86SNathan Whitehorn 4098486eb86SNathan Whitehorn if (mask & (1 << 0)) { 4108486eb86SNathan Whitehorn DPRINTF(("SPEAKER ")); 4118486eb86SNathan Whitehorn x &= ~SCREAMER_MUTE_SPEAKER; 4128486eb86SNathan Whitehorn } 4138486eb86SNathan Whitehorn if (mask & (1 << 1)) { 4148486eb86SNathan Whitehorn DPRINTF(("HEADPHONES ")); 4158486eb86SNathan Whitehorn x &= ~SCREAMER_MUTE_HEADPHONES; 4168486eb86SNathan Whitehorn } 4178486eb86SNathan Whitehorn 4188486eb86SNathan Whitehorn DPRINTF(("\n")); 4198486eb86SNathan Whitehorn 4208486eb86SNathan Whitehorn if (d->device_id == 5 || d->device_id == 11) { 4218486eb86SNathan Whitehorn DPRINTF(("Enabling programmable output.\n")); 4228486eb86SNathan Whitehorn x |= SCREAMER_PROG_OUTPUT0; 4238486eb86SNathan Whitehorn } 4248486eb86SNathan Whitehorn if (d->device_id == 8 || d->device_id == 11) { 4258486eb86SNathan Whitehorn x &= ~SCREAMER_MUTE_SPEAKER; 4268486eb86SNathan Whitehorn 4278486eb86SNathan Whitehorn if (mask & (1 << 0)) 4288486eb86SNathan Whitehorn x |= SCREAMER_PROG_OUTPUT1; /* enable speaker. */ 4298486eb86SNathan Whitehorn } 4308486eb86SNathan Whitehorn 4318486eb86SNathan Whitehorn screamer_write_locked(d, SCREAMER_CODEC_ADDR1, x); 4328486eb86SNathan Whitehorn d->output_mask = mask; 4338486eb86SNathan Whitehorn } 4348486eb86SNathan Whitehorn 4358486eb86SNathan Whitehorn static u_int 4368486eb86SNathan Whitehorn screamer_read_status(struct davbus_softc *d, u_int status) 4378486eb86SNathan Whitehorn { 4388486eb86SNathan Whitehorn int headphones; 4398486eb86SNathan Whitehorn 4408486eb86SNathan Whitehorn switch (d->device_id) { 4418486eb86SNathan Whitehorn case 5: /* Sawtooth */ 4428486eb86SNathan Whitehorn headphones = (status & 0x4); 4438486eb86SNathan Whitehorn break; 4448486eb86SNathan Whitehorn 4458486eb86SNathan Whitehorn case 8: 4468486eb86SNathan Whitehorn case 11: /* iMac DV */ 4478486eb86SNathan Whitehorn /* The iMac DV has 2 headphone outputs. */ 4488486eb86SNathan Whitehorn headphones = (status & 0x7); 4498486eb86SNathan Whitehorn break; 4508486eb86SNathan Whitehorn 4518486eb86SNathan Whitehorn default: 4528486eb86SNathan Whitehorn headphones = (status & 0x8); 4538486eb86SNathan Whitehorn } 4548486eb86SNathan Whitehorn 4558486eb86SNathan Whitehorn if (headphones) 4568486eb86SNathan Whitehorn return (1 << 1); 4578486eb86SNathan Whitehorn else 4588486eb86SNathan Whitehorn return (1 << 0); 4598486eb86SNathan Whitehorn } 4608486eb86SNathan Whitehorn 4618486eb86SNathan Whitehorn static int 4628486eb86SNathan Whitehorn screamer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 4638486eb86SNathan Whitehorn { 4648486eb86SNathan Whitehorn struct davbus_softc *d; 4658486eb86SNathan Whitehorn int lval, rval; 4668486eb86SNathan Whitehorn 4678486eb86SNathan Whitehorn lval = ((100 - left) * 15 / 100) & 0xf; 4688486eb86SNathan Whitehorn rval = ((100 - right) * 15 / 100) & 0xf; 4698486eb86SNathan Whitehorn DPRINTF(("volume %d %d\n", lval, rval)); 4708486eb86SNathan Whitehorn 4718486eb86SNathan Whitehorn d = mix_getdevinfo(m); 4728486eb86SNathan Whitehorn 4738486eb86SNathan Whitehorn switch (dev) { 4748486eb86SNathan Whitehorn case SOUND_MIXER_VOLUME: 4758486eb86SNathan Whitehorn mtx_lock(&d->mutex); 4768486eb86SNathan Whitehorn screamer_write_locked(d, SCREAMER_CODEC_ADDR2, (lval << 6) | 4778486eb86SNathan Whitehorn rval); 4788486eb86SNathan Whitehorn screamer_write_locked(d, SCREAMER_CODEC_ADDR4, (lval << 6) | 4798486eb86SNathan Whitehorn rval); 4808486eb86SNathan Whitehorn mtx_unlock(&d->mutex); 4818486eb86SNathan Whitehorn 4828486eb86SNathan Whitehorn return (left | (right << 8)); 4838486eb86SNathan Whitehorn } 4848486eb86SNathan Whitehorn 4858486eb86SNathan Whitehorn return (0); 4868486eb86SNathan Whitehorn } 4878486eb86SNathan Whitehorn 48890da2b28SAriff Abdullah static u_int32_t 4898486eb86SNathan Whitehorn screamer_setrecsrc(struct snd_mixer *m, u_int32_t src) 4908486eb86SNathan Whitehorn { 4918486eb86SNathan Whitehorn return (0); 4928486eb86SNathan Whitehorn } 4938486eb86SNathan Whitehorn 4948486eb86SNathan Whitehorn static int 4958486eb86SNathan Whitehorn davbus_attach(device_t self) 4968486eb86SNathan Whitehorn { 497a808d017SNathan Whitehorn struct davbus_softc *sc; 4988486eb86SNathan Whitehorn struct resource *dbdma_irq, *cintr; 4998486eb86SNathan Whitehorn void *cookie; 5008486eb86SNathan Whitehorn char compat[64]; 5018486eb86SNathan Whitehorn int rid, oirq, err; 5028486eb86SNathan Whitehorn 503a808d017SNathan Whitehorn sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); 504a808d017SNathan Whitehorn 5059772bc0bSNathan Whitehorn sc->aoa.sc_dev = self; 5068486eb86SNathan Whitehorn sc->node = ofw_bus_get_node(self); 5078486eb86SNathan Whitehorn sc->soundnode = OF_child(sc->node); 5088486eb86SNathan Whitehorn 5098486eb86SNathan Whitehorn /* Map the controller register space. */ 5108486eb86SNathan Whitehorn rid = 0; 5118486eb86SNathan Whitehorn sc->reg = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); 5128486eb86SNathan Whitehorn if (sc->reg == NULL) 5138486eb86SNathan Whitehorn return (ENXIO); 5148486eb86SNathan Whitehorn 5158486eb86SNathan Whitehorn /* Map the DBDMA channel register space. */ 5168486eb86SNathan Whitehorn rid = 1; 5178486eb86SNathan Whitehorn sc->aoa.sc_odma = bus_alloc_resource_any(self, SYS_RES_MEMORY, 5188486eb86SNathan Whitehorn &rid, RF_ACTIVE); 5198486eb86SNathan Whitehorn if (sc->aoa.sc_odma == NULL) 5208486eb86SNathan Whitehorn return (ENXIO); 5218486eb86SNathan Whitehorn 5228486eb86SNathan Whitehorn /* Establish the DBDMA channel edge-triggered interrupt. */ 5238486eb86SNathan Whitehorn rid = 1; 5248486eb86SNathan Whitehorn dbdma_irq = bus_alloc_resource_any(self, SYS_RES_IRQ, 5258486eb86SNathan Whitehorn &rid, RF_SHAREABLE | RF_ACTIVE); 5268486eb86SNathan Whitehorn if (dbdma_irq == NULL) 5278486eb86SNathan Whitehorn return (ENXIO); 5288486eb86SNathan Whitehorn 5298486eb86SNathan Whitehorn oirq = rman_get_start(dbdma_irq); 5308486eb86SNathan Whitehorn 5318486eb86SNathan Whitehorn DPRINTF(("interrupting at irq %d\n", oirq)); 5328486eb86SNathan Whitehorn 5338486eb86SNathan Whitehorn err = powerpc_config_intr(oirq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW); 5348486eb86SNathan Whitehorn if (err != 0) 5358486eb86SNathan Whitehorn return (err); 5368486eb86SNathan Whitehorn 5379772bc0bSNathan Whitehorn snd_setup_intr(self, dbdma_irq, INTR_MPSAFE, aoa_interrupt, 5389772bc0bSNathan Whitehorn sc, &cookie); 5398486eb86SNathan Whitehorn 5408486eb86SNathan Whitehorn /* Now initialize the controller. */ 5418486eb86SNathan Whitehorn 5428486eb86SNathan Whitehorn bzero(compat, sizeof(compat)); 5438486eb86SNathan Whitehorn OF_getprop(sc->soundnode, "compatible", compat, sizeof(compat)); 5448486eb86SNathan Whitehorn OF_getprop(sc->soundnode, "device-id", &sc->device_id, sizeof(u_int)); 5458486eb86SNathan Whitehorn 5468486eb86SNathan Whitehorn mtx_init(&sc->mutex, "DAVbus", NULL, MTX_DEF); 5478486eb86SNathan Whitehorn 5488486eb86SNathan Whitehorn device_printf(self, "codec: <%s>\n", compat); 5498486eb86SNathan Whitehorn 5508486eb86SNathan Whitehorn /* Setup the control interrupt. */ 5518486eb86SNathan Whitehorn rid = 0; 5528486eb86SNathan Whitehorn cintr = bus_alloc_resource_any(self, SYS_RES_IRQ, 5538486eb86SNathan Whitehorn &rid, RF_SHAREABLE | RF_ACTIVE); 5548486eb86SNathan Whitehorn if (cintr != NULL) 5558486eb86SNathan Whitehorn bus_setup_intr(self, cintr, INTR_TYPE_MISC | INTR_MPSAFE, 5568486eb86SNathan Whitehorn NULL, davbus_cint, sc, &cookie); 5578486eb86SNathan Whitehorn 5588486eb86SNathan Whitehorn /* Initialize controller registers. */ 5598486eb86SNathan Whitehorn bus_write_4(sc->reg, DAVBUS_SOUND_CTRL, DAVBUS_INPUT_SUBFRAME0 | 5608486eb86SNathan Whitehorn DAVBUS_OUTPUT_SUBFRAME0 | DAVBUS_RATE_44100 | DAVBUS_INTR_PORTCHG); 5618486eb86SNathan Whitehorn 5628486eb86SNathan Whitehorn /* Attach DBDMA engine and PCM layer */ 5639772bc0bSNathan Whitehorn err = aoa_attach(sc); 5648486eb86SNathan Whitehorn if (err) 5658486eb86SNathan Whitehorn return (err); 5668486eb86SNathan Whitehorn 5678486eb86SNathan Whitehorn /* Install codec module */ 5688486eb86SNathan Whitehorn if (strcmp(compat, "screamer") == 0) 5698486eb86SNathan Whitehorn mixer_init(self, &screamer_mixer_class, sc); 5708486eb86SNathan Whitehorn else if (strcmp(compat, "burgundy") == 0) 5718486eb86SNathan Whitehorn mixer_init(self, &burgundy_mixer_class, sc); 5728486eb86SNathan Whitehorn 5738486eb86SNathan Whitehorn return (0); 5748486eb86SNathan Whitehorn } 5758486eb86SNathan Whitehorn 5768486eb86SNathan Whitehorn static void 5778486eb86SNathan Whitehorn davbus_cint(void *ptr) 5788486eb86SNathan Whitehorn { 5798486eb86SNathan Whitehorn struct davbus_softc *d = ptr; 5808486eb86SNathan Whitehorn u_int reg, status, mask; 5818486eb86SNathan Whitehorn 5828486eb86SNathan Whitehorn mtx_lock(&d->mutex); 5838486eb86SNathan Whitehorn 5848486eb86SNathan Whitehorn reg = bus_read_4(d->reg, DAVBUS_SOUND_CTRL); 5858486eb86SNathan Whitehorn if (reg & DAVBUS_PORTCHG) { 5868486eb86SNathan Whitehorn 5878486eb86SNathan Whitehorn status = bus_read_4(d->reg, DAVBUS_CODEC_STATUS); 5888486eb86SNathan Whitehorn 5898486eb86SNathan Whitehorn if (d->read_status && d->set_outputs) { 5908486eb86SNathan Whitehorn 5918486eb86SNathan Whitehorn mask = (*d->read_status)(d, status); 5928486eb86SNathan Whitehorn (*d->set_outputs)(d, mask); 5938486eb86SNathan Whitehorn } 5948486eb86SNathan Whitehorn 5958486eb86SNathan Whitehorn /* Clear the interrupt. */ 5968486eb86SNathan Whitehorn bus_write_4(d->reg, DAVBUS_SOUND_CTRL, reg); 5978486eb86SNathan Whitehorn } 5988486eb86SNathan Whitehorn 5998486eb86SNathan Whitehorn mtx_unlock(&d->mutex); 6008486eb86SNathan Whitehorn } 6018486eb86SNathan Whitehorn 602