1d29d28f2Storek /* 2d29d28f2Storek * Copyright (c) 1991, 1992 The Regents of the University of California. 3d29d28f2Storek * All rights reserved. 4d29d28f2Storek * 5d29d28f2Storek * This software was developed by the Computer Systems Engineering group 6d29d28f2Storek * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 7d29d28f2Storek * contributed to Berkeley. 8d29d28f2Storek * 9b248cd2bSbostic * All advertising materials mentioning features or use of this software 10b248cd2bSbostic * must display the following acknowledgement: 11b248cd2bSbostic * This product includes software developed by the University of 12b248cd2bSbostic * California, Lawrence Berkeley Laboratories. 13b248cd2bSbostic * 14d29d28f2Storek * %sccs.include.redist.c% 15d29d28f2Storek * 16*9c96d15aSbostic * @(#)bsd_audio.c 7.3 (Berkeley) 10/11/92 17d29d28f2Storek * 18d29d28f2Storek * from: $Header: bsd_audio.c,v 1.14 92/07/03 23:21:23 mccanne Exp $ (LBL) 19d29d28f2Storek */ 20d29d28f2Storek #include "bsdaudio.h" 21d29d28f2Storek #if NBSDAUDIO > 0 22d29d28f2Storek 23*9c96d15aSbostic #include <sys/param.h> 24*9c96d15aSbostic #include <sys/systm.h> 25d29d28f2Storek 26d29d28f2Storek #if BSD < 199103 27d29d28f2Storek #ifndef SUNOS 28d29d28f2Storek #define SUNOS 29d29d28f2Storek #endif 30d29d28f2Storek #endif 31d29d28f2Storek 32*9c96d15aSbostic #include <sys/errno.h> 33*9c96d15aSbostic #include <sys/file.h> 34*9c96d15aSbostic #include <sys/proc.h> 35*9c96d15aSbostic #include <sys/user.h> 36*9c96d15aSbostic #include <sys/vnode.h> 37*9c96d15aSbostic #include <sys/ioctl.h> 38*9c96d15aSbostic #include <sys/time.h> 39d29d28f2Storek #ifndef SUNOS 40*9c96d15aSbostic #include <sys/tty.h> 41d29d28f2Storek #endif 42*9c96d15aSbostic #include <sys/uio.h> 43d29d28f2Storek 44d29d28f2Storek #ifdef SUNOS 45d29d28f2Storek #include <sundev/mbvar.h> 46d29d28f2Storek #include <sun4c/intreg.h> 47d29d28f2Storek #else 48*9c96d15aSbostic #include <sys/device.h> 49*9c96d15aSbostic #include <machine/autoconf.h> 50d29d28f2Storek #endif 51*9c96d15aSbostic #include <machine/cpu.h> 52d29d28f2Storek 53d29d28f2Storek /* 54d29d28f2Storek * Avoid name clashes with SunOS so we can config either the bsd or sun 55d29d28f2Storek * streams driver in a SunOS kernel. 56d29d28f2Storek */ 57d29d28f2Storek #ifdef SUNOS 58d29d28f2Storek #include "sbusdev/bsd_audioreg.h" 59d29d28f2Storek #include "sbusdev/bsd_audiovar.h" 60d29d28f2Storek #include "sbusdev/bsd_audioio.h" 61d29d28f2Storek struct selinfo { 62d29d28f2Storek struct proc *si_proc; 63d29d28f2Storek int si_coll; 64d29d28f2Storek }; 65d29d28f2Storek #else 66*9c96d15aSbostic #include <sparc/dev/bsd_audioreg.h> 67*9c96d15aSbostic #include <sparc/dev/bsd_audiovar.h> 68*9c96d15aSbostic #include <machine/bsd_audioio.h> 69d29d28f2Storek #endif 70d29d28f2Storek 71d29d28f2Storek #ifdef SUNOS 72d29d28f2Storek #include "bsd_audiocompat.h" 73d29d28f2Storek #endif 74d29d28f2Storek 75d29d28f2Storek /* 76d29d28f2Storek * Initial/default block size is patchable. 77d29d28f2Storek */ 78d29d28f2Storek int audio_blocksize = DEFBLKSIZE; 79d29d28f2Storek 80d29d28f2Storek /* 81d29d28f2Storek * Software state, per AMD79C30 audio chip. 82d29d28f2Storek */ 83d29d28f2Storek struct audio_softc { 84d29d28f2Storek #ifndef SUNOS 85d29d28f2Storek struct device sc_dev; /* base device */ 86d29d28f2Storek struct intrhand sc_hwih; /* hardware interrupt vector */ 87d29d28f2Storek struct intrhand sc_swih; /* software interrupt vector */ 88d29d28f2Storek #endif 89d29d28f2Storek int sc_interrupts; /* number of interrupts taken */ 90d29d28f2Storek 91d29d28f2Storek int sc_open; /* single use device */ 92d29d28f2Storek u_long sc_wseek; /* timestamp of last frame written */ 93d29d28f2Storek u_long sc_rseek; /* timestamp of last frame read */ 94d29d28f2Storek struct mapreg sc_map; /* current contents of map registers */ 95d29d28f2Storek struct selinfo sc_wsel; /* write selector */ 96d29d28f2Storek struct selinfo sc_rsel; /* read selector */ 97d29d28f2Storek /* 98d29d28f2Storek * keep track of levels so we don't have to convert back from 99d29d28f2Storek * MAP gain constants 100d29d28f2Storek */ 101d29d28f2Storek int sc_rlevel; /* record level */ 102d29d28f2Storek int sc_plevel; /* play level */ 103d29d28f2Storek int sc_mlevel; /* monitor level */ 104d29d28f2Storek 105d29d28f2Storek /* sc_au is special in that the hardware interrupt handler uses it */ 106d29d28f2Storek struct auio sc_au; /* recv and xmit buffers, etc */ 107d29d28f2Storek 108d29d28f2Storek }; 109d29d28f2Storek 110d29d28f2Storek /* interrupt interfaces */ 111d29d28f2Storek #ifndef AUDIO_C_HANDLER 112d29d28f2Storek int audiohwintr __P((void *)); 113d29d28f2Storek #endif 114d29d28f2Storek int audioswintr __P((void *)); 115d29d28f2Storek 116d29d28f2Storek /* forward declarations */ 117d29d28f2Storek int audio_sleep __P((struct aucb *, int)); 118d29d28f2Storek void audio_setmap __P((volatile struct amd7930 *, struct mapreg *)); 119d29d28f2Storek 120d29d28f2Storek static void init_amd(); 121d29d28f2Storek 122d29d28f2Storek #if !defined(AUDIO_C_HANDLER) || defined(SUNOS) 123d29d28f2Storek struct auio *audio_au; 124d29d28f2Storek extern void audio_trap(); 125d29d28f2Storek #endif 126d29d28f2Storek 127d29d28f2Storek #ifdef SUNOS 128d29d28f2Storek struct audio_softc audio_softc; 129d29d28f2Storek #define SOFTC(dev) &audio_softc 130d29d28f2Storek #define UIOMOVE(cp, len, code, uio) uiomove(cp, len, code, uio) 131d29d28f2Storek 132d29d28f2Storek #define AUDIOOPEN(d, f, i, p)\ 133d29d28f2Storek audioopen(d, f, i)\ 134d29d28f2Storek dev_t d; int f, i; 135d29d28f2Storek #define AUDIOCLOSE(d, f, i, p)\ 136d29d28f2Storek audioclose(d, f, i)\ 137d29d28f2Storek dev_t d; int f, i; 138d29d28f2Storek #define AUDIOREAD(d, u, f) \ 139d29d28f2Storek audioread(d, u) dev_t d; struct uio *u; 140d29d28f2Storek #define AUDIOWRITE(d, u, f) \ 141d29d28f2Storek audiowrite(d, u) dev_t d; struct uio *u; 142d29d28f2Storek #define AUDIOIOCTL(d, c, a, f, o)\ 143d29d28f2Storek audioioctl(d, c, a, f)\ 144d29d28f2Storek dev_t d; int c; caddr_t a; int f; 145d29d28f2Storek #define AUDIOSELECT(d, r, p)\ 146d29d28f2Storek audio_select(d, r, p)\ 147d29d28f2Storek dev_t d; int r; struct proc *p; 148d29d28f2Storek 149d29d28f2Storek 150d29d28f2Storek #define AUDIO_SET_SWINTR set_intreg(IR_SOFT_INT4, 1) 151d29d28f2Storek 152d29d28f2Storek int 153d29d28f2Storek audioselect(dev, rw) 154d29d28f2Storek register dev_t dev; 155d29d28f2Storek int rw; 156d29d28f2Storek { 157d29d28f2Storek return (audio_select(dev, rw, u.u_procp)); 158d29d28f2Storek } 159d29d28f2Storek 160d29d28f2Storek static void 161d29d28f2Storek selrecord(p, si) 162d29d28f2Storek struct proc *p; 163d29d28f2Storek struct selinfo *si; 164d29d28f2Storek { 165d29d28f2Storek if (si->si_proc != 0) 166d29d28f2Storek si->si_coll = 1; 167d29d28f2Storek else 168d29d28f2Storek si->si_proc = p; 169d29d28f2Storek } 170d29d28f2Storek #define SELWAKEUP(si) \ 171d29d28f2Storek {\ 172d29d28f2Storek if ((si)->si_proc != 0) {\ 173d29d28f2Storek selwakeup((si)->si_proc, (si)->si_coll); \ 174d29d28f2Storek (si)->si_proc = 0;\ 175d29d28f2Storek (si)->si_coll = 0;\ 176d29d28f2Storek }\ 177d29d28f2Storek } 178d29d28f2Storek 179d29d28f2Storek 180d29d28f2Storek static int audioattach(); 181d29d28f2Storek static int audioidentify(); 182d29d28f2Storek 183d29d28f2Storek struct dev_ops bsdaudio_ops = { 184d29d28f2Storek 0, 185d29d28f2Storek audioidentify, 186d29d28f2Storek audioattach, 187d29d28f2Storek }; 188d29d28f2Storek 189d29d28f2Storek static int 190d29d28f2Storek audioidentify(cp) 191d29d28f2Storek char *cp; 192d29d28f2Storek { 193d29d28f2Storek return (strcmp(cp, "audio") == 0); 194d29d28f2Storek } 195d29d28f2Storek 196d29d28f2Storek static int 197d29d28f2Storek audioattach(dev) 198d29d28f2Storek struct dev_info *dev; 199d29d28f2Storek { 200d29d28f2Storek register struct audio_softc *sc; 201d29d28f2Storek register volatile struct amd7930 *amd; 202d29d28f2Storek struct dev_reg *reg; 203d29d28f2Storek 204d29d28f2Storek sc = &audio_softc; 205d29d28f2Storek if (dev->devi_nreg != 1 || dev->devi_nintr != 1) { 206d29d28f2Storek printf("audio: bad config\n"); 207d29d28f2Storek return (-1); 208d29d28f2Storek } 209d29d28f2Storek reg = dev->devi_reg; 210d29d28f2Storek amd = (struct amd7930 *)map_regs(reg->reg_addr, reg->reg_size, 211d29d28f2Storek reg->reg_bustype); 212d29d28f2Storek sc->sc_au.au_amd = amd; 213d29d28f2Storek init_amd(amd); 214d29d28f2Storek 215d29d28f2Storek audio_au = &sc->sc_au; 216d29d28f2Storek #ifndef AUDIO_C_HANDLER 217d29d28f2Storek settrap(dev->devi_intr->int_pri, audio_trap); 218d29d28f2Storek #else 219d29d28f2Storek /* XXX */ 220d29d28f2Storek addintr(dev->devi_intr->int_pri, audiohwintr, dev->devi_name, 221d29d28f2Storek dev->devi_unit); 222d29d28f2Storek #endif 223d29d28f2Storek addintr(4, audioswintr, dev->devi_name, dev->devi_unit); 224d29d28f2Storek report_dev(dev); 225d29d28f2Storek 226d29d28f2Storek return (0); 227d29d28f2Storek } 228d29d28f2Storek #else 229d29d28f2Storek #define AUDIOOPEN(d, f, i, p) audioopen(dev_t d, int f, int i, struct proc *p) 230d29d28f2Storek #define AUDIOCLOSE(d, f, i, p) audioclose(dev_t d, int f, int i, \ 231d29d28f2Storek struct proc *p) 232d29d28f2Storek #define AUDIOREAD(d, u, f) audioread(dev_t d, struct uio *u, int f) 233d29d28f2Storek #define AUDIOWRITE(d, u, f) audiowrite(dev_t d, struct uio *u, int f) 234d29d28f2Storek #define AUDIOIOCTL(d, c, a, f, o)\ 235d29d28f2Storek audioioctl(dev_t dev, int c, caddr_t a, int f, struct proc *p) 236d29d28f2Storek #define AUDIOSELECT(d, r, p) audioselect(dev_t dev, int rw, struct proc *p) 237d29d28f2Storek #define SELWAKEUP selwakeup 238d29d28f2Storek 239d29d28f2Storek #define AUDIO_SET_SWINTR ienab_bis(IE_L6) 240d29d28f2Storek 241d29d28f2Storek /* autoconfiguration driver */ 242d29d28f2Storek void audioattach(struct device *, struct device *, void *); 243d29d28f2Storek struct cfdriver audiocd = 244d29d28f2Storek { NULL, "audio", matchbyname, audioattach, 245d29d28f2Storek DV_DULL, sizeof(struct audio_softc) }; 246d29d28f2Storek #define SOFTC(dev) audiocd.cd_devs[minor(dev)] 247d29d28f2Storek #define UIOMOVE(cp, len, code, uio) uiomove(cp, len, uio) 248d29d28f2Storek 249d29d28f2Storek /* 250d29d28f2Storek * Audio chip found. 251d29d28f2Storek */ 252d29d28f2Storek void 253d29d28f2Storek audioattach(parent, self, args) 254d29d28f2Storek struct device *parent, *self; 255d29d28f2Storek void *args; 256d29d28f2Storek { 257d29d28f2Storek register struct audio_softc *sc = (struct audio_softc *)self; 258d29d28f2Storek register struct romaux *ra = args; 259d29d28f2Storek register volatile struct amd7930 *amd; 260d29d28f2Storek register int pri; 261d29d28f2Storek 262d29d28f2Storek if (ra->ra_nintr != 1) { 263d29d28f2Storek printf(": expected 1 interrupt, got %d\n", ra->ra_nintr); 264d29d28f2Storek return; 265d29d28f2Storek } 266d29d28f2Storek pri = ra->ra_intr[0].int_pri; 267d29d28f2Storek printf(" pri %d, softpri %d\n", pri, PIL_AUSOFT); 268d29d28f2Storek amd = (volatile struct amd7930 *)(ra->ra_vaddr ? 269d29d28f2Storek ra->ra_vaddr : mapiodev(ra->ra_paddr, sizeof *amd)); 270d29d28f2Storek sc->sc_au.au_amd = amd; 271d29d28f2Storek 272d29d28f2Storek init_amd(amd); 273d29d28f2Storek 274d29d28f2Storek #ifndef AUDIO_C_HANDLER 275d29d28f2Storek audio_au = &sc->sc_au; 276d29d28f2Storek intr_fasttrap(pri, audio_trap); 277d29d28f2Storek #else 278d29d28f2Storek sc->sc_hwih.ih_fun = audiohwintr; 279d29d28f2Storek sc->sc_hwih.ih_arg = &sc->sc_au; 280d29d28f2Storek intr_establish(pri, &sc->sc_hwih); 281d29d28f2Storek #endif 282d29d28f2Storek sc->sc_swih.ih_fun = audioswintr; 283d29d28f2Storek sc->sc_swih.ih_arg = sc; 284d29d28f2Storek intr_establish(PIL_AUSOFT, &sc->sc_swih); 285d29d28f2Storek } 286d29d28f2Storek #endif 287d29d28f2Storek 288d29d28f2Storek static void 289d29d28f2Storek init_amd(amd) 290d29d28f2Storek register volatile struct amd7930 *amd; 291d29d28f2Storek { 292d29d28f2Storek /* disable interrupts */ 293d29d28f2Storek amd->cr = AMDR_INIT; 294d29d28f2Storek amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE; 295d29d28f2Storek 296d29d28f2Storek /* 297d29d28f2Storek * Initialize the mux unit. We use MCR3 to route audio (MAP) 298d29d28f2Storek * through channel Bb. MCR1 and MCR2 are unused. 299d29d28f2Storek * Setting the INT enable bit in MCR4 will generate an interrupt 300d29d28f2Storek * on each converted audio sample. 301d29d28f2Storek */ 302d29d28f2Storek amd->cr = AMDR_MUX_1_4; 303d29d28f2Storek amd->dr = 0; 304d29d28f2Storek amd->dr = 0; 305d29d28f2Storek amd->dr = (AMD_MCRCHAN_BB << 4) | AMD_MCRCHAN_BA; 306d29d28f2Storek amd->dr = AMD_MCR4_INT_ENABLE; 307d29d28f2Storek } 308d29d28f2Storek 309d29d28f2Storek static int audio_default_level = 150; 310d29d28f2Storek static void ausetrgain __P((struct audio_softc *, int)); 311d29d28f2Storek static void ausetpgain __P((struct audio_softc *, int)); 312d29d28f2Storek static int audiosetinfo __P((struct audio_softc *, struct audio_info *)); 313d29d28f2Storek static int audiogetinfo __P((struct audio_softc *, struct audio_info *)); 314d29d28f2Storek struct sun_audio_info; 315d29d28f2Storek static int sunaudiosetinfo __P((struct audio_softc *, 316d29d28f2Storek struct sun_audio_info *)); 317d29d28f2Storek static int sunaudiogetinfo __P((struct audio_softc *, 318d29d28f2Storek struct sun_audio_info *)); 319d29d28f2Storek static void audio_setmmr2 __P((volatile struct amd7930 *, int)); 320d29d28f2Storek 321d29d28f2Storek int 322d29d28f2Storek AUDIOOPEN(dev, flags, ifmt, p) 323d29d28f2Storek { 324d29d28f2Storek register struct audio_softc *sc; 325d29d28f2Storek register volatile struct amd7930 *amd; 326d29d28f2Storek int unit = minor(dev), error, s; 327d29d28f2Storek 328d29d28f2Storek #ifdef SUNOS 329d29d28f2Storek if (unit > 0) 330d29d28f2Storek return (ENXIO); 331d29d28f2Storek sc = &audio_softc; 332d29d28f2Storek #else 333d29d28f2Storek if (unit >= audiocd.cd_ndevs || (sc = audiocd.cd_devs[unit]) == NULL) 334d29d28f2Storek return (ENXIO); 335d29d28f2Storek #endif 336d29d28f2Storek if (sc->sc_open) 337d29d28f2Storek return (EBUSY); 338d29d28f2Storek sc->sc_open = 1; 339d29d28f2Storek 340d29d28f2Storek sc->sc_au.au_lowat = audio_blocksize; 341d29d28f2Storek sc->sc_au.au_hiwat = AUCB_SIZE - sc->sc_au.au_lowat; 342d29d28f2Storek sc->sc_au.au_blksize = audio_blocksize; 343d29d28f2Storek 344d29d28f2Storek /* set up read and write blocks and `dead sound' zero value. */ 345d29d28f2Storek AUCB_INIT(&sc->sc_au.au_rb); 346d29d28f2Storek sc->sc_au.au_rb.cb_thresh = AUCB_SIZE; 347d29d28f2Storek AUCB_INIT(&sc->sc_au.au_wb); 348d29d28f2Storek sc->sc_au.au_wb.cb_thresh = -1; 349d29d28f2Storek 350d29d28f2Storek /* nothing read or written yet */ 351d29d28f2Storek sc->sc_rseek = 0; 352d29d28f2Storek sc->sc_wseek = 0; 353d29d28f2Storek 354d29d28f2Storek bzero((char *)&sc->sc_map, sizeof sc->sc_map); 355d29d28f2Storek /* default to speaker */ 356d29d28f2Storek sc->sc_map.mr_mmr2 = AMD_MMR2_AINB | AMD_MMR2_LS; 357d29d28f2Storek 358d29d28f2Storek /* enable interrupts and set parameters established above */ 359d29d28f2Storek amd = sc->sc_au.au_amd; 360d29d28f2Storek audio_setmmr2(amd, sc->sc_map.mr_mmr2); 361d29d28f2Storek ausetrgain(sc, audio_default_level); 362d29d28f2Storek ausetpgain(sc, audio_default_level); 363d29d28f2Storek amd->cr = AMDR_INIT; 364d29d28f2Storek amd->dr = AMD_INIT_PMS_ACTIVE; 365d29d28f2Storek 366d29d28f2Storek return (0); 367d29d28f2Storek } 368d29d28f2Storek 369d29d28f2Storek static int 370d29d28f2Storek audio_drain(sc) 371d29d28f2Storek register struct audio_softc *sc; 372d29d28f2Storek { 373d29d28f2Storek register int error; 374d29d28f2Storek 375d29d28f2Storek while (!AUCB_EMPTY(&sc->sc_au.au_wb)) 376d29d28f2Storek if ((error = audio_sleep(&sc->sc_au.au_wb, 0)) != 0) 377d29d28f2Storek return (error); 378d29d28f2Storek return (0); 379d29d28f2Storek } 380d29d28f2Storek 381d29d28f2Storek /* 382d29d28f2Storek * Close an audio chip. 383d29d28f2Storek */ 384d29d28f2Storek /* ARGSUSED */ 385d29d28f2Storek int 386d29d28f2Storek AUDIOCLOSE(dev, flags, ifmt, p) 387d29d28f2Storek { 388d29d28f2Storek register struct audio_softc *sc = SOFTC(dev); 389d29d28f2Storek register volatile struct amd7930 *amd; 390d29d28f2Storek register struct aucb *cb; 391d29d28f2Storek register int s; 392d29d28f2Storek 393d29d28f2Storek /* 394d29d28f2Storek * Block until output drains, but allow ^C interrupt. 395d29d28f2Storek */ 396d29d28f2Storek sc->sc_au.au_lowat = 0; /* avoid excessive wakeups */ 397d29d28f2Storek s = splaudio(); 398d29d28f2Storek /* 399d29d28f2Storek * If there is pending output, let it drain (unless 400d29d28f2Storek * the output is paused). 401d29d28f2Storek */ 402d29d28f2Storek cb = &sc->sc_au.au_wb; 403d29d28f2Storek if (!AUCB_EMPTY(cb) && !cb->cb_pause) 404d29d28f2Storek (void)audio_drain(sc); 405d29d28f2Storek /* 406d29d28f2Storek * Disable interrupts, clear open flag, and done. 407d29d28f2Storek */ 408d29d28f2Storek amd = sc->sc_au.au_amd; 409d29d28f2Storek amd->cr = AMDR_INIT; 410d29d28f2Storek amd->dr = AMD_INIT_PMS_ACTIVE | AMD_INIT_INT_DISABLE; 411d29d28f2Storek splx(s); 412d29d28f2Storek sc->sc_open = 0; 413d29d28f2Storek return (0); 414d29d28f2Storek } 415d29d28f2Storek 416d29d28f2Storek int 417d29d28f2Storek audio_sleep(cb, thresh) 418d29d28f2Storek register struct aucb *cb; 419d29d28f2Storek register int thresh; 420d29d28f2Storek { 421d29d28f2Storek register int error; 422d29d28f2Storek 423d29d28f2Storek cb->cb_thresh = thresh; 424d29d28f2Storek error = tsleep((caddr_t)cb, (PZERO + 1) | PCATCH, "audio", 0); 425d29d28f2Storek return (error); 426d29d28f2Storek } 427d29d28f2Storek 428d29d28f2Storek int 429d29d28f2Storek AUDIOREAD(dev, uio, ioflag) 430d29d28f2Storek { 431d29d28f2Storek register struct audio_softc *sc = SOFTC(dev); 432d29d28f2Storek register struct aucb *cb; 433d29d28f2Storek register int s, n, head, taildata, error; 434d29d28f2Storek register int blocksize = sc->sc_au.au_blksize; 435d29d28f2Storek 436d29d28f2Storek if (uio->uio_resid == 0) 437d29d28f2Storek return (0); 438d29d28f2Storek cb = &sc->sc_au.au_rb; 439d29d28f2Storek error = 0; 440d29d28f2Storek s = splaudio(); 441d29d28f2Storek cb->cb_drops = 0; 442d29d28f2Storek sc->sc_rseek = sc->sc_au.au_stamp - AUCB_LEN(cb); 443d29d28f2Storek do { 444d29d28f2Storek while (AUCB_LEN(cb) < blocksize) { 445d29d28f2Storek #ifndef SUNOS 446d29d28f2Storek if (ioflag & IO_NDELAY) { 447d29d28f2Storek error = EWOULDBLOCK; 448d29d28f2Storek goto out; 449d29d28f2Storek } 450d29d28f2Storek #endif 451d29d28f2Storek if ((error = audio_sleep(cb, blocksize)) != 0) 452d29d28f2Storek goto out; 453d29d28f2Storek } 454d29d28f2Storek splx(s); 455d29d28f2Storek /* 456d29d28f2Storek * The space calculation can only err on the short 457d29d28f2Storek * side if an interrupt occurs during processing: 458d29d28f2Storek * only cb_tail is altered in the interrupt code. 459d29d28f2Storek */ 460d29d28f2Storek head = cb->cb_head; 461d29d28f2Storek if ((n = AUCB_LEN(cb)) > uio->uio_resid) 462d29d28f2Storek n = uio->uio_resid; 463d29d28f2Storek taildata = AUCB_SIZE - head; 464d29d28f2Storek if (n > taildata) { 465d29d28f2Storek error = UIOMOVE((caddr_t)cb->cb_data + head, 466d29d28f2Storek taildata, UIO_READ, uio); 467d29d28f2Storek if (error == 0) 468d29d28f2Storek error = UIOMOVE((caddr_t)cb->cb_data, 469d29d28f2Storek n - taildata, UIO_READ, uio); 470d29d28f2Storek } else 471d29d28f2Storek error = UIOMOVE((caddr_t)cb->cb_data + head, n, 472d29d28f2Storek UIO_READ, uio); 473d29d28f2Storek if (error) 474d29d28f2Storek return (error); 475d29d28f2Storek head = AUCB_MOD(head + n); 476d29d28f2Storek (void) splaudio(); 477d29d28f2Storek cb->cb_head = head; 478d29d28f2Storek } while (uio->uio_resid >= blocksize); 479d29d28f2Storek out: 480d29d28f2Storek splx(s); 481d29d28f2Storek return (error); 482d29d28f2Storek } 483d29d28f2Storek 484d29d28f2Storek int 485d29d28f2Storek AUDIOWRITE(dev, uio, ioflag) 486d29d28f2Storek { 487d29d28f2Storek register struct audio_softc *sc = SOFTC(dev); 488d29d28f2Storek register struct aucb *cb = &sc->sc_au.au_wb; 489d29d28f2Storek register int s, n, tail, tailspace, error, first, watermark, drops; 490d29d28f2Storek 491d29d28f2Storek error = 0; 492d29d28f2Storek first = 1; 493d29d28f2Storek s = splaudio(); 494d29d28f2Storek while (uio->uio_resid > 0) { 495d29d28f2Storek watermark = sc->sc_au.au_hiwat; 496d29d28f2Storek while (AUCB_LEN(cb) > watermark) { 497d29d28f2Storek #ifndef SUNOS 498d29d28f2Storek if (ioflag & IO_NDELAY) { 499d29d28f2Storek error = EWOULDBLOCK; 500d29d28f2Storek goto out; 501d29d28f2Storek } 502d29d28f2Storek #endif 503d29d28f2Storek if ((error = audio_sleep(cb, watermark)) != 0) 504d29d28f2Storek goto out; 505d29d28f2Storek watermark = sc->sc_au.au_lowat; 506d29d28f2Storek } 507d29d28f2Storek splx(s); 508d29d28f2Storek /* 509d29d28f2Storek * The only value that can change on an interrupt is 510d29d28f2Storek * cb->cb_head. We only pull that out once to decide 511d29d28f2Storek * how much to write into cb_data; if we lose a race 512d29d28f2Storek * and cb_head changes, we will merely be overly 513d29d28f2Storek * conservative. For a legitimate time stamp, 514d29d28f2Storek * however, we need to synchronize the accesses to 515d29d28f2Storek * au_stamp and cb_head at a high ipl below. 516d29d28f2Storek */ 517d29d28f2Storek if ((n = AUCB_SIZE - AUCB_LEN(cb) - 1) > uio->uio_resid) 518d29d28f2Storek n = uio->uio_resid; 519d29d28f2Storek tail = cb->cb_tail; 520d29d28f2Storek tailspace = AUCB_SIZE - tail; 521d29d28f2Storek if (n > tailspace) { 522d29d28f2Storek /* write first part at tail and rest at head */ 523d29d28f2Storek error = UIOMOVE((caddr_t)cb->cb_data + tail, 524d29d28f2Storek tailspace, UIO_WRITE, uio); 525d29d28f2Storek if (error == 0) 526d29d28f2Storek error = UIOMOVE((caddr_t)cb->cb_data, 527d29d28f2Storek n - tailspace, UIO_WRITE, uio); 528d29d28f2Storek } else 529d29d28f2Storek error = UIOMOVE((caddr_t)cb->cb_data + tail, n, 530d29d28f2Storek UIO_WRITE, uio); 531d29d28f2Storek if (error) 532d29d28f2Storek return (error); 533d29d28f2Storek /* 534d29d28f2Storek * We cannot do this outside the loop because if the 535d29d28f2Storek * buffer is empty, an indeterminate amount of time 536d29d28f2Storek * will pass before the output starts to drain. 537d29d28f2Storek */ 538d29d28f2Storek (void)splaudio(); 539d29d28f2Storek tail = AUCB_MOD(tail + n); 540d29d28f2Storek if (first) { 541d29d28f2Storek first = 0; 542d29d28f2Storek sc->sc_wseek = sc->sc_au.au_stamp + AUCB_LEN(cb) + 1; 543d29d28f2Storek /* 544d29d28f2Storek * To guarantee that a write is contiguous in the 545d29d28f2Storek * sample space, we clear the drop count the first 546d29d28f2Storek * time through. If we later get drops, we will 547d29d28f2Storek * break out of the loop below, before writing 548d29d28f2Storek * a new frame. 549d29d28f2Storek * XXX I think we're one iteration too late! 550d29d28f2Storek */ 551d29d28f2Storek cb->cb_drops = 0; 552d29d28f2Storek } 553d29d28f2Storek cb->cb_tail = tail; 554d29d28f2Storek if (cb->cb_drops != 0) 555d29d28f2Storek break; 556d29d28f2Storek } 557d29d28f2Storek out: 558d29d28f2Storek splx(s); 559d29d28f2Storek return (error); 560d29d28f2Storek } 561d29d28f2Storek 562d29d28f2Storek /* Sun audio compatibility */ 563d29d28f2Storek struct sun_audio_prinfo { 564d29d28f2Storek u_int sample_rate; 565d29d28f2Storek u_int channels; 566d29d28f2Storek u_int precision; 567d29d28f2Storek u_int encoding; 568d29d28f2Storek u_int gain; 569d29d28f2Storek u_int port; 570d29d28f2Storek u_int reserved0[4]; 571d29d28f2Storek u_int samples; 572d29d28f2Storek u_int eof; 573d29d28f2Storek u_char pause; 574d29d28f2Storek u_char error; 575d29d28f2Storek u_char waiting; 576d29d28f2Storek u_char reserved1[3]; 577d29d28f2Storek u_char open; 578d29d28f2Storek u_char active; 579d29d28f2Storek }; 580d29d28f2Storek struct sun_audio_info { 581d29d28f2Storek struct sun_audio_prinfo play; 582d29d28f2Storek struct sun_audio_prinfo record; 583d29d28f2Storek u_int monitor_gain; 584d29d28f2Storek u_int reserved[4]; 585d29d28f2Storek }; 586d29d28f2Storek 587d29d28f2Storek #ifndef SUNOS 588d29d28f2Storek #define SUNAUDIO_GETINFO _IOR('A', 1, struct sun_audio_info) 589d29d28f2Storek #define SUNAUDIO_SETINFO _IOWR('A', 2, struct sun_audio_info) 590d29d28f2Storek #else 591d29d28f2Storek #define SUNAUDIO_GETINFO _IOR(A, 1, struct sun_audio_info) 592d29d28f2Storek #define SUNAUDIO_SETINFO _IOWR(A, 2, struct sun_audio_info) 593d29d28f2Storek #endif 594d29d28f2Storek 595d29d28f2Storek int 596d29d28f2Storek AUDIOIOCTL(dev, cmd, addr, flag, p) 597d29d28f2Storek { 598d29d28f2Storek register struct audio_softc *sc = SOFTC(dev); 599d29d28f2Storek int error = 0, i, s; 600d29d28f2Storek 601d29d28f2Storek switch (cmd) { 602d29d28f2Storek 603d29d28f2Storek case AUDIO_GETMAP: 604d29d28f2Storek bcopy((caddr_t)&sc->sc_map, addr, sizeof(sc->sc_map)); 605d29d28f2Storek break; 606d29d28f2Storek 607d29d28f2Storek case AUDIO_SETMAP: 608d29d28f2Storek bcopy(addr, (caddr_t)&sc->sc_map, sizeof(sc->sc_map)); 609d29d28f2Storek sc->sc_map.mr_mmr2 &= 0x7f; 610d29d28f2Storek audio_setmap(sc->sc_au.au_amd, &sc->sc_map); 611d29d28f2Storek break; 612d29d28f2Storek 613d29d28f2Storek case AUDIO_FLUSH: 614d29d28f2Storek s = splaudio(); 615d29d28f2Storek AUCB_INIT(&sc->sc_au.au_rb); 616d29d28f2Storek AUCB_INIT(&sc->sc_au.au_wb); 617d29d28f2Storek splx(s); 618d29d28f2Storek sc->sc_wseek = 0; 619d29d28f2Storek sc->sc_rseek = 0; 620d29d28f2Storek break; 621d29d28f2Storek 622d29d28f2Storek /* 623d29d28f2Storek * Number of read samples dropped. We don't know where or 624d29d28f2Storek * when they were dropped. 625d29d28f2Storek */ 626d29d28f2Storek case AUDIO_RERROR: 627d29d28f2Storek *(int *)addr = sc->sc_au.au_rb.cb_drops != 0; 628d29d28f2Storek break; 629d29d28f2Storek 630d29d28f2Storek /* 631d29d28f2Storek * Timestamp of last frame written. 632d29d28f2Storek */ 633d29d28f2Storek case AUDIO_WSEEK: 634d29d28f2Storek *(u_long *)addr = sc->sc_wseek; 635d29d28f2Storek break; 636d29d28f2Storek 637d29d28f2Storek case AUDIO_SETINFO: 638d29d28f2Storek error = audiosetinfo(sc, (struct audio_info *)addr); 639d29d28f2Storek break; 640d29d28f2Storek 641d29d28f2Storek case AUDIO_GETINFO: 642d29d28f2Storek error = audiogetinfo(sc, (struct audio_info *)addr); 643d29d28f2Storek break; 644d29d28f2Storek 645d29d28f2Storek case SUNAUDIO_GETINFO: 646d29d28f2Storek error = sunaudiogetinfo(sc, (struct sun_audio_info *)addr); 647d29d28f2Storek break; 648d29d28f2Storek 649d29d28f2Storek case SUNAUDIO_SETINFO: 650d29d28f2Storek error = sunaudiosetinfo(sc, (struct sun_audio_info *)addr); 651d29d28f2Storek break; 652d29d28f2Storek 653d29d28f2Storek case AUDIO_DRAIN: 654d29d28f2Storek s = splaudio(); 655d29d28f2Storek error = audio_drain(sc); 656d29d28f2Storek splx(s); 657d29d28f2Storek break; 658d29d28f2Storek 659d29d28f2Storek default: 660d29d28f2Storek error = EINVAL; 661d29d28f2Storek break; 662d29d28f2Storek } 663d29d28f2Storek return (error); 664d29d28f2Storek } 665d29d28f2Storek 666d29d28f2Storek int 667d29d28f2Storek AUDIOSELECT(dev, rw, p) 668d29d28f2Storek { 669d29d28f2Storek register struct audio_softc *sc = SOFTC(dev); 670d29d28f2Storek register struct aucb *cb; 671d29d28f2Storek register int s = splaudio(); 672d29d28f2Storek 673d29d28f2Storek switch (rw) { 674d29d28f2Storek 675d29d28f2Storek case FREAD: 676d29d28f2Storek cb = &sc->sc_au.au_rb; 677d29d28f2Storek if (AUCB_LEN(cb) >= sc->sc_au.au_blksize) { 678d29d28f2Storek splx(s); 679d29d28f2Storek return (1); 680d29d28f2Storek } 681d29d28f2Storek selrecord(p, &sc->sc_rsel); 682d29d28f2Storek cb->cb_thresh = sc->sc_au.au_blksize; 683d29d28f2Storek break; 684d29d28f2Storek 685d29d28f2Storek case FWRITE: 686d29d28f2Storek cb = &sc->sc_au.au_wb; 687d29d28f2Storek if (AUCB_LEN(cb) <= sc->sc_au.au_lowat) { 688d29d28f2Storek splx(s); 689d29d28f2Storek return (1); 690d29d28f2Storek } 691d29d28f2Storek selrecord(p, &sc->sc_wsel); 692d29d28f2Storek cb->cb_thresh = sc->sc_au.au_lowat; 693d29d28f2Storek break; 694d29d28f2Storek } 695d29d28f2Storek splx(s); 696d29d28f2Storek return (0); 697d29d28f2Storek } 698d29d28f2Storek 699d29d28f2Storek #ifdef AUDIO_C_HANDLER 700d29d28f2Storek int 701d29d28f2Storek audiohwintr(au0) 702d29d28f2Storek void *au0; 703d29d28f2Storek { 704d29d28f2Storek #ifdef SUNOS 705d29d28f2Storek register struct auio *au = audio_au; 706d29d28f2Storek #else 707d29d28f2Storek register struct auio *au = au0; 708d29d28f2Storek #endif 709d29d28f2Storek register volatile struct amd7930 *amd = au->au_amd; 710d29d28f2Storek register struct aucb *cb; 711d29d28f2Storek register int h, t, k; 712d29d28f2Storek 713d29d28f2Storek k = amd->ir; /* clear interrupt */ 714d29d28f2Storek ++au->au_stamp; 715d29d28f2Storek 716d29d28f2Storek /* receive incoming data */ 717d29d28f2Storek cb = &au->au_rb; 718d29d28f2Storek h = cb->cb_head; 719d29d28f2Storek t = cb->cb_tail; 720d29d28f2Storek k = AUCB_MOD(t + 1); 721d29d28f2Storek if (h == k) 722d29d28f2Storek cb->cb_drops++; 723d29d28f2Storek else if (cb->cb_pause != 0) 724d29d28f2Storek cb->cb_pdrops++; 725d29d28f2Storek else { 726d29d28f2Storek cb->cb_data[t] = amd->bbrb; 727d29d28f2Storek cb->cb_tail = t = k; 728d29d28f2Storek } 729d29d28f2Storek if (AUCB_MOD(t - h) >= cb->cb_thresh) { 730d29d28f2Storek cb->cb_thresh = AUCB_SIZE; 731d29d28f2Storek cb->cb_waking = 1; 732d29d28f2Storek AUDIO_SET_SWINTR; 733d29d28f2Storek } 734d29d28f2Storek /* send outgoing data */ 735d29d28f2Storek cb = &au->au_wb; 736d29d28f2Storek h = cb->cb_head; 737d29d28f2Storek t = cb->cb_tail; 738d29d28f2Storek if (h == t) 739d29d28f2Storek cb->cb_drops++; 740d29d28f2Storek else if (cb->cb_pause != 0) 741d29d28f2Storek cb->cb_pdrops++; 742d29d28f2Storek else { 743d29d28f2Storek cb->cb_head = h = AUCB_MOD(h + 1); 744d29d28f2Storek amd->bbtb = cb->cb_data[h]; 745d29d28f2Storek } 746d29d28f2Storek if (AUCB_MOD(t - h) <= cb->cb_thresh) { 747d29d28f2Storek cb->cb_thresh = -1; 748d29d28f2Storek cb->cb_waking = 1; 749d29d28f2Storek AUDIO_SET_SWINTR; 750d29d28f2Storek } 751d29d28f2Storek return (1); 752d29d28f2Storek } 753d29d28f2Storek #endif 754d29d28f2Storek 755d29d28f2Storek int 756d29d28f2Storek audioswintr(sc0) 757d29d28f2Storek void *sc0; 758d29d28f2Storek { 759d29d28f2Storek register struct audio_softc *sc; 760d29d28f2Storek register int s, ret = 0; 761d29d28f2Storek #ifdef SUNOS 762d29d28f2Storek sc = &audio_softc; 763d29d28f2Storek #else 764d29d28f2Storek sc = sc0; 765d29d28f2Storek #endif 766d29d28f2Storek s = splaudio(); 767d29d28f2Storek if (sc->sc_au.au_rb.cb_waking != 0) { 768d29d28f2Storek sc->sc_au.au_rb.cb_waking = 0; 769d29d28f2Storek splx(s); 770d29d28f2Storek ret = 1; 771d29d28f2Storek wakeup((caddr_t)&sc->sc_au.au_rb); 772d29d28f2Storek SELWAKEUP(&sc->sc_rsel); 773d29d28f2Storek (void) splaudio(); 774d29d28f2Storek } 775d29d28f2Storek if (sc->sc_au.au_wb.cb_waking != 0) { 776d29d28f2Storek sc->sc_au.au_wb.cb_waking = 0; 777d29d28f2Storek splx(s); 778d29d28f2Storek ret = 1; 779d29d28f2Storek wakeup((caddr_t)&sc->sc_au.au_wb); 780d29d28f2Storek SELWAKEUP(&sc->sc_wsel); 781d29d28f2Storek } else 782d29d28f2Storek splx(s); 783d29d28f2Storek return (ret); 784d29d28f2Storek } 785d29d28f2Storek 786d29d28f2Storek /* Write 16 bits of data from variable v to the data port of the audio chip */ 787d29d28f2Storek 788d29d28f2Storek #define WAMD16(amd, v) ((amd)->dr = v, (amd)->dr = v >> 8) 789d29d28f2Storek 790d29d28f2Storek void 791d29d28f2Storek audio_setmap(amd, map) 792d29d28f2Storek register volatile struct amd7930 *amd; 793d29d28f2Storek register struct mapreg *map; 794d29d28f2Storek { 795d29d28f2Storek register int i, s, v; 796d29d28f2Storek 797d29d28f2Storek s = splaudio(); 798d29d28f2Storek amd->cr = AMDR_MAP_1_10; 799d29d28f2Storek for (i = 0; i < 8; i++) { 800d29d28f2Storek v = map->mr_x[i]; 801d29d28f2Storek WAMD16(amd, v); 802d29d28f2Storek } 803d29d28f2Storek for (i = 0; i < 8; ++i) { 804d29d28f2Storek v = map->mr_r[i]; 805d29d28f2Storek WAMD16(amd, v); 806d29d28f2Storek } 807d29d28f2Storek v = map->mr_gx; WAMD16(amd, v); 808d29d28f2Storek v = map->mr_gr; WAMD16(amd, v); 809d29d28f2Storek v = map->mr_ger; WAMD16(amd, v); 810d29d28f2Storek v = map->mr_stgr; WAMD16(amd, v); 811d29d28f2Storek v = map->mr_ftgr; WAMD16(amd, v); 812d29d28f2Storek v = map->mr_atgr; WAMD16(amd, v); 813d29d28f2Storek amd->dr = map->mr_mmr1; 814d29d28f2Storek amd->dr = map->mr_mmr2; 815d29d28f2Storek splx(s); 816d29d28f2Storek } 817d29d28f2Storek 818d29d28f2Storek /* 819d29d28f2Storek * Set the mmr1 register and one other 16 bit register in the audio chip. 820d29d28f2Storek * The other register is indicated by op and val. 821d29d28f2Storek */ 822d29d28f2Storek void 823d29d28f2Storek audio_setmmr1(amd, mmr1, op, val) 824d29d28f2Storek register volatile struct amd7930 *amd; 825d29d28f2Storek register int mmr1; 826d29d28f2Storek register int op; 827d29d28f2Storek register int val; 828d29d28f2Storek { 829d29d28f2Storek register int s = splaudio(); 830d29d28f2Storek 831d29d28f2Storek amd->cr = AMDR_MAP_MMR1; 832d29d28f2Storek amd->dr = mmr1; 833d29d28f2Storek amd->cr = op; 834d29d28f2Storek WAMD16(amd, val); 835d29d28f2Storek splx(s); 836d29d28f2Storek } 837d29d28f2Storek 838d29d28f2Storek /* 839d29d28f2Storek * Set only the mmr1 regsiter, and one other. 840d29d28f2Storek */ 841d29d28f2Storek static void 842d29d28f2Storek audio_setmmr2(amd, mmr2) 843d29d28f2Storek register volatile struct amd7930 *amd; 844d29d28f2Storek register int mmr2; 845d29d28f2Storek { 846d29d28f2Storek register int s = splaudio(); 847d29d28f2Storek 848d29d28f2Storek amd->cr = AMDR_MAP_MMR2; 849d29d28f2Storek amd->dr = mmr2; 850d29d28f2Storek splx(s); 851d29d28f2Storek } 852d29d28f2Storek 853d29d28f2Storek static u_short ger_coeff[] = { 854d29d28f2Storek 0xaaaa, 0x9bbb, 0x79ac, 0x099a, 0x4199, 0x3199, 0x9cde, 0x9def, 855d29d28f2Storek 0x749c, 0x549d, 0x6aae, 0xabcd, 0xabdf, 0x7429, 0x64ab, 0x6aff, 856d29d28f2Storek 0x2abd, 0xbeef, 0x5cce, 0x75cd, 0x0099, 0x554c, 0x43dd, 0x33dd, 857d29d28f2Storek 0x52ef, 0x771b, 0x5542, 0x41dd, 0x31dd, 0x441f, 0x431f, 0x331f, 858d29d28f2Storek 0x40dd, 0x11dd, 0x440f, 0x411f, 0x311f, 0x5520, 0x10dd, 0x4211, 859d29d28f2Storek 0x410f, 0x111f, 0x600b, 0x00dd, 0x4210, 0x400f, 0x110f, 0x2210, 860d29d28f2Storek 0x7200, 0x4200, 0x2110, 0x100f, 0x2200, 0x1110, 0x000b, 0x2100, 861d29d28f2Storek 0x000f, 862d29d28f2Storek #define NGER (sizeof(ger_coeff) / sizeof(ger_coeff[0])) 863d29d28f2Storek }; 864d29d28f2Storek 865d29d28f2Storek static u_short gx_coeff[] = { 866d29d28f2Storek 0x0808, 0x4cb2, 0x3dac, 0x2ae5, 0x2533, 0x2222, 0x2122, 0x1fd3, 867d29d28f2Storek 0x12a2, 0x121b, 0x113b, 0x0bc3, 0x10f2, 0x03ba, 0x02ca, 0x021d, 868d29d28f2Storek 0x015a, 0x0122, 0x0112, 0x00ec, 0x0032, 0x0021, 0x0013, 0x0011, 869d29d28f2Storek 0x000e, 870d29d28f2Storek #define NGX (sizeof(gx_coeff) / sizeof(gx_coeff[0])) 871d29d28f2Storek }; 872d29d28f2Storek 873d29d28f2Storek static u_short stg_coeff[] = { 874d29d28f2Storek 0x8b7c, 0x8b44, 0x8b35, 0x8b2a, 0x8b24, 0x8b22, 0x9123, 0x912e, 875d29d28f2Storek 0x912a, 0x9132, 0x913b, 0x914b, 0x91f9, 0x91c5, 0x91b6, 0x9212, 876d29d28f2Storek 0x91a4, 0x9222, 0x9232, 0x92fb, 0x92aa, 0x9327, 0x93b3, 0x94b3, 877d29d28f2Storek 0x9f91, 0x9cea, 0x9bf9, 0x9aac, 0x9a4a, 0xa222, 0xa2a2, 0xa68d, 878d29d28f2Storek 0xaaa3, 0xb242, 0xbb52, 0xcbb2, 0x0808, 879d29d28f2Storek #define NSTG (sizeof(stg_coeff) / sizeof(stg_coeff[0])) 880d29d28f2Storek }; 881d29d28f2Storek 882d29d28f2Storek static void 883d29d28f2Storek ausetrgain(sc, level) 884d29d28f2Storek register struct audio_softc *sc; 885d29d28f2Storek register int level; 886d29d28f2Storek { 887d29d28f2Storek level &= 0xff; 888d29d28f2Storek sc->sc_rlevel = level; 889d29d28f2Storek if (level != 0) 890d29d28f2Storek sc->sc_map.mr_mmr1 |= AMD_MMR1_GX; 891d29d28f2Storek else 892d29d28f2Storek sc->sc_map.mr_mmr1 &=~ AMD_MMR1_GX; 893d29d28f2Storek 894d29d28f2Storek sc->sc_map.mr_gx = gx_coeff[(level * NGX) / 256]; 895d29d28f2Storek audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1, 896d29d28f2Storek AMDR_MAP_GX, sc->sc_map.mr_gx); 897d29d28f2Storek } 898d29d28f2Storek 899d29d28f2Storek static void 900d29d28f2Storek ausetpgain(sc, level) 901d29d28f2Storek register struct audio_softc *sc; 902d29d28f2Storek register int level; 903d29d28f2Storek { 904d29d28f2Storek level &= 0xff; 905d29d28f2Storek sc->sc_plevel = level; 906d29d28f2Storek if (level != 0) 907d29d28f2Storek sc->sc_map.mr_mmr1 |= AMD_MMR1_GER; 908d29d28f2Storek else 909d29d28f2Storek sc->sc_map.mr_mmr1 &=~ AMD_MMR1_GER; 910d29d28f2Storek 911d29d28f2Storek sc->sc_map.mr_ger = ger_coeff[(level * NGER) / 256]; 912d29d28f2Storek audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1, 913d29d28f2Storek AMDR_MAP_GER, sc->sc_map.mr_ger); 914d29d28f2Storek } 915d29d28f2Storek 916d29d28f2Storek static void 917d29d28f2Storek ausetmgain(sc, level) 918d29d28f2Storek register struct audio_softc *sc; 919d29d28f2Storek register int level; 920d29d28f2Storek { 921d29d28f2Storek level &= 0xff; 922d29d28f2Storek sc->sc_mlevel = level; 923d29d28f2Storek if (level != 0) 924d29d28f2Storek sc->sc_map.mr_mmr1 |= AMD_MMR1_STG; 925d29d28f2Storek else 926d29d28f2Storek sc->sc_map.mr_mmr1 &=~ AMD_MMR1_STG; 927d29d28f2Storek 928d29d28f2Storek sc->sc_map.mr_stgr = stg_coeff[(level * NSTG) / 256]; 929d29d28f2Storek audio_setmmr1(sc->sc_au.au_amd, sc->sc_map.mr_mmr1, 930d29d28f2Storek AMDR_MAP_STG, sc->sc_map.mr_stgr); 931d29d28f2Storek } 932d29d28f2Storek 933d29d28f2Storek static int 934d29d28f2Storek audiosetinfo(sc, ai) 935d29d28f2Storek struct audio_softc *sc; 936d29d28f2Storek struct audio_info *ai; 937d29d28f2Storek { 938d29d28f2Storek struct audio_prinfo *r = &ai->record, *p = &ai->play; 939d29d28f2Storek register int s, bsize; 940d29d28f2Storek 941d29d28f2Storek if (p->gain != ~0) 942d29d28f2Storek ausetpgain(sc, p->gain); 943d29d28f2Storek if (r->gain != ~0) 944d29d28f2Storek ausetrgain(sc, r->gain); 945d29d28f2Storek if (ai->monitor_gain != ~0) 946d29d28f2Storek ausetmgain(sc, p->gain); 947d29d28f2Storek if (p->port == AUDIO_SPEAKER) { 948d29d28f2Storek sc->sc_map.mr_mmr2 |= AMD_MMR2_LS; 949d29d28f2Storek audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2); 950d29d28f2Storek } else if (p->port == AUDIO_HEADPHONE) { 951d29d28f2Storek sc->sc_map.mr_mmr2 &=~ AMD_MMR2_LS; 952d29d28f2Storek audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2); 953d29d28f2Storek } 954d29d28f2Storek if (p->pause != (u_char)~0) 955d29d28f2Storek sc->sc_au.au_wb.cb_pause = p->pause; 956d29d28f2Storek if (r->pause != (u_char)~0) 957d29d28f2Storek sc->sc_au.au_rb.cb_pause = r->pause; 958d29d28f2Storek 959d29d28f2Storek if (ai->blocksize != ~0) { 960d29d28f2Storek if (ai->blocksize == 0) 961d29d28f2Storek bsize = ai->blocksize = DEFBLKSIZE; 962d29d28f2Storek else if (ai->blocksize > MAXBLKSIZE) 963d29d28f2Storek bsize = ai->blocksize = MAXBLKSIZE; 964d29d28f2Storek else 965d29d28f2Storek bsize = ai->blocksize; 966d29d28f2Storek 967d29d28f2Storek s = splaudio(); 968d29d28f2Storek sc->sc_au.au_blksize = bsize; 969d29d28f2Storek /* AUDIO_FLUSH */ 970d29d28f2Storek AUCB_INIT(&sc->sc_au.au_rb); 971d29d28f2Storek AUCB_INIT(&sc->sc_au.au_wb); 972d29d28f2Storek splx(s); 973d29d28f2Storek 974d29d28f2Storek } 975d29d28f2Storek if (ai->hiwat != ~0 && (unsigned)ai->hiwat < AUCB_SIZE) 976d29d28f2Storek sc->sc_au.au_hiwat = ai->hiwat; 977d29d28f2Storek if (ai->lowat != ~0 && ai->lowat < AUCB_SIZE) 978d29d28f2Storek sc->sc_au.au_lowat = ai->lowat; 979d29d28f2Storek 980d29d28f2Storek return (0); 981d29d28f2Storek } 982d29d28f2Storek 983d29d28f2Storek static int 984d29d28f2Storek sunaudiosetinfo(sc, ai) 985d29d28f2Storek struct audio_softc *sc; 986d29d28f2Storek struct sun_audio_info *ai; 987d29d28f2Storek { 988d29d28f2Storek struct sun_audio_prinfo *r = &ai->record, *p = &ai->play; 989d29d28f2Storek 990d29d28f2Storek if (p->gain != ~0) 991d29d28f2Storek ausetpgain(sc, p->gain); 992d29d28f2Storek if (r->gain != ~0) 993d29d28f2Storek ausetrgain(sc, r->gain); 994d29d28f2Storek if (ai->monitor_gain != ~0) 995d29d28f2Storek ausetmgain(sc, p->gain); 996d29d28f2Storek if (p->port == AUDIO_SPEAKER) { 997d29d28f2Storek sc->sc_map.mr_mmr2 |= AMD_MMR2_LS; 998d29d28f2Storek audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2); 999d29d28f2Storek } else if (p->port == AUDIO_HEADPHONE) { 1000d29d28f2Storek sc->sc_map.mr_mmr2 &=~ AMD_MMR2_LS; 1001d29d28f2Storek audio_setmmr2(sc->sc_au.au_amd, sc->sc_map.mr_mmr2); 1002d29d28f2Storek } 1003d29d28f2Storek /* 1004d29d28f2Storek * The bsd driver does not distinguish between paused and active. 1005d29d28f2Storek * (In the sun driver, not active means samples are not ouput 1006d29d28f2Storek * at all, but paused means the last streams buffer is drained 1007d29d28f2Storek * and then output stops.) If either are 0, then when stop output. 1008d29d28f2Storek * Otherwise, if either are non-zero, we resume. 1009d29d28f2Storek */ 1010d29d28f2Storek if (p->pause == 0 || p->active == 0) 1011d29d28f2Storek sc->sc_au.au_wb.cb_pause = 0; 1012d29d28f2Storek else if (p->pause != (u_char)~0 || p->active != (u_char)~0) 1013d29d28f2Storek sc->sc_au.au_wb.cb_pause = 1; 1014d29d28f2Storek if (r->pause == 0 || r->active == 0) 1015d29d28f2Storek sc->sc_au.au_rb.cb_pause = 0; 1016d29d28f2Storek else if (r->pause != (u_char)~0 || r->active != (u_char)~0) 1017d29d28f2Storek sc->sc_au.au_rb.cb_pause = 1; 1018d29d28f2Storek 1019d29d28f2Storek return (0); 1020d29d28f2Storek } 1021d29d28f2Storek 1022d29d28f2Storek static int 1023d29d28f2Storek audiogetinfo(sc, ai) 1024d29d28f2Storek struct audio_softc *sc; 1025d29d28f2Storek struct audio_info *ai; 1026d29d28f2Storek { 1027d29d28f2Storek struct audio_prinfo *r = &ai->record, *p = &ai->play; 1028d29d28f2Storek 1029d29d28f2Storek p->sample_rate = r->sample_rate = 8000; 1030d29d28f2Storek p->channels = r->channels = 1; 1031d29d28f2Storek p->precision = r->precision = 8; 1032d29d28f2Storek p->encoding = r->encoding = AUDIO_ENCODING_ULAW; 1033d29d28f2Storek 1034d29d28f2Storek ai->monitor_gain = sc->sc_mlevel; 1035d29d28f2Storek r->gain = sc->sc_rlevel; 1036d29d28f2Storek p->gain = sc->sc_plevel; 1037d29d28f2Storek r->port = 1; p->port = (sc->sc_map.mr_mmr2 & AMD_MMR2_LS) ? 1038d29d28f2Storek AUDIO_SPEAKER : AUDIO_HEADPHONE; 1039d29d28f2Storek 1040d29d28f2Storek p->pause = sc->sc_au.au_wb.cb_pause; 1041d29d28f2Storek r->pause = sc->sc_au.au_rb.cb_pause; 1042d29d28f2Storek p->error = sc->sc_au.au_wb.cb_drops != 0; 1043d29d28f2Storek r->error = sc->sc_au.au_rb.cb_drops != 0; 1044d29d28f2Storek 1045d29d28f2Storek p->open = sc->sc_open; 1046d29d28f2Storek r->open = sc->sc_open; 1047d29d28f2Storek 1048d29d28f2Storek p->samples = sc->sc_au.au_stamp - sc->sc_au.au_wb.cb_pdrops; 1049d29d28f2Storek r->samples = sc->sc_au.au_stamp - sc->sc_au.au_rb.cb_pdrops; 1050d29d28f2Storek 1051d29d28f2Storek p->seek = sc->sc_wseek; 1052d29d28f2Storek r->seek = sc->sc_rseek; 1053d29d28f2Storek 1054d29d28f2Storek ai->blocksize = sc->sc_au.au_blksize; 1055d29d28f2Storek ai->hiwat = sc->sc_au.au_hiwat; 1056d29d28f2Storek ai->lowat = sc->sc_au.au_lowat; 1057d29d28f2Storek 1058d29d28f2Storek return (0); 1059d29d28f2Storek } 1060d29d28f2Storek 1061d29d28f2Storek static int 1062d29d28f2Storek sunaudiogetinfo(sc, ai) 1063d29d28f2Storek struct audio_softc *sc; 1064d29d28f2Storek struct sun_audio_info *ai; 1065d29d28f2Storek { 1066d29d28f2Storek struct sun_audio_prinfo *r = &ai->record, *p = &ai->play; 1067d29d28f2Storek 1068d29d28f2Storek p->sample_rate = r->sample_rate = 8000; 1069d29d28f2Storek p->channels = r->channels = 1; 1070d29d28f2Storek p->precision = r->precision = 8; 1071d29d28f2Storek p->encoding = r->encoding = AUDIO_ENCODING_ULAW; 1072d29d28f2Storek 1073d29d28f2Storek ai->monitor_gain = sc->sc_mlevel; 1074d29d28f2Storek r->gain = sc->sc_rlevel; 1075d29d28f2Storek p->gain = sc->sc_plevel; 1076d29d28f2Storek r->port = 1; p->port = (sc->sc_map.mr_mmr2 & AMD_MMR2_LS) ? 1077d29d28f2Storek AUDIO_SPEAKER : AUDIO_HEADPHONE; 1078d29d28f2Storek 1079d29d28f2Storek p->active = p->pause = sc->sc_au.au_wb.cb_pause; 1080d29d28f2Storek r->active = r->pause = sc->sc_au.au_rb.cb_pause; 1081d29d28f2Storek p->error = sc->sc_au.au_wb.cb_drops != 0; 1082d29d28f2Storek r->error = sc->sc_au.au_rb.cb_drops != 0; 1083d29d28f2Storek 1084d29d28f2Storek p->waiting = 0; 1085d29d28f2Storek r->waiting = 0; 1086d29d28f2Storek p->eof = 0; 1087d29d28f2Storek r->eof = 0; 1088d29d28f2Storek 1089d29d28f2Storek p->open = sc->sc_open; 1090d29d28f2Storek r->open = sc->sc_open; 1091d29d28f2Storek 1092d29d28f2Storek p->samples = sc->sc_au.au_stamp - sc->sc_au.au_wb.cb_pdrops; 1093d29d28f2Storek r->samples = sc->sc_au.au_stamp - sc->sc_au.au_rb.cb_pdrops; 1094d29d28f2Storek 1095d29d28f2Storek return (0); 1096d29d28f2Storek } 1097d29d28f2Storek #endif 1098