fms.c revision 1.29
1214501Srpaulo/* $OpenBSD: fms.c,v 1.29 2016/09/19 06:46:44 ratchov Exp $ */ 2214501Srpaulo/* $NetBSD: fms.c,v 1.5.4.1 2000/06/30 16:27:50 simonb Exp $ */ 3214501Srpaulo 4214501Srpaulo/*- 5214501Srpaulo * Copyright (c) 1999 The NetBSD Foundation, Inc. 6214501Srpaulo * All rights reserved. 7214501Srpaulo * 8214501Srpaulo * This code is derived from software contributed to The NetBSD Foundation 9214501Srpaulo * by Witold J. Wnuk. 10214501Srpaulo * 11214501Srpaulo * Redistribution and use in source and binary forms, with or without 12214501Srpaulo * modification, are permitted provided that the following conditions 13214501Srpaulo * are met: 14214501Srpaulo * 1. Redistributions of source code must retain the above copyright 15214501Srpaulo * notice, this list of conditions and the following disclaimer. 16214501Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 17214501Srpaulo * notice, this list of conditions and the following disclaimer in the 18214501Srpaulo * documentation and/or other materials provided with the distribution. 19214501Srpaulo * 20214501Srpaulo * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21214501Srpaulo * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22214501Srpaulo * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23214501Srpaulo * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24214501Srpaulo * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25214501Srpaulo * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26214501Srpaulo * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27214501Srpaulo * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28214501Srpaulo * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29214501Srpaulo * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30214501Srpaulo * POSSIBILITY OF SUCH DAMAGE. 31214501Srpaulo */ 32214501Srpaulo 33214501Srpaulo/* 34214501Srpaulo * Forte Media FM801 Audio Device Driver 35214501Srpaulo */ 36214501Srpaulo 37214501Srpaulo#include "radio.h" 38214501Srpaulo 39214501Srpaulo#include <sys/param.h> 40214501Srpaulo#include <sys/systm.h> 41214501Srpaulo#include <sys/kernel.h> 42214501Srpaulo#include <sys/malloc.h> 43214501Srpaulo#include <sys/device.h> 44214501Srpaulo#include <sys/audioio.h> 45214501Srpaulo 46214501Srpaulo#include <machine/bus.h> 47214501Srpaulo#include <machine/cpu.h> 48214501Srpaulo 49214501Srpaulo#include <dev/pci/pcidevs.h> 50214501Srpaulo#include <dev/pci/pcivar.h> 51214501Srpaulo 52214501Srpaulo#include <dev/audio_if.h> 53214501Srpaulo#include <dev/ic/ac97.h> 54214501Srpaulo#if 0 55214501Srpaulo#include <dev/ic/mpuvar.h> 56214501Srpaulo#endif 57214501Srpaulo 58214501Srpaulo#include <dev/pci/fmsreg.h> 59214501Srpaulo#include <dev/pci/fmsvar.h> 60214501Srpaulo 61214501Srpaulo 62214501Srpaulostruct fms_dma { 63214501Srpaulo struct fms_dma *next; 64214501Srpaulo caddr_t addr; 65214501Srpaulo size_t size; 66214501Srpaulo bus_dmamap_t map; 67214501Srpaulo bus_dma_segment_t seg; 68214501Srpaulo}; 69214501Srpaulo 70214501Srpaulo 71214501Srpaulo 72214501Srpauloint fms_match(struct device *, void *, void *); 73214501Srpaulovoid fms_attach(struct device *, struct device *, void *); 74214501Srpauloint fms_intr(void *); 75214501Srpaulo 76214501Srpauloint fms_open(void *, int); 77214501Srpaulovoid fms_close(void *); 78214501Srpauloint fms_set_params(void *, int, int, struct audio_params *, 79214501Srpaulo struct audio_params *); 80214501Srpauloint fms_round_blocksize(void *, int); 81214501Srpauloint fms_halt_output(void *); 82214501Srpauloint fms_halt_input(void *); 83214501Srpauloint fms_set_port(void *, mixer_ctrl_t *); 84214501Srpauloint fms_get_port(void *, mixer_ctrl_t *); 85214501Srpauloint fms_query_devinfo(void *, mixer_devinfo_t *); 86214501Srpaulovoid *fms_malloc(void *, int, size_t, int, int); 87214501Srpaulovoid fms_free(void *, void *, int); 88214501Srpauloint fms_get_props(void *); 89214501Srpauloint fms_trigger_output(void *, void *, void *, int, void (*)(void *), 90214501Srpaulo void *, struct audio_params *); 91214501Srpauloint fms_trigger_input(void *, void *, void *, int, void (*)(void *), 92214501Srpaulo void *, struct audio_params *); 93214501Srpaulo 94214501Srpaulostruct cfdriver fms_cd = { 95214501Srpaulo NULL, "fms", DV_DULL 96214501Srpaulo}; 97214501Srpaulo 98214501Srpaulostruct cfattach fms_ca = { 99214501Srpaulo sizeof (struct fms_softc), fms_match, fms_attach 100214501Srpaulo}; 101214501Srpaulo 102214501Srpaulostruct audio_hw_if fms_hw_if = { 103214501Srpaulo fms_open, 104214501Srpaulo fms_close, 105214501Srpaulo fms_set_params, 106214501Srpaulo fms_round_blocksize, 107214501Srpaulo NULL, 108214501Srpaulo NULL, 109214501Srpaulo NULL, 110214501Srpaulo NULL, 111214501Srpaulo NULL, 112214501Srpaulo fms_halt_output, 113214501Srpaulo fms_halt_input, 114214501Srpaulo NULL, 115214501Srpaulo NULL, 116214501Srpaulo fms_set_port, 117214501Srpaulo fms_get_port, 118214501Srpaulo fms_query_devinfo, 119214501Srpaulo fms_malloc, 120214501Srpaulo fms_free, 121214501Srpaulo NULL, 122214501Srpaulo fms_get_props, 123214501Srpaulo fms_trigger_output, 124214501Srpaulo fms_trigger_input 125214501Srpaulo}; 126214501Srpaulo 127214501Srpauloint fms_attach_codec(void *, struct ac97_codec_if *); 128214501Srpauloint fms_read_codec(void *, u_int8_t, u_int16_t *); 129214501Srpauloint fms_write_codec(void *, u_int8_t, u_int16_t); 130214501Srpaulovoid fms_reset_codec(void *); 131214501Srpaulo 132214501Srpauloint fms_allocmem(struct fms_softc *, size_t, size_t, 133214501Srpaulo struct fms_dma *); 134214501Srpauloint fms_freemem(struct fms_softc *, struct fms_dma *); 135214501Srpaulo 136214501Srpauloint 137214501Srpaulofms_match(struct device *parent, void *match, void *aux) 138214501Srpaulo{ 139214501Srpaulo struct pci_attach_args *pa = (struct pci_attach_args *) aux; 140214501Srpaulo 141214501Srpaulo if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_FORTEMEDIA && 142214501Srpaulo PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_FORTEMEDIA_FM801) 143214501Srpaulo return (1); 144214501Srpaulo return (0); 145214501Srpaulo} 146214501Srpaulo 147214501Srpaulovoid 148214501Srpaulofms_attach(struct device *parent, struct device *self, void *aux) 149214501Srpaulo{ 150214501Srpaulo struct pci_attach_args *pa = aux; 151214501Srpaulo struct fms_softc *sc = (struct fms_softc *) self; 152214501Srpaulo struct audio_attach_args aa; 153214501Srpaulo pci_chipset_tag_t pc = pa->pa_pc; 154214501Srpaulo pcitag_t pt = pa->pa_tag; 155214501Srpaulo pci_intr_handle_t ih; 156214501Srpaulo bus_size_t iosize; 157214501Srpaulo const char *intrstr; 158214501Srpaulo u_int16_t k1; 159214501Srpaulo int i; 160214501Srpaulo 161214501Srpaulo if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_IO, 0, &sc->sc_iot, 162214501Srpaulo &sc->sc_ioh, NULL, &iosize, 0)) { 163214501Srpaulo printf(": can't map i/o space\n"); 164214501Srpaulo return; 165214501Srpaulo } 166214501Srpaulo 167214501Srpaulo if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, 0x30, 2, 168214501Srpaulo &sc->sc_mpu_ioh)) { 169214501Srpaulo printf(": can't get mpu subregion handle\n"); 170214501Srpaulo bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize); 171214501Srpaulo return; 172214501Srpaulo } 173214501Srpaulo 174214501Srpaulo if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, 0x68, 4, 175214501Srpaulo &sc->sc_opl_ioh)) { 176214501Srpaulo printf(": can't get opl subregion handle\n"); 177214501Srpaulo bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize); 178214501Srpaulo return; 179214501Srpaulo } 180214501Srpaulo 181214501Srpaulo if (pci_intr_map(pa, &ih)) { 182214501Srpaulo printf(": couldn't map interrupt\n"); 183214501Srpaulo bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize); 184214501Srpaulo return; 185214501Srpaulo } 186214501Srpaulo intrstr = pci_intr_string(pc, ih); 187214501Srpaulo 188214501Srpaulo sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO | IPL_MPSAFE, 189214501Srpaulo fms_intr, sc, sc->sc_dev.dv_xname); 190214501Srpaulo if (sc->sc_ih == NULL) { 191214501Srpaulo printf(": couldn't establish interrupt"); 192214501Srpaulo if (intrstr != NULL) 193214501Srpaulo printf(" at %s", intrstr); 194214501Srpaulo printf("\n"); 195214501Srpaulo bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize); 196214501Srpaulo return; 197214501Srpaulo } 198214501Srpaulo 199214501Srpaulo printf(": %s\n", intrstr); 200214501Srpaulo 201214501Srpaulo sc->sc_dmat = pa->pa_dmat; 202214501Srpaulo 203214501Srpaulo /* Disable legacy audio (SBPro compatibility) */ 204214501Srpaulo pci_conf_write(pc, pt, 0x40, 0); 205214501Srpaulo 206214501Srpaulo /* Reset codec and AC'97 */ 207214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0020); 208214501Srpaulo delay(2); /* > 1us according to AC'97 documentation */ 209214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0000); 210214501Srpaulo delay(1); /* > 168.2ns according to AC'97 documentation */ 211214501Srpaulo 212214501Srpaulo /* Set up volume */ 213214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PCM_VOLUME, 0x0808); 214214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_FM_VOLUME, 0x0808); 215214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_I2S_VOLUME, 0x0808); 216214501Srpaulo 217214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_RECORD_SOURCE, 0x0000); 218214501Srpaulo 219214501Srpaulo /* Unmask playback, record and mpu interrupts, mask the rest */ 220214501Srpaulo k1 = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_INTMASK); 221214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_INTMASK, 222214501Srpaulo (k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) | 223214501Srpaulo FM_INTMASK_VOL); 224214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_INTSTATUS, 225214501Srpaulo FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU | 226214501Srpaulo FM_INTSTATUS_VOL); 227214501Srpaulo 228214501Srpaulo#if NRADIO > 0 229214501Srpaulo fmsradio_attach(sc); 230214501Srpaulo#endif /* NRADIO > 0 */ 231214501Srpaulo 232214501Srpaulo sc->host_if.arg = sc; 233214501Srpaulo sc->host_if.attach = fms_attach_codec; 234214501Srpaulo sc->host_if.read = fms_read_codec; 235214501Srpaulo sc->host_if.write = fms_write_codec; 236214501Srpaulo sc->host_if.reset = fms_reset_codec; 237214501Srpaulo 238214501Srpaulo if (ac97_attach(&sc->host_if) != 0) 239214501Srpaulo return; 240214501Srpaulo 241214501Srpaulo /* Turn mute off */ 242214501Srpaulo for (i = 0; i < 3; i++) { 243214501Srpaulo static struct { 244214501Srpaulo char *class, *device; 245214501Srpaulo } d[] = { 246214501Srpaulo { AudioCoutputs, AudioNmaster }, 247214501Srpaulo { AudioCinputs, AudioNdac }, 248214501Srpaulo { AudioCrecord, AudioNvolume } 249214501Srpaulo }; 250214501Srpaulo struct mixer_ctrl ctl; 251214501Srpaulo 252214501Srpaulo ctl.type = AUDIO_MIXER_ENUM; 253214501Srpaulo ctl.un.ord = 0; 254214501Srpaulo ctl.dev = sc->codec_if->vtbl->get_portnum_by_name(sc->codec_if, 255214501Srpaulo d[i].class, d[i].device, AudioNmute); 256214501Srpaulo fms_set_port(sc, &ctl); 257214501Srpaulo } 258214501Srpaulo 259214501Srpaulo audio_attach_mi(&fms_hw_if, sc, &sc->sc_dev); 260214501Srpaulo 261214501Srpaulo aa.type = AUDIODEV_TYPE_OPL; 262214501Srpaulo aa.hwif = NULL; 263214501Srpaulo aa.hdl = NULL; 264214501Srpaulo config_found(&sc->sc_dev, &aa, audioprint); 265214501Srpaulo 266214501Srpaulo aa.type = AUDIODEV_TYPE_MPU; 267214501Srpaulo aa.hwif = NULL; 268214501Srpaulo aa.hdl = NULL; 269214501Srpaulo sc->sc_mpu_dev = config_found(&sc->sc_dev, &aa, audioprint); 270214501Srpaulo} 271214501Srpaulo 272214501Srpaulo/* 273214501Srpaulo * Each AC-link frame takes 20.8us, data should be ready in next frame, 274214501Srpaulo * we allow more than two. 275214501Srpaulo */ 276214501Srpaulo#define TIMO 50 277214501Srpauloint 278214501Srpaulofms_read_codec(void *addr, u_int8_t reg, u_int16_t *val) 279214501Srpaulo{ 280214501Srpaulo struct fms_softc *sc = addr; 281214501Srpaulo int i; 282214501Srpaulo 283214501Srpaulo /* Poll until codec is ready */ 284214501Srpaulo for (i = 0; i < TIMO && bus_space_read_2(sc->sc_iot, sc->sc_ioh, 285214501Srpaulo FM_CODEC_CMD) & FM_CODEC_CMD_BUSY; i++) 286214501Srpaulo delay(1); 287214501Srpaulo if (i >= TIMO) { 288214501Srpaulo printf("fms: codec busy\n"); 289214501Srpaulo return 1; 290214501Srpaulo } 291214501Srpaulo 292214501Srpaulo /* Write register index, read access */ 293214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CMD, 294214501Srpaulo reg | FM_CODEC_CMD_READ); 295214501Srpaulo 296214501Srpaulo /* Poll until we have valid data */ 297214501Srpaulo for (i = 0; i < TIMO && !(bus_space_read_2(sc->sc_iot, sc->sc_ioh, 298214501Srpaulo FM_CODEC_CMD) & FM_CODEC_CMD_VALID); i++) 299214501Srpaulo delay(1); 300214501Srpaulo if (i >= TIMO) { 301214501Srpaulo printf("fms: no data from codec\n"); 302214501Srpaulo return 1; 303214501Srpaulo } 304214501Srpaulo 305214501Srpaulo /* Read data */ 306214501Srpaulo *val = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_DATA); 307214501Srpaulo return 0; 308214501Srpaulo} 309214501Srpaulo 310214501Srpauloint 311214501Srpaulofms_write_codec(void *addr, u_int8_t reg, u_int16_t val) 312214501Srpaulo{ 313214501Srpaulo struct fms_softc *sc = addr; 314214501Srpaulo int i; 315214501Srpaulo 316214501Srpaulo /* Poll until codec is ready */ 317214501Srpaulo for (i = 0; i < TIMO && bus_space_read_2(sc->sc_iot, sc->sc_ioh, 318214501Srpaulo FM_CODEC_CMD) & FM_CODEC_CMD_BUSY; i++) 319214501Srpaulo delay(1); 320214501Srpaulo if (i >= TIMO) { 321214501Srpaulo printf("fms: codec busy\n"); 322214501Srpaulo return 1; 323214501Srpaulo } 324214501Srpaulo 325214501Srpaulo /* Write data */ 326214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_DATA, val); 327214501Srpaulo /* Write index register, write access */ 328214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CMD, reg); 329214501Srpaulo return 0; 330214501Srpaulo} 331214501Srpaulo#undef TIMO 332214501Srpaulo 333214501Srpauloint 334214501Srpaulofms_attach_codec(void *addr, struct ac97_codec_if *cif) 335214501Srpaulo{ 336214501Srpaulo struct fms_softc *sc = addr; 337214501Srpaulo 338214501Srpaulo sc->codec_if = cif; 339214501Srpaulo return 0; 340214501Srpaulo} 341214501Srpaulo 342214501Srpaulo/* Cold Reset */ 343214501Srpaulovoid 344214501Srpaulofms_reset_codec(void *addr) 345214501Srpaulo{ 346214501Srpaulo struct fms_softc *sc = addr; 347214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0020); 348214501Srpaulo delay(2); 349214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0000); 350214501Srpaulo delay(1); 351214501Srpaulo} 352214501Srpaulo 353214501Srpauloint 354214501Srpaulofms_intr(void *arg) 355214501Srpaulo{ 356214501Srpaulo struct fms_softc *sc = arg; 357214501Srpaulo u_int16_t istat; 358214501Srpaulo 359214501Srpaulo mtx_enter(&audio_lock); 360214501Srpaulo istat = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_INTSTATUS); 361214501Srpaulo 362214501Srpaulo if (istat & FM_INTSTATUS_PLAY) { 363214501Srpaulo if ((sc->sc_play_nextblk += sc->sc_play_blksize) >= 364214501Srpaulo sc->sc_play_end) 365214501Srpaulo sc->sc_play_nextblk = sc->sc_play_start; 366214501Srpaulo 367214501Srpaulo bus_space_write_4(sc->sc_iot, sc->sc_ioh, 368214501Srpaulo sc->sc_play_flip++ & 1 ? 369214501Srpaulo FM_PLAY_DMABUF2 : FM_PLAY_DMABUF1, sc->sc_play_nextblk); 370214501Srpaulo 371214501Srpaulo if (sc->sc_pintr) 372214501Srpaulo sc->sc_pintr(sc->sc_parg); 373214501Srpaulo else 374214501Srpaulo printf("unexpected play intr\n"); 375214501Srpaulo } 376214501Srpaulo 377214501Srpaulo if (istat & FM_INTSTATUS_REC) { 378214501Srpaulo if ((sc->sc_rec_nextblk += sc->sc_rec_blksize) >= 379214501Srpaulo sc->sc_rec_end) 380214501Srpaulo sc->sc_rec_nextblk = sc->sc_rec_start; 381214501Srpaulo 382214501Srpaulo bus_space_write_4(sc->sc_iot, sc->sc_ioh, 383214501Srpaulo sc->sc_rec_flip++ & 1 ? 384214501Srpaulo FM_REC_DMABUF2 : FM_REC_DMABUF1, sc->sc_rec_nextblk); 385214501Srpaulo 386214501Srpaulo if (sc->sc_rintr) 387214501Srpaulo sc->sc_rintr(sc->sc_rarg); 388214501Srpaulo else 389214501Srpaulo printf("unexpected rec intr\n"); 390214501Srpaulo } 391214501Srpaulo 392214501Srpaulo#if 0 393214501Srpaulo if (istat & FM_INTSTATUS_MPU) 394214501Srpaulo mpu_intr(sc->sc_mpu_dev); 395214501Srpaulo#endif 396214501Srpaulo 397214501Srpaulo bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_INTSTATUS, 398 istat & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC)); 399 mtx_leave(&audio_lock); 400 return 1; 401} 402 403int 404fms_open(void *addr, int flags) 405{ 406 /* UNUSED struct fms_softc *sc = addr;*/ 407 408 return 0; 409} 410 411void 412fms_close(void *addr) 413{ 414 /* UNUSED struct fms_softc *sc = addr;*/ 415} 416 417/* 418 * Range below -limit- is set to -rate- 419 * What a pity FM801 does not have 24000 420 * 24000 -> 22050 sounds rather poor 421 */ 422struct { 423 int limit; 424 int rate; 425} fms_rates[11] = { 426 { 6600, 5500 }, 427 { 8750, 8000 }, 428 { 10250, 9600 }, 429 { 13200, 11025 }, 430 { 17500, 16000 }, 431 { 20500, 19200 }, 432 { 26500, 22050 }, 433 { 35000, 32000 }, 434 { 41000, 38400 }, 435 { 46000, 44100 }, 436 { 48000, 48000 }, 437 /* anything above -> 48000 */ 438}; 439 440int 441fms_set_params(void *addr, int setmode, int usemode, struct audio_params *play, 442 struct audio_params *rec) 443{ 444 struct fms_softc *sc = addr; 445 int i; 446 447 if (setmode & AUMODE_PLAY) { 448 switch(play->encoding) { 449 case AUDIO_ENCODING_SLINEAR_LE: 450 if (play->precision != 16) 451 return EINVAL; 452 break; 453 case AUDIO_ENCODING_ULINEAR_LE: 454 case AUDIO_ENCODING_ULINEAR_BE: 455 if (play->precision != 8) 456 return EINVAL; 457 break; 458 default: 459 return EINVAL; 460 } 461 play->bps = AUDIO_BPS(play->precision); 462 play->msb = 1; 463 464 for (i = 0; i < 10 && play->sample_rate > fms_rates[i].limit; 465 i++) 466 ; 467 play->sample_rate = fms_rates[i].rate; 468 sc->sc_play_reg = (play->channels == 2 ? FM_PLAY_STEREO : 0) | 469 (play->precision == 16 ? FM_PLAY_16BIT : 0) | 470 (i << 8); 471 } 472 473 if (setmode & AUMODE_RECORD) { 474 475 switch(rec->encoding) { 476 case AUDIO_ENCODING_SLINEAR_LE: 477 if (rec->precision != 16) 478 return EINVAL; 479 break; 480 case AUDIO_ENCODING_ULINEAR_LE: 481 case AUDIO_ENCODING_ULINEAR_BE: 482 if (rec->precision != 8) 483 return EINVAL; 484 break; 485 default: 486 return EINVAL; 487 } 488 rec->bps = AUDIO_BPS(rec->precision); 489 rec->msb = 1; 490 491 for (i = 0; i < 10 && rec->sample_rate > fms_rates[i].limit; 492 i++) 493 ; 494 rec->sample_rate = fms_rates[i].rate; 495 sc->sc_rec_reg = 496 (rec->channels == 2 ? FM_REC_STEREO : 0) | 497 (rec->precision == 16 ? FM_REC_16BIT : 0) | 498 (i << 8); 499 } 500 501 return 0; 502} 503 504int 505fms_round_blocksize(void *addr, int blk) 506{ 507 return (blk + 0xf) & ~0xf; 508} 509 510int 511fms_halt_output(void *addr) 512{ 513 struct fms_softc *sc = addr; 514 u_int16_t k1; 515 516 mtx_enter(&audio_lock); 517 k1 = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_CTL); 518 bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_CTL, 519 (k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) | 520 FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST); 521 mtx_leave(&audio_lock); 522 return 0; 523} 524 525int 526fms_halt_input(void *addr) 527{ 528 struct fms_softc *sc = addr; 529 u_int16_t k1; 530 531 mtx_enter(&audio_lock); 532 k1 = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_REC_CTL); 533 bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_REC_CTL, 534 (k1 & ~(FM_REC_STOPNOW | FM_REC_START)) | 535 FM_REC_BUF1_LAST | FM_REC_BUF2_LAST); 536 mtx_leave(&audio_lock); 537 return 0; 538} 539 540int 541fms_set_port(void *addr, mixer_ctrl_t *cp) 542{ 543 struct fms_softc *sc = addr; 544 545 return (sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp)); 546} 547 548int 549fms_get_port(void *addr, mixer_ctrl_t *cp) 550{ 551 struct fms_softc *sc = addr; 552 553 return (sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp)); 554} 555 556void * 557fms_malloc(void *addr, int direction, size_t size, int pool, int flags) 558{ 559 struct fms_softc *sc = addr; 560 struct fms_dma *p; 561 int error; 562 int rseg; 563 564 p = malloc(sizeof(*p), pool, flags); 565 if (!p) 566 return 0; 567 568 p->size = size; 569 if ((error = bus_dmamem_alloc(sc->sc_dmat, size, NBPG, 0, &p->seg, 1, 570 &rseg, BUS_DMA_NOWAIT)) != 0) { 571 printf("%s: unable to allocate dma, error = %d\n", 572 sc->sc_dev.dv_xname, error); 573 goto fail_alloc; 574 } 575 576 if ((error = bus_dmamem_map(sc->sc_dmat, &p->seg, rseg, size, &p->addr, 577 BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { 578 printf("%s: unable to map dma, error = %d\n", 579 sc->sc_dev.dv_xname, error); 580 goto fail_map; 581 } 582 583 if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, 584 BUS_DMA_NOWAIT, &p->map)) != 0) { 585 printf("%s: unable to create dma map, error = %d\n", 586 sc->sc_dev.dv_xname, error); 587 goto fail_create; 588 } 589 590 if ((error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, size, NULL, 591 BUS_DMA_NOWAIT)) != 0) { 592 printf("%s: unable to load dma map, error = %d\n", 593 sc->sc_dev.dv_xname, error); 594 goto fail_load; 595 } 596 597 p->next = sc->sc_dmas; 598 sc->sc_dmas = p; 599 600 return p->addr; 601 602 603fail_load: 604 bus_dmamap_destroy(sc->sc_dmat, p->map); 605fail_create: 606 bus_dmamem_unmap(sc->sc_dmat, p->addr, size); 607fail_map: 608 bus_dmamem_free(sc->sc_dmat, &p->seg, 1); 609fail_alloc: 610 free(p, pool, 0); 611 return 0; 612} 613 614void 615fms_free(void *addr, void *ptr, int pool) 616{ 617 struct fms_softc *sc = addr; 618 struct fms_dma **pp, *p; 619 620 for (pp = &(sc->sc_dmas); (p = *pp) != NULL; pp = &p->next) 621 if (p->addr == ptr) { 622 bus_dmamap_unload(sc->sc_dmat, p->map); 623 bus_dmamap_destroy(sc->sc_dmat, p->map); 624 bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size); 625 bus_dmamem_free(sc->sc_dmat, &p->seg, 1); 626 627 *pp = p->next; 628 free(p, pool, 0); 629 return; 630 } 631 632 panic("fms_free: trying to free unallocated memory"); 633} 634 635int 636fms_get_props(void *addr) 637{ 638 return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | 639 AUDIO_PROP_FULLDUPLEX; 640} 641 642int 643fms_query_devinfo(void *addr, mixer_devinfo_t *dip) 644{ 645 struct fms_softc *sc = addr; 646 647 return (sc->codec_if->vtbl->query_devinfo(sc->codec_if, dip)); 648} 649 650int 651fms_trigger_output(void *addr, void *start, void *end, int blksize, 652 void (*intr)(void *), void *arg, struct audio_params *param) 653{ 654 struct fms_softc *sc = addr; 655 struct fms_dma *p; 656 657 sc->sc_pintr = intr; 658 sc->sc_parg = arg; 659 660 for (p = sc->sc_dmas; p && p->addr != start; p = p->next) 661 ; 662 663 if (!p) 664 panic("fms_trigger_output: request with bad start " 665 "address (%p)", start); 666 667 sc->sc_play_start = p->map->dm_segs[0].ds_addr; 668 sc->sc_play_end = sc->sc_play_start + ((char *)end - (char *)start); 669 sc->sc_play_blksize = blksize; 670 sc->sc_play_nextblk = sc->sc_play_start + sc->sc_play_blksize; 671 sc->sc_play_flip = 0; 672 mtx_enter(&audio_lock); 673 bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_DMALEN, blksize - 1); 674 bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_PLAY_DMABUF1, 675 sc->sc_play_start); 676 bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_PLAY_DMABUF2, 677 sc->sc_play_nextblk); 678 bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_CTL, 679 FM_PLAY_START | FM_PLAY_STOPNOW | sc->sc_play_reg); 680 mtx_leave(&audio_lock); 681 return 0; 682} 683 684 685int 686fms_trigger_input(void *addr, void *start, void *end, int blksize, 687 void (*intr)(void *), void *arg, struct audio_params *param) 688{ 689 struct fms_softc *sc = addr; 690 struct fms_dma *p; 691 692 sc->sc_rintr = intr; 693 sc->sc_rarg = arg; 694 695 for (p = sc->sc_dmas; p && p->addr != start; p = p->next) 696 ; 697 698 if (!p) 699 panic("fms_trigger_input: request with bad start " 700 "address (%p)", start); 701 702 sc->sc_rec_start = p->map->dm_segs[0].ds_addr; 703 sc->sc_rec_end = sc->sc_rec_start + ((char *)end - (char *)start); 704 sc->sc_rec_blksize = blksize; 705 sc->sc_rec_nextblk = sc->sc_rec_start + sc->sc_rec_blksize; 706 sc->sc_rec_flip = 0; 707 mtx_enter(&audio_lock); 708 bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_REC_DMALEN, blksize - 1); 709 bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_REC_DMABUF1, 710 sc->sc_rec_start); 711 bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_REC_DMABUF2, 712 sc->sc_rec_nextblk); 713 bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_REC_CTL, 714 FM_REC_START | FM_REC_STOPNOW | sc->sc_rec_reg); 715 mtx_leave(&audio_lock); 716 return 0; 717} 718