1187692Snwhitehorn/*- 2187692Snwhitehorn * Copyright 2008 by Marco Trillo. All rights reserved. 3187692Snwhitehorn * 4187692Snwhitehorn * Redistribution and use in source and binary forms, with or without 5187692Snwhitehorn * modification, are permitted provided that the following conditions 6187692Snwhitehorn * are met: 7187692Snwhitehorn * 1. Redistributions of source code must retain the above copyright 8187692Snwhitehorn * notice, this list of conditions and the following disclaimer. 9187692Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright 10187692Snwhitehorn * notice, this list of conditions and the following disclaimer in the 11187692Snwhitehorn * documentation and/or other materials provided with the distribution. 12187692Snwhitehorn * 13187692Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14187692Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15187692Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16187692Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17187692Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 18187692Snwhitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19187692Snwhitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 20187692Snwhitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21187692Snwhitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22187692Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23187692Snwhitehorn * SUCH DAMAGE. 24187692Snwhitehorn * 25187692Snwhitehorn * $FreeBSD$ 26187692Snwhitehorn */ 27187692Snwhitehorn/*- 28187692Snwhitehorn * Copyright (c) 2002, 2003 Tsubai Masanari. All rights reserved. 29187692Snwhitehorn * 30187692Snwhitehorn * Redistribution and use in source and binary forms, with or without 31187692Snwhitehorn * modification, are permitted provided that the following conditions 32187692Snwhitehorn * are met: 33187692Snwhitehorn * 1. Redistributions of source code must retain the above copyright 34187692Snwhitehorn * notice, this list of conditions and the following disclaimer. 35187692Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright 36187692Snwhitehorn * notice, this list of conditions and the following disclaimer in the 37187692Snwhitehorn * documentation and/or other materials provided with the distribution. 38187692Snwhitehorn * 3. The name of the author may not be used to endorse or promote products 39187692Snwhitehorn * derived from this software without specific prior written permission. 40187692Snwhitehorn * 41187692Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 42187692Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43187692Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 44187692Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 45187692Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46187692Snwhitehorn * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 47187692Snwhitehorn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 48187692Snwhitehorn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 49187692Snwhitehorn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 50187692Snwhitehorn * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 51187692Snwhitehorn * 52187692Snwhitehorn * NetBSD: snapper.c,v 1.28 2008/05/16 03:49:54 macallan Exp 53187692Snwhitehorn * Id: snapper.c,v 1.11 2002/10/31 17:42:13 tsubai Exp 54187692Snwhitehorn */ 55187692Snwhitehorn 56187692Snwhitehorn/* 57187692Snwhitehorn * Apple I2S audio controller. 58187692Snwhitehorn */ 59187692Snwhitehorn 60187692Snwhitehorn#include <sys/param.h> 61187692Snwhitehorn#include <sys/systm.h> 62187692Snwhitehorn#include <sys/kernel.h> 63187692Snwhitehorn#include <sys/module.h> 64187692Snwhitehorn#include <sys/bus.h> 65187692Snwhitehorn#include <sys/malloc.h> 66187692Snwhitehorn#include <sys/lock.h> 67187692Snwhitehorn#include <sys/mutex.h> 68187692Snwhitehorn#include <machine/dbdma.h> 69187692Snwhitehorn#include <machine/intr_machdep.h> 70187692Snwhitehorn#include <machine/resource.h> 71187692Snwhitehorn#include <machine/bus.h> 72187692Snwhitehorn#include <machine/pio.h> 73187692Snwhitehorn#include <sys/rman.h> 74187692Snwhitehorn#include <dev/ofw/ofw_bus.h> 75193640Sariff 76193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 77193640Sariff#include "opt_snd.h" 78193640Sariff#endif 79193640Sariff 80187692Snwhitehorn#include <dev/sound/pcm/sound.h> 81187692Snwhitehorn#include <dev/sound/macio/aoa.h> 82193640Sariff 83187692Snwhitehorn#include <powerpc/powermac/macgpiovar.h> 84187692Snwhitehorn 85187692Snwhitehornstruct i2s_softc { 86187692Snwhitehorn struct aoa_softc aoa; 87187692Snwhitehorn phandle_t node; 88187692Snwhitehorn phandle_t soundnode; 89187692Snwhitehorn struct resource *reg; 90187692Snwhitehorn u_int output_mask; 91187692Snwhitehorn struct mtx port_mtx; 92187692Snwhitehorn}; 93187692Snwhitehorn 94187692Snwhitehornstatic int i2s_probe(device_t); 95187692Snwhitehornstatic int i2s_attach(device_t); 96187692Snwhitehornstatic void i2s_postattach(void *); 97187692Snwhitehornstatic int i2s_setup(struct i2s_softc *, u_int, u_int, u_int); 98187692Snwhitehornstatic void i2s_mute_headphone (struct i2s_softc *, int); 99187692Snwhitehornstatic void i2s_mute_lineout (struct i2s_softc *, int); 100187692Snwhitehornstatic void i2s_mute_speaker (struct i2s_softc *, int); 101187692Snwhitehornstatic void i2s_set_outputs(void *, u_int); 102187692Snwhitehorn 103187692Snwhitehornstatic struct intr_config_hook *i2s_delayed_attach = NULL; 104187692Snwhitehorn 105187692Snwhitehornkobj_class_t i2s_mixer_class = NULL; 106187692Snwhitehorndevice_t i2s_mixer = NULL; 107187692Snwhitehorn 108187692Snwhitehornstatic device_method_t pcm_i2s_methods[] = { 109187692Snwhitehorn /* Device interface. */ 110187692Snwhitehorn DEVMETHOD(device_probe, i2s_probe), 111187692Snwhitehorn DEVMETHOD(device_attach, i2s_attach), 112187692Snwhitehorn 113187692Snwhitehorn { 0, 0 } 114187692Snwhitehorn}; 115187692Snwhitehorn 116187692Snwhitehornstatic driver_t pcm_i2s_driver = { 117187692Snwhitehorn "pcm", 118187692Snwhitehorn pcm_i2s_methods, 119187717Snwhitehorn PCM_SOFTC_SIZE 120187692Snwhitehorn}; 121187692Snwhitehorn 122187692SnwhitehornDRIVER_MODULE(pcm_i2s, macio, pcm_i2s_driver, pcm_devclass, 0, 0); 123187692SnwhitehornMODULE_DEPEND(pcm_i2s, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 124187692Snwhitehorn 125187692Snwhitehornstatic int aoagpio_probe(device_t); 126187692Snwhitehornstatic int aoagpio_attach(device_t); 127187692Snwhitehorn 128187692Snwhitehornstatic device_method_t aoagpio_methods[] = { 129187692Snwhitehorn /* Device interface. */ 130187692Snwhitehorn DEVMETHOD(device_probe, aoagpio_probe), 131187692Snwhitehorn DEVMETHOD(device_attach, aoagpio_attach), 132187692Snwhitehorn 133187692Snwhitehorn { 0, 0 } 134187692Snwhitehorn}; 135187692Snwhitehorn 136187692Snwhitehornstruct aoagpio_softc { 137187692Snwhitehorn device_t dev; 138187692Snwhitehorn int ctrl; 139187692Snwhitehorn int detect_active; /* for extint-gpio */ 140187692Snwhitehorn int level; /* for extint-gpio */ 141187692Snwhitehorn struct i2s_softc *i2s; /* for extint-gpio */ 142187692Snwhitehorn}; 143187692Snwhitehorn 144187692Snwhitehornstatic driver_t aoagpio_driver = { 145187692Snwhitehorn "aoagpio", 146187692Snwhitehorn aoagpio_methods, 147187692Snwhitehorn sizeof(struct aoagpio_softc) 148187692Snwhitehorn}; 149187692Snwhitehornstatic devclass_t aoagpio_devclass; 150187692Snwhitehorn 151187692SnwhitehornDRIVER_MODULE(aoagpio, macgpio, aoagpio_driver, aoagpio_devclass, 0, 0); 152187692Snwhitehorn 153187692Snwhitehorn 154187692Snwhitehorn/***************************************************************************** 155187692Snwhitehorn Probe and attachment routines. 156187692Snwhitehorn *****************************************************************************/ 157187692Snwhitehornstatic int 158187692Snwhitehorni2s_probe(device_t self) 159187692Snwhitehorn{ 160187692Snwhitehorn const char *name; 161223554Snwhitehorn phandle_t subchild; 162223554Snwhitehorn char subchildname[255]; 163187692Snwhitehorn 164187692Snwhitehorn name = ofw_bus_get_name(self); 165187692Snwhitehorn if (!name) 166187692Snwhitehorn return (ENXIO); 167187692Snwhitehorn 168187692Snwhitehorn if (strcmp(name, "i2s") != 0) 169187692Snwhitehorn return (ENXIO); 170223554Snwhitehorn 171223554Snwhitehorn /* 172223554Snwhitehorn * Do not attach to "lightshow" I2S devices on Xserves. This controller 173223554Snwhitehorn * is used there to control the LEDs on the front panel, and this 174223554Snwhitehorn * driver can't handle it. 175223554Snwhitehorn */ 176223554Snwhitehorn subchild = OF_child(OF_child(ofw_bus_get_node(self))); 177223554Snwhitehorn if (subchild != 0 && OF_getprop(subchild, "name", subchildname, 178223554Snwhitehorn sizeof(subchildname)) > 0 && strcmp(subchildname, "lightshow") == 0) 179223554Snwhitehorn return (ENXIO); 180187692Snwhitehorn 181187692Snwhitehorn device_set_desc(self, "Apple I2S Audio Controller"); 182187692Snwhitehorn 183187692Snwhitehorn return (0); 184187692Snwhitehorn} 185187692Snwhitehorn 186187692Snwhitehornstatic phandle_t of_find_firstchild_byname(phandle_t, const char *); 187187692Snwhitehorn 188187692Snwhitehornstatic int 189187692Snwhitehorni2s_attach(device_t self) 190187692Snwhitehorn{ 191187717Snwhitehorn struct i2s_softc *sc; 192187692Snwhitehorn struct resource *dbdma_irq; 193187692Snwhitehorn void *dbdma_ih; 194187692Snwhitehorn int rid, oirq, err; 195187692Snwhitehorn phandle_t port; 196187717Snwhitehorn 197187717Snwhitehorn sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); 198187692Snwhitehorn 199188259Snwhitehorn sc->aoa.sc_dev = self; 200187692Snwhitehorn sc->node = ofw_bus_get_node(self); 201187692Snwhitehorn 202187692Snwhitehorn port = of_find_firstchild_byname(sc->node, "i2s-a"); 203187692Snwhitehorn if (port == -1) 204187692Snwhitehorn return (ENXIO); 205187692Snwhitehorn sc->soundnode = of_find_firstchild_byname(port, "sound"); 206187692Snwhitehorn if (sc->soundnode == -1) 207187692Snwhitehorn return (ENXIO); 208187692Snwhitehorn 209187692Snwhitehorn mtx_init(&sc->port_mtx, "port_mtx", NULL, MTX_DEF); 210187692Snwhitehorn 211187692Snwhitehorn /* Map the controller register space. */ 212187692Snwhitehorn rid = 0; 213187692Snwhitehorn sc->reg = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE); 214187692Snwhitehorn if (sc->reg == NULL) 215187692Snwhitehorn return ENXIO; 216187692Snwhitehorn 217187692Snwhitehorn /* Map the DBDMA channel register space. */ 218187692Snwhitehorn rid = 1; 219187692Snwhitehorn sc->aoa.sc_odma = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, 220187692Snwhitehorn RF_ACTIVE); 221187692Snwhitehorn if (sc->aoa.sc_odma == NULL) 222187692Snwhitehorn return ENXIO; 223187692Snwhitehorn 224187692Snwhitehorn /* Establish the DBDMA channel edge-triggered interrupt. */ 225187692Snwhitehorn rid = 1; 226187692Snwhitehorn dbdma_irq = bus_alloc_resource_any(self, SYS_RES_IRQ, 227187692Snwhitehorn &rid, RF_SHAREABLE | RF_ACTIVE); 228187692Snwhitehorn if (dbdma_irq == NULL) 229187692Snwhitehorn return (ENXIO); 230187692Snwhitehorn 231187692Snwhitehorn /* Now initialize the controller. */ 232187692Snwhitehorn err = i2s_setup(sc, 44100, 16, 64); 233187692Snwhitehorn if (err != 0) 234187692Snwhitehorn return (err); 235187692Snwhitehorn 236188259Snwhitehorn snd_setup_intr(self, dbdma_irq, INTR_MPSAFE, aoa_interrupt, 237188259Snwhitehorn sc, &dbdma_ih); 238187692Snwhitehorn 239187692Snwhitehorn oirq = rman_get_start(dbdma_irq); 240187692Snwhitehorn err = powerpc_config_intr(oirq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW); 241187692Snwhitehorn if (err != 0) 242187692Snwhitehorn return (err); 243187692Snwhitehorn 244187692Snwhitehorn /* 245187692Snwhitehorn * Register a hook for delayed attach in order to allow 246187692Snwhitehorn * the I2C controller to attach. 247187692Snwhitehorn */ 248187692Snwhitehorn if ((i2s_delayed_attach = malloc(sizeof(struct intr_config_hook), 249187692Snwhitehorn M_TEMP, M_WAITOK | M_ZERO)) == NULL) 250187692Snwhitehorn return (ENOMEM); 251187692Snwhitehorn 252187692Snwhitehorn i2s_delayed_attach->ich_func = i2s_postattach; 253188259Snwhitehorn i2s_delayed_attach->ich_arg = sc; 254187692Snwhitehorn 255187692Snwhitehorn if (config_intrhook_establish(i2s_delayed_attach) != 0) 256187692Snwhitehorn return (ENOMEM); 257187692Snwhitehorn 258188259Snwhitehorn return (aoa_attach(sc)); 259187692Snwhitehorn} 260187692Snwhitehorn 261187692Snwhitehorn/***************************************************************************** 262187692Snwhitehorn GPIO routines. 263187692Snwhitehorn *****************************************************************************/ 264187692Snwhitehorn 265187692Snwhitehornenum gpio_ctrl { 266187692Snwhitehorn AMP_MUTE, 267187692Snwhitehorn HEADPHONE_MUTE, 268187692Snwhitehorn LINEOUT_MUTE, 269187692Snwhitehorn AUDIO_HW_RESET, 270187692Snwhitehorn HEADPHONE_DETECT, 271187692Snwhitehorn LINEOUT_DETECT, 272187692Snwhitehorn GPIO_CTRL_NUM 273187692Snwhitehorn}; 274187692Snwhitehorn 275187692Snwhitehorn#define GPIO_CTRL_EXTINT_SET \ 276187692Snwhitehorn ((1 << HEADPHONE_DETECT) | \ 277187692Snwhitehorn (1 << LINEOUT_DETECT)) 278187692Snwhitehorn 279187692Snwhitehornstatic struct aoagpio_softc *gpio_ctrls[GPIO_CTRL_NUM] = 280187692Snwhitehorn {NULL, NULL, NULL, NULL, NULL, NULL}; 281187692Snwhitehorn 282187692Snwhitehornstatic struct gpio_match { 283187692Snwhitehorn const char *name; 284187692Snwhitehorn enum gpio_ctrl ctrl; 285187692Snwhitehorn} gpio_controls[] = { 286187692Snwhitehorn {"headphone-mute", HEADPHONE_MUTE}, 287187692Snwhitehorn {"lineout-mute", LINEOUT_MUTE}, 288187692Snwhitehorn {"amp-mute", AMP_MUTE}, 289187692Snwhitehorn {"headphone-detect", HEADPHONE_DETECT}, 290187692Snwhitehorn {"lineout-detect", LINEOUT_DETECT}, 291187692Snwhitehorn {"line-output-detect", LINEOUT_DETECT}, 292187692Snwhitehorn {"audio-hw-reset", AUDIO_HW_RESET}, 293187692Snwhitehorn {"hw-reset", AUDIO_HW_RESET}, 294187692Snwhitehorn {NULL, GPIO_CTRL_NUM} 295187692Snwhitehorn}; 296187692Snwhitehorn 297187692Snwhitehornstatic void i2s_cint(struct i2s_softc *); 298187692Snwhitehorn 299187692Snwhitehornstatic void 300187692Snwhitehornaoagpio_int(void *cookie) 301187692Snwhitehorn{ 302187692Snwhitehorn device_t self = cookie; 303187692Snwhitehorn struct aoagpio_softc *sc; 304187692Snwhitehorn 305187692Snwhitehorn sc = device_get_softc(self); 306187692Snwhitehorn 307187692Snwhitehorn if (macgpio_read(self) & GPIO_LEVEL_RO) 308187692Snwhitehorn sc->level = sc->detect_active; 309187692Snwhitehorn else 310187692Snwhitehorn sc->level = !(sc->detect_active); 311187692Snwhitehorn 312187692Snwhitehorn if (sc->i2s) 313187692Snwhitehorn i2s_cint(sc->i2s); 314187692Snwhitehorn} 315187692Snwhitehorn 316187692Snwhitehornstatic int 317187692Snwhitehornaoagpio_probe(device_t gpio) 318187692Snwhitehorn{ 319187692Snwhitehorn phandle_t node; 320187692Snwhitehorn char bname[32]; 321187692Snwhitehorn const char *name; 322187692Snwhitehorn struct gpio_match *m; 323187692Snwhitehorn struct aoagpio_softc *sc; 324187692Snwhitehorn 325187692Snwhitehorn node = ofw_bus_get_node(gpio); 326187692Snwhitehorn if (node == 0 || node == -1) 327187692Snwhitehorn return (EINVAL); 328187692Snwhitehorn 329187692Snwhitehorn bzero(bname, sizeof(bname)); 330187692Snwhitehorn if (OF_getprop(node, "audio-gpio", bname, sizeof(bname)) > 2) 331187692Snwhitehorn name = bname; 332187692Snwhitehorn else 333187692Snwhitehorn name = ofw_bus_get_name(gpio); 334187692Snwhitehorn 335187692Snwhitehorn /* Try to find a match. */ 336187692Snwhitehorn for (m = gpio_controls; m->name != NULL; m++) { 337187692Snwhitehorn if (strcmp(name, m->name) == 0) { 338187692Snwhitehorn sc = device_get_softc(gpio); 339187692Snwhitehorn gpio_ctrls[m->ctrl] = sc; 340187692Snwhitehorn sc->dev = gpio; 341187692Snwhitehorn sc->ctrl = m->ctrl; 342187692Snwhitehorn sc->level = 0; 343187692Snwhitehorn sc->detect_active = 0; 344187692Snwhitehorn sc->i2s = NULL; 345187692Snwhitehorn 346187692Snwhitehorn OF_getprop(node, "audio-gpio-active-state", 347187692Snwhitehorn &sc->detect_active, sizeof(sc->detect_active)); 348187692Snwhitehorn 349187692Snwhitehorn if ((1 << m->ctrl) & GPIO_CTRL_EXTINT_SET) 350187692Snwhitehorn aoagpio_int(gpio); 351187692Snwhitehorn 352187692Snwhitehorn device_set_desc(gpio, m->name); 353187692Snwhitehorn device_quiet(gpio); 354187692Snwhitehorn return (0); 355187692Snwhitehorn } 356187692Snwhitehorn } 357187692Snwhitehorn 358187692Snwhitehorn return (ENXIO); 359187692Snwhitehorn} 360187692Snwhitehorn 361187692Snwhitehornstatic int 362187692Snwhitehornaoagpio_attach(device_t gpio) 363187692Snwhitehorn{ 364187692Snwhitehorn struct aoagpio_softc *sc; 365187692Snwhitehorn struct resource *r; 366187692Snwhitehorn void *cookie; 367187692Snwhitehorn int irq, rid = 0; 368187692Snwhitehorn 369187692Snwhitehorn sc = device_get_softc(gpio); 370187692Snwhitehorn 371187692Snwhitehorn if ((1 << sc->ctrl) & GPIO_CTRL_EXTINT_SET) { 372187692Snwhitehorn r = bus_alloc_resource_any(gpio, SYS_RES_IRQ, &rid, RF_ACTIVE); 373187692Snwhitehorn if (r == NULL) 374187692Snwhitehorn return (ENXIO); 375187692Snwhitehorn 376187692Snwhitehorn irq = rman_get_start(r); 377187692Snwhitehorn DPRINTF(("interrupting at irq %d\n", irq)); 378187692Snwhitehorn 379187692Snwhitehorn if (powerpc_config_intr(irq, INTR_TRIGGER_EDGE, 380187692Snwhitehorn INTR_POLARITY_LOW) != 0) 381187692Snwhitehorn return (ENXIO); 382187692Snwhitehorn 383187692Snwhitehorn bus_setup_intr(gpio, r, INTR_TYPE_MISC | INTR_MPSAFE | 384187692Snwhitehorn INTR_ENTROPY, NULL, aoagpio_int, gpio, &cookie); 385187692Snwhitehorn } 386187692Snwhitehorn 387187692Snwhitehorn return (0); 388187692Snwhitehorn} 389187692Snwhitehorn 390187692Snwhitehorn/* 391187692Snwhitehorn * I2S module registers 392187692Snwhitehorn */ 393187692Snwhitehorn#define I2S_INT 0x00 394187692Snwhitehorn#define I2S_FORMAT 0x10 395187692Snwhitehorn#define I2S_FRAMECOUNT 0x40 396187692Snwhitehorn#define I2S_FRAMEMATCH 0x50 397187692Snwhitehorn#define I2S_WORDSIZE 0x60 398187692Snwhitehorn 399187692Snwhitehorn/* I2S_INT register definitions */ 400187692Snwhitehorn#define I2S_INT_CLKSTOPPEND 0x01000000 /* clock-stop interrupt pending */ 401187692Snwhitehorn 402187692Snwhitehorn/* I2S_FORMAT register definitions */ 403187692Snwhitehorn#define CLKSRC_49MHz 0x80000000 /* Use 49152000Hz Osc. */ 404187692Snwhitehorn#define CLKSRC_45MHz 0x40000000 /* Use 45158400Hz Osc. */ 405187692Snwhitehorn#define CLKSRC_18MHz 0x00000000 /* Use 18432000Hz Osc. */ 406187692Snwhitehorn#define MCLK_DIV_MASK 0x1f000000 /* MCLK = SRC / DIV */ 407187692Snwhitehorn#define SCLK_DIV_MASK 0x00f00000 /* SCLK = MCLK / DIV */ 408187692Snwhitehorn#define SCLK_MASTER 0x00080000 /* Master mode */ 409187692Snwhitehorn#define SCLK_SLAVE 0x00000000 /* Slave mode */ 410187692Snwhitehorn#define SERIAL_FORMAT 0x00070000 411187692Snwhitehorn#define SERIAL_SONY 0x00000000 412187692Snwhitehorn#define SERIAL_64x 0x00010000 413187692Snwhitehorn#define SERIAL_32x 0x00020000 414187692Snwhitehorn#define SERIAL_DAV 0x00040000 415187692Snwhitehorn#define SERIAL_SILICON 0x00050000 416187692Snwhitehorn 417187692Snwhitehorn/* I2S_WORDSIZE register definitions */ 418187692Snwhitehorn#define INPUT_STEREO (2 << 24) 419187692Snwhitehorn#define INPUT_MONO (1 << 24) 420187692Snwhitehorn#define INPUT_16BIT (0 << 16) 421187692Snwhitehorn#define INPUT_24BIT (3 << 16) 422187692Snwhitehorn#define OUTPUT_STEREO (2 << 8) 423187692Snwhitehorn#define OUTPUT_MONO (1 << 8) 424187692Snwhitehorn#define OUTPUT_16BIT (0 << 0) 425187692Snwhitehorn#define OUTPUT_24BIT (3 << 0) 426187692Snwhitehorn 427187692Snwhitehorn/* Master clock, needed by some codecs. We hardcode this 428187692Snwhitehorn to 256 * fs as this is valid for most codecs. */ 429187692Snwhitehorn#define MCLK_FS 256 430187692Snwhitehorn 431187692Snwhitehorn/* Number of clock sources we can use. */ 432187692Snwhitehorn#define NCLKS 3 433187692Snwhitehornstatic const struct i2s_clksrc { 434187692Snwhitehorn u_int cs_clock; 435187692Snwhitehorn u_int cs_reg; 436187692Snwhitehorn} clksrc[NCLKS] = { 437187692Snwhitehorn {49152000, CLKSRC_49MHz}, 438187692Snwhitehorn {45158400, CLKSRC_45MHz}, 439187692Snwhitehorn {18432000, CLKSRC_18MHz} 440187692Snwhitehorn}; 441187692Snwhitehorn 442187692Snwhitehorn/* Configure the I2S controller for the required settings. 443187692Snwhitehorn 'rate' is the frame rate. 444187692Snwhitehorn 'wordsize' is the sample size (usually 16 bits). 445187692Snwhitehorn 'sclk_fs' is the SCLK/framerate ratio, which needs to be equal 446187692Snwhitehorn or greater to the number of bits per frame. */ 447187692Snwhitehorn 448187692Snwhitehornstatic int 449187692Snwhitehorni2s_setup(struct i2s_softc *sc, u_int rate, u_int wordsize, u_int sclk_fs) 450187692Snwhitehorn{ 451187692Snwhitehorn u_int mclk, mdiv, sdiv; 452187692Snwhitehorn u_int reg = 0, x, wordformat; 453187692Snwhitehorn u_int i; 454187692Snwhitehorn 455187692Snwhitehorn /* Make sure the settings are consistent... */ 456187692Snwhitehorn if ((wordsize * 2) > sclk_fs) 457187692Snwhitehorn return (EINVAL); 458187692Snwhitehorn 459187692Snwhitehorn if (sclk_fs != 32 && sclk_fs != 64) 460187692Snwhitehorn return (EINVAL); 461187692Snwhitehorn 462187692Snwhitehorn /* 463187692Snwhitehorn * Find a clock source to derive the master clock (MCLK) 464187692Snwhitehorn * and the I2S bit block (SCLK) and set the divisors as 465187692Snwhitehorn * appropriate. 466187692Snwhitehorn */ 467187692Snwhitehorn mclk = rate * MCLK_FS; 468187692Snwhitehorn sdiv = MCLK_FS / sclk_fs; 469187692Snwhitehorn 470187692Snwhitehorn for (i = 0; i < NCLKS; ++i) { 471187692Snwhitehorn if ((clksrc[i].cs_clock % mclk) == 0) { 472187692Snwhitehorn reg = clksrc[i].cs_reg; 473187692Snwhitehorn mdiv = clksrc[i].cs_clock / mclk; 474187692Snwhitehorn break; 475187692Snwhitehorn } 476187692Snwhitehorn } 477187692Snwhitehorn if (reg == 0) 478187692Snwhitehorn return (EINVAL); 479187692Snwhitehorn 480187692Snwhitehorn switch (mdiv) { 481187692Snwhitehorn /* exception cases */ 482187692Snwhitehorn case 1: 483187692Snwhitehorn x = 14; 484187692Snwhitehorn break; 485187692Snwhitehorn case 3: 486187692Snwhitehorn x = 13; 487187692Snwhitehorn break; 488187692Snwhitehorn case 5: 489187692Snwhitehorn x = 12; 490187692Snwhitehorn break; 491187692Snwhitehorn default: 492187692Snwhitehorn x = (mdiv / 2) - 1; 493187692Snwhitehorn break; 494187692Snwhitehorn } 495187692Snwhitehorn reg |= (x << 24) & MCLK_DIV_MASK; 496187692Snwhitehorn 497187692Snwhitehorn switch (sdiv) { 498187692Snwhitehorn case 1: 499187692Snwhitehorn x = 8; 500187692Snwhitehorn break; 501187692Snwhitehorn case 3: 502187692Snwhitehorn x = 9; 503187692Snwhitehorn break; 504187692Snwhitehorn default: 505187692Snwhitehorn x = (sdiv / 2) - 1; 506187692Snwhitehorn break; 507187692Snwhitehorn } 508187692Snwhitehorn reg |= (x << 20) & SCLK_DIV_MASK; 509187692Snwhitehorn 510187692Snwhitehorn /* 511187692Snwhitehorn * XXX use master mode for now. This needs to be 512187692Snwhitehorn * revisited if we want to add recording from SPDIF some day. 513187692Snwhitehorn */ 514187692Snwhitehorn reg |= SCLK_MASTER; 515187692Snwhitehorn 516187692Snwhitehorn switch (sclk_fs) { 517187692Snwhitehorn case 64: 518187692Snwhitehorn reg |= SERIAL_64x; 519187692Snwhitehorn break; 520187692Snwhitehorn case 32: 521187692Snwhitehorn reg |= SERIAL_32x; 522187692Snwhitehorn break; 523187692Snwhitehorn } 524187692Snwhitehorn 525187692Snwhitehorn /* stereo input and output */ 526187692Snwhitehorn wordformat = INPUT_STEREO | OUTPUT_STEREO; 527187692Snwhitehorn 528187692Snwhitehorn switch (wordsize) { 529187692Snwhitehorn case 16: 530187692Snwhitehorn wordformat |= INPUT_16BIT | OUTPUT_16BIT; 531187692Snwhitehorn break; 532187692Snwhitehorn case 24: 533187692Snwhitehorn wordformat |= INPUT_24BIT | OUTPUT_24BIT; 534187692Snwhitehorn break; 535187692Snwhitehorn default: 536187692Snwhitehorn return (EINVAL); 537187692Snwhitehorn } 538187692Snwhitehorn 539187692Snwhitehorn x = bus_read_4(sc->reg, I2S_WORDSIZE); 540187692Snwhitehorn if (x != wordformat) 541187692Snwhitehorn bus_write_4(sc->reg, I2S_WORDSIZE, wordformat); 542187692Snwhitehorn 543187692Snwhitehorn x = bus_read_4(sc->reg, I2S_FORMAT); 544187692Snwhitehorn if (x != reg) { 545187692Snwhitehorn /* 546187692Snwhitehorn * XXX to change the format we need to stop the clock 547187692Snwhitehorn * via the FCR registers. For now, rely on the firmware 548187692Snwhitehorn * to set sane defaults (44100). 549187692Snwhitehorn */ 550187692Snwhitehorn printf("i2s_setup: changing format not supported yet.\n"); 551193640Sariff return (ENOTSUP); 552187692Snwhitehorn 553187692Snwhitehorn#ifdef notyet 554187692Snwhitehorn if (obio_fcr_isset(OBIO_FCR1, I2S0CLKEN)) { 555187692Snwhitehorn 556187692Snwhitehorn bus_space_write_4(sc->sc_tag, sc->sc_bsh, I2S_INT, 557187692Snwhitehorn I2S_INT_CLKSTOPPEND); 558187692Snwhitehorn 559187692Snwhitehorn obio_fcr_clear(OBIO_FCR1, I2S0CLKEN); 560187692Snwhitehorn 561187692Snwhitehorn for (timo = 1000; timo > 0; timo--) { 562187692Snwhitehorn if (bus_space_read_4(sc->sc_tag, sc->sc_bsh, 563187692Snwhitehorn I2S_INT) & I2S_INT_CLKSTOPPEND) 564187692Snwhitehorn break; 565187692Snwhitehorn 566187692Snwhitehorn DELAY(10); 567187692Snwhitehorn } 568187692Snwhitehorn 569187692Snwhitehorn if (timo == 0) 570187692Snwhitehorn printf("%s: timeout waiting for clock to stop\n", 571187692Snwhitehorn sc->sc_dev.dv_xname); 572187692Snwhitehorn } 573187692Snwhitehorn 574187692Snwhitehorn bus_space_write_4(sc->sc_tag, sc->sc_bsh, I2S_FORMAT, reg); 575187692Snwhitehorn 576187692Snwhitehorn obio_fcr_set(OBIO_FCR1, I2S0CLKEN); 577187692Snwhitehorn#endif 578187692Snwhitehorn } 579187692Snwhitehorn 580187692Snwhitehorn return (0); 581187692Snwhitehorn} 582187692Snwhitehorn 583187692Snwhitehorn 584187692Snwhitehorn/* XXX this does not belong here. */ 585187692Snwhitehornstatic phandle_t 586187692Snwhitehornof_find_firstchild_byname(phandle_t node, const char *req_name) 587187692Snwhitehorn{ 588187692Snwhitehorn char name[32]; /* max name len per OF spec. */ 589187692Snwhitehorn phandle_t n; 590187692Snwhitehorn 591187692Snwhitehorn for (n = OF_child(node); n != -1; n = OF_peer(n)) { 592187692Snwhitehorn bzero(name, sizeof(name)); 593187692Snwhitehorn OF_getprop(n, "name", name, sizeof(name)); 594187692Snwhitehorn 595187692Snwhitehorn if (strcmp(name, req_name) == 0) 596187692Snwhitehorn return (n); 597187692Snwhitehorn } 598187692Snwhitehorn 599187692Snwhitehorn return (-1); 600187692Snwhitehorn} 601187692Snwhitehorn 602187692Snwhitehorn 603187692Snwhitehornstatic u_int 604187692Snwhitehorngpio_read(enum gpio_ctrl ctrl) 605187692Snwhitehorn{ 606187692Snwhitehorn struct aoagpio_softc *sc; 607187692Snwhitehorn 608187692Snwhitehorn if ((sc = gpio_ctrls[ctrl]) == NULL) 609187692Snwhitehorn return (0); 610187692Snwhitehorn 611187692Snwhitehorn return (macgpio_read(sc->dev) & GPIO_DATA); 612187692Snwhitehorn} 613187692Snwhitehorn 614187692Snwhitehornstatic void 615187692Snwhitehorngpio_write(enum gpio_ctrl ctrl, u_int x) 616187692Snwhitehorn{ 617187692Snwhitehorn struct aoagpio_softc *sc; 618187692Snwhitehorn u_int reg; 619187692Snwhitehorn 620187692Snwhitehorn if ((sc = gpio_ctrls[ctrl]) == NULL) 621187692Snwhitehorn return; 622187692Snwhitehorn 623187692Snwhitehorn reg = GPIO_DDR_OUTPUT; 624187692Snwhitehorn if (x) 625187692Snwhitehorn reg |= GPIO_DATA; 626187692Snwhitehorn 627187692Snwhitehorn macgpio_write(sc->dev, reg); 628187692Snwhitehorn} 629187692Snwhitehorn 630187692Snwhitehornstatic void 631187692Snwhitehorni2s_cint(struct i2s_softc *sc) 632187692Snwhitehorn{ 633187692Snwhitehorn u_int mask = 0; 634187692Snwhitehorn 635187692Snwhitehorn if (gpio_ctrls[HEADPHONE_DETECT] && 636187692Snwhitehorn gpio_ctrls[HEADPHONE_DETECT]->level) 637187692Snwhitehorn mask |= 1 << 1; 638187692Snwhitehorn 639187692Snwhitehorn if (gpio_ctrls[LINEOUT_DETECT] && 640187692Snwhitehorn gpio_ctrls[LINEOUT_DETECT]->level) 641187692Snwhitehorn mask |= 1 << 2; 642187692Snwhitehorn 643187692Snwhitehorn if (mask == 0) 644187692Snwhitehorn mask = 1 << 0; /* fall back to speakers. */ 645187692Snwhitehorn 646187692Snwhitehorn i2s_set_outputs(sc, mask); 647187692Snwhitehorn} 648187692Snwhitehorn 649187692Snwhitehorn#define reset_active 0 650187692Snwhitehorn 651187692Snwhitehorn/* these values are in microseconds */ 652187692Snwhitehorn#define RESET_SETUP_TIME 5000 653187692Snwhitehorn#define RESET_HOLD_TIME 20000 654187692Snwhitehorn#define RESET_RELEASE_TIME 10000 655187692Snwhitehorn 656187692Snwhitehornstatic void 657187692Snwhitehorni2s_audio_hw_reset(struct i2s_softc *sc) 658187692Snwhitehorn{ 659187692Snwhitehorn if (gpio_ctrls[AUDIO_HW_RESET]) { 660187692Snwhitehorn DPRINTF(("resetting codec\n")); 661187692Snwhitehorn 662187692Snwhitehorn gpio_write(AUDIO_HW_RESET, !reset_active); /* Negate RESET */ 663187692Snwhitehorn DELAY(RESET_SETUP_TIME); 664187692Snwhitehorn 665187692Snwhitehorn gpio_write(AUDIO_HW_RESET, reset_active); /* Assert RESET */ 666187692Snwhitehorn DELAY(RESET_HOLD_TIME); 667187692Snwhitehorn 668187692Snwhitehorn gpio_write(AUDIO_HW_RESET, !reset_active); /* Negate RESET */ 669187692Snwhitehorn DELAY(RESET_RELEASE_TIME); 670187692Snwhitehorn 671187692Snwhitehorn } else { 672187692Snwhitehorn DPRINTF(("no audio_hw_reset\n")); 673187692Snwhitehorn } 674187692Snwhitehorn} 675187692Snwhitehorn 676187692Snwhitehorn#define AMP_ACTIVE 0 /* XXX OF */ 677187692Snwhitehorn#define HEADPHONE_ACTIVE 0 /* XXX OF */ 678187692Snwhitehorn#define LINEOUT_ACTIVE 0 /* XXX OF */ 679187692Snwhitehorn 680187692Snwhitehorn#define MUTE_CONTROL(xxx, yyy) \ 681187692Snwhitehornstatic void \ 682187692Snwhitehorni2s_mute_##xxx(struct i2s_softc *sc, int mute) \ 683187692Snwhitehorn{ \ 684187692Snwhitehorn int x; \ 685187692Snwhitehorn \ 686187692Snwhitehorn if (gpio_ctrls[yyy##_MUTE] == NULL) \ 687187692Snwhitehorn return; \ 688187692Snwhitehorn if (mute) \ 689187692Snwhitehorn x = yyy##_ACTIVE; \ 690187692Snwhitehorn else \ 691187692Snwhitehorn x = ! yyy##_ACTIVE; \ 692187692Snwhitehorn \ 693187692Snwhitehorn if (x != gpio_read(yyy##_MUTE)) \ 694187692Snwhitehorn gpio_write(yyy##_MUTE, x); \ 695187692Snwhitehorn} 696187692Snwhitehorn 697187692SnwhitehornMUTE_CONTROL(speaker, AMP) 698187692SnwhitehornMUTE_CONTROL(headphone, HEADPHONE) 699187692SnwhitehornMUTE_CONTROL(lineout, LINEOUT) 700187692Snwhitehorn 701187692Snwhitehornstatic void 702187692Snwhitehorni2s_set_outputs(void *ptr, u_int mask) 703187692Snwhitehorn{ 704187692Snwhitehorn struct i2s_softc *sc = ptr; 705187692Snwhitehorn 706187692Snwhitehorn if (mask == sc->output_mask) 707187692Snwhitehorn return; 708187692Snwhitehorn 709187692Snwhitehorn mtx_lock(&sc->port_mtx); 710187692Snwhitehorn 711187692Snwhitehorn i2s_mute_speaker(sc, 1); 712187692Snwhitehorn i2s_mute_headphone(sc, 1); 713187692Snwhitehorn i2s_mute_lineout(sc, 1); 714187692Snwhitehorn 715187692Snwhitehorn DPRINTF(("enabled outputs: ")); 716187692Snwhitehorn 717187692Snwhitehorn if (mask & (1 << 0)) { 718187692Snwhitehorn DPRINTF(("SPEAKER ")); 719187692Snwhitehorn i2s_mute_speaker(sc, 0); 720187692Snwhitehorn } 721187692Snwhitehorn if (mask & (1 << 1)) { 722187692Snwhitehorn DPRINTF(("HEADPHONE ")); 723187692Snwhitehorn i2s_mute_headphone(sc, 0); 724187692Snwhitehorn } 725187692Snwhitehorn if (mask & (1 << 2)) { 726187692Snwhitehorn DPRINTF(("LINEOUT ")); 727187692Snwhitehorn i2s_mute_lineout(sc, 0); 728187692Snwhitehorn } 729187692Snwhitehorn 730187692Snwhitehorn DPRINTF(("\n")); 731187692Snwhitehorn sc->output_mask = mask; 732187692Snwhitehorn 733187692Snwhitehorn mtx_unlock(&sc->port_mtx); 734187692Snwhitehorn} 735187692Snwhitehorn 736187692Snwhitehornstatic void 737188259Snwhitehorni2s_postattach(void *xsc) 738187692Snwhitehorn{ 739188259Snwhitehorn struct i2s_softc *sc = xsc; 740188259Snwhitehorn device_t self; 741187692Snwhitehorn int i; 742187692Snwhitehorn 743188259Snwhitehorn self = sc->aoa.sc_dev; 744187692Snwhitehorn 745187692Snwhitehorn /* Reset the codec. */ 746187692Snwhitehorn i2s_audio_hw_reset(sc); 747187692Snwhitehorn 748187692Snwhitehorn /* If we have a codec, initialize it. */ 749187692Snwhitehorn if (i2s_mixer) 750187692Snwhitehorn mixer_init(self, i2s_mixer_class, i2s_mixer); 751187692Snwhitehorn 752187692Snwhitehorn /* Read initial port status. */ 753187692Snwhitehorn i2s_cint(sc); 754187692Snwhitehorn 755187692Snwhitehorn /* Enable GPIO interrupt callback. */ 756187692Snwhitehorn for (i = 0; i < GPIO_CTRL_NUM; i++) 757187692Snwhitehorn if (gpio_ctrls[i]) 758187692Snwhitehorn gpio_ctrls[i]->i2s = sc; 759187692Snwhitehorn 760187692Snwhitehorn config_intrhook_disestablish(i2s_delayed_attach); 761187692Snwhitehorn free(i2s_delayed_attach, M_TEMP); 762187692Snwhitehorn} 763187692Snwhitehorn 764