1 /*- 2 * Copyright (c) 2012 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Paul Fleischer <paul (at) xpg.dk> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 #include <sys/param.h> 32 #include <sys/device.h> 33 #include <sys/kmem.h> 34 35 #include <sys/bus.h> 36 37 #include <arch/arm/s3c2xx0/s3c2440_dma.h> 38 #include <arch/arm/s3c2xx0/s3c2xx0var.h> 39 #include <arch/arm/s3c2xx0/s3c2440reg.h> 40 #include <arch/arm/s3c2xx0/s3c2440_i2s.h> 41 42 /*#define S3C2440_I2S_DEBUG*/ 43 44 #ifdef S3C2440_I2S_DEBUG 45 #define DPRINTF(x) do {printf x; } while (/*CONSTCOND*/0) 46 #else 47 #define DPRINTF(s) do {} while (/*CONSTCOND*/0) 48 #endif 49 50 struct s3c2440_i2s_softc { 51 device_t sc_dev; 52 kmutex_t *sc_intr_lock; 53 bus_space_tag_t sc_iot; 54 bus_space_handle_t sc_i2s_ioh; 55 56 int sc_master_clock; 57 int sc_serial_clock; 58 int sc_dir; 59 int sc_sample_width; 60 int sc_bus_format; 61 62 bus_dma_segment_t sc_dr; 63 }; 64 65 static void s3c2440_i2s_xfer_complete(dmac_xfer_t, void *); 66 67 static int s3c2440_i2s_match(device_t, cfdata_t, void *); 68 static void s3c2440_i2s_attach(device_t, device_t , void *); 69 static int s3c2440_i2s_search(device_t, cfdata_t, const int *, void *); 70 static int s3c2440_i2s_print(void *, const char *); 71 static int s3c2440_i2s_init(struct s3c2440_i2s_softc*); 72 73 CFATTACH_DECL_NEW(ssiis, sizeof(struct s3c2440_i2s_softc), s3c2440_i2s_match, 74 s3c2440_i2s_attach, NULL, NULL); 75 76 int 77 s3c2440_i2s_match(device_t parent, cfdata_t match, void *aux) 78 { 79 80 return 1; 81 } 82 83 void 84 s3c2440_i2s_attach(device_t parent, device_t self, void *aux) 85 { 86 struct s3c2440_i2s_softc *sc = device_private(self); 87 DPRINTF(("%s\n", __func__)); 88 89 sc->sc_dev = self; 90 91 s3c2440_i2s_init(sc); 92 93 printf("\n"); 94 95 config_search(self, NULL, 96 CFARGS(.search = s3c2440_i2s_search)); 97 } 98 99 static int 100 s3c2440_i2s_print(void *aux, const char *name) 101 { 102 return UNCONF; 103 } 104 105 static int 106 s3c2440_i2s_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux) 107 { 108 struct s3c2440_i2s_attach_args ia; 109 DPRINTF(("%s\n", __func__)); 110 111 ia.i2sa_handle = device_private(parent); 112 113 if (config_probe(parent, cf, &ia)) 114 config_attach(parent, cf, &ia, s3c2440_i2s_print, CFARGS_NONE); 115 116 return 1; 117 } 118 119 void 120 s3c2440_i2s_set_intr_lock(void *handle, kmutex_t *sc_intr_lock) 121 { 122 struct s3c2440_i2s_softc *sc = handle; 123 124 sc->sc_intr_lock = sc_intr_lock; 125 } 126 127 int 128 s3c2440_i2s_init(struct s3c2440_i2s_softc *i2s_sc) 129 { 130 struct s3c2xx0_softc *sc = s3c2xx0_softc; /* Shortcut */ 131 uint32_t reg; 132 133 i2s_sc->sc_iot = sc->sc_iot; 134 135 if (bus_space_map(sc->sc_iot, S3C2440_IIS_BASE, S3C24X0_IIS_SIZE, 0, 136 &i2s_sc->sc_i2s_ioh)) { 137 printf("Failed to map I2S registers\n"); 138 return ENOMEM; 139 } 140 141 i2s_sc->sc_master_clock = 0; 142 i2s_sc->sc_serial_clock = 48; 143 i2s_sc->sc_dir = 0; 144 i2s_sc->sc_sample_width = 0; 145 i2s_sc->sc_bus_format = 0; 146 147 reg = bus_space_read_4(sc->sc_iot, sc->sc_clkman_ioh, CLKMAN_CLKCON); 148 bus_space_write_4(sc->sc_iot, sc->sc_clkman_ioh, CLKMAN_CLKCON, reg | CLKCON_IIS); 149 150 /* Setup GPIO pins to use I2S */ 151 reg = bus_space_read_4(sc->sc_iot, sc->sc_gpio_ioh, GPIO_PECON); 152 reg = GPIO_SET_FUNC(reg, 0, 2); 153 reg = GPIO_SET_FUNC(reg, 1, 2); 154 reg = GPIO_SET_FUNC(reg, 2, 2); 155 reg = GPIO_SET_FUNC(reg, 3, 2); 156 reg = GPIO_SET_FUNC(reg, 4, 2); 157 bus_space_write_4(sc->sc_iot, sc->sc_gpio_ioh, GPIO_PECON, reg); 158 159 /* Disable Pull-up resistor for all I2S pins */ 160 reg = bus_space_read_4(sc->sc_iot, sc->sc_gpio_ioh, GPIO_PEUP); 161 162 reg = GPIO_SET_DATA(reg, 0, 1); 163 reg = GPIO_SET_DATA(reg, 1, 1); 164 reg = GPIO_SET_DATA(reg, 2, 1); 165 reg = GPIO_SET_DATA(reg, 3, 1); 166 reg = GPIO_SET_DATA(reg, 4, 1); 167 168 bus_space_write_4(sc->sc_iot, sc->sc_gpio_ioh, GPIO_PEUP, reg); 169 170 i2s_sc->sc_dr.ds_addr = S3C2440_IIS_BASE + IISFIFO; 171 i2s_sc->sc_dr.ds_len = 4; 172 173 return 0; 174 } 175 176 void 177 s3c2440_i2s_set_direction(void *handle, int direction) 178 { 179 struct s3c2440_i2s_softc *sc = handle; 180 sc->sc_dir = direction; 181 } 182 183 void 184 s3c2440_i2s_set_sample_rate(void *handle, int sample_rate) 185 { 186 struct s3c2440_i2s_softc *sc = handle; 187 int codecClock; 188 int codecClockPrescaler; 189 int pclk = s3c2xx0_softc->sc_pclk; /* Peripherical Clock in Hz*/ 190 191 DPRINTF(("%s\n", __func__)); 192 193 /* TODO: Add selection of 256fs when needed */ 194 sc->sc_master_clock = 384; 195 196 codecClock = sample_rate * sc->sc_master_clock; 197 codecClockPrescaler = pclk/codecClock; 198 199 DPRINTF(("CODEC Clock: %d Hz\n", codecClock)); 200 DPRINTF(("Prescaler: %d\n", codecClockPrescaler)); 201 DPRINTF(("Actual CODEC Clock: %d Hz\n", pclk/(codecClockPrescaler+1))); 202 DPRINTF(("Actual Sampling rate: %d Hz\n", 203 (pclk/(codecClockPrescaler+1))/sc->sc_master_clock)); 204 205 bus_space_write_4(sc->sc_iot, sc->sc_i2s_ioh, IISPSR, 206 IISPSR_PRESCALER_A(codecClockPrescaler) | 207 IISPSR_PRESCALER_B(codecClockPrescaler)); 208 } 209 210 void 211 s3c2440_i2s_set_sample_width(void *handle, int width) 212 { 213 struct s3c2440_i2s_softc *sc = handle; 214 sc->sc_sample_width = width; 215 } 216 217 void 218 s3c2440_i2s_set_bus_format(void *handle, int format) 219 { 220 struct s3c2440_i2s_softc *sc = handle; 221 222 sc->sc_bus_format = format; 223 } 224 225 int 226 s3c2440_i2s_commit(void *handle) 227 { 228 uint32_t iisfcon, iiscon, iismod; 229 struct s3c2440_i2s_softc *sc = handle; 230 231 DPRINTF(("%s\n", __func__)); 232 233 iisfcon = 0; 234 iiscon = IISCON_IFACE_EN | IISCON_PRESCALER_EN; 235 iismod = 0; 236 237 if ( (sc->sc_dir & S3C2440_I2S_TRANSMIT) ) { 238 iisfcon |= IISFCON_TX_DMA_EN | IISFCON_TX_FIFO_EN; 239 iiscon |= IISCON_TX_DMA_EN; 240 iismod |= IISMOD_MODE_TRANSMIT; 241 } 242 243 if ( (sc->sc_dir & S3C2440_I2S_RECEIVE) ) { 244 iisfcon |= IISFCON_RX_DMA_EN | IISFCON_RX_FIFO_EN; 245 iiscon |= IISCON_RX_DMA_EN; 246 iismod |= IISMOD_MODE_RECEIVE; 247 } 248 249 if (iisfcon == 0) { 250 return EINVAL; 251 } 252 253 254 if (sc->sc_bus_format == S3C2440_I2S_BUS_MSB) 255 iismod |= IISMOD_IFACE_MSB; 256 257 switch (sc->sc_master_clock) { 258 case 256: 259 iismod |= IISMOD_MASTER_FREQ256; 260 break; 261 case 384: 262 iismod |= IISMOD_MASTER_FREQ384; 263 break; 264 default: 265 return EINVAL; 266 267 } 268 269 switch (sc->sc_serial_clock) { 270 case 16: 271 iismod |= IISMOD_SERIAL_FREQ16; 272 break; 273 case 32: 274 iismod |= IISMOD_SERIAL_FREQ32; 275 break; 276 case 48: 277 iismod |= IISMOD_SERIAL_FREQ48; 278 break; 279 default: 280 return EINVAL; 281 } 282 283 if (sc->sc_sample_width == 16) 284 iismod |= IISMOD_16BIT; 285 286 bus_space_write_4(sc->sc_iot, sc->sc_i2s_ioh, IISFCON, iisfcon); 287 bus_space_write_4(sc->sc_iot, sc->sc_i2s_ioh, IISMOD, iismod); 288 bus_space_write_4(sc->sc_iot, sc->sc_i2s_ioh, IISCON, iiscon); 289 290 return 0; 291 } 292 293 int 294 s3c2440_i2s_disable(void *handle) 295 { 296 return 0; 297 } 298 299 int 300 s3c2440_i2s_get_master_clock(void *handle) 301 { 302 struct s3c2440_i2s_softc *sc = handle; 303 return sc->sc_master_clock; 304 } 305 306 int 307 s3c2440_i2s_get_serial_clock(void *handle) 308 { 309 struct s3c2440_i2s_softc *sc = handle; 310 311 return sc->sc_serial_clock; 312 } 313 314 int 315 s3c2440_i2s_alloc(void *handle, 316 int direction, size_t size, int flags, 317 s3c2440_i2s_buf_t *out) 318 { 319 int retval = 0; 320 struct s3c2xx0_softc *sc = s3c2xx0_softc; /* Shortcut */ 321 s3c2440_i2s_buf_t buf; 322 323 DPRINTF(("%s\n", __func__)); 324 325 *out = kmem_alloc(sizeof(struct s3c2440_i2s_buf), KM_SLEEP); 326 buf = *out; 327 buf->i2b_parent = handle; 328 buf->i2b_size = size; 329 buf->i2b_nsegs = S3C2440_I2S_BUF_MAX_SEGS; 330 buf->i2b_xfer = NULL; 331 buf->i2b_cb = NULL; 332 buf->i2b_cb_cookie = NULL; 333 334 /* We first allocate some DMA-friendly memory for the buffer... */ 335 retval = bus_dmamem_alloc(sc->sc_dmat, buf->i2b_size, NBPG, 0, 336 buf->i2b_segs, buf->i2b_nsegs, &buf->i2b_nsegs, 337 BUS_DMA_WAITOK); 338 if (retval != 0) { 339 printf("%s: Failed to allocate DMA memory\n", __func__); 340 goto cleanup_dealloc; 341 } 342 343 DPRINTF(("%s: Using %d DMA segments\n", __func__, buf->i2b_nsegs)); 344 345 retval = bus_dmamem_map(sc->sc_dmat, buf->i2b_segs, buf->i2b_nsegs, 346 buf->i2b_size, &buf->i2b_addr, BUS_DMA_WAITOK); 347 348 if (retval != 0) { 349 printf("%s: Failed to map DMA memory\n", __func__); 350 goto cleanup_dealloc_dma; 351 } 352 353 DPRINTF(("%s: Playback DMA buffer mapped at %p\n", __func__, 354 buf->i2b_addr)); 355 356 /* XXX: Not sure if nsegments is really 1...*/ 357 retval = bus_dmamap_create(sc->sc_dmat, buf->i2b_size, 1, 358 buf->i2b_size, 0, BUS_DMA_WAITOK, 359 &buf->i2b_dmamap); 360 if (retval != 0) { 361 printf("%s: Failed to create DMA map\n", __func__); 362 goto cleanup_unmap_dma; 363 } 364 365 DPRINTF(("%s: DMA map created successfully\n", __func__)); 366 367 buf->i2b_xfer = s3c2440_dmac_allocate_xfer(); 368 369 return 0; 370 cleanup_unmap_dma: 371 bus_dmamem_unmap(sc->sc_dmat, &buf->i2b_addr, buf->i2b_size); 372 cleanup_dealloc_dma: 373 bus_dmamem_free(sc->sc_dmat, buf->i2b_segs, buf->i2b_nsegs); 374 cleanup_dealloc: 375 kmem_free(*out, sizeof(struct s3c2440_i2s_buf)); 376 return retval; 377 } 378 379 void 380 s3c2440_i2s_free(s3c2440_i2s_buf_t buf) 381 { 382 struct s3c2xx0_softc *sc = s3c2xx0_softc; /* Shortcut */ 383 384 if (buf->i2b_xfer != NULL) { 385 s3c2440_dmac_free_xfer(buf->i2b_xfer); 386 } 387 388 bus_dmamap_unload(sc->sc_dmat, buf->i2b_dmamap); 389 bus_dmamap_destroy(sc->sc_dmat, buf->i2b_dmamap); 390 bus_dmamem_unmap(sc->sc_dmat, &buf->i2b_addr, buf->i2b_size); 391 bus_dmamem_free(sc->sc_dmat, buf->i2b_segs, buf->i2b_nsegs); 392 kmem_free(buf, sizeof(struct s3c2440_i2s_buf)); 393 } 394 395 int 396 s3c2440_i2s_output(s3c2440_i2s_buf_t buf, void *block, int bsize, 397 void (*callback)(void*), void *cb_cookie) 398 { 399 struct s3c2xx0_softc *sc = s3c2xx0_softc; /* Shortcut */ 400 struct s3c2440_i2s_softc *i2s = buf->i2b_parent; 401 int retval; 402 dmac_xfer_t xfer = buf->i2b_xfer; 403 404 retval = bus_dmamap_load(sc->sc_dmat, buf->i2b_dmamap, block, 405 bsize, NULL, BUS_DMA_NOWAIT); 406 if (retval != 0) { 407 printf("Failed to load DMA map\n"); 408 return retval; 409 } 410 411 xfer->dx_desc[DMAC_DESC_DST].xd_bus_type = DMAC_BUS_TYPE_PERIPHERAL; 412 xfer->dx_desc[DMAC_DESC_DST].xd_increment = FALSE; 413 xfer->dx_desc[DMAC_DESC_DST].xd_nsegs = 1; 414 xfer->dx_desc[DMAC_DESC_DST].xd_dma_segs = &i2s->sc_dr; 415 416 xfer->dx_desc[DMAC_DESC_SRC].xd_bus_type = DMAC_BUS_TYPE_SYSTEM; 417 xfer->dx_desc[DMAC_DESC_SRC].xd_increment = TRUE; 418 xfer->dx_desc[DMAC_DESC_SRC].xd_nsegs = buf->i2b_dmamap->dm_nsegs; 419 xfer->dx_desc[DMAC_DESC_SRC].xd_dma_segs = buf->i2b_dmamap->dm_segs; 420 421 xfer->dx_peripheral = DMAC_PERIPH_I2SSDO; 422 423 if (i2s->sc_sample_width == 16) 424 xfer->dx_xfer_width = DMAC_XFER_WIDTH_16BIT; 425 else if (i2s->sc_sample_width == 8) 426 xfer->dx_xfer_width = DMAC_XFER_WIDTH_8BIT; 427 428 xfer->dx_done = s3c2440_i2s_xfer_complete; 429 xfer->dx_cookie = buf; 430 xfer->dx_xfer_mode = DMAC_XFER_MODE_HANDSHAKE; 431 432 buf->i2b_cb = callback; 433 buf->i2b_cb_cookie = cb_cookie; 434 435 s3c2440_dmac_start_xfer(buf->i2b_xfer); 436 437 return 0; 438 } 439 440 int 441 s3c2440_i2s_halt_output(s3c2440_i2s_buf_t buf) 442 { 443 /*int retval;*/ 444 struct s3c2xx0_softc *sc = s3c2xx0_softc; /* Shortcut */ 445 446 DPRINTF(("Aborting DMA transfer\n")); 447 /*do { 448 retval =*/ s3c2440_dmac_abort_xfer(buf->i2b_xfer); 449 /*} while(retval != 0);*/ 450 DPRINTF(("Aborting DMA transfer: SUCCESS\n")); 451 452 bus_dmamap_unload(sc->sc_dmat, buf->i2b_dmamap); 453 454 return 0; 455 } 456 457 int 458 s3c2440_i2s_input(s3c2440_i2s_buf_t buf, void *block, int bsize, 459 void (*callback)(void*), void *cb_cookie) 460 { 461 struct s3c2xx0_softc *sc = s3c2xx0_softc; /* Shortcut */ 462 struct s3c2440_i2s_softc *i2s = buf->i2b_parent; 463 int retval; 464 dmac_xfer_t xfer = buf->i2b_xfer; 465 466 retval = bus_dmamap_load(sc->sc_dmat, buf->i2b_dmamap, block, 467 bsize, NULL, BUS_DMA_NOWAIT); 468 if (retval != 0) { 469 printf("Failed to load DMA map\n"); 470 return retval; 471 } 472 473 xfer->dx_desc[DMAC_DESC_SRC].xd_bus_type = DMAC_BUS_TYPE_PERIPHERAL; 474 xfer->dx_desc[DMAC_DESC_SRC].xd_increment = FALSE; 475 xfer->dx_desc[DMAC_DESC_SRC].xd_nsegs = 1; 476 xfer->dx_desc[DMAC_DESC_SRC].xd_dma_segs = &i2s->sc_dr; 477 478 xfer->dx_desc[DMAC_DESC_DST].xd_bus_type = DMAC_BUS_TYPE_SYSTEM; 479 xfer->dx_desc[DMAC_DESC_DST].xd_increment = TRUE; 480 xfer->dx_desc[DMAC_DESC_DST].xd_nsegs = buf->i2b_dmamap->dm_nsegs; 481 xfer->dx_desc[DMAC_DESC_DST].xd_dma_segs = buf->i2b_dmamap->dm_segs; 482 483 xfer->dx_peripheral = DMAC_PERIPH_I2SSDI; 484 485 if (i2s->sc_sample_width == 16) 486 xfer->dx_xfer_width = DMAC_XFER_WIDTH_16BIT; 487 else if (i2s->sc_sample_width == 8) 488 xfer->dx_xfer_width = DMAC_XFER_WIDTH_8BIT; 489 490 xfer->dx_done = s3c2440_i2s_xfer_complete; 491 xfer->dx_cookie = buf; 492 xfer->dx_xfer_mode = DMAC_XFER_MODE_HANDSHAKE; 493 494 buf->i2b_cb = callback; 495 buf->i2b_cb_cookie = cb_cookie; 496 497 s3c2440_dmac_start_xfer(buf->i2b_xfer); 498 499 return 0; 500 } 501 502 static void 503 s3c2440_i2s_xfer_complete(dmac_xfer_t xfer, void *cookie) 504 { 505 struct s3c2xx0_softc *sc = s3c2xx0_softc; /* Shortcut */ 506 s3c2440_i2s_buf_t buf = cookie; 507 struct s3c2440_i2s_softc *i2s = buf->i2b_parent; 508 509 bus_dmamap_unload(sc->sc_dmat, buf->i2b_dmamap); 510 511 mutex_spin_enter(i2s->sc_intr_lock); 512 (buf->i2b_cb)(buf->i2b_cb_cookie); 513 mutex_spin_exit(i2s->sc_intr_lock); 514 } 515