| 1 | /* $NetBSD: cs4281.c,v 1.52 2016/07/07 06:55:41 msaitoh Exp $ */ |
| 2 | |
| 3 | /* |
| 4 | * Copyright (c) 2000 Tatoku Ogaito. All rights reserved. |
| 5 | * |
| 6 | * Redistribution and use in source and binary forms, with or without |
| 7 | * modification, are permitted provided that the following conditions |
| 8 | * are met: |
| 9 | * 1. Redistributions of source code must retain the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer. |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright |
| 12 | * notice, this list of conditions and the following disclaimer in the |
| 13 | * documentation and/or other materials provided with the distribution. |
| 14 | * 3. All advertising materials mentioning features or use of this software |
| 15 | * must display the following acknowledgement: |
| 16 | * This product includes software developed by Tatoku Ogaito |
| 17 | * for the NetBSD Project. |
| 18 | * 4. The name of the author may not be used to endorse or promote products |
| 19 | * derived from this software without specific prior written permission |
| 20 | * |
| 21 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| 22 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| 23 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 24 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 25 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 26 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 30 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 31 | */ |
| 32 | |
| 33 | /* |
| 34 | * Cirrus Logic CS4281 driver. |
| 35 | * Data sheets can be found |
| 36 | * http://www.cirrus.com/ftp/pub/4281.pdf |
| 37 | * ftp://ftp.alsa-project.org/pub/manuals/cirrus/cs4281tm.pdf |
| 38 | * |
| 39 | * TODO: |
| 40 | * 1: midi and FM support |
| 41 | * 2: ... |
| 42 | * |
| 43 | */ |
| 44 | |
| 45 | #include <sys/cdefs.h> |
| 46 | __KERNEL_RCSID(0, "$NetBSD: cs4281.c,v 1.52 2016/07/07 06:55:41 msaitoh Exp $" ); |
| 47 | |
| 48 | #include <sys/param.h> |
| 49 | #include <sys/systm.h> |
| 50 | #include <sys/kernel.h> |
| 51 | #include <sys/malloc.h> |
| 52 | #include <sys/fcntl.h> |
| 53 | #include <sys/device.h> |
| 54 | #include <sys/systm.h> |
| 55 | |
| 56 | #include <dev/pci/pcidevs.h> |
| 57 | #include <dev/pci/pcivar.h> |
| 58 | #include <dev/pci/cs4281reg.h> |
| 59 | #include <dev/pci/cs428xreg.h> |
| 60 | |
| 61 | #include <sys/audioio.h> |
| 62 | #include <dev/audio_if.h> |
| 63 | #include <dev/midi_if.h> |
| 64 | #include <dev/mulaw.h> |
| 65 | #include <dev/auconv.h> |
| 66 | |
| 67 | #include <dev/ic/ac97reg.h> |
| 68 | #include <dev/ic/ac97var.h> |
| 69 | |
| 70 | #include <dev/pci/cs428x.h> |
| 71 | |
| 72 | #include <sys/bus.h> |
| 73 | |
| 74 | #if defined(ENABLE_SECONDARY_CODEC) |
| 75 | #define MAX_CHANNELS (4) |
| 76 | #define MAX_FIFO_SIZE 32 /* 128/4channels */ |
| 77 | #else |
| 78 | #define MAX_CHANNELS (2) |
| 79 | #define MAX_FIFO_SIZE 64 /* 128/2channels */ |
| 80 | #endif |
| 81 | |
| 82 | /* IF functions for audio driver */ |
| 83 | static int cs4281_match(device_t, cfdata_t, void *); |
| 84 | static void cs4281_attach(device_t, device_t, void *); |
| 85 | static int cs4281_intr(void *); |
| 86 | static int cs4281_query_encoding(void *, struct audio_encoding *); |
| 87 | static int cs4281_set_params(void *, int, int, audio_params_t *, |
| 88 | audio_params_t *, stream_filter_list_t *, |
| 89 | stream_filter_list_t *); |
| 90 | static int cs4281_halt_output(void *); |
| 91 | static int cs4281_halt_input(void *); |
| 92 | static int cs4281_getdev(void *, struct audio_device *); |
| 93 | static int cs4281_trigger_output(void *, void *, void *, int, |
| 94 | void (*)(void *), void *, |
| 95 | const audio_params_t *); |
| 96 | static int cs4281_trigger_input(void *, void *, void *, int, |
| 97 | void (*)(void *), void *, |
| 98 | const audio_params_t *); |
| 99 | |
| 100 | static int cs4281_reset_codec(void *); |
| 101 | |
| 102 | /* Internal functions */ |
| 103 | static uint8_t cs4281_sr2regval(int); |
| 104 | static void cs4281_set_dac_rate(struct cs428x_softc *, int); |
| 105 | static void cs4281_set_adc_rate(struct cs428x_softc *, int); |
| 106 | static int cs4281_init(struct cs428x_softc *, int); |
| 107 | |
| 108 | /* Power Management */ |
| 109 | static bool cs4281_suspend(device_t, const pmf_qual_t *); |
| 110 | static bool cs4281_resume(device_t, const pmf_qual_t *); |
| 111 | |
| 112 | static const struct audio_hw_if cs4281_hw_if = { |
| 113 | NULL, /* open */ |
| 114 | NULL, /* close */ |
| 115 | NULL, |
| 116 | cs4281_query_encoding, |
| 117 | cs4281_set_params, |
| 118 | cs428x_round_blocksize, |
| 119 | NULL, |
| 120 | NULL, |
| 121 | NULL, |
| 122 | NULL, |
| 123 | NULL, |
| 124 | cs4281_halt_output, |
| 125 | cs4281_halt_input, |
| 126 | NULL, |
| 127 | cs4281_getdev, |
| 128 | NULL, |
| 129 | cs428x_mixer_set_port, |
| 130 | cs428x_mixer_get_port, |
| 131 | cs428x_query_devinfo, |
| 132 | cs428x_malloc, |
| 133 | cs428x_free, |
| 134 | cs428x_round_buffersize, |
| 135 | cs428x_mappage, |
| 136 | cs428x_get_props, |
| 137 | cs4281_trigger_output, |
| 138 | cs4281_trigger_input, |
| 139 | NULL, |
| 140 | cs428x_get_locks, |
| 141 | }; |
| 142 | |
| 143 | #if NMIDI > 0 && 0 |
| 144 | /* Midi Interface */ |
| 145 | static void cs4281_midi_close(void*); |
| 146 | static void cs4281_midi_getinfo(void *, struct midi_info *); |
| 147 | static int cs4281_midi_open(void *, int, void (*)(void *, int), |
| 148 | void (*)(void *), void *); |
| 149 | static int cs4281_midi_output(void *, int); |
| 150 | |
| 151 | static const struct midi_hw_if cs4281_midi_hw_if = { |
| 152 | cs4281_midi_open, |
| 153 | cs4281_midi_close, |
| 154 | cs4281_midi_output, |
| 155 | cs4281_midi_getinfo, |
| 156 | 0, |
| 157 | cs428x_get_locks, |
| 158 | }; |
| 159 | #endif |
| 160 | |
| 161 | CFATTACH_DECL_NEW(clct, sizeof(struct cs428x_softc), |
| 162 | cs4281_match, cs4281_attach, NULL, NULL); |
| 163 | |
| 164 | static struct audio_device cs4281_device = { |
| 165 | "CS4281" , |
| 166 | "" , |
| 167 | "cs4281" |
| 168 | }; |
| 169 | |
| 170 | |
| 171 | static int |
| 172 | cs4281_match(device_t parent, cfdata_t match, void *aux) |
| 173 | { |
| 174 | struct pci_attach_args *pa; |
| 175 | |
| 176 | pa = (struct pci_attach_args *)aux; |
| 177 | if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_CIRRUS) |
| 178 | return 0; |
| 179 | if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_CIRRUS_CS4281) |
| 180 | return 1; |
| 181 | return 0; |
| 182 | } |
| 183 | |
| 184 | static void |
| 185 | cs4281_attach(device_t parent, device_t self, void *aux) |
| 186 | { |
| 187 | struct cs428x_softc *sc; |
| 188 | struct pci_attach_args *pa; |
| 189 | pci_chipset_tag_t pc; |
| 190 | char const *intrstr; |
| 191 | pcireg_t reg; |
| 192 | int error; |
| 193 | char intrbuf[PCI_INTRSTR_LEN]; |
| 194 | |
| 195 | sc = device_private(self); |
| 196 | sc->sc_dev = self; |
| 197 | pa = (struct pci_attach_args *)aux; |
| 198 | pc = pa->pa_pc; |
| 199 | |
| 200 | pci_aprint_devinfo(pa, "Audio controller" ); |
| 201 | |
| 202 | sc->sc_pc = pa->pa_pc; |
| 203 | sc->sc_pt = pa->pa_tag; |
| 204 | |
| 205 | /* Map I/O register */ |
| 206 | if (pci_mapreg_map(pa, PCI_BA0, |
| 207 | PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0, |
| 208 | &sc->ba0t, &sc->ba0h, NULL, NULL)) { |
| 209 | aprint_error_dev(sc->sc_dev, "can't map BA0 space\n" ); |
| 210 | return; |
| 211 | } |
| 212 | if (pci_mapreg_map(pa, PCI_BA1, |
| 213 | PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0, |
| 214 | &sc->ba1t, &sc->ba1h, NULL, NULL)) { |
| 215 | aprint_error_dev(sc->sc_dev, "can't map BA1 space\n" ); |
| 216 | return; |
| 217 | } |
| 218 | |
| 219 | sc->sc_dmatag = pa->pa_dmat; |
| 220 | |
| 221 | /* power up chip */ |
| 222 | if ((error = pci_activate(pa->pa_pc, pa->pa_tag, self, |
| 223 | pci_activate_null)) && error != EOPNOTSUPP) { |
| 224 | aprint_error_dev(sc->sc_dev, "cannot activate %d\n" , error); |
| 225 | return; |
| 226 | } |
| 227 | |
| 228 | /* Enable the device (set bus master flag) */ |
| 229 | reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); |
| 230 | pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, |
| 231 | reg | PCI_COMMAND_MASTER_ENABLE); |
| 232 | |
| 233 | #if 0 |
| 234 | /* LATENCY_TIMER setting */ |
| 235 | temp1 = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG); |
| 236 | if (PCI_LATTIMER(temp1) < 32) { |
| 237 | temp1 &= 0xffff00ff; |
| 238 | temp1 |= 0x00002000; |
| 239 | pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, temp1); |
| 240 | } |
| 241 | #endif |
| 242 | |
| 243 | /* Map and establish the interrupt. */ |
| 244 | if (pci_intr_map(pa, &sc->intrh)) { |
| 245 | aprint_error_dev(sc->sc_dev, "couldn't map interrupt\n" ); |
| 246 | return; |
| 247 | } |
| 248 | intrstr = pci_intr_string(pc, sc->intrh, intrbuf, sizeof(intrbuf)); |
| 249 | |
| 250 | mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); |
| 251 | mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO); |
| 252 | |
| 253 | sc->sc_ih = pci_intr_establish(sc->sc_pc, sc->intrh, IPL_AUDIO, |
| 254 | cs4281_intr, sc); |
| 255 | if (sc->sc_ih == NULL) { |
| 256 | aprint_error_dev(sc->sc_dev, "couldn't establish interrupt" ); |
| 257 | if (intrstr != NULL) |
| 258 | aprint_error(" at %s" , intrstr); |
| 259 | aprint_error("\n" ); |
| 260 | mutex_destroy(&sc->sc_lock); |
| 261 | mutex_destroy(&sc->sc_intr_lock); |
| 262 | return; |
| 263 | } |
| 264 | aprint_normal_dev(sc->sc_dev, "interrupting at %s\n" , intrstr); |
| 265 | |
| 266 | /* |
| 267 | * Sound System start-up |
| 268 | */ |
| 269 | if (cs4281_init(sc, 1) != 0) { |
| 270 | mutex_destroy(&sc->sc_lock); |
| 271 | mutex_destroy(&sc->sc_intr_lock); |
| 272 | return; |
| 273 | } |
| 274 | |
| 275 | sc->type = TYPE_CS4281; |
| 276 | sc->halt_input = cs4281_halt_input; |
| 277 | sc->halt_output = cs4281_halt_output; |
| 278 | |
| 279 | sc->dma_size = CS4281_BUFFER_SIZE / MAX_CHANNELS; |
| 280 | sc->dma_align = 0x10; |
| 281 | sc->hw_blocksize = sc->dma_size / 2; |
| 282 | |
| 283 | /* AC 97 attachment */ |
| 284 | sc->host_if.arg = sc; |
| 285 | sc->host_if.attach = cs428x_attach_codec; |
| 286 | sc->host_if.read = cs428x_read_codec; |
| 287 | sc->host_if.write = cs428x_write_codec; |
| 288 | sc->host_if.reset = cs4281_reset_codec; |
| 289 | if (ac97_attach(&sc->host_if, self, &sc->sc_lock) != 0) { |
| 290 | aprint_error_dev(sc->sc_dev, "ac97_attach failed\n" ); |
| 291 | mutex_destroy(&sc->sc_lock); |
| 292 | mutex_destroy(&sc->sc_intr_lock); |
| 293 | return; |
| 294 | } |
| 295 | audio_attach_mi(&cs4281_hw_if, sc, sc->sc_dev); |
| 296 | |
| 297 | #if NMIDI > 0 && 0 |
| 298 | midi_attach_mi(&cs4281_midi_hw_if, sc, sc->sc_dev); |
| 299 | #endif |
| 300 | |
| 301 | if (!pmf_device_register(self, cs4281_suspend, cs4281_resume)) |
| 302 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
| 303 | } |
| 304 | |
| 305 | static int |
| 306 | cs4281_intr(void *p) |
| 307 | { |
| 308 | struct cs428x_softc *sc; |
| 309 | uint32_t intr, hdsr0, hdsr1; |
| 310 | char *empty_dma; |
| 311 | int handled; |
| 312 | |
| 313 | sc = p; |
| 314 | handled = 0; |
| 315 | hdsr0 = 0; |
| 316 | hdsr1 = 0; |
| 317 | |
| 318 | mutex_spin_enter(&sc->sc_intr_lock); |
| 319 | |
| 320 | /* grab interrupt register */ |
| 321 | intr = BA0READ4(sc, CS4281_HISR); |
| 322 | |
| 323 | DPRINTF(("cs4281_intr:" )); |
| 324 | /* not for me */ |
| 325 | if ((intr & HISR_INTENA) == 0) { |
| 326 | /* clear the interrupt register */ |
| 327 | BA0WRITE4(sc, CS4281_HICR, HICR_CHGM | HICR_IEV); |
| 328 | mutex_spin_exit(&sc->sc_intr_lock); |
| 329 | return 0; |
| 330 | } |
| 331 | |
| 332 | if (intr & HISR_DMA0) |
| 333 | hdsr0 = BA0READ4(sc, CS4281_HDSR0); /* clear intr condition */ |
| 334 | if (intr & HISR_DMA1) |
| 335 | hdsr1 = BA0READ4(sc, CS4281_HDSR1); /* clear intr condition */ |
| 336 | /* clear the interrupt register */ |
| 337 | BA0WRITE4(sc, CS4281_HICR, HICR_CHGM | HICR_IEV); |
| 338 | |
| 339 | #ifdef CS4280_DEBUG |
| 340 | DPRINTF(("intr = 0x%08x, hdsr0 = 0x%08x hdsr1 = 0x%08x\n" , |
| 341 | intr, hdsr0, hdsr1)); |
| 342 | #else |
| 343 | __USE(hdsr0); |
| 344 | __USE(hdsr1); |
| 345 | #endif |
| 346 | |
| 347 | /* Playback Interrupt */ |
| 348 | if (intr & HISR_DMA0) { |
| 349 | handled = 1; |
| 350 | if (sc->sc_prun) { |
| 351 | DPRINTF((" PB DMA 0x%x(%d)" , |
| 352 | (int)BA0READ4(sc, CS4281_DCA0), |
| 353 | (int)BA0READ4(sc, CS4281_DCC0))); |
| 354 | if ((sc->sc_pi%sc->sc_pcount) == 0) |
| 355 | sc->sc_pintr(sc->sc_parg); |
| 356 | /* copy buffer */ |
| 357 | ++sc->sc_pi; |
| 358 | empty_dma = sc->sc_pdma->addr; |
| 359 | if (sc->sc_pi&1) |
| 360 | empty_dma += sc->hw_blocksize; |
| 361 | memcpy(empty_dma, sc->sc_pn, sc->hw_blocksize); |
| 362 | sc->sc_pn += sc->hw_blocksize; |
| 363 | if (sc->sc_pn >= sc->sc_pe) |
| 364 | sc->sc_pn = sc->sc_ps; |
| 365 | } else { |
| 366 | aprint_error_dev(sc->sc_dev, "unexpected play intr\n" ); |
| 367 | } |
| 368 | } |
| 369 | if (intr & HISR_DMA1) { |
| 370 | handled = 1; |
| 371 | if (sc->sc_rrun) { |
| 372 | /* copy from DMA */ |
| 373 | DPRINTF((" CP DMA 0x%x(%d)" , (int)BA0READ4(sc, CS4281_DCA1), |
| 374 | (int)BA0READ4(sc, CS4281_DCC1))); |
| 375 | ++sc->sc_ri; |
| 376 | empty_dma = sc->sc_rdma->addr; |
| 377 | if ((sc->sc_ri & 1) == 0) |
| 378 | empty_dma += sc->hw_blocksize; |
| 379 | memcpy(sc->sc_rn, empty_dma, sc->hw_blocksize); |
| 380 | sc->sc_rn += sc->hw_blocksize; |
| 381 | if (sc->sc_rn >= sc->sc_re) |
| 382 | sc->sc_rn = sc->sc_rs; |
| 383 | if ((sc->sc_ri % sc->sc_rcount) == 0) |
| 384 | sc->sc_rintr(sc->sc_rarg); |
| 385 | } else { |
| 386 | aprint_error_dev(sc->sc_dev, |
| 387 | "unexpected record intr\n" ); |
| 388 | } |
| 389 | } |
| 390 | DPRINTF(("\n" )); |
| 391 | |
| 392 | mutex_spin_exit(&sc->sc_intr_lock); |
| 393 | |
| 394 | return handled; |
| 395 | } |
| 396 | |
| 397 | static int |
| 398 | cs4281_query_encoding(void *addr, struct audio_encoding *fp) |
| 399 | { |
| 400 | |
| 401 | switch (fp->index) { |
| 402 | case 0: |
| 403 | strcpy(fp->name, AudioEulinear); |
| 404 | fp->encoding = AUDIO_ENCODING_ULINEAR; |
| 405 | fp->precision = 8; |
| 406 | fp->flags = 0; |
| 407 | break; |
| 408 | case 1: |
| 409 | strcpy(fp->name, AudioEmulaw); |
| 410 | fp->encoding = AUDIO_ENCODING_ULAW; |
| 411 | fp->precision = 8; |
| 412 | fp->flags = AUDIO_ENCODINGFLAG_EMULATED; |
| 413 | break; |
| 414 | case 2: |
| 415 | strcpy(fp->name, AudioEalaw); |
| 416 | fp->encoding = AUDIO_ENCODING_ALAW; |
| 417 | fp->precision = 8; |
| 418 | fp->flags = AUDIO_ENCODINGFLAG_EMULATED; |
| 419 | break; |
| 420 | case 3: |
| 421 | strcpy(fp->name, AudioEslinear); |
| 422 | fp->encoding = AUDIO_ENCODING_SLINEAR; |
| 423 | fp->precision = 8; |
| 424 | fp->flags = 0; |
| 425 | break; |
| 426 | case 4: |
| 427 | strcpy(fp->name, AudioEslinear_le); |
| 428 | fp->encoding = AUDIO_ENCODING_SLINEAR_LE; |
| 429 | fp->precision = 16; |
| 430 | fp->flags = 0; |
| 431 | break; |
| 432 | case 5: |
| 433 | strcpy(fp->name, AudioEulinear_le); |
| 434 | fp->encoding = AUDIO_ENCODING_ULINEAR_LE; |
| 435 | fp->precision = 16; |
| 436 | fp->flags = 0; |
| 437 | break; |
| 438 | case 6: |
| 439 | strcpy(fp->name, AudioEslinear_be); |
| 440 | fp->encoding = AUDIO_ENCODING_SLINEAR_BE; |
| 441 | fp->precision = 16; |
| 442 | fp->flags = 0; |
| 443 | break; |
| 444 | case 7: |
| 445 | strcpy(fp->name, AudioEulinear_be); |
| 446 | fp->encoding = AUDIO_ENCODING_ULINEAR_BE; |
| 447 | fp->precision = 16; |
| 448 | fp->flags = 0; |
| 449 | break; |
| 450 | default: |
| 451 | return EINVAL; |
| 452 | } |
| 453 | return 0; |
| 454 | } |
| 455 | |
| 456 | static int |
| 457 | cs4281_set_params(void *addr, int setmode, int usemode, |
| 458 | audio_params_t *play, audio_params_t *rec, stream_filter_list_t *pfil, |
| 459 | stream_filter_list_t *rfil) |
| 460 | { |
| 461 | audio_params_t hw; |
| 462 | struct cs428x_softc *sc; |
| 463 | audio_params_t *p; |
| 464 | stream_filter_list_t *fil; |
| 465 | int mode; |
| 466 | |
| 467 | sc = addr; |
| 468 | for (mode = AUMODE_RECORD; mode != -1; |
| 469 | mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) { |
| 470 | if ((setmode & mode) == 0) |
| 471 | continue; |
| 472 | |
| 473 | p = mode == AUMODE_PLAY ? play : rec; |
| 474 | |
| 475 | if (p == play) { |
| 476 | DPRINTFN(5, |
| 477 | ("play: sample=%u precision=%u channels=%u\n" , |
| 478 | p->sample_rate, p->precision, p->channels)); |
| 479 | if (p->sample_rate < 6023 || p->sample_rate > 48000 || |
| 480 | (p->precision != 8 && p->precision != 16) || |
| 481 | (p->channels != 1 && p->channels != 2)) { |
| 482 | return EINVAL; |
| 483 | } |
| 484 | } else { |
| 485 | DPRINTFN(5, |
| 486 | ("rec: sample=%u precision=%u channels=%u\n" , |
| 487 | p->sample_rate, p->precision, p->channels)); |
| 488 | if (p->sample_rate < 6023 || p->sample_rate > 48000 || |
| 489 | (p->precision != 8 && p->precision != 16) || |
| 490 | (p->channels != 1 && p->channels != 2)) { |
| 491 | return EINVAL; |
| 492 | } |
| 493 | } |
| 494 | hw = *p; |
| 495 | fil = mode == AUMODE_PLAY ? pfil : rfil; |
| 496 | |
| 497 | switch (p->encoding) { |
| 498 | case AUDIO_ENCODING_SLINEAR_BE: |
| 499 | break; |
| 500 | case AUDIO_ENCODING_SLINEAR_LE: |
| 501 | break; |
| 502 | case AUDIO_ENCODING_ULINEAR_BE: |
| 503 | break; |
| 504 | case AUDIO_ENCODING_ULINEAR_LE: |
| 505 | break; |
| 506 | case AUDIO_ENCODING_ULAW: |
| 507 | hw.encoding = AUDIO_ENCODING_SLINEAR_LE; |
| 508 | fil->append(fil, mode == AUMODE_PLAY ? mulaw_to_linear8 |
| 509 | : linear8_to_mulaw, &hw); |
| 510 | break; |
| 511 | case AUDIO_ENCODING_ALAW: |
| 512 | hw.encoding = AUDIO_ENCODING_SLINEAR_LE; |
| 513 | fil->append(fil, mode == AUMODE_PLAY ? alaw_to_linear8 |
| 514 | : linear8_to_alaw, &hw); |
| 515 | break; |
| 516 | default: |
| 517 | return EINVAL; |
| 518 | } |
| 519 | } |
| 520 | |
| 521 | /* set sample rate */ |
| 522 | cs4281_set_dac_rate(sc, play->sample_rate); |
| 523 | cs4281_set_adc_rate(sc, rec->sample_rate); |
| 524 | return 0; |
| 525 | } |
| 526 | |
| 527 | static int |
| 528 | cs4281_halt_output(void *addr) |
| 529 | { |
| 530 | struct cs428x_softc *sc; |
| 531 | |
| 532 | sc = addr; |
| 533 | BA0WRITE4(sc, CS4281_DCR0, BA0READ4(sc, CS4281_DCR0) | DCRn_MSK); |
| 534 | sc->sc_prun = 0; |
| 535 | return 0; |
| 536 | } |
| 537 | |
| 538 | static int |
| 539 | cs4281_halt_input(void *addr) |
| 540 | { |
| 541 | struct cs428x_softc *sc; |
| 542 | |
| 543 | sc = addr; |
| 544 | BA0WRITE4(sc, CS4281_DCR1, BA0READ4(sc, CS4281_DCR1) | DCRn_MSK); |
| 545 | sc->sc_rrun = 0; |
| 546 | return 0; |
| 547 | } |
| 548 | |
| 549 | static int |
| 550 | cs4281_getdev(void *addr, struct audio_device *retp) |
| 551 | { |
| 552 | |
| 553 | *retp = cs4281_device; |
| 554 | return 0; |
| 555 | } |
| 556 | |
| 557 | static int |
| 558 | cs4281_trigger_output(void *addr, void *start, void *end, int blksize, |
| 559 | void (*intr)(void *), void *arg, |
| 560 | const audio_params_t *param) |
| 561 | { |
| 562 | struct cs428x_softc *sc; |
| 563 | uint32_t fmt; |
| 564 | struct cs428x_dma *p; |
| 565 | int dma_count; |
| 566 | |
| 567 | sc = addr; |
| 568 | fmt = 0; |
| 569 | #ifdef DIAGNOSTIC |
| 570 | if (sc->sc_prun) |
| 571 | printf("cs4281_trigger_output: already running\n" ); |
| 572 | #endif |
| 573 | sc->sc_prun = 1; |
| 574 | |
| 575 | DPRINTF(("cs4281_trigger_output: sc=%p start=%p end=%p " |
| 576 | "blksize=%d intr=%p(%p)\n" , addr, start, end, blksize, intr, arg)); |
| 577 | sc->sc_pintr = intr; |
| 578 | sc->sc_parg = arg; |
| 579 | |
| 580 | /* stop playback DMA */ |
| 581 | BA0WRITE4(sc, CS4281_DCR0, BA0READ4(sc, CS4281_DCR0) | DCRn_MSK); |
| 582 | |
| 583 | DPRINTF(("param: precision=%d channels=%d encoding=%d\n" , |
| 584 | param->precision, param->channels, param->encoding)); |
| 585 | for (p = sc->sc_dmas; p != NULL && BUFADDR(p) != start; p = p->next) |
| 586 | continue; |
| 587 | if (p == NULL) { |
| 588 | printf("cs4281_trigger_output: bad addr %p\n" , start); |
| 589 | return EINVAL; |
| 590 | } |
| 591 | |
| 592 | sc->sc_pcount = blksize / sc->hw_blocksize; |
| 593 | sc->sc_ps = (char *)start; |
| 594 | sc->sc_pe = (char *)end; |
| 595 | sc->sc_pdma = p; |
| 596 | sc->sc_pbuf = KERNADDR(p); |
| 597 | sc->sc_pi = 0; |
| 598 | sc->sc_pn = sc->sc_ps; |
| 599 | if (blksize >= sc->dma_size) { |
| 600 | sc->sc_pn = sc->sc_ps + sc->dma_size; |
| 601 | memcpy(sc->sc_pbuf, start, sc->dma_size); |
| 602 | ++sc->sc_pi; |
| 603 | } else { |
| 604 | sc->sc_pn = sc->sc_ps + sc->hw_blocksize; |
| 605 | memcpy(sc->sc_pbuf, start, sc->hw_blocksize); |
| 606 | } |
| 607 | |
| 608 | dma_count = sc->dma_size; |
| 609 | if (param->precision != 8) |
| 610 | dma_count /= 2; /* 16 bit */ |
| 611 | if (param->channels > 1) |
| 612 | dma_count /= 2; /* Stereo */ |
| 613 | |
| 614 | DPRINTF(("cs4281_trigger_output: DMAADDR(p)=0x%x count=%d\n" , |
| 615 | (int)DMAADDR(p), dma_count)); |
| 616 | BA0WRITE4(sc, CS4281_DBA0, DMAADDR(p)); |
| 617 | BA0WRITE4(sc, CS4281_DBC0, dma_count-1); |
| 618 | |
| 619 | /* set playback format */ |
| 620 | fmt = BA0READ4(sc, CS4281_DMR0) & ~DMRn_FMTMSK; |
| 621 | if (param->precision == 8) |
| 622 | fmt |= DMRn_SIZE8; |
| 623 | if (param->channels == 1) |
| 624 | fmt |= DMRn_MONO; |
| 625 | if (param->encoding == AUDIO_ENCODING_ULINEAR_BE || |
| 626 | param->encoding == AUDIO_ENCODING_SLINEAR_BE) |
| 627 | fmt |= DMRn_BEND; |
| 628 | if (param->encoding == AUDIO_ENCODING_ULINEAR_BE || |
| 629 | param->encoding == AUDIO_ENCODING_ULINEAR_LE) |
| 630 | fmt |= DMRn_USIGN; |
| 631 | BA0WRITE4(sc, CS4281_DMR0, fmt); |
| 632 | |
| 633 | /* set sample rate */ |
| 634 | sc->sc_prate = param->sample_rate; |
| 635 | cs4281_set_dac_rate(sc, param->sample_rate); |
| 636 | |
| 637 | /* start DMA */ |
| 638 | BA0WRITE4(sc, CS4281_DCR0, BA0READ4(sc, CS4281_DCR0) & ~DCRn_MSK); |
| 639 | /* Enable interrupts */ |
| 640 | BA0WRITE4(sc, CS4281_HICR, HICR_IEV | HICR_CHGM); |
| 641 | |
| 642 | DPRINTF(("HICR =0x%08x(expected 0x00000001)\n" , BA0READ4(sc, CS4281_HICR))); |
| 643 | DPRINTF(("HIMR =0x%08x(expected 0x00f0fc3f)\n" , BA0READ4(sc, CS4281_HIMR))); |
| 644 | DPRINTF(("DMR0 =0x%08x(expected 0x2???0018)\n" , BA0READ4(sc, CS4281_DMR0))); |
| 645 | DPRINTF(("DCR0 =0x%08x(expected 0x00030000)\n" , BA0READ4(sc, CS4281_DCR0))); |
| 646 | DPRINTF(("FCR0 =0x%08x(expected 0x81000f00)\n" , BA0READ4(sc, CS4281_FCR0))); |
| 647 | DPRINTF(("DACSR=0x%08x(expected 1 for 44kHz 5 for 8kHz)\n" , |
| 648 | BA0READ4(sc, CS4281_DACSR))); |
| 649 | DPRINTF(("SRCSA=0x%08x(expected 0x0b0a0100)\n" , BA0READ4(sc, CS4281_SRCSA))); |
| 650 | DPRINTF(("SSPM&SSPM_PSRCEN =0x%08x(expected 0x00000010)\n" , |
| 651 | BA0READ4(sc, CS4281_SSPM) & SSPM_PSRCEN)); |
| 652 | |
| 653 | return 0; |
| 654 | } |
| 655 | |
| 656 | static int |
| 657 | cs4281_trigger_input(void *addr, void *start, void *end, int blksize, |
| 658 | void (*intr)(void *), void *arg, |
| 659 | const audio_params_t *param) |
| 660 | { |
| 661 | struct cs428x_softc *sc; |
| 662 | struct cs428x_dma *p; |
| 663 | uint32_t fmt; |
| 664 | int dma_count; |
| 665 | |
| 666 | sc = addr; |
| 667 | fmt = 0; |
| 668 | #ifdef DIAGNOSTIC |
| 669 | if (sc->sc_rrun) |
| 670 | printf("cs4281_trigger_input: already running\n" ); |
| 671 | #endif |
| 672 | sc->sc_rrun = 1; |
| 673 | DPRINTF(("cs4281_trigger_input: sc=%p start=%p end=%p " |
| 674 | "blksize=%d intr=%p(%p)\n" , addr, start, end, blksize, intr, arg)); |
| 675 | sc->sc_rintr = intr; |
| 676 | sc->sc_rarg = arg; |
| 677 | |
| 678 | /* stop recording DMA */ |
| 679 | BA0WRITE4(sc, CS4281_DCR1, BA0READ4(sc, CS4281_DCR1) | DCRn_MSK); |
| 680 | |
| 681 | for (p = sc->sc_dmas; p && BUFADDR(p) != start; p = p->next) |
| 682 | continue; |
| 683 | if (!p) { |
| 684 | printf("cs4281_trigger_input: bad addr %p\n" , start); |
| 685 | return EINVAL; |
| 686 | } |
| 687 | |
| 688 | sc->sc_rcount = blksize / sc->hw_blocksize; |
| 689 | sc->sc_rs = (char *)start; |
| 690 | sc->sc_re = (char *)end; |
| 691 | sc->sc_rdma = p; |
| 692 | sc->sc_rbuf = KERNADDR(p); |
| 693 | sc->sc_ri = 0; |
| 694 | sc->sc_rn = sc->sc_rs; |
| 695 | |
| 696 | dma_count = sc->dma_size; |
| 697 | if (param->precision != 8) |
| 698 | dma_count /= 2; |
| 699 | if (param->channels > 1) |
| 700 | dma_count /= 2; |
| 701 | |
| 702 | DPRINTF(("cs4281_trigger_input: DMAADDR(p)=0x%x count=%d\n" , |
| 703 | (int)DMAADDR(p), dma_count)); |
| 704 | BA0WRITE4(sc, CS4281_DBA1, DMAADDR(p)); |
| 705 | BA0WRITE4(sc, CS4281_DBC1, dma_count-1); |
| 706 | |
| 707 | /* set recording format */ |
| 708 | fmt = BA0READ4(sc, CS4281_DMR1) & ~DMRn_FMTMSK; |
| 709 | if (param->precision == 8) |
| 710 | fmt |= DMRn_SIZE8; |
| 711 | if (param->channels == 1) |
| 712 | fmt |= DMRn_MONO; |
| 713 | if (param->encoding == AUDIO_ENCODING_ULINEAR_BE || |
| 714 | param->encoding == AUDIO_ENCODING_SLINEAR_BE) |
| 715 | fmt |= DMRn_BEND; |
| 716 | if (param->encoding == AUDIO_ENCODING_ULINEAR_BE || |
| 717 | param->encoding == AUDIO_ENCODING_ULINEAR_LE) |
| 718 | fmt |= DMRn_USIGN; |
| 719 | BA0WRITE4(sc, CS4281_DMR1, fmt); |
| 720 | |
| 721 | /* set sample rate */ |
| 722 | sc->sc_rrate = param->sample_rate; |
| 723 | cs4281_set_adc_rate(sc, param->sample_rate); |
| 724 | |
| 725 | /* Start DMA */ |
| 726 | BA0WRITE4(sc, CS4281_DCR1, BA0READ4(sc, CS4281_DCR1) & ~DCRn_MSK); |
| 727 | /* Enable interrupts */ |
| 728 | BA0WRITE4(sc, CS4281_HICR, HICR_IEV | HICR_CHGM); |
| 729 | |
| 730 | DPRINTF(("HICR=0x%08x\n" , BA0READ4(sc, CS4281_HICR))); |
| 731 | DPRINTF(("HIMR=0x%08x\n" , BA0READ4(sc, CS4281_HIMR))); |
| 732 | DPRINTF(("DMR1=0x%08x\n" , BA0READ4(sc, CS4281_DMR1))); |
| 733 | DPRINTF(("DCR1=0x%08x\n" , BA0READ4(sc, CS4281_DCR1))); |
| 734 | |
| 735 | return 0; |
| 736 | } |
| 737 | |
| 738 | static bool |
| 739 | cs4281_suspend(device_t dv, const pmf_qual_t *qual) |
| 740 | { |
| 741 | struct cs428x_softc *sc = device_private(dv); |
| 742 | |
| 743 | mutex_enter(&sc->sc_lock); |
| 744 | mutex_spin_exit(&sc->sc_intr_lock); |
| 745 | |
| 746 | /* save current playback status */ |
| 747 | if (sc->sc_prun) { |
| 748 | sc->sc_suspend_state.cs4281.dcr0 = BA0READ4(sc, CS4281_DCR0); |
| 749 | sc->sc_suspend_state.cs4281.dmr0 = BA0READ4(sc, CS4281_DMR0); |
| 750 | sc->sc_suspend_state.cs4281.dbc0 = BA0READ4(sc, CS4281_DBC0); |
| 751 | sc->sc_suspend_state.cs4281.dba0 = BA0READ4(sc, CS4281_DBA0); |
| 752 | } |
| 753 | |
| 754 | /* save current capture status */ |
| 755 | if (sc->sc_rrun) { |
| 756 | sc->sc_suspend_state.cs4281.dcr1 = BA0READ4(sc, CS4281_DCR1); |
| 757 | sc->sc_suspend_state.cs4281.dmr1 = BA0READ4(sc, CS4281_DMR1); |
| 758 | sc->sc_suspend_state.cs4281.dbc1 = BA0READ4(sc, CS4281_DBC1); |
| 759 | sc->sc_suspend_state.cs4281.dba1 = BA0READ4(sc, CS4281_DBA1); |
| 760 | } |
| 761 | /* Stop DMA */ |
| 762 | BA0WRITE4(sc, CS4281_DCR0, BA0READ4(sc, CS4281_DCR0) | DCRn_MSK); |
| 763 | BA0WRITE4(sc, CS4281_DCR1, BA0READ4(sc, CS4281_DCR1) | DCRn_MSK); |
| 764 | |
| 765 | mutex_spin_exit(&sc->sc_intr_lock); |
| 766 | mutex_exit(&sc->sc_lock); |
| 767 | |
| 768 | return true; |
| 769 | } |
| 770 | |
| 771 | static bool |
| 772 | cs4281_resume(device_t dv, const pmf_qual_t *qual) |
| 773 | { |
| 774 | struct cs428x_softc *sc = device_private(dv); |
| 775 | |
| 776 | mutex_enter(&sc->sc_lock); |
| 777 | mutex_spin_enter(&sc->sc_intr_lock); |
| 778 | |
| 779 | cs4281_init(sc, 0); |
| 780 | cs4281_reset_codec(sc); |
| 781 | |
| 782 | /* restore ac97 registers */ |
| 783 | mutex_spin_exit(&sc->sc_intr_lock); |
| 784 | (*sc->codec_if->vtbl->restore_ports)(sc->codec_if); |
| 785 | mutex_spin_enter(&sc->sc_intr_lock); |
| 786 | |
| 787 | /* restore DMA related status */ |
| 788 | if (sc->sc_prun) { |
| 789 | cs4281_set_dac_rate(sc, sc->sc_prate); |
| 790 | BA0WRITE4(sc, CS4281_DBA0, sc->sc_suspend_state.cs4281.dba0); |
| 791 | BA0WRITE4(sc, CS4281_DBC0, sc->sc_suspend_state.cs4281.dbc0); |
| 792 | BA0WRITE4(sc, CS4281_DMR0, sc->sc_suspend_state.cs4281.dmr0); |
| 793 | BA0WRITE4(sc, CS4281_DCR0, sc->sc_suspend_state.cs4281.dcr0); |
| 794 | } |
| 795 | if (sc->sc_rrun) { |
| 796 | cs4281_set_adc_rate(sc, sc->sc_rrate); |
| 797 | BA0WRITE4(sc, CS4281_DBA1, sc->sc_suspend_state.cs4281.dba1); |
| 798 | BA0WRITE4(sc, CS4281_DBC1, sc->sc_suspend_state.cs4281.dbc1); |
| 799 | BA0WRITE4(sc, CS4281_DMR1, sc->sc_suspend_state.cs4281.dmr1); |
| 800 | BA0WRITE4(sc, CS4281_DCR1, sc->sc_suspend_state.cs4281.dcr1); |
| 801 | } |
| 802 | /* enable intterupts */ |
| 803 | if (sc->sc_prun || sc->sc_rrun) |
| 804 | BA0WRITE4(sc, CS4281_HICR, HICR_IEV | HICR_CHGM); |
| 805 | |
| 806 | mutex_spin_exit(&sc->sc_intr_lock); |
| 807 | mutex_exit(&sc->sc_lock); |
| 808 | |
| 809 | return true; |
| 810 | } |
| 811 | |
| 812 | /* control AC97 codec */ |
| 813 | static int |
| 814 | cs4281_reset_codec(void *addr) |
| 815 | { |
| 816 | struct cs428x_softc *sc; |
| 817 | uint16_t data; |
| 818 | uint32_t dat32; |
| 819 | int n; |
| 820 | |
| 821 | sc = addr; |
| 822 | |
| 823 | DPRINTFN(3, ("cs4281_reset_codec\n" )); |
| 824 | |
| 825 | /* Reset codec */ |
| 826 | BA0WRITE4(sc, CS428X_ACCTL, 0); |
| 827 | delay(50); /* delay 50us */ |
| 828 | |
| 829 | BA0WRITE4(sc, CS4281_SPMC, 0); |
| 830 | delay(100); /* delay 100us */ |
| 831 | BA0WRITE4(sc, CS4281_SPMC, SPMC_RSTN); |
| 832 | #if defined(ENABLE_SECONDARY_CODEC) |
| 833 | BA0WRITE4(sc, CS4281_SPMC, SPMC_RSTN | SPCM_ASDIN2E); |
| 834 | BA0WRITE4(sc, CS4281_SERMC, SERMC_TCID); |
| 835 | #endif |
| 836 | delay(50000); /* XXX: delay 50ms */ |
| 837 | |
| 838 | /* Enable ASYNC generation */ |
| 839 | BA0WRITE4(sc, CS428X_ACCTL, ACCTL_ESYN); |
| 840 | |
| 841 | /* Wait for codec ready. Linux driver waits 50ms here */ |
| 842 | n = 0; |
| 843 | while ((BA0READ4(sc, CS428X_ACSTS) & ACSTS_CRDY) == 0) { |
| 844 | delay(100); |
| 845 | if (++n > 1000) { |
| 846 | printf("reset_codec: AC97 codec ready timeout\n" ); |
| 847 | return ETIMEDOUT; |
| 848 | } |
| 849 | } |
| 850 | #if defined(ENABLE_SECONDARY_CODEC) |
| 851 | /* secondary codec ready*/ |
| 852 | n = 0; |
| 853 | while ((BA0READ4(sc, CS4281_ACSTS2) & ACSTS2_CRDY2) == 0) { |
| 854 | delay(100); |
| 855 | if (++n > 1000) |
| 856 | return 0; |
| 857 | } |
| 858 | #endif |
| 859 | /* Set the serial timing configuration */ |
| 860 | /* XXX: undocumented but the Linux driver do this */ |
| 861 | BA0WRITE4(sc, CS4281_SERMC, SERMC_PTCAC97); |
| 862 | |
| 863 | /* Wait for codec ready signal */ |
| 864 | n = 0; |
| 865 | do { |
| 866 | delay(1000); |
| 867 | if (++n > 1000) { |
| 868 | aprint_error_dev(sc->sc_dev, |
| 869 | "timeout waiting for codec ready\n" ); |
| 870 | return ETIMEDOUT; |
| 871 | } |
| 872 | dat32 = BA0READ4(sc, CS428X_ACSTS) & ACSTS_CRDY; |
| 873 | } while (dat32 == 0); |
| 874 | |
| 875 | /* Enable Valid Frame output on ASDOUT */ |
| 876 | BA0WRITE4(sc, CS428X_ACCTL, ACCTL_ESYN | ACCTL_VFRM); |
| 877 | |
| 878 | /* Wait until codec calibration is finished. Codec register 26h */ |
| 879 | n = 0; |
| 880 | do { |
| 881 | delay(1); |
| 882 | if (++n > 1000) { |
| 883 | aprint_error_dev(sc->sc_dev, |
| 884 | "timeout waiting for codec calibration\n" ); |
| 885 | return ETIMEDOUT; |
| 886 | } |
| 887 | cs428x_read_codec(sc, AC97_REG_POWER, &data); |
| 888 | } while ((data & 0x0f) != 0x0f); |
| 889 | |
| 890 | /* Set the serial timing configuration again */ |
| 891 | /* XXX: undocumented but the Linux driver do this */ |
| 892 | BA0WRITE4(sc, CS4281_SERMC, SERMC_PTCAC97); |
| 893 | |
| 894 | /* Wait until we've sampled input slots 3 & 4 as valid */ |
| 895 | n = 0; |
| 896 | do { |
| 897 | delay(1000); |
| 898 | if (++n > 1000) { |
| 899 | aprint_error_dev(sc->sc_dev, "timeout waiting for " |
| 900 | "sampled input slots as valid\n" ); |
| 901 | return ETIMEDOUT; |
| 902 | } |
| 903 | dat32 = BA0READ4(sc, CS428X_ACISV) & (ACISV_ISV3 | ACISV_ISV4) ; |
| 904 | } while (dat32 != (ACISV_ISV3 | ACISV_ISV4)); |
| 905 | |
| 906 | /* Start digital data transfer of audio data to the codec */ |
| 907 | BA0WRITE4(sc, CS428X_ACOSV, (ACOSV_SLV3 | ACOSV_SLV4)); |
| 908 | return 0; |
| 909 | } |
| 910 | |
| 911 | |
| 912 | /* Internal functions */ |
| 913 | |
| 914 | /* convert sample rate to register value */ |
| 915 | static uint8_t |
| 916 | cs4281_sr2regval(int rate) |
| 917 | { |
| 918 | uint8_t retval; |
| 919 | |
| 920 | /* We don't have to change here. but anyway ... */ |
| 921 | if (rate > 48000) |
| 922 | rate = 48000; |
| 923 | if (rate < 6023) |
| 924 | rate = 6023; |
| 925 | |
| 926 | switch (rate) { |
| 927 | case 8000: |
| 928 | retval = 5; |
| 929 | break; |
| 930 | case 11025: |
| 931 | retval = 4; |
| 932 | break; |
| 933 | case 16000: |
| 934 | retval = 3; |
| 935 | break; |
| 936 | case 22050: |
| 937 | retval = 2; |
| 938 | break; |
| 939 | case 44100: |
| 940 | retval = 1; |
| 941 | break; |
| 942 | case 48000: |
| 943 | retval = 0; |
| 944 | break; |
| 945 | default: |
| 946 | retval = 1536000/rate; /* == 24576000/(rate*16) */ |
| 947 | } |
| 948 | return retval; |
| 949 | } |
| 950 | |
| 951 | static void |
| 952 | cs4281_set_adc_rate(struct cs428x_softc *sc, int rate) |
| 953 | { |
| 954 | |
| 955 | BA0WRITE4(sc, CS4281_ADCSR, cs4281_sr2regval(rate)); |
| 956 | } |
| 957 | |
| 958 | static void |
| 959 | cs4281_set_dac_rate(struct cs428x_softc *sc, int rate) |
| 960 | { |
| 961 | |
| 962 | BA0WRITE4(sc, CS4281_DACSR, cs4281_sr2regval(rate)); |
| 963 | } |
| 964 | |
| 965 | static int |
| 966 | cs4281_init(struct cs428x_softc *sc, int init) |
| 967 | { |
| 968 | int n; |
| 969 | uint16_t data; |
| 970 | uint32_t dat32; |
| 971 | |
| 972 | /* set "Configuration Write Protect" register to |
| 973 | * 0x4281 to allow to write */ |
| 974 | BA0WRITE4(sc, CS4281_CWPR, 0x4281); |
| 975 | |
| 976 | /* |
| 977 | * Unset "Full Power-Down bit of Extended PCI Power Management |
| 978 | * Control" register to release the reset state. |
| 979 | */ |
| 980 | dat32 = BA0READ4(sc, CS4281_EPPMC); |
| 981 | if (dat32 & EPPMC_FPDN) { |
| 982 | BA0WRITE4(sc, CS4281_EPPMC, dat32 & ~EPPMC_FPDN); |
| 983 | } |
| 984 | |
| 985 | /* Start PLL out in known state */ |
| 986 | BA0WRITE4(sc, CS4281_CLKCR1, 0); |
| 987 | /* Start serial ports out in known state */ |
| 988 | BA0WRITE4(sc, CS4281_SERMC, 0); |
| 989 | |
| 990 | /* Reset codec */ |
| 991 | BA0WRITE4(sc, CS428X_ACCTL, 0); |
| 992 | delay(50); /* delay 50us */ |
| 993 | |
| 994 | BA0WRITE4(sc, CS4281_SPMC, 0); |
| 995 | delay(100); /* delay 100us */ |
| 996 | BA0WRITE4(sc, CS4281_SPMC, SPMC_RSTN); |
| 997 | #if defined(ENABLE_SECONDARY_CODEC) |
| 998 | BA0WRITE4(sc, CS4281_SPMC, SPMC_RSTN | SPCM_ASDIN2E); |
| 999 | BA0WRITE4(sc, CS4281_SERMC, SERMC_TCID); |
| 1000 | #endif |
| 1001 | delay(50000); /* XXX: delay 50ms */ |
| 1002 | |
| 1003 | /* Turn on Sound System clocks based on ABITCLK */ |
| 1004 | BA0WRITE4(sc, CS4281_CLKCR1, CLKCR1_DLLP); |
| 1005 | delay(50000); /* XXX: delay 50ms */ |
| 1006 | BA0WRITE4(sc, CS4281_CLKCR1, CLKCR1_SWCE | CLKCR1_DLLP); |
| 1007 | |
| 1008 | /* Set enables for sections that are needed in the SSPM registers */ |
| 1009 | BA0WRITE4(sc, CS4281_SSPM, |
| 1010 | SSPM_MIXEN | /* Mixer */ |
| 1011 | SSPM_CSRCEN | /* Capture SRC */ |
| 1012 | SSPM_PSRCEN | /* Playback SRC */ |
| 1013 | SSPM_JSEN | /* Joystick */ |
| 1014 | SSPM_ACLEN | /* AC LINK */ |
| 1015 | SSPM_FMEN /* FM */ |
| 1016 | ); |
| 1017 | |
| 1018 | /* Wait for clock stabilization */ |
| 1019 | n = 0; |
| 1020 | #if 1 |
| 1021 | /* what document says */ |
| 1022 | while ((BA0READ4(sc, CS4281_CLKCR1)& (CLKCR1_DLLRDY | CLKCR1_CLKON)) |
| 1023 | != (CLKCR1_DLLRDY | CLKCR1_CLKON)) { |
| 1024 | delay(100); |
| 1025 | if (++n > 1000) { |
| 1026 | aprint_error_dev(sc->sc_dev, |
| 1027 | "timeout waiting for clock stabilization\n" ); |
| 1028 | return -1; |
| 1029 | } |
| 1030 | } |
| 1031 | #else |
| 1032 | /* Cirrus driver for Linux does */ |
| 1033 | while (!(BA0READ4(sc, CS4281_CLKCR1) & CLKCR1_DLLRDY)) { |
| 1034 | delay(1000); |
| 1035 | if (++n > 1000) { |
| 1036 | aprint_error_dev(sc->sc_dev, |
| 1037 | "timeout waiting for clock stabilization\n" ); |
| 1038 | return -1; |
| 1039 | } |
| 1040 | } |
| 1041 | #endif |
| 1042 | |
| 1043 | /* Enable ASYNC generation */ |
| 1044 | BA0WRITE4(sc, CS428X_ACCTL, ACCTL_ESYN); |
| 1045 | |
| 1046 | /* Wait for codec ready. Linux driver waits 50ms here */ |
| 1047 | n = 0; |
| 1048 | while ((BA0READ4(sc, CS428X_ACSTS) & ACSTS_CRDY) == 0) { |
| 1049 | delay(100); |
| 1050 | if (++n > 1000) { |
| 1051 | aprint_error_dev(sc->sc_dev, |
| 1052 | "timeout waiting for codec ready\n" ); |
| 1053 | return -1; |
| 1054 | } |
| 1055 | } |
| 1056 | |
| 1057 | #if defined(ENABLE_SECONDARY_CODEC) |
| 1058 | /* secondary codec ready*/ |
| 1059 | n = 0; |
| 1060 | while ((BA0READ4(sc, CS4281_ACSTS2) & ACSTS2_CRDY2) == 0) { |
| 1061 | delay(100); |
| 1062 | if (++n > 1000) { |
| 1063 | aprint_error_dev(sc->sc_dev, |
| 1064 | "timeout waiting for secondary codec ready\n" ); |
| 1065 | return -1; |
| 1066 | } |
| 1067 | } |
| 1068 | #endif |
| 1069 | |
| 1070 | /* Set the serial timing configuration */ |
| 1071 | /* XXX: undocumented but the Linux driver do this */ |
| 1072 | BA0WRITE4(sc, CS4281_SERMC, SERMC_PTCAC97); |
| 1073 | |
| 1074 | /* Wait for codec ready signal */ |
| 1075 | n = 0; |
| 1076 | do { |
| 1077 | delay(1000); |
| 1078 | if (++n > 1000) { |
| 1079 | aprint_error_dev(sc->sc_dev, |
| 1080 | "timeout waiting for codec ready\n" ); |
| 1081 | return -1; |
| 1082 | } |
| 1083 | dat32 = BA0READ4(sc, CS428X_ACSTS) & ACSTS_CRDY; |
| 1084 | } while (dat32 == 0); |
| 1085 | |
| 1086 | /* Enable Valid Frame output on ASDOUT */ |
| 1087 | BA0WRITE4(sc, CS428X_ACCTL, ACCTL_ESYN | ACCTL_VFRM); |
| 1088 | |
| 1089 | /* Wait until codec calibration is finished. codec register 26h */ |
| 1090 | n = 0; |
| 1091 | do { |
| 1092 | delay(1); |
| 1093 | if (++n > 1000) { |
| 1094 | aprint_error_dev(sc->sc_dev, |
| 1095 | "timeout waiting for codec calibration\n" ); |
| 1096 | return -1; |
| 1097 | } |
| 1098 | cs428x_read_codec(sc, AC97_REG_POWER, &data); |
| 1099 | } while ((data & 0x0f) != 0x0f); |
| 1100 | |
| 1101 | /* Set the serial timing configuration again */ |
| 1102 | /* XXX: undocumented but the Linux driver do this */ |
| 1103 | BA0WRITE4(sc, CS4281_SERMC, SERMC_PTCAC97); |
| 1104 | |
| 1105 | /* Wait until we've sampled input slots 3 & 4 as valid */ |
| 1106 | n = 0; |
| 1107 | do { |
| 1108 | delay(1000); |
| 1109 | if (++n > 1000) { |
| 1110 | aprint_error_dev(sc->sc_dev, "timeout waiting for " |
| 1111 | "sampled input slots as valid\n" ); |
| 1112 | return -1; |
| 1113 | } |
| 1114 | dat32 = BA0READ4(sc, CS428X_ACISV) & (ACISV_ISV3 | ACISV_ISV4); |
| 1115 | } while (dat32 != (ACISV_ISV3 | ACISV_ISV4)); |
| 1116 | |
| 1117 | /* Start digital data transfer of audio data to the codec */ |
| 1118 | BA0WRITE4(sc, CS428X_ACOSV, (ACOSV_SLV3 | ACOSV_SLV4)); |
| 1119 | |
| 1120 | cs428x_write_codec(sc, AC97_REG_HEADPHONE_VOLUME, 0); |
| 1121 | cs428x_write_codec(sc, AC97_REG_MASTER_VOLUME, 0); |
| 1122 | |
| 1123 | /* Power on the DAC */ |
| 1124 | cs428x_read_codec(sc, AC97_REG_POWER, &data); |
| 1125 | cs428x_write_codec(sc, AC97_REG_POWER, data & 0xfdff); |
| 1126 | |
| 1127 | /* Wait until we sample a DAC ready state. |
| 1128 | * Not documented, but Linux driver does. |
| 1129 | */ |
| 1130 | for (n = 0; n < 32; ++n) { |
| 1131 | delay(1000); |
| 1132 | cs428x_read_codec(sc, AC97_REG_POWER, &data); |
| 1133 | if (data & 0x02) |
| 1134 | break; |
| 1135 | } |
| 1136 | |
| 1137 | /* Power on the ADC */ |
| 1138 | cs428x_read_codec(sc, AC97_REG_POWER, &data); |
| 1139 | cs428x_write_codec(sc, AC97_REG_POWER, data & 0xfeff); |
| 1140 | |
| 1141 | /* Wait until we sample ADC ready state. |
| 1142 | * Not documented, but Linux driver does. |
| 1143 | */ |
| 1144 | for (n = 0; n < 32; ++n) { |
| 1145 | delay(1000); |
| 1146 | cs428x_read_codec(sc, AC97_REG_POWER, &data); |
| 1147 | if (data & 0x01) |
| 1148 | break; |
| 1149 | } |
| 1150 | |
| 1151 | #if 0 |
| 1152 | /* Initialize AC-Link features */ |
| 1153 | /* variable sample-rate support */ |
| 1154 | mem = BA0READ4(sc, CS4281_SERMC); |
| 1155 | mem |= (SERMC_ODSEN1 | SERMC_ODSEN2); |
| 1156 | BA0WRITE4(sc, CS4281_SERMC, mem); |
| 1157 | /* XXX: more... */ |
| 1158 | |
| 1159 | /* Initialize SSCR register features */ |
| 1160 | /* XXX: hardware volume setting */ |
| 1161 | BA0WRITE4(sc, CS4281_SSCR, ~SSCR_HVC); /* disable HW volume setting */ |
| 1162 | #endif |
| 1163 | |
| 1164 | /* disable Sound Blaster Pro emulation */ |
| 1165 | /* XXX: |
| 1166 | * Cannot set since the documents does not describe which bit is |
| 1167 | * correspond to SSCR_SB. Since the reset value of SSCR is 0, |
| 1168 | * we can ignore it.*/ |
| 1169 | #if 0 |
| 1170 | BA0WRITE4(sc, CS4281_SSCR, SSCR_SB); |
| 1171 | #endif |
| 1172 | |
| 1173 | /* map AC97 PCM playback to DMA Channel 0 */ |
| 1174 | /* Reset FEN bit to setup first */ |
| 1175 | BA0WRITE4(sc, CS4281_FCR0, (BA0READ4(sc, CS4281_FCR0) & ~FCRn_FEN)); |
| 1176 | /* |
| 1177 | *| RS[4:0]/| | |
| 1178 | *| LS[4:0] | AC97 | Slot Function |
| 1179 | *|---------+--------+-------------------- |
| 1180 | *| 0 | 3 | Left PCM Playback |
| 1181 | *| 1 | 4 | Right PCM Playback |
| 1182 | *| 2 | 5 | Phone Line 1 DAC |
| 1183 | *| 3 | 6 | Center PCM Playback |
| 1184 | *.... |
| 1185 | * quoted from Table 29(p109) |
| 1186 | */ |
| 1187 | dat32 = 0x01 << 24 | /* RS[4:0] = 1 see above */ |
| 1188 | 0x00 << 16 | /* LS[4:0] = 0 see above */ |
| 1189 | 0x0f << 8 | /* SZ[6:0] = 15 size of buffer */ |
| 1190 | 0x00 << 0 ; /* OF[6:0] = 0 offset */ |
| 1191 | BA0WRITE4(sc, CS4281_FCR0, dat32); |
| 1192 | BA0WRITE4(sc, CS4281_FCR0, dat32 | FCRn_FEN); |
| 1193 | |
| 1194 | /* map AC97 PCM record to DMA Channel 1 */ |
| 1195 | /* Reset FEN bit to setup first */ |
| 1196 | BA0WRITE4(sc, CS4281_FCR1, (BA0READ4(sc, CS4281_FCR1) & ~FCRn_FEN)); |
| 1197 | /* |
| 1198 | *| RS[4:0]/| |
| 1199 | *| LS[4:0] | AC97 | Slot Function |
| 1200 | *|---------+------+------------------- |
| 1201 | *| 10 | 3 | Left PCM Record |
| 1202 | *| 11 | 4 | Right PCM Record |
| 1203 | *| 12 | 5 | Phone Line 1 ADC |
| 1204 | *| 13 | 6 | Mic ADC |
| 1205 | *.... |
| 1206 | * quoted from Table 30(p109) |
| 1207 | */ |
| 1208 | dat32 = 0x0b << 24 | /* RS[4:0] = 11 See above */ |
| 1209 | 0x0a << 16 | /* LS[4:0] = 10 See above */ |
| 1210 | 0x0f << 8 | /* SZ[6:0] = 15 Size of buffer */ |
| 1211 | 0x10 << 0 ; /* OF[6:0] = 16 offset */ |
| 1212 | |
| 1213 | /* XXX: I cannot understand why FCRn_PSH is needed here. */ |
| 1214 | BA0WRITE4(sc, CS4281_FCR1, dat32 | FCRn_PSH); |
| 1215 | BA0WRITE4(sc, CS4281_FCR1, dat32 | FCRn_FEN); |
| 1216 | |
| 1217 | #if 0 |
| 1218 | /* Disable DMA Channel 2, 3 */ |
| 1219 | BA0WRITE4(sc, CS4281_FCR2, (BA0READ4(sc, CS4281_FCR2) & ~FCRn_FEN)); |
| 1220 | BA0WRITE4(sc, CS4281_FCR3, (BA0READ4(sc, CS4281_FCR3) & ~FCRn_FEN)); |
| 1221 | #endif |
| 1222 | |
| 1223 | /* Set the SRC Slot Assignment accordingly */ |
| 1224 | /*| PLSS[4:0]/ |
| 1225 | *| PRSS[4:0] | AC97 | Slot Function |
| 1226 | *|-----------+------+---------------- |
| 1227 | *| 0 | 3 | Left PCM Playback |
| 1228 | *| 1 | 4 | Right PCM Playback |
| 1229 | *| 2 | 5 | phone line 1 DAC |
| 1230 | *| 3 | 6 | Center PCM Playback |
| 1231 | *| 4 | 7 | Left Surround PCM Playback |
| 1232 | *| 5 | 8 | Right Surround PCM Playback |
| 1233 | *...... |
| 1234 | * |
| 1235 | *| CLSS[4:0]/ |
| 1236 | *| CRSS[4:0] | AC97 | Codec |Slot Function |
| 1237 | *|-----------+------+-------+----------------- |
| 1238 | *| 10 | 3 |Primary| Left PCM Record |
| 1239 | *| 11 | 4 |Primary| Right PCM Record |
| 1240 | *| 12 | 5 |Primary| Phone Line 1 ADC |
| 1241 | *| 13 | 6 |Primary| Mic ADC |
| 1242 | *|..... |
| 1243 | *| 20 | 3 | Sec. | Left PCM Record |
| 1244 | *| 21 | 4 | Sec. | Right PCM Record |
| 1245 | *| 22 | 5 | Sec. | Phone Line 1 ADC |
| 1246 | *| 23 | 6 | Sec. | Mic ADC |
| 1247 | */ |
| 1248 | dat32 = 0x0b << 24 | /* CRSS[4:0] Right PCM Record(primary) */ |
| 1249 | 0x0a << 16 | /* CLSS[4:0] Left PCM Record(primary) */ |
| 1250 | 0x01 << 8 | /* PRSS[4:0] Right PCM Playback */ |
| 1251 | 0x00 << 0; /* PLSS[4:0] Left PCM Playback */ |
| 1252 | BA0WRITE4(sc, CS4281_SRCSA, dat32); |
| 1253 | |
| 1254 | /* Set interrupt to occurred at Half and Full terminal |
| 1255 | * count interrupt enable for DMA channel 0 and 1. |
| 1256 | * To keep DMA stop, set MSK. |
| 1257 | */ |
| 1258 | dat32 = DCRn_HTCIE | DCRn_TCIE | DCRn_MSK; |
| 1259 | BA0WRITE4(sc, CS4281_DCR0, dat32); |
| 1260 | BA0WRITE4(sc, CS4281_DCR1, dat32); |
| 1261 | |
| 1262 | /* Set Auto-Initialize Contorl enable */ |
| 1263 | BA0WRITE4(sc, CS4281_DMR0, |
| 1264 | DMRn_DMA | DMRn_AUTO | DMRn_TR_READ); |
| 1265 | BA0WRITE4(sc, CS4281_DMR1, |
| 1266 | DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE); |
| 1267 | |
| 1268 | /* Clear DMA Mask in HIMR */ |
| 1269 | dat32 = ~HIMR_DMAIM & ~HIMR_D1IM & ~HIMR_D0IM; |
| 1270 | BA0WRITE4(sc, CS4281_HIMR, |
| 1271 | BA0READ4(sc, CS4281_HIMR) & dat32); |
| 1272 | |
| 1273 | /* set current status */ |
| 1274 | if (init != 0) { |
| 1275 | sc->sc_prun = 0; |
| 1276 | sc->sc_rrun = 0; |
| 1277 | } |
| 1278 | |
| 1279 | /* setup playback volume */ |
| 1280 | BA0WRITE4(sc, CS4281_PPRVC, 7); |
| 1281 | BA0WRITE4(sc, CS4281_PPLVC, 7); |
| 1282 | |
| 1283 | return 0; |
| 1284 | } |
| 1285 | |