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@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/malloc.h> 34 #include <sys/kmem.h> 35 36 #include <sys/bus.h> 37 38 #include <arch/arm/s3c2xx0/s3c2440_dma.h> 39 #include <arch/arm/s3c2xx0/s3c2xx0var.h> 40 #include <arch/arm/s3c2xx0/s3c2440reg.h> 41 #include <arch/arm/s3c2xx0/s3c2440_i2s.h> 42 43 /*#define S3C2440_I2S_DEBUG*/ 44 45 #ifdef S3C2440_I2S_DEBUG 46 #define DPRINTF(x) do {printf x; } while (/*CONSTCOND*/0) 47 #else 48 #define DPRINTF(s) do {} while (/*CONSTCOND*/0) 49 #endif 50 51 struct s3c2440_i2s_softc { 52 device_t sc_dev; 53 kmutex_t *sc_intr_lock; 54 bus_space_tag_t sc_iot; 55 bus_space_handle_t sc_i2s_ioh; 56 57 int sc_master_clock; 58 int sc_serial_clock; 59 int sc_dir; 60 int sc_sample_width; 61 int sc_bus_format; 62 63 bus_dma_segment_t sc_dr; 64 }; 65 66 static void s3c2440_i2s_xfer_complete(dmac_xfer_t, void *); 67 68 static int s3c2440_i2s_match(device_t, cfdata_t, void *); 69 static void s3c2440_i2s_attach(device_t, device_t , void *); 70 static int s3c2440_i2s_search(device_t, cfdata_t, const int *, void *); 71 static int s3c2440_i2s_print(void *, const char *); 72 static int s3c2440_i2s_init(struct s3c2440_i2s_softc*); 73 74 CFATTACH_DECL_NEW(ssiis, sizeof(struct s3c2440_i2s_softc), s3c2440_i2s_match, 75 s3c2440_i2s_attach, NULL, NULL); 76 77 int 78 s3c2440_i2s_match(device_t parent, cfdata_t match, void *aux) 79 { 80 81 return 1; 82 } 83 84 void 85 s3c2440_i2s_attach(device_t parent, device_t self, void *aux) 86 { 87 struct s3c2440_i2s_softc *sc = device_private(self); 88 DPRINTF(("%s\n", __func__)); 89 90 sc->sc_dev = self; 91 92 s3c2440_i2s_init(sc); 93 94 printf("\n"); 95 96 config_search_ia(s3c2440_i2s_search, self, "ssiis", NULL); 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_match(parent, cf, &ia)) 114 config_attach(parent, cf, &ia, s3c2440_i2s_print); 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 resister 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 kalloc_flags = KM_SLEEP; 320 int dma_flags = BUS_DMA_WAITOK; 321 int retval = 0; 322 struct s3c2xx0_softc *sc = s3c2xx0_softc; /* Shortcut */ 323 s3c2440_i2s_buf_t buf; 324 325 DPRINTF(("%s\n", __func__)); 326 327 if (flags & M_NOWAIT) { 328 kalloc_flags = KM_NOSLEEP; 329 dma_flags = BUS_DMA_NOWAIT; 330 } 331 332 *out = kmem_alloc(sizeof(struct s3c2440_i2s_buf), kalloc_flags); 333 if (*out == NULL) { 334 DPRINTF(("Failed to allocate memory\n")); 335 return ENOMEM; 336 } 337 338 buf = *out; 339 buf->i2b_parent = handle; 340 buf->i2b_size = size; 341 buf->i2b_nsegs = S3C2440_I2S_BUF_MAX_SEGS; 342 buf->i2b_xfer = NULL; 343 buf->i2b_cb = NULL; 344 buf->i2b_cb_cookie = NULL; 345 346 /* We first allocate some DMA-friendly memory for the buffer... */ 347 retval = bus_dmamem_alloc(sc->sc_dmat, buf->i2b_size, NBPG, 0, 348 buf->i2b_segs, buf->i2b_nsegs, &buf->i2b_nsegs, 349 dma_flags); 350 if (retval != 0) { 351 printf("%s: Failed to allocate DMA memory\n", __func__); 352 goto cleanup_dealloc; 353 } 354 355 DPRINTF(("%s: Using %d DMA segments\n", __func__, buf->i2b_nsegs)); 356 357 retval = bus_dmamem_map(sc->sc_dmat, buf->i2b_segs, buf->i2b_nsegs, 358 buf->i2b_size, &buf->i2b_addr, dma_flags); 359 360 if (retval != 0) { 361 printf("%s: Failed to map DMA memory\n", __func__); 362 goto cleanup_dealloc_dma; 363 } 364 365 DPRINTF(("%s: Playback DMA buffer mapped at %p\n", __func__, 366 buf->i2b_addr)); 367 368 /* XXX: Not sure if nsegments is really 1...*/ 369 retval = bus_dmamap_create(sc->sc_dmat, buf->i2b_size, 1, 370 buf->i2b_size, 0, dma_flags, 371 &buf->i2b_dmamap); 372 if (retval != 0) { 373 printf("%s: Failed to create DMA map\n", __func__); 374 goto cleanup_unmap_dma; 375 } 376 377 DPRINTF(("%s: DMA map created successfully\n", __func__)); 378 379 buf->i2b_xfer = s3c2440_dmac_allocate_xfer(M_NOWAIT); 380 if (buf->i2b_xfer == NULL) { 381 retval = ENOMEM; 382 goto cleanup_destroy_dmamap; 383 } 384 385 return 0; 386 cleanup_destroy_dmamap: 387 bus_dmamap_destroy(sc->sc_dmat, buf->i2b_dmamap); 388 cleanup_unmap_dma: 389 bus_dmamem_unmap(sc->sc_dmat, &buf->i2b_addr, buf->i2b_size); 390 cleanup_dealloc_dma: 391 bus_dmamem_free(sc->sc_dmat, buf->i2b_segs, buf->i2b_nsegs); 392 cleanup_dealloc: 393 kmem_free(*out, sizeof(struct s3c2440_i2s_buf)); 394 return retval; 395 } 396 397 void 398 s3c2440_i2s_free(s3c2440_i2s_buf_t buf) 399 { 400 struct s3c2xx0_softc *sc = s3c2xx0_softc; /* Shortcut */ 401 402 if (buf->i2b_xfer != NULL) { 403 s3c2440_dmac_free_xfer(buf->i2b_xfer); 404 } 405 406 bus_dmamap_unload(sc->sc_dmat, buf->i2b_dmamap); 407 bus_dmamap_destroy(sc->sc_dmat, buf->i2b_dmamap); 408 bus_dmamem_unmap(sc->sc_dmat, &buf->i2b_addr, buf->i2b_size); 409 bus_dmamem_free(sc->sc_dmat, buf->i2b_segs, buf->i2b_nsegs); 410 kmem_free(buf, sizeof(struct s3c2440_i2s_buf)); 411 } 412 413 int 414 s3c2440_i2s_output(s3c2440_i2s_buf_t buf, void *block, int bsize, 415 void (*callback)(void*), void *cb_cookie) 416 { 417 struct s3c2xx0_softc *sc = s3c2xx0_softc; /* Shortcut */ 418 struct s3c2440_i2s_softc *i2s = buf->i2b_parent; 419 int retval; 420 dmac_xfer_t xfer = buf->i2b_xfer; 421 422 retval = bus_dmamap_load(sc->sc_dmat, buf->i2b_dmamap, block, 423 bsize, NULL, BUS_DMA_NOWAIT); 424 if (retval != 0) { 425 printf("Failed to load DMA map\n"); 426 return retval; 427 } 428 429 xfer->dx_desc[DMAC_DESC_DST].xd_bus_type = DMAC_BUS_TYPE_PERIPHERAL; 430 xfer->dx_desc[DMAC_DESC_DST].xd_increment = FALSE; 431 xfer->dx_desc[DMAC_DESC_DST].xd_nsegs = 1; 432 xfer->dx_desc[DMAC_DESC_DST].xd_dma_segs = &i2s->sc_dr; 433 434 xfer->dx_desc[DMAC_DESC_SRC].xd_bus_type = DMAC_BUS_TYPE_SYSTEM; 435 xfer->dx_desc[DMAC_DESC_SRC].xd_increment = TRUE; 436 xfer->dx_desc[DMAC_DESC_SRC].xd_nsegs = buf->i2b_dmamap->dm_nsegs; 437 xfer->dx_desc[DMAC_DESC_SRC].xd_dma_segs = buf->i2b_dmamap->dm_segs; 438 439 xfer->dx_peripheral = DMAC_PERIPH_I2SSDO; 440 441 if (i2s->sc_sample_width == 16) 442 xfer->dx_xfer_width = DMAC_XFER_WIDTH_16BIT; 443 else if (i2s->sc_sample_width == 8) 444 xfer->dx_xfer_width = DMAC_XFER_WIDTH_8BIT; 445 446 xfer->dx_done = s3c2440_i2s_xfer_complete; 447 xfer->dx_cookie = buf; 448 xfer->dx_xfer_mode = DMAC_XFER_MODE_HANDSHAKE; 449 450 buf->i2b_cb = callback; 451 buf->i2b_cb_cookie = cb_cookie; 452 453 s3c2440_dmac_start_xfer(buf->i2b_xfer); 454 455 return 0; 456 } 457 458 int 459 s3c2440_i2s_halt_output(s3c2440_i2s_buf_t buf) 460 { 461 /*int retval;*/ 462 struct s3c2xx0_softc *sc = s3c2xx0_softc; /* Shortcut */ 463 464 DPRINTF(("Aborting DMA transfer\n")); 465 /*do { 466 retval =*/ s3c2440_dmac_abort_xfer(buf->i2b_xfer); 467 /*} while(retval != 0);*/ 468 DPRINTF(("Aborting DMA transfer: SUCCESS\n")); 469 470 bus_dmamap_unload(sc->sc_dmat, buf->i2b_dmamap); 471 472 return 0; 473 } 474 475 int 476 s3c2440_i2s_input(s3c2440_i2s_buf_t buf, void *block, int bsize, 477 void (*callback)(void*), void *cb_cookie) 478 { 479 struct s3c2xx0_softc *sc = s3c2xx0_softc; /* Shortcut */ 480 struct s3c2440_i2s_softc *i2s = buf->i2b_parent; 481 int retval; 482 dmac_xfer_t xfer = buf->i2b_xfer; 483 484 retval = bus_dmamap_load(sc->sc_dmat, buf->i2b_dmamap, block, 485 bsize, NULL, BUS_DMA_NOWAIT); 486 if (retval != 0) { 487 printf("Failed to load DMA map\n"); 488 return retval; 489 } 490 491 xfer->dx_desc[DMAC_DESC_SRC].xd_bus_type = DMAC_BUS_TYPE_PERIPHERAL; 492 xfer->dx_desc[DMAC_DESC_SRC].xd_increment = FALSE; 493 xfer->dx_desc[DMAC_DESC_SRC].xd_nsegs = 1; 494 xfer->dx_desc[DMAC_DESC_SRC].xd_dma_segs = &i2s->sc_dr; 495 496 xfer->dx_desc[DMAC_DESC_DST].xd_bus_type = DMAC_BUS_TYPE_SYSTEM; 497 xfer->dx_desc[DMAC_DESC_DST].xd_increment = TRUE; 498 xfer->dx_desc[DMAC_DESC_DST].xd_nsegs = buf->i2b_dmamap->dm_nsegs; 499 xfer->dx_desc[DMAC_DESC_DST].xd_dma_segs = buf->i2b_dmamap->dm_segs; 500 501 xfer->dx_peripheral = DMAC_PERIPH_I2SSDI; 502 503 if (i2s->sc_sample_width == 16) 504 xfer->dx_xfer_width = DMAC_XFER_WIDTH_16BIT; 505 else if (i2s->sc_sample_width == 8) 506 xfer->dx_xfer_width = DMAC_XFER_WIDTH_8BIT; 507 508 xfer->dx_done = s3c2440_i2s_xfer_complete; 509 xfer->dx_cookie = buf; 510 xfer->dx_xfer_mode = DMAC_XFER_MODE_HANDSHAKE; 511 512 buf->i2b_cb = callback; 513 buf->i2b_cb_cookie = cb_cookie; 514 515 s3c2440_dmac_start_xfer(buf->i2b_xfer); 516 517 return 0; 518 } 519 520 static void 521 s3c2440_i2s_xfer_complete(dmac_xfer_t xfer, void *cookie) 522 { 523 struct s3c2xx0_softc *sc = s3c2xx0_softc; /* Shortcut */ 524 s3c2440_i2s_buf_t buf = cookie; 525 struct s3c2440_i2s_softc *i2s = buf->i2b_parent; 526 527 bus_dmamap_unload(sc->sc_dmat, buf->i2b_dmamap); 528 529 mutex_spin_enter(i2s->sc_intr_lock); 530 (buf->i2b_cb)(buf->i2b_cb_cookie); 531 mutex_spin_exit(i2s->sc_intr_lock); 532 } 533