cs4231_ebus.c revision 1.7
1/* $NetBSD: cs4231_ebus.c,v 1.7 2002/09/27 20:37:48 thorpej Exp $ */ 2 3/* 4 * Copyright (c) 2002 Valeriy E. Ushakov 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/errno.h> 33#include <sys/device.h> 34#include <sys/malloc.h> 35 36#include <machine/autoconf.h> 37#include <machine/cpu.h> 38#include <dev/ebus/ebusreg.h> 39#include <dev/ebus/ebusvar.h> 40 41#include <sys/audioio.h> 42#include <dev/audio_if.h> 43 44#include <dev/ic/ad1848reg.h> 45#include <dev/ic/cs4231reg.h> 46#include <dev/ic/ad1848var.h> 47#include <dev/ic/cs4231var.h> 48 49#ifdef AUDIO_DEBUG 50int cs4231_ebus_debug = 0; 51#define DPRINTF(x) if (cs4231_ebus_debug) printf x 52#else 53#define DPRINTF(x) 54#endif 55 56 57struct cs4231_ebus_softc { 58 struct cs4231_softc sc_cs4231; 59 60 bus_space_tag_t sc_bt; 61 bus_space_handle_t sc_pdmareg; /* playback DMA */ 62 bus_space_handle_t sc_cdmareg; /* record DMA */ 63}; 64 65 66void cs4231_ebus_attach(struct device *, struct device *, void *); 67int cs4231_ebus_match(struct device *, struct cfdata *, void *); 68 69const struct cfattach audiocs_ebus_ca = { 70 sizeof(struct cs4231_ebus_softc), cs4231_ebus_match, cs4231_ebus_attach 71}; 72 73 74/* audio_hw_if methods specific to ebus dma */ 75static int cs4231_ebus_trigger_output(void *, void *, void *, int, 76 void (*)(void *), void *, 77 struct audio_params *); 78static int cs4231_ebus_trigger_input(void *, void *, void *, int, 79 void (*)(void *), void *, 80 struct audio_params *); 81static int cs4231_ebus_halt_output(void *); 82static int cs4231_ebus_halt_input(void *); 83 84struct audio_hw_if audiocs_ebus_hw_if = { 85 cs4231_open, 86 cs4231_close, 87 NULL, /* drain */ 88 ad1848_query_encoding, 89 ad1848_set_params, 90 cs4231_round_blocksize, 91 ad1848_commit_settings, 92 NULL, /* init_output */ 93 NULL, /* init_input */ 94 NULL, /* start_output */ 95 NULL, /* start_input */ 96 cs4231_ebus_halt_output, 97 cs4231_ebus_halt_input, 98 NULL, /* speaker_ctl */ 99 cs4231_getdev, 100 NULL, /* setfd */ 101 cs4231_set_port, 102 cs4231_get_port, 103 cs4231_query_devinfo, 104 cs4231_malloc, 105 cs4231_free, 106 cs4231_round_buffersize, 107 NULL, /* mappage */ 108 cs4231_get_props, 109 cs4231_ebus_trigger_output, 110 cs4231_ebus_trigger_input, 111 NULL, /* dev_ioctl */ 112}; 113 114#ifdef AUDIO_DEBUG 115static void cs4231_ebus_regdump(char *, struct cs4231_ebus_softc *); 116#endif 117 118static int cs4231_ebus_dma_reset(bus_space_tag_t, bus_space_handle_t); 119static int cs4231_ebus_trigger_transfer(struct cs4231_softc *, 120 struct cs_transfer *, 121 bus_space_tag_t, bus_space_handle_t, 122 int, void *, void *, int, void (*)(void *), void *, 123 struct audio_params *); 124static void cs4231_ebus_dma_advance(struct cs_transfer *, 125 bus_space_tag_t, bus_space_handle_t); 126static int cs4231_ebus_dma_intr(struct cs_transfer *, 127 bus_space_tag_t, bus_space_handle_t); 128static int cs4231_ebus_intr(void *); 129 130 131int 132cs4231_ebus_match(parent, cf, aux) 133 struct device *parent; 134 struct cfdata *cf; 135 void *aux; 136{ 137 struct ebus_attach_args *ea = aux; 138 139 if (strcmp(ea->ea_name, AUDIOCS_PROM_NAME) == 0) 140 return (1); 141#ifdef __sparc__ /* XXX: Krups */ 142 if (strcmp(ea->ea_name, "sound") == 0) 143 return (1); 144#endif 145 146 return (0); 147} 148 149 150void 151cs4231_ebus_attach(parent, self, aux) 152 struct device *parent, *self; 153 void *aux; 154{ 155 struct cs4231_ebus_softc *ebsc = (struct cs4231_ebus_softc *)self; 156 struct cs4231_softc *sc = &ebsc->sc_cs4231; 157 struct ebus_attach_args *ea = aux; 158 bus_space_handle_t bh; 159 int i; 160 161 sc->sc_bustag = ebsc->sc_bt = ea->ea_bustag; 162 sc->sc_dmatag = ea->ea_dmatag; 163 164 /* 165 * These are the register we get from the prom: 166 * - CS4231 registers 167 * - Playback EBus DMA controller 168 * - Capture EBus DMA controller 169 * - AUXIO audio register (codec powerdown) 170 * 171 * Map my registers in, if they aren't already in virtual 172 * address space. 173 */ 174 if (bus_space_map(ea->ea_bustag, EBUS_ADDR_FROM_REG(&ea->ea_reg[0]), 175 ea->ea_reg[0].size, 0, &bh) != 0) { 176 printf(": unable to map registers\n"); 177 return; 178 } 179 180 /* XXX: map playback DMA registers (we just know where they are) */ 181 if (bus_space_map(ea->ea_bustag, 182 BUS_ADDR(0x14, 0x702000), /* XXX: magic num */ 183 EBUS_DMAC_SIZE, 184 0, &ebsc->sc_pdmareg) != 0) 185 { 186 printf(": unable to map playback DMA registers\n"); 187 return; 188 } 189 190 /* XXX: map capture DMA registers (we just know where they are) */ 191 if (bus_space_map(ea->ea_bustag, 192 BUS_ADDR(0x14, 0x704000), /* XXX: magic num */ 193 EBUS_DMAC_SIZE, 194 0, &ebsc->sc_cdmareg) != 0) 195 { 196 printf(": unable to map capture DMA registers\n"); 197 return; 198 } 199 200 /* establish interrupt channels */ 201 for (i = 0; i < ea->ea_nintr; ++i) 202 bus_intr_establish(ea->ea_bustag, 203 ea->ea_intr[i], IPL_AUDIO, 0, 204 cs4231_ebus_intr, ebsc); 205 206 cs4231_common_attach(sc, bh); 207 printf("\n"); 208 209 /* XXX: todo: move to cs4231_common_attach, pass hw_if as arg? */ 210 audio_attach_mi(&audiocs_ebus_hw_if, sc, &sc->sc_ad1848.sc_dev); 211} 212 213 214#ifdef AUDIO_DEBUG 215static void 216cs4231_ebus_regdump(label, ebsc) 217 char *label; 218 struct cs4231_ebus_softc *ebsc; 219{ 220 /* char bits[128]; */ 221 222 printf("cs4231regdump(%s): regs:", label); 223 /* XXX: dump ebus dma and aux registers */ 224 ad1848_dump_regs(&ebsc->sc_cs4231.sc_ad1848); 225} 226#endif /* AUDIO_DEBUG */ 227 228 229/* XXX: nothing CS4231-specific in this code... */ 230static int 231cs4231_ebus_dma_reset(dt, dh) 232 bus_space_tag_t dt; 233 bus_space_handle_t dh; 234{ 235 u_int32_t csr; 236 int timo; 237 238 /* reset, also clear TC, just in case */ 239 bus_space_write_4(dt, dh, EBUS_DMAC_DCSR, EBDMA_RESET | EBDMA_TC); 240 241 for (timo = 50000; timo != 0; --timo) { 242 csr = bus_space_read_4(dt, dh, EBUS_DMAC_DCSR); 243 if ((csr & (EBDMA_CYC_PEND | EBDMA_DRAIN)) == 0) 244 break; 245 } 246 247 if (timo == 0) { 248 char bits[128]; 249 250 printf("cs4231_ebus_dma_reset: timed out: csr=%s\n", 251 bitmask_snprintf(csr, EBUS_DCSR_BITS, 252 bits, sizeof(bits))); 253 return (ETIMEDOUT); 254 } 255 256 bus_space_write_4(dt, dh, EBUS_DMAC_DCSR, csr & ~EBDMA_RESET); 257 return (0); 258} 259 260 261static void 262cs4231_ebus_dma_advance(t, dt, dh) 263 struct cs_transfer *t; 264 bus_space_tag_t dt; 265 bus_space_handle_t dh; 266{ 267 bus_addr_t dmaaddr; 268 bus_size_t dmasize; 269 270 cs4231_transfer_advance(t, &dmaaddr, &dmasize); 271 272 bus_space_write_4(dt, dh, EBUS_DMAC_DNBR, (u_int32_t)dmasize); 273 bus_space_write_4(dt, dh, EBUS_DMAC_DNAR, (u_int32_t)dmaaddr); 274} 275 276 277/* 278 * Trigger transfer "t" using DMA controller "dmac". 279 * "iswrite" defines direction of the transfer. 280 */ 281static int 282cs4231_ebus_trigger_transfer(sc, t, dt, dh, iswrite, 283 start, end, blksize, 284 intr, arg, param) 285 struct cs4231_softc *sc; 286 struct cs_transfer *t; 287 bus_space_tag_t dt; 288 bus_space_handle_t dh; 289 int iswrite; 290 void *start, *end; 291 int blksize; 292 void (*intr)(void *); 293 void *arg; 294 struct audio_params *param; 295{ 296 u_int32_t csr; 297 bus_addr_t dmaaddr; 298 bus_size_t dmasize; 299 int ret; 300 301 ret = cs4231_transfer_init(sc, t, &dmaaddr, &dmasize, 302 start, end, blksize, intr, arg); 303 if (ret != 0) 304 return (ret); 305 306 ret = cs4231_ebus_dma_reset(dt, dh); 307 if (ret != 0) 308 return (ret); 309 310 csr = bus_space_read_4(dt, dh, EBUS_DMAC_DCSR); 311 bus_space_write_4(dt, dh, EBUS_DMAC_DCSR, 312 csr | EBDMA_EN_NEXT | (iswrite ? EBDMA_WRITE : 0) 313 | EBDMA_EN_DMA | EBDMA_EN_CNT | EBDMA_INT_EN); 314 315 /* first load: propagated to DACR/DBCR */ 316 bus_space_write_4(dt, dh, EBUS_DMAC_DNBR, (u_int32_t)dmasize); 317 bus_space_write_4(dt, dh, EBUS_DMAC_DNAR, (u_int32_t)dmaaddr); 318 319 /* next load: goes to DNAR/DNBR */ 320 cs4231_ebus_dma_advance(t, dt, dh); 321 322 return (0); 323} 324 325 326static int 327cs4231_ebus_trigger_output(addr, start, end, blksize, intr, arg, param) 328 void *addr; 329 void *start, *end; 330 int blksize; 331 void (*intr)(void *); 332 void *arg; 333 struct audio_params *param; 334{ 335 struct cs4231_ebus_softc *ebsc = addr; 336 struct cs4231_softc *sc = &ebsc->sc_cs4231; 337 int cfg, ret; 338 339 ret = cs4231_ebus_trigger_transfer(sc, &sc->sc_playback, 340 ebsc->sc_bt, ebsc->sc_pdmareg, 341 0, /* iswrite */ 342 start, end, blksize, 343 intr, arg, param); 344 if (ret != 0) 345 return (ret); 346 347 ad_write(&sc->sc_ad1848, SP_LOWER_BASE_COUNT, 0xff); 348 ad_write(&sc->sc_ad1848, SP_UPPER_BASE_COUNT, 0xff); 349 350 cfg = ad_read(&sc->sc_ad1848, SP_INTERFACE_CONFIG); 351 ad_write(&sc->sc_ad1848, SP_INTERFACE_CONFIG, cfg | PLAYBACK_ENABLE); 352 353 return (0); 354} 355 356 357static int 358cs4231_ebus_trigger_input(addr, start, end, blksize, intr, arg, param) 359 void *addr; 360 void *start, *end; 361 int blksize; 362 void (*intr)(void *); 363 void *arg; 364 struct audio_params *param; 365{ 366 struct cs4231_ebus_softc *ebsc = addr; 367 struct cs4231_softc *sc = &ebsc->sc_cs4231; 368 int cfg, ret; 369 370 ret = cs4231_ebus_trigger_transfer(sc, &sc->sc_capture, 371 ebsc->sc_bt, ebsc->sc_cdmareg, 372 1, /* iswrite */ 373 start, end, blksize, 374 intr, arg, param); 375 if (ret != 0) 376 return (ret); 377 378 ad_write(&sc->sc_ad1848, CS_LOWER_REC_CNT, 0xff); 379 ad_write(&sc->sc_ad1848, CS_UPPER_REC_CNT, 0xff); 380 381 cfg = ad_read(&sc->sc_ad1848, SP_INTERFACE_CONFIG); 382 ad_write(&sc->sc_ad1848, SP_INTERFACE_CONFIG, cfg | CAPTURE_ENABLE); 383 384 return (0); 385} 386 387 388static int 389cs4231_ebus_halt_output(addr) 390 void *addr; 391{ 392 struct cs4231_ebus_softc *ebsc = addr; 393 struct cs4231_softc *sc = &ebsc->sc_cs4231; 394 u_int32_t csr; 395 int cfg; 396 397 sc->sc_playback.t_active = 0; 398 399 csr = bus_space_read_4(ebsc->sc_bt, ebsc->sc_pdmareg, EBUS_DMAC_DCSR); 400 bus_space_write_4(ebsc->sc_bt, ebsc->sc_pdmareg, EBUS_DMAC_DCSR, 401 csr & ~EBDMA_EN_DMA); 402 403 cfg = ad_read(&sc->sc_ad1848, SP_INTERFACE_CONFIG); 404 ad_write(&sc->sc_ad1848, SP_INTERFACE_CONFIG, 405 cfg & ~PLAYBACK_ENABLE); 406 407 return (0); 408} 409 410 411static int 412cs4231_ebus_halt_input(addr) 413 void *addr; 414{ 415 struct cs4231_ebus_softc *ebsc = addr; 416 struct cs4231_softc *sc = &ebsc->sc_cs4231; 417 u_int32_t csr; 418 int cfg; 419 420 sc->sc_capture.t_active = 0; 421 422 csr = bus_space_read_4(ebsc->sc_bt, ebsc->sc_cdmareg, EBUS_DMAC_DCSR); 423 bus_space_write_4(ebsc->sc_bt, ebsc->sc_cdmareg, EBUS_DMAC_DCSR, 424 csr & ~EBDMA_EN_DMA); 425 426 cfg = ad_read(&sc->sc_ad1848, SP_INTERFACE_CONFIG); 427 ad_write(&sc->sc_ad1848, SP_INTERFACE_CONFIG, 428 cfg & ~CAPTURE_ENABLE); 429 430 return (0); 431} 432 433 434static int 435cs4231_ebus_dma_intr(t, dt, dh) 436 struct cs_transfer *t; 437 bus_space_tag_t dt; 438 bus_space_handle_t dh; 439{ 440 u_int32_t csr; 441#ifdef AUDIO_DEBUG 442 char bits[128]; 443#endif 444 445 /* read DMA status, clear TC bit by writing it back */ 446 csr = bus_space_read_4(dt, dh, EBUS_DMAC_DCSR); 447 bus_space_write_4(dt, dh, EBUS_DMAC_DCSR, csr); 448 DPRINTF(("audiocs: %s dcsr=%s\n", t->t_name, 449 bitmask_snprintf(csr, EBUS_DCSR_BITS, bits, sizeof(bits)))); 450 451 if (csr & EBDMA_ERR_PEND) { 452 ++t->t_ierrcnt.ev_count; 453 printf("audiocs: %s DMA error, resetting\n", t->t_name); 454 cs4231_ebus_dma_reset(dt, dh); 455 /* how to notify audio(9)??? */ 456 return (1); 457 } 458 459 if ((csr & EBDMA_INT_PEND) == 0) 460 return (0); 461 462 ++t->t_intrcnt.ev_count; 463 464 if ((csr & EBDMA_TC) == 0) { /* can this happen? */ 465 printf("audiocs: %s INT_PEND but !TC\n", t->t_name); 466 return (1); 467 } 468 469 if (!t->t_active) 470 return (1); 471 472 cs4231_ebus_dma_advance(t, dt, dh); 473 474 /* call audio(9) framework while dma is chugging along */ 475 if (t->t_intr != NULL) 476 (*t->t_intr)(t->t_arg); 477 return (1); 478} 479 480 481static int 482cs4231_ebus_intr(arg) 483 void *arg; 484{ 485 struct cs4231_ebus_softc *ebsc = arg; 486 struct cs4231_softc *sc = &ebsc->sc_cs4231; 487 int status; 488 int ret; 489#ifdef AUDIO_DEBUG 490 char bits[128]; 491#endif 492 493 status = ADREAD(&sc->sc_ad1848, AD1848_STATUS); 494 495#ifdef AUDIO_DEBUG 496 if (cs4231_ebus_debug > 1) 497 cs4231_ebus_regdump("audiointr", ebsc); 498 499 DPRINTF(("%s: status: %s\n", sc->sc_ad1848.sc_dev.dv_xname, 500 bitmask_snprintf(status, AD_R2_BITS, bits, sizeof(bits)))); 501#endif 502 503 if (status & INTERRUPT_STATUS) { 504#ifdef AUDIO_DEBUG 505 int reason; 506 507 reason = ad_read(&sc->sc_ad1848, CS_IRQ_STATUS); 508 DPRINTF(("%s: i24: %s\n", sc->sc_ad1848.sc_dev.dv_xname, 509 bitmask_snprintf(reason, CS_I24_BITS, bits, sizeof(bits)))); 510#endif 511 /* clear interrupt from ad1848 */ 512 ADWRITE(&sc->sc_ad1848, AD1848_STATUS, 0); 513 } 514 515 ret = 0; 516 517 if (cs4231_ebus_dma_intr(&sc->sc_capture, 518 ebsc->sc_bt, ebsc->sc_cdmareg) != 0) 519 { 520 ++sc->sc_intrcnt.ev_count; 521 ret = 1; 522 } 523 524 if (cs4231_ebus_dma_intr(&sc->sc_playback, 525 ebsc->sc_bt, ebsc->sc_pdmareg) != 0) 526 { 527 ++sc->sc_intrcnt.ev_count; 528 ret = 1; 529 } 530 531 return (ret); 532} 533