Mercurial > illumos > illumos-gate
changeset 11936:54dc8a89ba0d
PSARC 2009/689 Audio DDI Simplifications
PSARC 2009/674 Decoupling audio device interrupts
6921849 interrupt free audio
6921851 audio DDI simplifications
6930541 Synchronization issue between usb_ac and usb_as
line wrap: on
line diff
--- a/usr/src/cmd/audio/audiotest/audiotest.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/cmd/audio/audiotest/audiotest.c Tue Mar 16 09:30:41 2010 -0700 @@ -21,7 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* @@ -386,14 +386,14 @@ perror(dn); errno = err; describe_error(errno); - return (0); + return (-1); } ainfo.dev = -1; if (ioctl(fd, SNDCTL_AUDIOINFO, &ainfo) == -1) { perror("SNDCTL_AUDIOINFO"); (void) close(fd); - return (1); + return (-1); } (void) printf(_("\n*** Scanning sound adapter #%d ***\n"), @@ -405,13 +405,13 @@ if (!ainfo.enabled) { (void) printf(_(" - Device not present - Skipping\n")); (void) close(fd); - return (1); + return (0); } if (!(ainfo.caps & PCM_CAP_OUTPUT)) { (void) printf(_(" - Skipping input only device\n")); (void) close(fd); - return (1); + return (0); } (void) printf(_(" - Performing audio playback test... ")); @@ -419,6 +419,9 @@ code = testdsp(fd, flags, tcfg); (void) close(fd); + if (code < 0) { + return (code); + } return (code == 1); } @@ -472,6 +475,7 @@ int maxdev; int flags = 0; int status = 0; + int errors = 0; int numdev; extern int optind; testcfg_t *tcfg; @@ -540,12 +544,18 @@ do { char *dn; oss_audioinfo ainfo; + int rv; + status = 0; if (numdev > 0) { for (t = 0; t < numdev; t++) { dn = argv[optind + t]; - if (!test_device(dn, flags, tcfg)) + rv = test_device(dn, flags, tcfg); + if (rv < 0) { + errors++; + } else if (rv) { status++; + } } } else { for (t = 0; t < maxdev; t++) { @@ -557,17 +567,21 @@ continue; } dn = ainfo.devnode; - if (!test_device(dn, flags, tcfg)) + rv = test_device(dn, flags, tcfg); + if (rv < 0) { + errors++; + } else if (rv) { status++; + } } } - if (status == 0) + if (errors == 0) (void) printf(_("\n*** All tests completed OK ***\n")); else (void) printf(_("\n*** Errors were detected ***\n")); - } while (flags & TF_LOOP); + } while (status && (flags & TF_LOOP)); (void) close(mixerfd);
--- a/usr/src/pkg/manifests/driver-audio-audioixp.mf Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/pkg/manifests/driver-audio-audioixp.mf Tue Mar 16 09:30:41 2010 -0700 @@ -46,7 +46,6 @@ alias=pci1002,4370 file path=kernel/drv/$(ARCH64)/audioixp group=sys file path=kernel/drv/audioixp group=sys -file path=kernel/drv/audioixp.conf group=sys reboot-needed=false legacy pkg=SUNWadixp arch=$(ARCH) category=system \ desc="SunOS audio device driver for ATI IXP integrated audio hardware" \ hotline="Please contact your local service provider" \
--- a/usr/src/pkg/manifests/driver-audio-audiovia823x.mf Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/pkg/manifests/driver-audio-audiovia823x.mf Tue Mar 16 09:30:41 2010 -0700 @@ -43,12 +43,10 @@ driver name=audiovia823x alias=pci1106,3059 file path=kernel/drv/$(ARCH64)/audiovia823x group=sys file path=kernel/drv/audiovia823x group=sys -file path=kernel/drv/audiovia823x.conf group=sys reboot-needed=false legacy pkg=SUNWvia823x arch=$(ARCH) category=system \ desc="SunOS audio device driver for VIA VT823x south bridges" \ hotline="Please contact your local service provider" \ name="SUNW Audio Driver for VIA VT823x" vendor="Sun Microsystems, Inc." \ version=11.11,REV=2009.11.11 license cr_Sun license=cr_Sun -license lic_OSBL license=lic_OSBL -license lic_OSBL_preamble license=lic_OSBL_preamble +license lic_CDDL license=lic_CDDL
--- a/usr/src/pkg/manifests/driver-audio.mf Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/pkg/manifests/driver-audio.mf Tue Mar 16 09:30:41 2010 -0700 @@ -58,14 +58,9 @@ $(i386_ONLY)file path=kernel/drv/audio group=sys file path=kernel/drv/audio.conf group=sys reboot-needed=false $(i386_ONLY)file path=kernel/drv/audio1575 group=sys -file path=kernel/drv/audio1575.conf group=sys reboot-needed=false -$(sparc_ONLY)file path=kernel/drv/audiocs.conf group=sys reboot-needed=false $(i386_ONLY)file path=kernel/drv/audioens group=sys -file path=kernel/drv/audioens.conf group=sys reboot-needed=false $(i386_ONLY)file path=kernel/drv/audiopci group=sys -file path=kernel/drv/audiopci.conf group=sys reboot-needed=false $(i386_ONLY)file path=kernel/drv/audiots group=sys -file path=kernel/drv/audiots.conf group=sys reboot-needed=false file path=kernel/misc/$(ARCH64)/ac97 group=sys mode=0755 reboot-needed=true $(i386_ONLY)file path=kernel/misc/ac97 group=sys mode=0755 reboot-needed=true legacy pkg=SUNWaudd arch=$(ARCH) category=system \
--- a/usr/src/uts/common/io/audio/ac97/ac97.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/ac97/ac97.c Tue Mar 16 09:30:41 2010 -0700 @@ -21,7 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -39,7 +39,7 @@ * This is the initial value for many controls. This is * a 75% level. */ -#define INIT_VAL_MAIN ((75 << 8) | 75) +#define INIT_VAL_MAIN 75 #define INIT_VAL_ST ((75 << 8) | 75) #define INIT_VAL_MN 75 #define INIT_IGAIN_ST ((50 << 8) | 50) @@ -102,8 +102,6 @@ uint16_t shadow[NUM_SHADOW]; - boolean_t suspended; /* true if suspended */ - kt_did_t resumer; /* resumer if suspended */ uint32_t flags; #define AC97_FLAG_AMPLIFIER (1 << 0) /* ext. amp on by default */ #define AC97_FLAG_MICBOOST (1 << 1) /* micboost on by default */ @@ -128,7 +126,6 @@ void (*codec_init)(ac97_t *); void (*codec_reset)(ac97_t *); - kmutex_t ac_lock; list_t ctrls; uint64_t inputs; @@ -657,13 +654,7 @@ SHADOW(ac, reg) = val; } - /* - * Don't touch hardware _unless_ if we are suspended, unless we - * are in the process of resuming. - */ - if ((!ac->suspended) || (ac->resumer == ddi_get_kt_did())) { - ac->wr(ac->private, reg, val); - } + ac->wr(ac->private, reg, val); } /* @@ -678,10 +669,7 @@ if ((reg < LAST_SHADOW_REG) && (reg > 0)) { return (SHADOW(ac, reg)); } - if ((!ac->suspended) || (ac->resumer == ddi_get_kt_did())) { - return (ac->rd(ac->private, reg)); - } - return (0); + return (ac->rd(ac->private, reg)); } /* @@ -739,16 +727,6 @@ for (int i = 2; i < LAST_SHADOW_REG; i += sizeof (uint16_t)) { ac->wr(ac->private, i, SHADOW(ac, i)); } - - /* - * Then go and do the controls. This is important because some of - * the controls might use registers that aren't shadowed. Doing it - * a second time also may help guarantee that it all works. - */ - for (ac97_ctrl_t *ctrl = list_head(&ac->ctrls); ctrl; - ctrl = list_next(&ac->ctrls, ctrl)) { - ctrl->actrl_write_fn(ctrl, ctrl->actrl_value); - } } /* @@ -760,13 +738,11 @@ { ac97_ctrl_t *ctrl; - mutex_enter(&ac->ac_lock); for (ctrl = list_head(&ac->ctrls); ctrl; ctrl = list_next(&ac->ctrls, ctrl)) { ctrl->actrl_value = ctrl->actrl_initval; ctrl->actrl_write_fn(ctrl, ctrl->actrl_initval); } - mutex_exit(&ac->ac_lock); } /* @@ -1057,11 +1033,7 @@ int ac97_control_get(ac97_ctrl_t *ctrl, uint64_t *value) { - ac97_t *ac = ctrl->actrl_ac97; - - mutex_enter(&ac->ac_lock); *value = ctrl->actrl_value; - mutex_exit(&ac->ac_lock); return (0); } @@ -1069,7 +1041,6 @@ int ac97_control_set(ac97_ctrl_t *ctrl, uint64_t value) { - ac97_t *ac = ctrl->actrl_ac97; uint8_t v1, v2; /* a bit of quick checking */ @@ -1100,10 +1071,8 @@ break; } - mutex_enter(&ac->ac_lock); ctrl->actrl_value = value; ctrl->actrl_write_fn(ctrl, value); - mutex_exit(&ac->ac_lock); return (0); } @@ -1121,28 +1090,6 @@ } /* - * This simply sets a flag to block calls to the underlying - * hardware driver to get or set hardware controls. This is usually - * called just before a power down of devices. Once this gets called any - * calls to set controls will not touch the real hardware. But - * since all control updates are always saved in soft registers it - * is a simple mater to update the hardware with the latest values - * on resume which also unblocks calls to the hardware controls. - */ -void -ac97_suspend(ac97_t *ac) -{ - mutex_enter(&ac->ac_lock); - - /* This will prevent any new operations from starting! */ - ac->suspended = B_TRUE; - ac->resumer = 0; - - /* XXX - should we powerdown codec's here?? */ - mutex_exit(&ac->ac_lock); -} - -/* * Reset the analog codec hardware * * Reset all analog AC97 hardware, input ADC's, output DAC's and MIXER. @@ -1278,59 +1225,15 @@ } /* - * This will reset and re-initialize the device. - * It has two modes of operation that affect how it handles - * all controls. - * - * It re-initializes the device and then can either reset - * all controls back to their initial values or it can - * re-load all controls with their last updated values. - * - * initval - If this is none zero then all controls will - * be restored to their initial values. + * This will reset and re-initialize the device. It is still incumbent + * on the caller (or the audio framework) to replay control settings! */ void ac97_reset(ac97_t *ac) { - /* If we are about to suspend so no point in going on */ - mutex_enter(&ac->ac_lock); - if (ac->suspended) { - mutex_exit(&ac->ac_lock); - return; - } ac_analog_reset(ac); ac_hw_reset(ac); ac_restore(ac); - - mutex_exit(&ac->ac_lock); -} - -/* - * Given the need to resume the hardware this reloads the base hardware - * and then takes the stored values for each control and sends them - * to the hardware again. - */ -void -ac97_resume(ac97_t *ac) -{ - - /* - * This should only be called when already suspended. - * this takes us out of suspend state after it brings the - * controls back to life. - */ - ASSERT(ac->suspended); - mutex_enter(&ac->ac_lock); - ac->resumer = ddi_get_kt_did(); - - /* We simply call reset since the operation is the same */ - ac_analog_reset(ac); - ac_hw_reset(ac); - ac_restore(ac); - - ac->resumer = 0; - ac->suspended = B_FALSE; - mutex_exit(&ac->ac_lock); } /* @@ -1699,8 +1602,6 @@ list_create(&ac->ctrls, sizeof (struct ac97_ctrl), offsetof(struct ac97_ctrl, actrl_linkage)); - mutex_init(&ac->ac_lock, NULL, MUTEX_DRIVER, NULL); - #define PROP_FLAG(prop, flag, def) \ if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, \ (prop), (def))) { \ @@ -1808,7 +1709,6 @@ } list_destroy(&ac->ctrls); - mutex_destroy(&ac->ac_lock); kmem_free(ac, sizeof (ac97_t)); }
--- a/usr/src/uts/common/io/audio/drv/audio1575/audio1575.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audio1575/audio1575.c Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -70,7 +70,7 @@ /* * Entry point routine prototypes */ -static int audio1575_open(void *, int, unsigned *, unsigned *, caddr_t *); +static int audio1575_open(void *, int, unsigned *, caddr_t *); static void audio1575_close(void *); static int audio1575_start(void *); static void audio1575_stop(void *); @@ -97,11 +97,6 @@ }; /* - * interrupt handler - */ -static uint_t audio1575_intr(caddr_t, caddr_t); - -/* * Local Routine Prototypes */ static int audio1575_attach(dev_info_t *); @@ -111,12 +106,7 @@ static int audio1575_alloc_port(audio1575_state_t *, int, uint8_t); static void audio1575_free_port(audio1575_port_t *); -static void audio1575_start_port(audio1575_port_t *); -static void audio1575_stop_port(audio1575_port_t *); -static void audio1575_reset_port(audio1575_port_t *); -static void audio1575_update_port(audio1575_port_t *); -static int audio1575_setup_intr(audio1575_state_t *); static int audio1575_codec_sync(audio1575_state_t *); static void audio1575_write_ac97(void *, uint8_t, uint16_t); static uint16_t audio1575_read_ac97(void *, uint8_t); @@ -374,119 +364,6 @@ return (DDI_SUCCESS); } - -/* - * audio1575_intr() - * - * Description: - * Interrupt service routine for both play and record. For play we - * get the next buffers worth of audio. For record we send it on to - * the mixer. - * - * Each of buffer descriptor has a field IOC(interrupt on completion) - * When both this and the IOC bit of correspondent dma control register - * is set, it means that the controller should issue an interrupt upon - * completion of this buffer. Note that in the clearing of the interrupts - * below that the PCM IN and PCM out interrupts ar cleared by their - * respective control registers and not by writing a '1' to the INTRSR - * the interrupt status register. Only CPRINTR,SPINTR,and GPIOINTR - * require a '1' written to the INTRSR register to clear those - * interrupts. See comments below. - * - * Arguments: - * caddr_t arg Pointer to the interrupting device's state - * structure - * - * Returns: - * DDI_INTR_CLAIMED Interrupt claimed and processed - * DDI_INTR_UNCLAIMED Interrupt not claimed, and thus ignored - */ -static uint_t -audio1575_intr(caddr_t arg, caddr_t dontcare) -{ - audio1575_state_t *statep = (void *)arg; - uint32_t intrsr; - uint8_t index; - audio1575_port_t *consume = NULL; - audio1575_port_t *produce = NULL; - - _NOTE(ARGUNUSED(dontcare)); - - mutex_enter(&statep->lock); - - intrsr = GET32(M1575_INTRSR_REG); - - /* check if device is interrupting */ - if (intrsr == 0) { - if (statep->ksp) { - /* increment the spurious ino5 interrupt cnt */ - M1575_KIOP(statep)->intrs[KSTAT_INTR_SPURIOUS]++; - } - - mutex_exit(&statep->lock); - return (DDI_INTR_UNCLAIMED); - } - - /* update the kernel interrupt statistics */ - if (statep->ksp) { - M1575_KIOP(statep)->intrs[KSTAT_INTR_HARD]++; - } - - /* - * The Uli M1575 generates an interrupt for each interrupt - * type. therefore we only process one interrupt type - * per invocation of the audio1575_intr() routine. - * WARNING: DO NOT attempt to optimize this by looping - * until the INTRSR register is clear as this will - * generate spurious ino5 interrupts. - */ - if (GET16(M1575_PCMISR_REG) & M1575_PCMISR_BCIS) { - /* Clear PCM IN interrupt */ - PUT16(M1575_PCMISR_REG, M1575_SR_CLR); - /* - * Note: This interrupt is not cleared by writing a '1' - * to the M1575_INTRSR_REG according to the M1575 Super I/O - * data sheet on page 189. - */ - - /* update the LVI -- we just set it to the current value - 1 */ - index = GET8(M1575_PCMICIV_REG); - index = (index - 1) % M1575_BD_NUMS; - PUT8(M1575_PCMILVIV_REG, index); - produce = statep->ports[M1575_REC]; - - } else if (GET16(M1575_PCMOSR_REG) & M1575_PCMOSR_BCIS) { - /* Clear PCM OUT interrupt */ - PUT16(M1575_PCMOSR_REG, M1575_SR_CLR); - /* - * Note: This interrupt is not cleared by writing a '1' - * to the M1575_INTRSR_REG according to the M1575 Super I/O - * data sheet on page 189. - */ - - /* update the LVI -- we just set it to the current value - 1 */ - index = GET8(M1575_PCMOCIV_REG); - index = (index - 1) % M1575_BD_NUMS; - PUT8(M1575_PCMOLVIV_REG, index); - consume = statep->ports[M1575_PLAY]; - - } else { - /* Clear other interrupts (there should not be any) */ - PUT32(M1575_INTRSR_REG, (intrsr & M1575_INTR_MASK)); - } - - mutex_exit(&statep->lock); - - if (produce) { - audio_engine_produce(produce->engine); - } - if (consume) { - audio_engine_consume(consume->engine); - } - - return (DDI_INTR_CLAIMED); -} - /* * audio1575_open() * @@ -496,8 +373,7 @@ * Arguments: * void *arg The DMA engine to set up * int flag Open flags - * unsigned *fragfrp Receives number of frames per fragment - * unsigned *nfragsp Receives number of fragments + * unsigned *nframesp Receives number of frames * caddr_t *bufp Receives kernel data buffer * * Returns: @@ -505,23 +381,16 @@ * errno on failure */ static int -audio1575_open(void *arg, int flag, - unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp) +audio1575_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp) { audio1575_port_t *port = arg; _NOTE(ARGUNUSED(flag)); - port->started = B_FALSE; port->count = 0; - *fragfrp = port->fragfr; - *nfragsp = M1575_BD_NUMS; + *nframesp = port->nframes; *bufp = port->samp_kaddr; - mutex_enter(&port->statep->lock); - audio1575_reset_port(port); - mutex_exit(&port->statep->lock); - return (0); } @@ -540,13 +409,7 @@ static void audio1575_close(void *arg) { - audio1575_port_t *port = arg; - audio1575_state_t *statep = port->statep; - - mutex_enter(&statep->lock); - audio1575_stop_port(port); - port->started = B_FALSE; - mutex_exit(&statep->lock); + _NOTE(ARGUNUSED(arg)); } /* @@ -566,10 +429,11 @@ audio1575_state_t *statep = port->statep; mutex_enter(&statep->lock); - if (port->started) { - audio1575_stop_port(port); + if (port->num == M1575_REC) { + SET32(M1575_DMACR_REG, M1575_DMACR_PCMIPAUSE); + } else { + SET32(M1575_DMACR_REG, M1575_DMACR_PCMOPAUSE); } - port->started = B_FALSE; mutex_exit(&statep->lock); } @@ -592,14 +456,70 @@ audio1575_state_t *statep = port->statep; mutex_enter(&statep->lock); - if (!port->started) { - audio1575_start_port(port); - port->started = B_TRUE; + + port->offset = 0; + + if (port->num == M1575_REC) { + /* Uli FIFO madness ... */ + SET32(M1575_FIFOCR1_REG, M1575_FIFOCR1_PCMIRST); + SET32(M1575_DMACR_REG, M1575_DMACR_PCMIPAUSE); + + PUT8(M1575_PCMICR_REG, 0); + PUT8(M1575_PCMICR_REG, M1575_CR_RR); + + PUT32(M1575_PCMIBDBAR_REG, port->bdl_paddr); + PUT8(M1575_PCMILVIV_REG, M1575_BD_NUMS - 1); + + CLR32(M1575_DMACR_REG, M1575_DMACR_PCMIPAUSE); + + /* ULi says do fifo resets here */ + SET32(M1575_FIFOCR1_REG, M1575_FIFOCR1_PCMIRST); + CLR32(M1575_DMACR_REG, M1575_DMACR_PCMIPAUSE); + PUT8(M1575_PCMICR_REG, 0); + SET32(M1575_DMACR_REG, M1575_DMACR_PCMISTART); + + } else { + + uint32_t scr; + + /* Uli FIFO madness ... */ + SET32(M1575_FIFOCR1_REG, M1575_FIFOCR1_PCMORST); + SET32(M1575_DMACR_REG, M1575_DMACR_PCMOPAUSE); + + /* configure the number of channels properly */ + scr = GET32(M1575_SCR_REG); + scr &= ~(M1575_SCR_6CHL_MASK | M1575_SCR_CHAMOD_MASK); + scr |= M1575_SCR_6CHL_2; /* select our proper ordering */ + switch (port->nchan) { + case 2: + scr |= M1575_SCR_CHAMOD_2; + break; + case 4: + scr |= M1575_SCR_CHAMOD_4; + break; + case 6: + scr |= M1575_SCR_CHAMOD_6; + break; + } + PUT32(M1575_SCR_REG, scr); + + PUT8(M1575_PCMOCR_REG, 0); + PUT8(M1575_PCMOCR_REG, M1575_CR_RR); + + PUT32(M1575_PCMOBDBAR_REG, port->bdl_paddr); + PUT8(M1575_PCMOLVIV_REG, M1575_BD_NUMS - 1); + + CLR32(M1575_DMACR_REG, M1575_DMACR_PCMOPAUSE); + PUT8(M1575_PCMOCR_REG, 0); + SET32(M1575_DMACR_REG, M1575_DMACR_PCMOSTART); } + mutex_exit(&statep->lock); return (0); } + + /* * audio1575_format() * @@ -678,10 +598,42 @@ audio1575_port_t *port = arg; audio1575_state_t *statep = port->statep; uint64_t val; + uint8_t civ; + unsigned n; + int civoff; + int lvioff; + int picoff; mutex_enter(&statep->lock); - audio1575_update_port(port); - val = port->count + (port->picb / port->nchan); + + if (port->num == M1575_REC) { + civoff = M1575_PCMICIV_REG; + lvioff = M1575_PCMILVIV_REG; + picoff = M1575_PCMIPICB_REG; + } else { + civoff = M1575_PCMOCIV_REG; + lvioff = M1575_PCMOLVIV_REG; + picoff = M1575_PCMOPICB_REG; + } + + /* + * Read the position counters. We also take this opportunity + * to update the last valid index to the one just previous to + * the one we're working on (so we'll fully loop.) + */ + n = GET16(picoff); + civ = GET8(civoff); + PUT8(lvioff, (civ - 1) % M1575_BD_NUMS); + + n = port->samp_size - (n * sizeof (int16_t)); + if (n < port->offset) { + val = (port->samp_size - port->offset) + n; + } else { + val = n - port->offset; + } + port->offset = n; + port->count += (val / (port->nchan * sizeof (int16_t))); + val = port->count; mutex_exit(&statep->lock); return (val); @@ -706,197 +658,6 @@ } /* - * audio1575_start_port() - * - * Description: - * This routine starts the DMA engine. - * - * Arguments: - * audio1575_port_t *port Port of DMA engine to start. - */ -static void -audio1575_start_port(audio1575_port_t *port) -{ - audio1575_state_t *statep = port->statep; - - ASSERT(mutex_owned(&statep->lock)); - - /* if suspended, then do nothing else */ - if (statep->suspended) { - return; - } - - if (port->num == M1575_REC) { - /* ULi says do fifo resets here */ - SET32(M1575_FIFOCR1_REG, M1575_FIFOCR1_PCMIRST); - CLR32(M1575_DMACR_REG, M1575_DMACR_PCMIPAUSE); - PUT8(M1575_PCMICR_REG, M1575_PCMICR_IOCE); - SET32(M1575_DMACR_REG, M1575_DMACR_PCMISTART); - } else { - CLR32(M1575_DMACR_REG, M1575_DMACR_PCMOPAUSE); - PUT8(M1575_PCMOCR_REG, M1575_PCMOCR_IOCE); - SET32(M1575_DMACR_REG, M1575_DMACR_PCMOSTART); - } -} - -/* - * audio1575_stop_port() - * - * Description: - * This routine stops the DMA engine. - * - * Arguments: - * audio1575_port_t *port Port of DMA engine to stop. - */ -static void -audio1575_stop_port(audio1575_port_t *port) -{ - audio1575_state_t *statep = port->statep; - - ASSERT(mutex_owned(&statep->lock)); - - /* if suspended, then do nothing else */ - if (statep->suspended) { - return; - } - - if (port->num == M1575_REC) { - SET32(M1575_DMACR_REG, M1575_DMACR_PCMIPAUSE); - } else { - SET32(M1575_DMACR_REG, M1575_DMACR_PCMOPAUSE); - } -} - -/* - * audio1575_reset_port() - * - * Description: - * This routine resets the DMA engine pareparing it for work. - * - * Arguments: - * audio1575_port_t *port Port of DMA engine to reset. - */ -static void -audio1575_reset_port(audio1575_port_t *port) -{ - audio1575_state_t *statep = port->statep; - - ASSERT(mutex_owned(&statep->lock)); - - port->civ = 0; - port->picb = 0; - - if (statep->suspended) - return; - - if (port->num == M1575_REC) { - /* Uli FIFO madness ... */ - SET32(M1575_FIFOCR1_REG, M1575_FIFOCR1_PCMIRST); - SET32(M1575_DMACR_REG, M1575_DMACR_PCMIPAUSE); - - PUT8(M1575_PCMICR_REG, 0); - PUT8(M1575_PCMICR_REG, M1575_CR_RR | M1575_CR_IOCE); - - PUT32(M1575_PCMIBDBAR_REG, port->bdl_paddr); - PUT8(M1575_PCMILVIV_REG, M1575_BD_NUMS - 1); - - CLR32(M1575_DMACR_REG, M1575_DMACR_PCMIPAUSE); - - } else { - - uint32_t scr; - - /* Uli FIFO madness ... */ - SET32(M1575_FIFOCR1_REG, M1575_FIFOCR1_PCMORST); - SET32(M1575_DMACR_REG, M1575_DMACR_PCMOPAUSE); - - /* configure the number of channels properly */ - scr = GET32(M1575_SCR_REG); - scr &= ~(M1575_SCR_6CHL_MASK | M1575_SCR_CHAMOD_MASK); - scr |= M1575_SCR_6CHL_2; /* select our proper ordering */ - switch (port->nchan) { - case 2: - scr |= M1575_SCR_CHAMOD_2; - break; - case 4: - scr |= M1575_SCR_CHAMOD_4; - break; - case 6: - scr |= M1575_SCR_CHAMOD_6; - break; - } - PUT32(M1575_SCR_REG, scr); - - PUT8(M1575_PCMOCR_REG, 0); - PUT8(M1575_PCMOCR_REG, M1575_CR_RR | M1575_CR_IOCE); - - PUT32(M1575_PCMOBDBAR_REG, port->bdl_paddr); - PUT8(M1575_PCMOLVIV_REG, M1575_BD_NUMS - 1); - - CLR32(M1575_DMACR_REG, M1575_DMACR_PCMOPAUSE); - } -} - -/* - * audio1575_update_port() - * - * Description: - * This routine updates the ports frame counter from hardware, and - * gracefully handles wraps. - * - * Arguments: - * audio1575_port_t *port The port to update. - */ -static void -audio1575_update_port(audio1575_port_t *port) -{ - audio1575_state_t *statep = port->statep; - uint8_t civ; - uint16_t picb; - unsigned n; - int civoff; - int picoff; - - if (port->num == M1575_REC) { - civoff = M1575_PCMICIV_REG; - picoff = M1575_PCMIPICB_REG; - } else { - civoff = M1575_PCMOCIV_REG; - picoff = M1575_PCMOPICB_REG; - } - - if (statep->suspended) { - civ = 0; - picb = 0; - } else { - /* - * We read the position counters, but we're careful to avoid - * the situation where the position counter resets at the end - * of a buffer. - */ - for (int i = 0; i < 2; i++) { - civ = GET8(civoff); - picb = GET16(picoff); - if (GET8(civoff) == civ) { - /* - * Chip did not start a new index, so - * the picb is valid. - */ - break; - } - } - if (civ >= port->civ) { - n = civ - port->civ; - } else { - n = civ + (M1575_BD_NUMS - port->civ); - } - port->count += (n * port->fragfr); - } - port->civ = civ; - port->picb = picb; -} - -/* * audio1575_attach() * * Description: @@ -945,11 +706,6 @@ goto error; } - if (audio1575_setup_intr(statep) != DDI_SUCCESS) { - /* message already noted */ - goto error; - } - /* Enable PCI I/O and Memory Spaces */ audio1575_pci_enable(statep); @@ -1003,22 +759,6 @@ goto error; } - /* set up kernel statistics */ - if ((statep->ksp = kstat_create(M1575_NAME, - ddi_get_instance(dip), M1575_NAME, "controller", - KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT)) != NULL) { - kstat_install(statep->ksp); - } - - /* Enable PCI Interrupts */ - pci_config_put8(statep->pcih, M1575_PCIMISC_REG, M1575_PCIMISC_INTENB); - - /* enable audio interrupts */ - if (ddi_intr_enable(statep->ih) != DDI_SUCCESS) { - audio_dev_warn(adev, "ddi_intr_enable() failure"); - goto error; - } - /* register with the framework */ if (audio_dev_register(adev) != DDI_SUCCESS) { audio_dev_warn(adev, "unable to register with framework"); @@ -1066,82 +806,6 @@ /* *********************** Local Routines *************************** */ /* - * audio1575_setup_intr() - * - * Description: - * This routine initializes the audio driver's interrupt handle and - * mutex. - * - * Arguments: - * audio1575_state_t *state The device's state structure - * - * Returns: - * DDI_SUCCESS Interrupt handle & mutex initialized - * DDI_FAILURE Interrupt handle & mutex not initialized - */ -int -audio1575_setup_intr(audio1575_state_t *statep) -{ - audio_dev_t *adev; - dev_info_t *dip; - uint_t ipri; - int actual; - int rv; - int itype; - int count; - ddi_intr_handle_t ih = NULL; - - dip = statep->dip; - adev = statep->adev; - - /* get supported interrupt types */ - rv = ddi_intr_get_supported_types(dip, &itype); - if ((rv != DDI_SUCCESS) || (!(itype & DDI_INTR_TYPE_FIXED))) { - audio_dev_warn(adev, "Fixed type interrupts not supported"); - return (DDI_FAILURE); - } - - /* make sure we only have one fixed type interrupt */ - rv = ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_FIXED, &count); - if ((rv != DDI_SUCCESS) || (count != 1)) { - audio_dev_warn(adev, "No fixed interrupts"); - return (DDI_FAILURE); - } - - rv = ddi_intr_alloc(statep->dip, &ih, DDI_INTR_TYPE_FIXED, - 0, 1, &actual, DDI_INTR_ALLOC_STRICT); - if ((rv != DDI_SUCCESS) || (actual != 1)) { - audio_dev_warn(adev, "Can't alloc interrupt handle"); - return (DDI_FAILURE); - } - - /* test for a high level interrupt */ - if (ddi_intr_get_pri(ih, &ipri) != DDI_SUCCESS) { - audio_dev_warn(adev, "Can't get interrupt priority"); - (void) ddi_intr_free(ih); - return (DDI_FAILURE); - } - if (ipri >= ddi_intr_get_hilevel_pri()) { - audio_dev_warn(adev, "Unsupported high level interrupt"); - (void) ddi_intr_free(ih); - return (DDI_FAILURE); - } - - if (ddi_intr_add_handler(ih, audio1575_intr, statep, NULL) != - DDI_SUCCESS) { - audio_dev_warn(adev, "Can't add interrupt handler"); - (void) ddi_intr_free(ih); - return (DDI_FAILURE); - } - - statep->ih = ih; - mutex_init(&statep->lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - mutex_init(&statep->ac_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - - return (DDI_SUCCESS); -} - -/* * audio1575_alloc_port() * * Description: @@ -1165,11 +829,9 @@ uint_t count; int dir; unsigned caps; - char *prop; audio_dev_t *adev; audio1575_port_t *port; uint32_t *kaddr; - uint32_t paddr; int rc; dev_info_t *dip; @@ -1180,45 +842,27 @@ statep->ports[num] = port; port->num = num; port->statep = statep; - port->started = B_FALSE; port->nchan = nchan; if (num == M1575_REC) { - prop = "record-interrupts"; dir = DDI_DMA_READ; caps = ENGINE_INPUT_CAP; port->sync_dir = DDI_DMA_SYNC_FORKERNEL; } else { - prop = "play-interrupts"; dir = DDI_DMA_WRITE; caps = ENGINE_OUTPUT_CAP; port->sync_dir = DDI_DMA_SYNC_FORDEV; } - port->intrs = ddi_prop_get_int(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, prop, M1575_INTS); - - /* make sure the values are good */ - if (port->intrs < M1575_MIN_INTS) { - audio_dev_warn(adev, "%s too low, %d, resetting to %d", - prop, port->intrs, M1575_INTS); - port->intrs = M1575_INTS; - } else if (port->intrs > M1575_MAX_INTS) { - audio_dev_warn(adev, "%s too high, %d, resetting to %d", - prop, port->intrs, M1575_INTS); - port->intrs = M1575_INTS; - } - /* - * Figure out how much space we need. Sample rate is 48kHz, and - * we need to store 32 chunks. (Note that this means that low - * interrupt frequencies will require more RAM. We could probably - * do some cleverness to use a shorter BD list.) + * We use one big sample area. The sample area must be larger + * than about 1.5 framework fragment sizes. (Currently 480 * + * 1.5 = 720 frames.) This is necessary to ensure that we + * don't have to involve an interrupt service routine on our + * own, to keep the last valid index updated reasonably. */ - port->fragfr = 48000 / port->intrs; - port->fragfr = M1575_ROUNDUP(port->fragfr, M1575_MOD_SIZE); - port->samp_size = port->fragfr * port->nchan * 2; - port->samp_size *= M1575_BD_NUMS; + port->nframes = 2048; + port->samp_size = port->nframes * port->nchan * sizeof (int16_t); /* allocate dma handle */ rc = ddi_dma_alloc_handle(dip, &sample_buf_dma_attr, DDI_DMA_SLEEP, @@ -1274,20 +918,17 @@ * Wire up the BD list. We do this *before* binding the BD list * so that we don't have to do an extra ddi_dma_sync. */ - paddr = port->samp_paddr; kaddr = (void *)port->bdl_kaddr; for (int i = 0; i < M1575_BD_NUMS; i++) { /* set base address of buffer */ - ddi_put32(port->bdl_acch, kaddr, paddr); + ddi_put32(port->bdl_acch, kaddr, port->samp_paddr); kaddr++; /* set size in frames, and enable IOC interrupt */ ddi_put32(port->bdl_acch, kaddr, - ((port->fragfr * port->nchan) | (1U << 31))); + ((port->samp_size / sizeof (int16_t)) | (1U << 31))); kaddr++; - - paddr += (port->fragfr * port->nchan * 2); } rc = ddi_dma_addr_bind_handle(port->bdl_dmah, NULL, port->bdl_kaddr, @@ -1319,7 +960,7 @@ * deallocates the DMA handles. * * Arguments: - * audio810_port_t *port The port structure for a DMA engine. + * audio1575_port_t *port The port structure for a DMA engine. */ static void audio1575_free_port(audio1575_port_t *port) @@ -1628,8 +1269,7 @@ /* * clear the interrupt control and status register - * READ/WRITE/READ workaround required - * for buggy hardware + * READ/WRITE/READ workaround required to flush PCI caches */ PUT32(M1575_INTRCR_REG, 0); @@ -1693,10 +1333,7 @@ audio1575_state_t *statep = arg; int i; - mutex_enter(&statep->ac_lock); - if (audio1575_codec_sync(statep) != DDI_SUCCESS) { - mutex_exit(&statep->ac_lock); return; } @@ -1715,8 +1352,6 @@ drv_usecwait(1); } - mutex_exit(&statep->ac_lock); - if (i < M1575_LOOP_CTR) { (void) audio1575_read_ac97(statep, reg); } @@ -1744,9 +1379,7 @@ uint16_t data = 0xffff; int i; - mutex_enter(&statep->ac_lock); if ((audio1575_codec_sync(statep)) != DDI_SUCCESS) { - mutex_exit(&statep->ac_lock); return (data); } @@ -1779,7 +1412,6 @@ } } - mutex_exit(&statep->ac_lock); return (data); } @@ -1860,33 +1492,9 @@ } /* allow ac97 operations again */ - ac97_resume(statep->ac97); - - mutex_enter(&statep->lock); - - ASSERT(statep->suspended); - statep->suspended = B_FALSE; - - for (int i = 0; i < M1575_NUM_PORTS; i++) { - - audio1575_port_t *port = statep->ports[i]; + ac97_reset(statep->ac97); - if (port != NULL) { - /* reset framework DMA engine buffer */ - if (port->engine != NULL) { - audio_engine_reset(port->engine); - } - - /* reset and initialize hardware ports */ - audio1575_reset_port(port); - if (port->started) { - audio1575_start_port(port); - } else { - audio1575_stop_port(port); - } - } - } - mutex_exit(&statep->lock); + audio_dev_resume(adev); return (DDI_SUCCESS); } @@ -1910,18 +1518,7 @@ statep = ddi_get_driver_private(dip); - ac97_suspend(statep->ac97); - - mutex_enter(&statep->lock); - - statep->suspended = B_TRUE; - - /* - * stop all DMA operations - */ - audio1575_dma_stop(statep, B_FALSE); - - mutex_exit(&statep->lock); + audio_dev_suspend(statep->adev); return (DDI_SUCCESS); } @@ -1935,9 +1532,6 @@ * * Arguments: * audio1575_state_t *state The device soft state. - * - * Returns: - * None */ void audio1575_destroy(audio1575_state_t *statep) @@ -1962,18 +1556,6 @@ /* Disable PCI I/O and Memory Spaces */ audio1575_pci_disable(statep); - if (statep->ih != NULL) { - (void) ddi_intr_disable(statep->ih); - (void) ddi_intr_remove_handler(statep->ih); - (void) ddi_intr_free(statep->ih); - mutex_destroy(&statep->lock); - mutex_destroy(&statep->ac_lock); - } - - if (statep->ksp != NULL) { - kstat_delete(statep->ksp); - } - audio1575_free_port(statep->ports[M1575_PLAY]); audio1575_free_port(statep->ports[M1575_REC]);
--- a/usr/src/uts/common/io/audio/drv/audio1575/audio1575.conf Tue Mar 16 09:43:38 2010 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# Configuration file for the audio1575 audio driver. -# -# WARNING: This is an UNSTABLE configuration file. Its contents -# may change at any time. -# - -# -# play-interrupts sets the number of interrupts per second when playing. -# This affects the resolution of various things, such as sample counts. -# record-interrupts does the same for record interrupts. -# -# These may be tuned to get more accurate information by increasing the -# count. However, the larger the interrupts per second the larger the -# load on the system. So use this capability cautiously. The audio1575 -# driver enforces a maximum and minimum count. -# -# It should also be understood that not all interrupt rates are legal. -# The hardware is restricted to DMA buffers being allocated on certain -# boundaries. If those boundaries are violated the driver will not be -# loaded and an error message is entered into the messages log count -# - -play-interrupts=175; -record-interrupts=175;
--- a/usr/src/uts/common/io/audio/drv/audio1575/audio1575.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audio1575/audio1575.h Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -36,10 +36,6 @@ #define M1575_NAME "audio1575" #define M1575_MOD_NAME "M1575 audio driver" -#define M1575_INTS (175) /* default interrupt rate */ -#define M1575_MIN_INTS (25) /* minimum interrupt rate */ -#define M1575_MAX_INTS (5000) /* maximum interrupt rate */ - /* * Implementation specific header file for the audio1575 device driver. */ @@ -59,7 +55,6 @@ #define M1575_MOD_SIZE (16) /* kstat interrupt counter define */ -#define M1575_KIOP(X) ((kstat_intr_t *)(X->ksp->ks_data)) #define M1575_ROUNDUP(x, algn) (((x) + ((algn) - 1)) & ~((algn) - 1)) /* PCI CFG SPACE REGISTERS for Audio (Device 29, Function 0) */ @@ -365,17 +360,13 @@ uint32_t bdl_paddr; int num; - unsigned intrs; - unsigned fragfr; + unsigned nframes; + uint32_t offset; uint64_t count; uint8_t nchan; - uint8_t civ; - uint16_t picb; unsigned sync_dir; - boolean_t started; - audio_engine_t *engine; }; typedef struct audio1575_port audio1575_port_t; @@ -386,22 +377,16 @@ */ struct audio1575_state { kmutex_t lock; /* intr mutex */ - kmutex_t ac_lock; /* ac'97 mutex */ dev_info_t *dip; /* dev instance ptr */ audio_dev_t *adev; /* audio handle */ ac97_t *ac97; /* ac'97 handle */ audio1575_port_t *ports[2]; /* DMA engines */ - ddi_intr_handle_t ih; /* intr handle */ - ddi_acc_handle_t pcih; /* pci config space */ ddi_acc_handle_t regsh; /* audio i/o regs */ caddr_t regsp; /* base of i/o regs */ - kstat_t *ksp; /* kernel statistics */ - - boolean_t suspended; /* if DDI_SUSPENDed */ uint8_t maxch; /* maximum channels */ }; typedef struct audio1575_state audio1575_state_t;
--- a/usr/src/uts/common/io/audio/drv/audio810/audio810.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audio810/audio810.c Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -28,24 +28,20 @@ * audio810 Audio Driver * * The driver is primarily targeted at providing audio support for the - * W1100z and W2100z systems, which use the AMD 8111 audio core and - * the Realtek ALC 655 codec. The ALC 655 chip supports only fixed 48k - * sample rate. However, the audio core of AMD 8111 is completely - * compatible to the Intel ICHx chips (Intel 8x0 chipsets), so the - * driver can work for the ICHx. We only support the 48k maximum - * rate, since we only have a single PCM out channel. + * Intel ICHx family of AC'97 controllers and compatible parts (such + * as those from nVidia and AMD.) * - * The AMD 8111 audio core, as an AC'97 controller, has independent - * channels for PCM in, PCM out, mic in, modem in, and modem out. - * The AC'97 controller is a PCI bus master with scatter/gather - * support. Each channel has a DMA engine. Currently, we use only - * the PCM in and PCM out channels. Each DMA engine uses one buffer - * descriptor list. And the buffer descriptor list is an array of up - * to 32 entries, each of which describes a data buffer. Each entry - * contains a pointer to a data buffer, control bits, and the length - * of the buffer being pointed to, where the length is expressed as - * the number of samples. This, combined with the 16-bit sample size, - * gives the actual physical length of the buffer. + * These audio parts have independent channels for PCM in, PCM out, + * mic in, and sometimes modem in, and modem out. The AC'97 + * controller is a PCI bus master with scatter/gather support. Each + * channel has a DMA engine. Currently, we use only the PCM in and PCM + * out channels. Each DMA engine uses one buffer descriptor list. And + * the buffer descriptor list is an array of up to 32 entries, each of + * which describes a data buffer. Each entry contains a pointer to a + * data buffer, control bits, and the length of the buffer being + * pointed to, where the length is expressed as the number of + * samples. This, combined with the 16-bit sample size, gives the + * actual physical length of the buffer. * * A workaround for the AD1980 and AD1985 codec: * Most vendors connect the surr-out of the codecs to the line-out jack. @@ -60,6 +56,26 @@ * NOTE: * This driver depends on the drv/audio and misc/ac97 * modules being loaded first. + * + * The audio framework guarantees that our entry points are exclusive + * with suspend and resume. This includes data flow and control entry + * points alike. + * + * The audio framework guarantees that only one control is being + * accessed on any given audio device at a time. + * + * The audio framework guarantees that entry points are themselves + * serialized for a given engine. + * + * We have no interrupt routine or other internal asynchronous routines. + * + * Our device uses completely separate registers for each engine, + * except for the start/stop registers, which are implemented in a + * manner that allows for them to be accessed concurrently safely from + * different threads. + * + * Hence, it turns out that we simply don't need any locking in this + * driver. */ #include <sys/types.h> #include <sys/modctl.h> @@ -83,7 +99,7 @@ /* * Entry point routine prototypes */ -static int audio810_open(void *, int, unsigned *, unsigned *, caddr_t *); +static int audio810_open(void *, int, unsigned *, caddr_t *); static void audio810_close(void *); static int audio810_start(void *); static void audio810_stop(void *); @@ -111,11 +127,6 @@ }; /* - * interrupt handler - */ -static uint_t audio810_intr(caddr_t); - -/* * Local Routine Prototypes */ static int audio810_attach(dev_info_t *); @@ -124,10 +135,6 @@ static int audio810_suspend(dev_info_t *); static int audio810_alloc_port(audio810_state_t *, int, uint8_t); -static void audio810_start_port(audio810_port_t *); -static void audio810_stop_port(audio810_port_t *); -static void audio810_reset_port(audio810_port_t *); -static void audio810_update_port(audio810_port_t *); static int audio810_codec_sync(audio810_state_t *); static void audio810_write_ac97(void *, uint8_t, uint16_t); static uint16_t audio810_read_ac97(void *, uint8_t); @@ -135,16 +142,13 @@ static void audio810_unmap_regs(audio810_state_t *); static void audio810_stop_dma(audio810_state_t *); static int audio810_chip_init(audio810_state_t *); +static void audio810_set_channels(audio810_state_t *); static void audio810_destroy(audio810_state_t *); /* * Global variables, but used only by this file. */ -/* driver name, so we don't have to call ddi_driver_name() or hard code strs */ -static char *audio810_name = I810_NAME; - - /* * DDI Structures */ @@ -385,92 +389,6 @@ } /* - * audio810_intr() - * - * Description: - * Interrupt service routine for both play and record. For play we - * get the next buffers worth of audio. For record we send it on to - * the mixer. - * - * Each of buffer descriptor has a field IOC(interrupt on completion) - * When both this and the IOC bit of correspondent dma control register - * is set, it means that the controller should issue an interrupt upon - * completion of this buffer. - * (AMD 8111 hypertransport I/O hub data sheet. 3.8.3 page 71) - * - * Arguments: - * caddr_t arg Pointer to the interrupting device's state - * structure - * - * Returns: - * DDI_INTR_CLAIMED Interrupt claimed and processed - * DDI_INTR_UNCLAIMED Interrupt not claimed, and thus ignored - */ -static uint_t -audio810_intr(caddr_t arg) -{ - audio810_state_t *statep; - uint16_t gsr; - - statep = (void *)arg; - mutex_enter(&statep->inst_lock); - - if (statep->suspended) { - mutex_exit(&statep->inst_lock); - return (DDI_INTR_UNCLAIMED); - } - - gsr = I810_BM_GET32(I810_REG_GSR); - - /* check if device is interrupting */ - if ((gsr & I810_GSR_USE_INTR) == 0) { - mutex_exit(&statep->inst_lock); - return (DDI_INTR_UNCLAIMED); - } - - for (int pnum = 0; pnum < I810_NUM_PORTS; pnum++) { - audio810_port_t *port; - uint8_t regoff, index; - - port = statep->ports[pnum]; - if (port == NULL) { - continue; - } - regoff = port->regoff; - - if (!(I810_BM_GET8(port->stsoff) & I810_BM_SR_BCIS)) - continue; - - /* update the LVI -- we just set it to the current value - 1 */ - index = I810_BM_GET8(regoff + I810_OFFSET_CIV); - index = (index - 1) % I810_BD_NUMS; - - I810_BM_PUT8(regoff + I810_OFFSET_LVI, index); - - /* clear any interrupt */ - I810_BM_PUT8(port->stsoff, - I810_BM_SR_LVBCI | I810_BM_SR_BCIS | I810_BM_SR_FIFOE); - } - - /* update the kernel interrupt statistics */ - if (statep->ksp) { - I810_KIOP(statep)->intrs[KSTAT_INTR_HARD]++; - } - - mutex_exit(&statep->inst_lock); - - /* notify the framework */ - if (gsr & I810_GSR_INTR_PIN) { - audio_engine_produce(statep->ports[I810_PCM_IN]->engine); - } - if (gsr & I810_GSR_INTR_POUT) { - audio_engine_consume(statep->ports[I810_PCM_OUT]->engine); - } - - return (DDI_INTR_CLAIMED); -} - -/* * audio810_open() * * Description: @@ -479,8 +397,7 @@ * Arguments: * void *arg The DMA engine to set up * int flag Open flags - * unsigned *fragfrp Receives number of frames per fragment - * unsigned *nfragsp Receives number of fragments + * unsigned *nframes Receives total number of frames * caddr_t *bufp Receives kernel data buffer * * Returns: @@ -488,23 +405,16 @@ * errno on failure */ static int -audio810_open(void *arg, int flag, unsigned *fragfrp, unsigned *nfragsp, - caddr_t *bufp) +audio810_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp) { audio810_port_t *port = arg; _NOTE(ARGUNUSED(flag)); - port->started = B_FALSE; port->count = 0; - *fragfrp = port->fragfr; - *nfragsp = port->nfrag; + *nframes = port->samp_frames; *bufp = port->samp_kaddr; - mutex_enter(&port->statep->inst_lock); - audio810_reset_port(port); - mutex_exit(&port->statep->inst_lock); - return (0); } @@ -522,13 +432,7 @@ static void audio810_close(void *arg) { - audio810_port_t *port = arg; - audio810_state_t *statep = port->statep; - - mutex_enter(&statep->inst_lock); - audio810_stop_port(port); - port->started = B_FALSE; - mutex_exit(&statep->inst_lock); + _NOTE(ARGUNUSED(arg)); } /* @@ -546,13 +450,11 @@ { audio810_port_t *port = arg; audio810_state_t *statep = port->statep; + uint8_t cr; - mutex_enter(&statep->inst_lock); - if (port->started) { - audio810_stop_port(port); - } - port->started = B_FALSE; - mutex_exit(&statep->inst_lock); + cr = I810_BM_GET8(port->regoff + I810_OFFSET_CR); + cr &= ~I810_BM_CR_RUN; + I810_BM_PUT8(port->regoff + I810_OFFSET_CR, cr); } /* @@ -572,13 +474,41 @@ { audio810_port_t *port = arg; audio810_state_t *statep = port->statep; + uint8_t regoff, cr; - mutex_enter(&statep->inst_lock); - if (!port->started) { - audio810_start_port(port); - port->started = B_TRUE; + regoff = port->regoff; + port->offset = 0; + + /* program multiple channel settings */ + if (port->num == I810_PCM_OUT) { + audio810_set_channels(statep); + + if (statep->quirk == QUIRK_SIS7012) { + /* + * SiS 7012 has special unmute bit. + */ + I810_BM_PUT8(I810_REG_SISCTL, I810_SISCTL_UNMUTE); + } } - mutex_exit(&statep->inst_lock); + + /* + * Perform full reset of the engine, but leave it turned off. + */ + I810_BM_PUT8(regoff + I810_OFFSET_CR, 0); + I810_BM_PUT8(regoff + I810_OFFSET_CR, I810_BM_CR_RST); + + /* program the offset of the BD list */ + I810_BM_PUT32(regoff + I810_OFFSET_BD_BASE, port->bdl_paddr); + + /* we set the last index to the full count -- all buffers are valid */ + I810_BM_PUT8(regoff + I810_OFFSET_LVI, I810_BD_NUMS - 1); + + cr = I810_BM_GET8(regoff + I810_OFFSET_CR); + cr |= I810_BM_CR_RUN; + I810_BM_PUT8(regoff + I810_OFFSET_CR, cr); + + (void) I810_BM_GET8(regoff + I810_OFFSET_CR); + return (0); } @@ -660,23 +590,35 @@ { audio810_port_t *port = arg; audio810_state_t *statep = port->statep; + uint8_t regoff = port->regoff; uint64_t val; - uint16_t picb; - uint64_t count; - uint8_t nchan; + uint32_t offset; + uint8_t civ; + + /* + * Read the position counters. We also take this opportunity + * to update the last valid index to the one just previous to + * the one we're working on (so we'll fully loop.) + */ + offset = I810_BM_GET16(port->picboff); + civ = I810_BM_GET8(regoff + I810_OFFSET_CIV); + I810_BM_PUT8(port->regoff + I810_OFFSET_LVI, (civ - 1) % I810_BD_NUMS); - mutex_enter(&statep->inst_lock); - audio810_update_port(port); - count = port->count; - picb = port->picb; - nchan = port->nchan; - mutex_exit(&statep->inst_lock); + /* SiS counts in bytes, all others in words. */ + if (statep->quirk != QUIRK_SIS7012) + offset *= 2; + + /* counter is reversed */ + offset = port->samp_size - offset; - if (statep->quirk == QUIRK_SIS7012) { - val = count + picb / (2 * nchan); + if (offset < port->offset) { + val = (port->samp_size - port->offset) + offset; } else { - val = count + (picb / nchan); + val = offset - port->offset; } + port->offset = offset; + port->count += (val / (port->nchan * 2)); + val = port->count; return (val); } @@ -712,14 +654,16 @@ * void *arg The DMA engine to query * * Returns: - * Play ahead in frames (4 fragments). + * Play ahead in frames. */ static unsigned audio810_playahead(void *arg) { audio810_port_t *port = arg; + audio810_state_t *statep = port->statep; - return (4 * port->fragfr); + /* Older ICH is likely to be emulated, deeper (40 ms) playahead */ + return (statep->quirk == QUIRK_OLDICH ? 1920 : 0); } @@ -727,210 +671,6 @@ /* *********************** Local Routines *************************** */ /* - * audio810_start_port() - * - * Description: - * This routine starts the DMA engine. - * - * Arguments: - * audio810_port_t *port Port of DMA engine to start. - */ -static void -audio810_start_port(audio810_port_t *port) -{ - audio810_state_t *statep = port->statep; - uint8_t cr; - - ASSERT(mutex_owned(&statep->inst_lock)); - - /* if suspended, then do nothing else */ - if (statep->suspended) { - return; - } - - cr = I810_BM_GET8(port->regoff + I810_OFFSET_CR); - cr |= I810_BM_CR_IOCE; - I810_BM_PUT8(port->regoff + I810_OFFSET_CR, cr); - cr |= I810_BM_CR_RUN; - I810_BM_PUT8(port->regoff + I810_OFFSET_CR, cr); -} - -/* - * audio810_stop_port() - * - * Description: - * This routine stops the DMA engine. - * - * Arguments: - * audio810_port_t *port Port of DMA engine to stop. - */ -static void -audio810_stop_port(audio810_port_t *port) -{ - audio810_state_t *statep = port->statep; - uint8_t cr; - - ASSERT(mutex_owned(&statep->inst_lock)); - - /* if suspended, then do nothing else */ - if (statep->suspended) { - return; - } - - cr = I810_BM_GET8(port->regoff + I810_OFFSET_CR); - cr &= ~I810_BM_CR_RUN; - I810_BM_PUT8(port->regoff + I810_OFFSET_CR, cr); -} - -/* - * audio810_reset_port() - * - * Description: - * This routine resets the DMA engine pareparing it for work. - * - * Arguments: - * audio810_port_t *port Port of DMA engine to reset. - */ -static void -audio810_reset_port(audio810_port_t *port) -{ - audio810_state_t *statep = port->statep; - uint32_t gcr; - - ASSERT(mutex_owned(&statep->inst_lock)); - - port->civ = 0; - port->picb = 0; - - if (statep->suspended) - return; - - /* - * Make sure we put once in stereo, to ensure we always start from - * front left. - */ - if (port->num == I810_PCM_OUT) { - - if (statep->quirk == QUIRK_SIS7012) { - /* - * SiS 7012 needs its own special multichannel config. - */ - gcr = I810_BM_GET32(I810_REG_GCR); - gcr &= ~I810_GCR_SIS_CHANNELS_MASK; - I810_BM_PUT32(I810_REG_GCR, gcr); - delay(drv_usectohz(50000)); /* 50 msec */ - - switch (statep->maxch) { - case 2: - gcr |= I810_GCR_SIS_2_CHANNELS; - break; - case 4: - gcr |= I810_GCR_SIS_4_CHANNELS; - break; - case 6: - gcr |= I810_GCR_SIS_6_CHANNELS; - break; - } - I810_BM_PUT32(I810_REG_GCR, gcr); - delay(drv_usectohz(50000)); /* 50 msec */ - - /* - * SiS 7012 has special unmute bit. - */ - I810_BM_PUT8(I810_REG_SISCTL, I810_SISCTL_UNMUTE); - - } else { - - /* - * All other devices work the same. - */ - gcr = I810_BM_GET32(I810_REG_GCR); - gcr &= ~I810_GCR_CHANNELS_MASK; - - I810_BM_PUT32(I810_REG_GCR, gcr); - delay(drv_usectohz(50000)); /* 50 msec */ - - switch (statep->maxch) { - case 2: - gcr |= I810_GCR_2_CHANNELS; - break; - case 4: - gcr |= I810_GCR_4_CHANNELS; - break; - case 6: - gcr |= I810_GCR_6_CHANNELS; - break; - } - I810_BM_PUT32(I810_REG_GCR, gcr); - delay(drv_usectohz(50000)); /* 50 msec */ - } - } - - /* - * Perform full reset of the engine, but leave it turned off. - */ - I810_BM_PUT8(port->regoff + I810_OFFSET_CR, 0); - I810_BM_PUT8(port->regoff + I810_OFFSET_CR, I810_BM_CR_RST); - - /* program the offset of the BD list */ - I810_BM_PUT32(port->regoff + I810_OFFSET_BD_BASE, port->bdl_paddr); - - /* we set the last index to the full count -- all buffers are valid */ - I810_BM_PUT8(port->regoff + I810_OFFSET_LVI, I810_BD_NUMS - 1); -} - -/* - * audio810_update_port() - * - * Description: - * This routine updates the ports frame counter from hardware, and - * gracefully handles wraps. - * - * Arguments: - * audio810_port_t *port The port to update. - */ -static void -audio810_update_port(audio810_port_t *port) -{ - audio810_state_t *statep = port->statep; - uint8_t regoff = port->regoff; - uint8_t civ; - uint16_t picb; - unsigned n; - - if (statep->suspended) { - civ = 0; - picb = 0; - } else { - /* - * We read the position counters, but we're careful to avoid - * the situation where the position counter resets at the end - * of a buffer. - */ - for (int i = 0; i < 2; i++) { - civ = I810_BM_GET8(regoff + I810_OFFSET_CIV); - picb = I810_BM_GET16(port->picboff); - if (I810_BM_GET8(regoff + I810_OFFSET_CIV) == civ) { - /* - * Chip did not start a new index, - * so the picb is valid. - */ - break; - } - } - - if (civ >= port->civ) { - n = civ - port->civ; - } else { - n = civ + (I810_BD_NUMS - port->civ); - } - port->count += (n * port->fragfr); - } - port->civ = civ; - port->picb = picb; -} - -/* * audio810_attach() * * Description: @@ -959,30 +699,13 @@ uint8_t nch; int maxch; - /* we don't support high level interrupts in the driver */ - if (ddi_intr_hilevel(dip, 0) != 0) { - cmn_err(CE_WARN, "!%s: unsupported high level interrupt", - audio810_name); - return (DDI_FAILURE); - } - /* allocate the soft state structure */ statep = kmem_zalloc(sizeof (*statep), KM_SLEEP); ddi_set_driver_private(dip, statep); - /* get iblock cookie information */ - if (ddi_get_iblock_cookie(dip, 0, &statep->iblock) != DDI_SUCCESS) { - cmn_err(CE_WARN, "!%s: cannot get iblock cookie", - audio810_name); - kmem_free(statep, sizeof (*statep)); - return (DDI_FAILURE); - } - mutex_init(&statep->inst_lock, NULL, MUTEX_DRIVER, statep->iblock); - mutex_init(&statep->ac_lock, NULL, MUTEX_DRIVER, statep->iblock); - if ((adev = audio_dev_alloc(dip, 0)) == NULL) { - cmn_err(CE_WARN, "!%s: unable to allocate audio dev", - audio810_name); + cmn_err(CE_WARN, "!%s%d: unable to allocate audio dev", + ddi_driver_name(dip), ddi_get_instance(dip)); goto error; } statep->adev = adev; @@ -1015,6 +738,7 @@ case 0x80862415: name = "Intel AC'97"; vers = "ICH"; + statep->quirk = QUIRK_OLDICH; break; case 0x80862425: name = "Intel AC'97"; @@ -1160,21 +884,6 @@ goto error; } - /* set up kernel statistics */ - if ((statep->ksp = kstat_create(I810_NAME, ddi_get_instance(dip), - I810_NAME, "controller", KSTAT_TYPE_INTR, 1, - KSTAT_FLAG_PERSISTENT)) != NULL) { - kstat_install(statep->ksp); - } - - /* set up the interrupt handler */ - if (ddi_add_intr(dip, 0, &statep->iblock, - NULL, audio810_intr, (caddr_t)statep) != DDI_SUCCESS) { - audio_dev_warn(adev, "bad interrupt specification"); - goto error; - } - statep->intr_added = B_TRUE; - if (audio_dev_register(adev) != DDI_SUCCESS) { audio_dev_warn(adev, "unable to register with framework"); goto error; @@ -1221,43 +930,33 @@ /* Restore the audio810 chip's state */ if (audio810_chip_init(statep) != DDI_SUCCESS) { /* - * Note that PM gurus say we should return - * success here. Failure of audio shouldn't - * be considered FATAL to the system. The - * upshot is that audio will not progress. + * Note that PM gurus say we should return success + * here. Failure of audio shouldn't be considered + * FATAL to the system. + * + * It turns out that the only way that the + * audio810_chip_init fails is that the codec won't + * re-initialize. Audio streams may or may not make + * progress; setting changes may or may not have the + * desired effect. What we'd really to do at this + * point is use FMA to offline the part. In the + * meantime, we just muddle on logging the error. + * + * Note that returning from this routine without + * allowing the audio_dev_resume() to take place can + * have bad effects, as the framework does not know + * what to do in the event of a failure of this + * nature. (It may be unsafe to call ENG_CLOSE(), for + * example.) */ - audio_dev_warn(adev, "DDI_RESUME failed to init chip"); - return (DDI_SUCCESS); + audio_dev_warn(adev, "failure to resume codec"); } - /* allow ac97 operations again */ - ac97_resume(statep->ac97); - - mutex_enter(&statep->inst_lock); - - ASSERT(statep->suspended); - statep->suspended = B_FALSE; - - for (int i = 0; i < I810_NUM_PORTS; i++) { - - audio810_port_t *port = statep->ports[i]; + /* Reset the AC'97 codec. */ + ac97_reset(statep->ac97); - if (port != NULL) { - /* reset framework DMA engine buffer */ - if (port->engine != NULL) { - audio_engine_reset(port->engine); - } - - /* reset and initialize hardware ports */ - audio810_reset_port(port); - if (port->started) { - audio810_start_port(port); - } else { - audio810_stop_port(port); - } - } - } - mutex_exit(&statep->inst_lock); + /* And let the framework know we're ready for business again. */ + audio_dev_resume(statep->adev); return (DDI_SUCCESS); } @@ -1313,19 +1012,11 @@ statep = ddi_get_driver_private(dip); ASSERT(statep != NULL); - ac97_suspend(statep->ac97); - - mutex_enter(&statep->inst_lock); - - ASSERT(statep->suspended == B_FALSE); + audio_dev_suspend(statep->adev); - statep->suspended = B_TRUE; /* stop new ops */ - - /* stop DMA engines */ + /* stop DMA engines - should be redundant (paranoia) */ audio810_stop_dma(statep); - mutex_exit(&statep->inst_lock); - return (DDI_SUCCESS); } @@ -1351,11 +1042,8 @@ uint_t count; int dir; unsigned caps; - char *prop; - char *nfprop; audio_dev_t *adev; audio810_port_t *port; - uint32_t paddr; int rc; dev_info_t *dip; i810_bd_entry_t *bdentry; @@ -1366,22 +1054,17 @@ port = kmem_zalloc(sizeof (*port), KM_SLEEP); statep->ports[num] = port; port->statep = statep; - port->started = B_FALSE; port->nchan = nchan; port->num = num; switch (num) { case I810_PCM_IN: - prop = "record-interrupts"; - nfprop = "record-fragments"; dir = DDI_DMA_READ; caps = ENGINE_INPUT_CAP; port->sync_dir = DDI_DMA_SYNC_FORKERNEL; port->regoff = I810_BASE_PCM_IN; break; case I810_PCM_OUT: - prop = "play-interrupts"; - nfprop = "play-fragments"; dir = DDI_DMA_WRITE; caps = ENGINE_OUTPUT_CAP; port->sync_dir = DDI_DMA_SYNC_FORDEV; @@ -1403,45 +1086,15 @@ port->picboff = port->regoff + I810_OFFSET_PICB; } - port->intrs = ddi_prop_get_int(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, prop, I810_INTS); - - /* make sure the values are good */ - if (port->intrs < I810_MIN_INTS) { - audio_dev_warn(adev, "%s too low, %d, resetting to %d", - prop, port->intrs, I810_INTS); - port->intrs = I810_INTS; - } else if (port->intrs > I810_MAX_INTS) { - audio_dev_warn(adev, "%s too high, %d, resetting to %d", - prop, port->intrs, I810_INTS); - port->intrs = I810_INTS; - } - - port->nfrag = ddi_prop_get_int(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, nfprop, I810_NFRAGS); - /* - * Note that fragments must divide evenly into I810_BD_NUMS (32). - * We also insist that the value be larger than our "playahead". + * We use one big sample area. The sample area must be larger + * than about 1.5 framework fragment sizes. (Currently 480 * + * 1.5 = 720 frames.) This is necessary to ensure that we + * don't have to involve an interrupt service routine on our + * own, to keep the last valid index updated reasonably. */ - if (port->nfrag <= 8) { - port->nfrag = 8; - } else if (port->nfrag <= 16) { - port->nfrag = 16; - } else { - port->nfrag = I810_BD_NUMS; - } - - /* - * Figure out how much space we need. Sample rate is 48kHz, and - * we need to store 32 chunks. (Note that this means that low - * interrupt frequencies will require more RAM. We could probably - * do some cleverness to use a shorter BD list.) - */ - port->fragfr = 48000 / port->intrs; - port->fragfr = I810_ROUNDUP(port->fragfr, I810_MOD_SIZE); - port->fragsz = port->fragfr * port->nchan * 2; - port->samp_size = port->fragsz * port->nfrag; + port->samp_frames = 4096; + port->samp_size = port->samp_frames * port->nchan * sizeof (int16_t); /* allocate dma handle */ rc = ddi_dma_alloc_handle(dip, &sample_buf_dma_attr, DDI_DMA_SLEEP, @@ -1506,26 +1159,18 @@ /* * Wire up the BD list. */ - paddr = port->samp_paddr; bdentry = (void *)port->bdl_kaddr; for (int i = 0; i < I810_BD_NUMS; i++) { /* set base address of buffer */ - ddi_put32(port->bdl_acch, &bdentry->buf_base, paddr); - /* - * SiS 7012 counts samples in bytes, all other count - * in words. - */ + ddi_put32(port->bdl_acch, &bdentry->buf_base, + port->samp_paddr); + /* SiS 7012 counts in bytes, all others in words */ ddi_put16(port->bdl_acch, &bdentry->buf_len, - statep->quirk == QUIRK_SIS7012 ? port->fragsz : - port->fragsz / 2); - ddi_put16(port->bdl_acch, &bdentry->buf_cmd, - BUF_CMD_IOC | BUF_CMD_BUP); - paddr += port->fragsz; - if ((i % port->nfrag) == (port->nfrag - 1)) { - /* handle wrap */ - paddr = port->samp_paddr; - } + statep->quirk == QUIRK_SIS7012 ? port->samp_size : + port->samp_size / 2); + ddi_put16(port->bdl_acch, &bdentry->buf_cmd, BUF_CMD_BUP); + bdentry++; } (void) ddi_dma_sync(port->bdl_dmah, 0, 0, DDI_DMA_SYNC_FORDEV); @@ -1742,8 +1387,7 @@ * audio810_chip_init() * * Description: - * This routine initializes the AMD 8111 audio controller. - * codec. + * This routine initializes the audio controller. * * Arguments: * audio810_state_t *state The device's state structure @@ -1828,6 +1472,72 @@ } /* + * audio810_set_channels() + * + * Description: + * This routine initializes the multichannel configuration. + * + * Arguments: + * audio810_state_t *state The device's state structure + */ +static void +audio810_set_channels(audio810_state_t *statep) +{ + uint32_t gcr; + + /* + * Configure multi-channel. + */ + if (statep->quirk == QUIRK_SIS7012) { + /* + * SiS 7012 needs its own special multichannel config. + */ + gcr = I810_BM_GET32(I810_REG_GCR); + gcr &= ~I810_GCR_SIS_CHANNELS_MASK; + I810_BM_PUT32(I810_REG_GCR, gcr); + delay(drv_usectohz(50000)); /* 50 msec */ + + switch (statep->maxch) { + case 2: + gcr |= I810_GCR_SIS_2_CHANNELS; + break; + case 4: + gcr |= I810_GCR_SIS_4_CHANNELS; + break; + case 6: + gcr |= I810_GCR_SIS_6_CHANNELS; + break; + } + I810_BM_PUT32(I810_REG_GCR, gcr); + delay(drv_usectohz(50000)); /* 50 msec */ + } else { + + /* + * All other devices work the same. + */ + gcr = I810_BM_GET32(I810_REG_GCR); + gcr &= ~I810_GCR_CHANNELS_MASK; + + I810_BM_PUT32(I810_REG_GCR, gcr); + delay(drv_usectohz(50000)); /* 50 msec */ + + switch (statep->maxch) { + case 2: + gcr |= I810_GCR_2_CHANNELS; + break; + case 4: + gcr |= I810_GCR_4_CHANNELS; + break; + case 6: + gcr |= I810_GCR_6_CHANNELS; + break; + } + I810_BM_PUT32(I810_REG_GCR, gcr); + delay(drv_usectohz(50000)); /* 50 msec */ + } +} + +/* * audio810_stop_dma() * * Description: @@ -1901,11 +1611,9 @@ { audio810_state_t *statep = arg; - mutex_enter(&statep->ac_lock); if (audio810_codec_sync(statep) == DDI_SUCCESS) { I810_AM_PUT16(reg, data); } - mutex_exit(&statep->ac_lock); (void) audio810_read_ac97(statep, reg); } @@ -1929,11 +1637,9 @@ audio810_state_t *statep = arg; uint16_t val = 0xffff; - mutex_enter(&statep->ac_lock); if (audio810_codec_sync(statep) == DDI_SUCCESS) { val = I810_AM_GET16(reg); } - mutex_exit(&statep->ac_lock); return (val); } @@ -1953,14 +1659,6 @@ /* stop DMA engines */ audio810_stop_dma(statep); - if (statep->intr_added) { - ddi_remove_intr(statep->dip, 0, NULL); - } - - if (statep->ksp) { - kstat_delete(statep->ksp); - } - for (int i = 0; i < I810_NUM_PORTS; i++) { audio810_free_port(statep->ports[i]); } @@ -1973,7 +1671,5 @@ if (statep->adev) audio_dev_free(statep->adev); - mutex_destroy(&statep->inst_lock); - mutex_destroy(&statep->ac_lock); kmem_free(statep, sizeof (*statep)); }
--- a/usr/src/uts/common/io/audio/drv/audio810/audio810.conf Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audio810/audio810.conf Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Configuration file for the audio810 audio driver. @@ -28,24 +28,6 @@ # may change at any time. # -# play-interrupts sets the number of interrupts per second when playing. -# This affects the resolution of various things, such as sample counts. -# record-interrupts does the same for record interrupts. -# -# These may be tuned to get more accurate information by increasing the -# count. However, the larger the interrupts per second the larger the -# load on the system. So use this capability cautiously. The audio810 -# driver enforces a maximum and minimum count. -# -# It should also be understood that not all interrupt rates are legal. -# The hardware is restricted to DMA buffers being allocated on certain -# boundaries. If those boundaries are violated the driver will not be -# loaded and an error message is entered into the messages log -# -# play-interrupts=120; -# record-interrupts=120; - -# # The presence of the ac97-speaker property enables the use of a monoaural # output, normally intended for use with a speaker phone. Most systems # do not connect this to anything. The value of the property indicates
--- a/usr/src/uts/common/io/audio/drv/audio810/audio810.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audio810/audio810.h Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -36,11 +36,6 @@ #define I810_NAME "audio810" #define I810_MOD_NAME "audio810 audio driver" -#define I810_INTS (120) /* default interrupt rate */ -#define I810_MIN_INTS (24) /* minimum interrupt rate */ -#define I810_MAX_INTS (500) /* maximum interrupt rate */ -#define I810_NFRAGS (8) /* default # fragments */ - /* * Misc. defines */ @@ -50,7 +45,6 @@ #define I810_MOD_SIZE (16) #define I810_ROUNDUP(x, algn) (((x) + ((algn) - 1)) & ~((algn) - 1)) -#define I810_KIOP(X) ((kstat_intr_t *)(X->ksp->ks_data)) /* The size of each entry of "reg" property is 5 integers */ #define I810_INTS_PER_REG_PROP 5 @@ -156,6 +150,7 @@ int num; ddi_dma_handle_t samp_dmah; ddi_acc_handle_t samp_acch; + uint32_t samp_frames; size_t samp_size; caddr_t samp_kaddr; uint32_t samp_paddr; @@ -166,22 +161,15 @@ caddr_t bdl_kaddr; uint32_t bdl_paddr; - unsigned intrs; - unsigned fragfr; - unsigned fragsz; + uint32_t offset; uint64_t count; - uint8_t nfrag; uint8_t nchan; uint8_t regoff; uint8_t stsoff; /* status offset */ uint8_t picboff; /* picb offset */ - uint8_t civ; - uint16_t picb; unsigned sync_dir; - boolean_t started; - audio_engine_t *engine; }; typedef struct audio810_port audio810_port_t; @@ -200,6 +188,7 @@ typedef enum i810_quirk { QUIRK_NONE = 0, + QUIRK_OLDICH, /* likely emulated, needs deeper playahead */ QUIRK_SIS7012, /* weird registers and such */ } i810_quirk_t; @@ -207,9 +196,6 @@ * audio810_state_t -per instance state and operation data */ struct audio810_state { - kmutex_t inst_lock; /* state protection lock */ - kmutex_t ac_lock; - ddi_iblock_cookie_t iblock; dev_info_t *dip; /* used by audio810_getinfo() */ audio_dev_t *adev; ac97_t *ac97; @@ -222,8 +208,6 @@ kstat_t *ksp; /* kernel statistics */ - boolean_t intr_added; - boolean_t suspended; /* suspend/resume state */ uint8_t maxch; i810_quirk_t quirk; };
--- a/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.c Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* @@ -89,66 +89,39 @@ 0 /* dma_attr_flags */ }; -static uint_t -cmpci_intr(caddr_t arg1, caddr_t arg2) -{ - cmpci_dev_t *dev = (void *)arg1; - uint32_t intstat, intctrl, intclear; - void (*cb0)(audio_engine_t *) = NULL; - void (*cb1)(audio_engine_t *) = NULL; - uint_t rv; +static int +cmpci_open(void *arg, int flag, uint_t *nframesp, caddr_t *bufp) +{ + cmpci_port_t *port = arg; + cmpci_dev_t *dev = port->dev; - _NOTE(ARGUNUSED(arg2)); - - rv = DDI_INTR_UNCLAIMED; + _NOTE(ARGUNUSED(flag)); mutex_enter(&dev->mutex); - if (dev->suspended) { - mutex_exit(&dev->mutex); - return (rv); - } - intclear = 0; - intstat = GET32(dev, REG_INTSTAT); - intctrl = GET32(dev, REG_INTCTRL); - if ((intstat & INTSTAT_CH0_INT) && (intctrl & INTCTRL_CH0_EN)) { - intclear |= INTCTRL_CH0_EN; - cb0 = dev->port[0].callb; - } - if ((intstat & INTSTAT_CH1_INT) && (intctrl & INTCTRL_CH1_EN)) { - intclear |= INTCTRL_CH1_EN; - cb1 = dev->port[1].callb; - } + *nframesp = port->nframes; + *bufp = port->kaddr; - /* toggle the bits that we are going to handle */ - if (intclear) { - CLR32(dev, REG_INTCTRL, intclear); - SET32(dev, REG_INTCTRL, intclear); - rv = DDI_INTR_CLAIMED; - - KSINTR(dev)->intrs[KSTAT_INTR_HARD]++; - } - + port->count = 0; mutex_exit(&dev->mutex); - if (cb0) { - (*cb0)(dev->port[0].engine); - } - if (cb1) { - (*cb1)(dev->port[1].engine); - } - - return (rv); + return (0); } static void -cmpci_reset_port(cmpci_port_t *port) +cmpci_close(void *arg) { - cmpci_dev_t *dev = port->dev; + _NOTE(ARGUNUSED(arg)); +} - if (dev->suspended) - return; +static int +cmpci_start(void *arg) +{ + cmpci_port_t *port = arg; + cmpci_dev_t *dev = port->dev; + + mutex_enter(&dev->mutex); port->offset = 0; @@ -222,7 +195,7 @@ PUT32(dev, port->reg_paddr, port->paddr); PUT16(dev, port->reg_bufsz, (port->bufsz / 4) - 1); - PUT16(dev, port->reg_fragsz, (port->fragfr * port->nchan / 2) - 1); + PUT16(dev, port->reg_fragsz, (port->bufsz / 4) - 1); /* Analog output */ if (port->capture) { @@ -231,77 +204,33 @@ } else { CLR32(dev, REG_FUNCTRL0, port->fc0_rec_bit); } -} - -static void -cmpci_start_port(cmpci_port_t *port) -{ - cmpci_dev_t *dev = port->dev; - - if (dev->suspended) - return; SET32(dev, REG_FUNCTRL0, port->fc0_en_bit); - SET32(dev, REG_INTCTRL, port->int_en_bit); -} - -static void -cmpci_stop_port(cmpci_port_t *port) -{ - cmpci_dev_t *dev = port->dev; - - if (dev->suspended) - return; - - CLR32(dev, REG_FUNCTRL0, port->fc0_en_bit); - CLR32(dev, REG_INTCTRL, port->int_en_bit); -} + mutex_exit(&dev->mutex); -static int -cmpci_open(void *arg, int flag, uint_t *fragfrp, uint_t *nfp, caddr_t *bufp) -{ - cmpci_port_t *port = arg; - cmpci_dev_t *dev = port->dev; - - _NOTE(ARGUNUSED(flag)); - - mutex_enter(&dev->mutex); - - *fragfrp = port->fragfr; - *nfp = port->nfrags; - *bufp = port->kaddr; - - port->count = 0; - port->open = B_TRUE; - - cmpci_reset_port(port); - cmpci_start_port(port); - - mutex_exit(&dev->mutex); return (0); } static void -cmpci_close(void *arg) +cmpci_stop(void *arg) { - cmpci_port_t *port = arg; - cmpci_dev_t *dev = port->dev; + cmpci_port_t *port = arg; + cmpci_dev_t *dev = port->dev; mutex_enter(&dev->mutex); - port->open = B_FALSE; - cmpci_stop_port(port); + CLR32(dev, REG_FUNCTRL0, port->fc0_en_bit); mutex_exit(&dev->mutex); } -static void -cmpci_update_port(cmpci_port_t *port) +static uint64_t +cmpci_count(void *arg) { + cmpci_port_t *port = arg; cmpci_dev_t *dev = port->dev; - uint32_t count; + uint64_t count; uint32_t offset; - if ((dev->suspended) || (!port->open)) - return; + mutex_enter(&dev->mutex); /* this gives us the offset in dwords */ offset = (port->bufsz / 4) - (GET16(dev, port->reg_bufsz) + 1); @@ -314,19 +243,6 @@ } port->count += count; port->offset = offset; -} - -static uint64_t -cmpci_count(void *arg) -{ - cmpci_port_t *port = arg; - cmpci_dev_t *dev = port->dev; - uint64_t count; - - mutex_enter(&dev->mutex); - cmpci_update_port(port); - - /* the count is in dwords */ count = port->count; mutex_exit(&dev->mutex); @@ -338,41 +254,6 @@ return (count / (port->nchan / 2)); } - -static int -cmpci_setup_interrupts(cmpci_dev_t *dev) -{ - int actual; - uint_t ipri; - - if ((ddi_intr_alloc(dev->dip, &dev->ihandle, DDI_INTR_TYPE_FIXED, - 0, 1, &actual, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) || - (actual != 1)) { - audio_dev_warn(dev->adev, "can't alloc intr handle"); - return (DDI_FAILURE); - } - - if (ddi_intr_get_pri(dev->ihandle, &ipri) != DDI_SUCCESS) { - audio_dev_warn(dev->adev, "can't determine intr priority"); - (void) ddi_intr_free(dev->ihandle); - dev->ihandle = NULL; - return (DDI_FAILURE); - } - - if (ddi_intr_add_handler(dev->ihandle, cmpci_intr, dev, - NULL) != DDI_SUCCESS) { - audio_dev_warn(dev->adev, "can't add intr handler"); - (void) ddi_intr_free(dev->ihandle); - dev->ihandle = NULL; - return (DDI_FAILURE); - } - - mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - - return (DDI_SUCCESS); -} - - #define MASK(nbits) ((1 << (nbits)) - 1) #define SCALE(val, nbits) \ ((uint8_t)((((val) * MASK(nbits)) / 100)) << (8 - (nbits))) @@ -405,9 +286,6 @@ uint64_t recsrcs; uint64_t monsrcs; - if (dev->suspended) - return; - /* reset all mix values */ outmix = inmix[0] = inmix[1] = 0; @@ -814,8 +692,8 @@ AUDIO_ENGINE_VERSION, /* version number */ cmpci_open, cmpci_close, - NULL, /* start */ - NULL, /* stop */ + cmpci_start, + cmpci_stop, cmpci_count, cmpci_format, cmpci_channels, @@ -831,13 +709,6 @@ { audio_dev_t *adev = dev->adev; int playch; - int intrs; - - dev->pintrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip, - DDI_PROP_DONTPASS, "play-interrupts", DEFINTS); - - dev->rintrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip, - DDI_PROP_DONTPASS, "record-interrupts", DEFINTS); playch = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip, DDI_PROP_DONTPASS, "channels", dev->maxch); @@ -877,68 +748,44 @@ case 0: caps = ENGINE_INPUT_CAP; dmaflags = DDI_DMA_READ | DDI_DMA_CONSISTENT; - port->callb = audio_engine_produce; port->reg_paddr = REG_CH0_PADDR; port->reg_bufsz = REG_CH0_BUFSZ; port->reg_fragsz = REG_CH0_FRAGSZ; port->fc0_rst_bit = FUNCTRL0_CH0_RST; port->fc0_rec_bit = FUNCTRL0_CH0_REC; port->fc0_en_bit = FUNCTRL0_CH0_EN; - port->int_en_bit = INTCTRL_CH0_EN; port->sync_dir = DDI_DMA_SYNC_FORKERNEL; port->capture = B_TRUE; port->fc1_rate_mask = FUNCTRL1_ADC_RATE_48K; port->chformat_mask = CHFORMAT_CH0_16ST; port->nchan = 2; - intrs = dev->rintrs; break; case 1: caps = ENGINE_OUTPUT_CAP; dmaflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT; - port->callb = audio_engine_consume; port->reg_paddr = REG_CH1_PADDR; port->reg_bufsz = REG_CH1_BUFSZ; port->reg_fragsz = REG_CH1_FRAGSZ; port->fc0_rst_bit = FUNCTRL0_CH1_RST; port->fc0_rec_bit = FUNCTRL0_CH1_REC; port->fc0_en_bit = FUNCTRL0_CH1_EN; - port->int_en_bit = INTCTRL_CH1_EN; port->sync_dir = DDI_DMA_SYNC_FORDEV; port->capture = B_FALSE; port->fc1_rate_mask = FUNCTRL1_DAC_RATE_48K; port->chformat_mask = CHFORMAT_CH1_16ST; port->nchan = playch; - intrs = dev->pintrs; break; } /* - * Calculate fragfr, nfrags, buf. - * - * 48 as minimum is chosen to ensure that we will have - * at least 4 fragments. 512 is just an arbitrary - * limit, and at the smallest frame size will result - * in no more than 176 fragments. - */ - intrs = min(512, max(48, intrs)); - - /* - * Two fragments are enough to get ping-pong buffers. - * The hardware could support considerably more than - * this, but it just wastes memory. - */ - port->nfrags = 2; - - /* * For efficiency, we'd like to have the fragments * evenly divisble by 64 bytes. Since frames are * already evenly divisble by 4 (16-bit stereo), this * is adequate. For a typical configuration (175 Hz * requested) this will translate to 166 Hz. */ - port->fragfr = P2ROUNDUP((48000 / intrs), 16); - port->nframes = port->nfrags * port->fragfr; + port->nframes = 2048; port->bufsz = port->nframes * port->nchan * 2; if (ddi_dma_alloc_handle(dev->dip, &dma_attr, DDI_DMA_DONTWAIT, @@ -973,13 +820,6 @@ cmpci_add_controls(dev); - dev->ksp = kstat_create(ddi_driver_name(dev->dip), - ddi_get_instance(dev->dip), ddi_driver_name(dev->dip), - "controller", KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT); - if (dev->ksp != NULL) { - kstat_install(dev->ksp); - } - cmpci_reset(dev); cmpci_configure_mixer(dev); @@ -994,16 +834,7 @@ void cmpci_destroy(cmpci_dev_t *dev) { - if (dev->ihandle != NULL) { - (void) ddi_intr_disable(dev->ihandle); - (void) ddi_intr_remove_handler(dev->ihandle); - (void) ddi_intr_free(dev->ihandle); - mutex_destroy(&dev->mutex); - } - - if (dev->ksp != NULL) { - kstat_delete(dev->ksp); - } + mutex_destroy(&dev->mutex); /* free up ports, including DMA resources for ports */ for (int i = 0; i < PORT_MAX; i++) { @@ -1069,6 +900,7 @@ dev = kmem_zalloc(sizeof (*dev), KM_SLEEP); dev->dip = dip; + mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL); ddi_set_driver_private(dip, dev); @@ -1130,17 +962,11 @@ break; } - if (cmpci_setup_interrupts(dev) != DDI_SUCCESS) { - audio_dev_warn(dev->adev, "can't register interrupts"); - goto err_exit; - } - if (cmpci_init(dev) != DDI_SUCCESS) { audio_dev_warn(dev->adev, "can't init device"); goto err_exit; } - (void) ddi_intr_enable(dev->ihandle); return (DDI_SUCCESS); err_exit: @@ -1151,27 +977,14 @@ static int cmpci_resume(cmpci_dev_t *dev) { - audio_engine_reset(dev->port[0].engine); - audio_engine_reset(dev->port[1].engine); - mutex_enter(&dev->mutex); - dev->suspended = B_FALSE; - cmpci_reset(dev); /* wait one millisecond, to give reset a chance to get up */ drv_usecwait(1000); - - cmpci_configure_mixer(dev); - - for (int i = 0; i < PORT_MAX; i++) { - cmpci_port_t *port = &dev->port[i]; + mutex_exit(&dev->mutex); - cmpci_reset_port(port); - if (port->open) { - cmpci_start_port(port); - } - } - mutex_exit(&dev->mutex); + audio_dev_resume(dev->adev); + return (DDI_SUCCESS); } @@ -1183,9 +996,6 @@ mutex_enter(&dev->mutex); - /* disable interrupts */ - CLR32(dev, REG_INTCTRL, INTCTRL_CH1_EN | INTCTRL_CH0_EN); - /* disable channels */ PUT32(dev, REG_FUNCTRL0, 0); @@ -1197,23 +1007,6 @@ } static int -cmpci_suspend(cmpci_dev_t *dev) -{ - mutex_enter(&dev->mutex); - - cmpci_update_port(&dev->port[0]); - cmpci_stop_port(&dev->port[0]); - - cmpci_update_port(&dev->port[1]); - cmpci_stop_port(&dev->port[1]); - - dev->suspended = B_TRUE; - mutex_exit(&dev->mutex); - - return (DDI_SUCCESS); -} - -static int cmpci_quiesce(dev_info_t *dip) { cmpci_dev_t *dev; @@ -1222,9 +1015,6 @@ return (DDI_FAILURE); } - /* disable interrupts */ - PUT32(dev, REG_INTCTRL, 0); - /* disable channels */ PUT32(dev, REG_FUNCTRL0, 0); @@ -1265,7 +1055,9 @@ return (cmpci_detach(dev)); case DDI_SUSPEND: - return (cmpci_suspend(dev)); + audio_dev_suspend(dev->adev); + return (DDI_SUCCESS); + default: return (DDI_FAILURE); }
--- a/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.h Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* @@ -265,8 +265,6 @@ ddi_dma_handle_t dmah; caddr_t kaddr; uint32_t paddr; - unsigned fragfr; - unsigned nfrags; unsigned nframes; unsigned bufsz; unsigned nchan; @@ -311,8 +309,6 @@ int maxch; - boolean_t suspended; - kmutex_t mutex; cmpci_port_t port[PORT_MAX]; cmpci_ctrl_t controls[CTL_NUM]; @@ -345,8 +341,6 @@ #define CLR32(dev, offset, v) PUT32(dev, offset, GET32(dev, offset) & ~(v)) #define SET32(dev, offset, v) PUT32(dev, offset, GET32(dev, offset) | (v)) -#define KSINTR(dev) ((kstat_intr_t *)((dev)->ksp->ks_data)) - #define BIT(n) (1U << (n)) #endif /* _AUDIOCMI_H */
--- a/usr/src/uts/common/io/audio/drv/audioemu10k/audioemu10k.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audioemu10k/audioemu10k.c Tue Mar 16 09:30:41 2010 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -109,9 +109,6 @@ static uint8_t side_routing[MAX_SENDS] = { SEND_SIDEL, SEND_SIDER, 0x3f, 0x3f }; -static uint8_t no_routing[MAX_SENDS] = { - 0x3f, 0x3f, 0x3f, 0x3f -}; /* * SB Live! cannot do DMA above 2G addresses. Audigy/2/4 have special 8k page @@ -142,7 +139,7 @@ static int emu10k_detach(emu10k_devc_t *); static int emu10k_suspend(emu10k_devc_t *); -static int emu10k_open(void *, int, unsigned *, unsigned *, caddr_t *); +static int emu10k_open(void *, int, unsigned *, caddr_t *); static void emu10k_close(void *); static int emu10k_start(void *); static void emu10k_stop(void *); @@ -157,9 +154,7 @@ static void emu10k_write_ac97(void *, uint8_t, uint16_t); static int emu10k_alloc_port(emu10k_devc_t *, int); static void emu10k_destroy(emu10k_devc_t *); -static int emu10k_setup_intrs(emu10k_devc_t *); static int emu10k_hwinit(emu10k_devc_t *); -static uint_t emu10k_intr(caddr_t, caddr_t); static void emu10k_init_effects(emu10k_devc_t *); static audio_engine_ops_t emu10k_engine_ops = { @@ -290,58 +285,6 @@ emu10k_write_reg(devc, reg, 0, value); } -static uint_t -emu10k_intr(caddr_t argp, caddr_t nocare) -{ - emu10k_devc_t *devc = (void *) argp; - emu10k_portc_t *portc; - uint32_t status; - audio_engine_t *cons = NULL, *prod = NULL; - - - _NOTE(ARGUNUSED (nocare)); - - mutex_enter(&devc->mutex); - if (devc->suspended) { - mutex_exit(&devc->mutex); - return (DDI_INTR_UNCLAIMED); - } - - status = INL(devc, devc->regs + INTPEND); - - if (status == 0) { - mutex_exit(&devc->mutex); - return (DDI_INTR_UNCLAIMED); - } - - if (status & INT_CL) { /* channel loop */ - emu10k_write_reg(devc, CLIPL, 0, (1U << 8)); - OUTL(devc, INT_CL, devc->regs + INTPEND); - portc = devc->portc[EMU10K_PLAY]; - if (portc->active) { - cons = portc->engine; - } - } - if (status & (INT_AF|INT_AH|INT_MF|INT_MH)) { /* ADC interrupt */ - OUTL(devc, INT_AF|INT_AH|INT_MF|INT_MH, devc->regs + INTPEND); - portc = devc->portc[EMU10K_REC]; - if (portc->active) { - prod = portc->engine; - } - } - - mutex_exit(&devc->mutex); - - if (cons) { - audio_engine_consume(cons); - } - if (prod) { - audio_engine_produce(prod); - } - - return (DDI_INTR_CLAIMED); -} - /* * Audio routines */ @@ -443,68 +386,19 @@ emu10k_write_reg(devc, PEFE_PITCHAMOUNT, voice, 0x00); } -static void -emu10k_setup_silence(emu10k_portc_t *portc, int voice) -{ - emu10k_devc_t *devc = portc->devc; - - emu10k_write_reg(devc, VEDS, voice, 0x0); /* OFF */ - emu10k_write_reg(devc, VTFT, voice, 0xffff); - emu10k_write_reg(devc, CVCF, voice, 0xffff); - - /* set stereo */ - emu10k_write_reg(devc, CPF, voice, 0x8000); - - /* SDL, ST, CA */ - emu10k_write_reg(devc, SDL, voice, portc->fragfr); - emu10k_write_reg(devc, SCSA, voice, 0); - emu10k_write_reg(devc, PTAB, voice, 0); - emu10k_write_reg(devc, QKBCA, voice, 0); - - emu10k_write_reg(devc, Z1, voice, 0); - emu10k_write_reg(devc, Z2, voice, 0); - - /* This is really a physical address */ - emu10k_write_reg(devc, MAPA, voice, - 0x1fff | (devc->silence_paddr << 1)); - emu10k_write_reg(devc, MAPB, voice, - 0x1fff | (devc->silence_paddr << 1)); - - emu10k_write_reg(devc, VTFT, voice, 0x0000ffff); - emu10k_write_reg(devc, CVCF, voice, 0x0000ffff); - emu10k_write_reg(devc, MEHA, voice, 0); - emu10k_write_reg(devc, MEDS, voice, 0x7f); - emu10k_write_reg(devc, MLV, voice, 0x8000); - emu10k_write_reg(devc, VLV, voice, 0x8000); - emu10k_write_reg(devc, VFM, voice, 0); - emu10k_write_reg(devc, TMFQ, voice, 0); - emu10k_write_reg(devc, VVFQ, voice, 0); - emu10k_write_reg(devc, MEV, voice, 0x8000); - emu10k_write_reg(devc, VEHA, voice, 0x7f7f); /* OK */ - /* No volume envelope delay (OK) */ - emu10k_write_reg(devc, VEV, voice, 0x8000); - emu10k_write_reg(devc, PEFE_FILTERAMOUNT, voice, 0x7f); - emu10k_write_reg(devc, PEFE_PITCHAMOUNT, voice, 0x00); -} - int -emu10k_open(void *arg, int flag, - unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp) +emu10k_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp) { emu10k_portc_t *portc = arg; emu10k_devc_t *devc = portc->devc; _NOTE(ARGUNUSED(flag)); - portc->started = B_FALSE; portc->active = B_FALSE; - *fragfrp = portc->fragfr; - *nfragsp = portc->nfrags; + *nframes = portc->nframes; *bufp = portc->buf_kaddr; mutex_enter(&devc->mutex); - if (!devc->suspended) - portc->reset_port(portc); portc->count = 0; mutex_exit(&devc->mutex); @@ -514,14 +408,7 @@ void emu10k_close(void *arg) { - emu10k_portc_t *portc = arg; - emu10k_devc_t *devc = portc->devc; - - mutex_enter(&devc->mutex); - if (!devc->suspended) - portc->stop_port(portc); - portc->started = B_FALSE; - mutex_exit(&devc->mutex); + _NOTE(ARGUNUSED(arg)); } int @@ -531,11 +418,8 @@ emu10k_devc_t *devc = portc->devc; mutex_enter(&devc->mutex); - if (!portc->started) { - if (!devc->suspended) - portc->start_port(portc); - portc->started = B_TRUE; - } + portc->reset_port(portc); + portc->start_port(portc); mutex_exit(&devc->mutex); return (0); } @@ -547,11 +431,7 @@ emu10k_devc_t *devc = portc->devc; mutex_enter(&devc->mutex); - if (portc->started) { - if (!devc->suspended) - portc->stop_port(portc); - portc->started = B_FALSE; - } + portc->stop_port(portc); mutex_exit(&devc->mutex); } @@ -596,8 +476,7 @@ uint64_t count; mutex_enter(&devc->mutex); - if (!devc->suspended) - portc->update_port(portc); + portc->update_port(portc); count = portc->count; mutex_exit(&devc->mutex); @@ -804,17 +683,6 @@ emu10k_prepare_voice(devc, 6); emu10k_prepare_voice(devc, 7); - emu10k_prepare_voice(devc, 8); - emu10k_prepare_voice(devc, 9); - - /* arrange to receive full loop interrupts on channel 8 */ - emu10k_write_reg(devc, CLIEL, 0, (1U << 8)); - - /* initialize our position counter... */ - portc->pos = - (emu10k_read_reg(devc, QKBCA, 0) & 0xffffff) - - (portc->memptr >> 2); - /* Trigger playback on all voices */ emu10k_write_reg(devc, VEDS, 0, 0x7f7f); emu10k_write_reg(devc, VEDS, 1, 0x7f7f); @@ -824,8 +692,6 @@ emu10k_write_reg(devc, VEDS, 5, 0x7f7f); emu10k_write_reg(devc, VEDS, 6, 0x7f7f); emu10k_write_reg(devc, VEDS, 7, 0x7f7f); - emu10k_write_reg(devc, VEDS, 8, 0x7f7f); - emu10k_write_reg(devc, VEDS, 9, 0x7f7f); portc->active = B_TRUE; } @@ -843,8 +709,6 @@ emu10k_stop_voice(devc, 5); emu10k_stop_voice(devc, 6); emu10k_stop_voice(devc, 7); - emu10k_stop_voice(devc, 8); - emu10k_stop_voice(devc, 9); portc->active = B_FALSE; } @@ -870,16 +734,7 @@ emu10k_reset_pair(portc, 0, front_routing, 0); emu10k_reset_pair(portc, 2, surr_routing, offs); } - emu10k_setup_silence(portc, 8); - emu10k_setup_silence(portc, 9); - /* - * This way we can use voices 8 and 9 for timing, we have - * programmed them to be just the size of a single fragment, - * that way when they loop we get a clean interrupt. - */ - emu10k_write_routing(devc, 8, no_routing); - emu10k_write_routing(devc, 9, no_routing); portc->pos = 0; } @@ -896,13 +751,23 @@ */ pos = emu10k_read_reg(devc, QKBCA, 0) & 0xffffff; pos -= (portc->memptr >> 2); + if (pos > portc->nframes) { + /* + * This should never happen! If it happens, we should + * throw an FMA fault. (When we support FMA.) For now + * we just assume the device is stuck, and report no + * change in position. + */ + pos = portc->pos; + } + ASSERT(pos <= portc->nframes); - if (pos <= portc->pos) { - cnt = portc->nframes - portc->pos; - cnt += pos; + if (pos < portc->pos) { + cnt = (portc->nframes - portc->pos) + pos; } else { cnt = (pos - portc->pos); } + ASSERT(cnt <= portc->nframes); if (portc->dopos) { emu10k_vars[0] = portc->pos; emu10k_vars[1] = pos; @@ -910,11 +775,6 @@ emu10k_vars[3] = cnt; portc->dopos = 0; } - if (cnt > portc->nframes) { - printf("Got bogus count %u\n", cnt); - cnt = portc->fragfr; - } - ASSERT(cnt <= portc->nframes); portc->count += cnt; portc->pos = pos; } @@ -925,10 +785,6 @@ emu10k_devc_t *devc = portc->devc; uint32_t tmp; - /* Intr enable */ - OUTL(devc, INL(devc, devc->regs + IE) | IE_MB | IE_AB, - devc->regs + IE); - tmp = 0; /* setup 48Kz */ if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) tmp |= 0x30; /* Left/right channel enable */ @@ -976,8 +832,6 @@ emu10k_write_reg(devc, ADCBS, 0, sz); emu10k_write_reg(devc, ADCSR, 0, 0); /* reset for phase */ portc->pos = 0; - OUTL(devc, INL(devc, devc->regs + IE) & ~(IE_MB | IE_AB), - devc->regs + IE); } void @@ -1015,7 +869,6 @@ portc = kmem_zalloc(sizeof (*portc), KM_SLEEP); devc->portc[num] = portc; portc->devc = devc; - portc->started = B_FALSE; portc->memptr = devc->audio_memptr; devc->audio_memptr += (DMABUF_SIZE + 4095) & ~4095; @@ -1032,9 +885,7 @@ portc->update_port = emu10k_update_rec; /* This is the minimum record buffer size. */ portc->buf_size = 4096; - portc->nfrags = 2; - portc->nframes = 4096 / 4; - portc->fragfr = portc->nframes / portc->nfrags; + portc->nframes = portc->buf_size / 4; break; case EMU10K_PLAY: portc->syncdir = DDI_DMA_SYNC_FORDEV; @@ -1045,10 +896,8 @@ portc->stop_port = emu10k_stop_play; portc->reset_port = emu10k_reset_play; portc->update_port = emu10k_update_play; - /* XXX: this could probably be tunable */ - portc->nfrags = 2; - portc->fragfr = 288; - portc->nframes = portc->nfrags * portc->fragfr; + /* This could probably be tunable. */ + portc->nframes = 2048; portc->buf_size = portc->nframes * portc->channels * 2; break; default: @@ -1122,54 +971,10 @@ return (DDI_SUCCESS); } -int -emu10k_setup_intrs(emu10k_devc_t *devc) -{ - uint_t ipri; - int actual; - int rv; - ddi_intr_handle_t ih[1]; - - rv = ddi_intr_alloc(devc->dip, ih, DDI_INTR_TYPE_FIXED, - 0, 1, &actual, DDI_INTR_ALLOC_STRICT); - if ((rv != DDI_SUCCESS) || (actual != 1)) { - audio_dev_warn(devc->adev, - "Can't alloc interrupt handle (rv %d actual %d)", - rv, actual); - return (DDI_FAILURE); - } - - if (ddi_intr_get_pri(ih[0], &ipri) != DDI_SUCCESS) { - audio_dev_warn(devc->adev, "Can't get interrupt priority"); - (void) ddi_intr_free(ih[0]); - return (DDI_FAILURE); - } - - if (ddi_intr_add_handler(ih[0], emu10k_intr, devc, NULL) != - DDI_SUCCESS) { - audio_dev_warn(devc->adev, "Can't add interrupt handler"); - (void) ddi_intr_free(ih[0]); - return (DDI_FAILURE); - } - - devc->ih = ih[0]; - mutex_init(&devc->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - return (DDI_SUCCESS); -} - void emu10k_destroy(emu10k_devc_t *devc) { - if (devc->ih != NULL) { - (void) ddi_intr_disable(devc->ih); - (void) ddi_intr_remove_handler(devc->ih); - (void) ddi_intr_free(devc->ih); - mutex_destroy(&devc->mutex); - } - - if (devc->ksp) { - kstat_delete(devc->ksp); - } + mutex_destroy(&devc->mutex); if (devc->silence_paddr) { (void) ddi_dma_unbind_handle(devc->silence_dmah); @@ -1282,38 +1087,6 @@ } } -static void -emu10k_refresh_mixer(emu10k_devc_t *devc) -{ - uint32_t val; - uint32_t set; - - for (int gpr = 0; gpr < MAX_GPR; gpr++) { - if (devc->gpr_shadow[gpr].valid) { - emu10k_write_reg(devc, gpr + GPR0, 0, - devc->gpr_shadow[gpr].value); - } - } - - set = devc->ctrls[CTL_JACK3].val; - if (devc->feature_mask & SB_INVSP) { - set = !set; - } - - if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) { - val = INL(devc, devc->regs + 0x18); - val &= ~A_IOCFG_GPOUT0; - val |= set ? 0x44 : 0x40; - OUTL(devc, val, devc->regs + 0x18); - - } else if (devc->feature_mask & SB_LIVE) { - val = INL(devc, devc->regs + HCFG); - val &= ~HCFG_GPOUT0; - val |= set ? HCFG_GPOUT0 : 0; - OUTL(devc, val, devc->regs + HCFG); - } -} - int emu10k_hwinit(emu10k_devc_t *devc) { @@ -1337,6 +1110,7 @@ emu10k_write_reg(devc, ADCBS, 0, 0x0); emu10k_write_reg(devc, ADCBA, 0, 0x0); + /* Ensure all interrupts are disabled */ OUTL(devc, 0, devc->regs + IE); emu10k_write_reg(devc, CLIEL, 0, 0x0); emu10k_write_reg(devc, CLIEH, 0, 0x0); @@ -1550,9 +1324,7 @@ ASSERT(gpr < MAX_GPR); devc->gpr_shadow[gpr].valid = B_TRUE; devc->gpr_shadow[gpr].value = value; - if (!devc->suspended) { - emu10k_write_reg(devc, gpr + GPR0, 0, value); - } + emu10k_write_reg(devc, gpr + GPR0, 0, value); } static int @@ -1659,19 +1431,17 @@ if (devc->feature_mask & SB_INVSP) { set_val = !set_val; } - if (!devc->suspended) { - if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) { - val = INL(devc, devc->regs + 0x18); - val &= ~A_IOCFG_GPOUT0; - val |= set_val ? 0x44 : 0x40; - OUTL(devc, val, devc->regs + 0x18); + if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) { + val = INL(devc, devc->regs + 0x18); + val &= ~A_IOCFG_GPOUT0; + val |= set_val ? 0x44 : 0x40; + OUTL(devc, val, devc->regs + 0x18); - } else if (devc->feature_mask & SB_LIVE) { - val = INL(devc, devc->regs + HCFG); - val &= ~HCFG_GPOUT0; - val |= set_val ? HCFG_GPOUT0 : 0; - OUTL(devc, val, devc->regs + HCFG); - } + } else if (devc->feature_mask & SB_LIVE) { + val = INL(devc, devc->regs + HCFG); + val &= ~HCFG_GPOUT0; + val |= set_val ? HCFG_GPOUT0 : 0; + OUTL(devc, val, devc->regs + HCFG); } mutex_exit(&devc->mutex); return (0); @@ -2240,8 +2010,7 @@ audio_dev_set_description(devc->adev, namebuf); audio_dev_set_version(devc->adev, model); - if (emu10k_setup_intrs(devc) != DDI_SUCCESS) - goto error; + mutex_init(&devc->mutex, NULL, MUTEX_DRIVER, 0); /* allocate static page table memory */ @@ -2260,7 +2029,7 @@ &devc->pt_dmah) != DDI_SUCCESS) { audio_dev_warn(devc->adev, "failed to allocate page table handle"); - return (DDI_FAILURE); + goto error; } if (ddi_dma_mem_alloc(devc->pt_dmah, devc->max_pages * 4, @@ -2269,7 +2038,7 @@ DDI_SUCCESS) { audio_dev_warn(devc->adev, "failed to allocate memory for page table"); - return (DDI_FAILURE); + goto error; } if (ddi_dma_addr_bind_handle(devc->pt_dmah, NULL, @@ -2277,7 +2046,7 @@ DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) { audio_dev_warn(devc->adev, "failed binding page table DMA handle"); - return (DDI_FAILURE); + goto error; } devc->page_map = (void *)devc->pt_kaddr; @@ -2289,7 +2058,7 @@ &devc->silence_dmah) != DDI_SUCCESS) { audio_dev_warn(devc->adev, "failed to allocate silent page handle"); - return (DDI_FAILURE); + goto error; } if (ddi_dma_mem_alloc(devc->silence_dmah, 4096, @@ -2298,7 +2067,7 @@ &devc->silence_acch) != DDI_SUCCESS) { audio_dev_warn(devc->adev, "failed to allocate silent page memory"); - return (DDI_FAILURE); + goto error; } (void) ddi_dma_sync(devc->silence_dmah, 0, 0, DDI_DMA_SYNC_FORDEV); @@ -2308,7 +2077,7 @@ DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) { audio_dev_warn(devc->adev, "failed binding silent page DMA handle"); - return (DDI_FAILURE); + goto error; } devc->silence_paddr = cookie.dmac_address; @@ -2346,19 +2115,11 @@ emu10k_create_controls(devc); - /* set up kernel statistics */ - if ((devc->ksp = kstat_create(EMU10K_NAME, ddi_get_instance(dip), - EMU10K_NAME, "controller", KSTAT_TYPE_INTR, - 1, KSTAT_FLAG_PERSISTENT)) != NULL) { - kstat_install(devc->ksp); - } - if (audio_dev_register(devc->adev) != DDI_SUCCESS) { audio_dev_warn(devc->adev, "unable to register audio device"); goto error; } - (void) ddi_intr_enable(devc->ih); ddi_report_dev(dip); return (DDI_SUCCESS); @@ -2372,15 +2133,9 @@ emu10k_resume(dev_info_t *dip) { emu10k_devc_t *devc; - emu10k_portc_t *portc; devc = ddi_get_driver_private(dip); - for (int i = 0; i < EMU10K_NUM_PORTC; i++) { - portc = devc->portc[i]; - audio_engine_reset(portc->engine); - } - mutex_enter(&devc->mutex); if (emu10k_hwinit(devc) != DDI_SUCCESS) { mutex_exit(&devc->mutex); @@ -2393,27 +2148,12 @@ return (DDI_SUCCESS); } - emu10k_refresh_mixer(devc); - - devc->suspended = B_FALSE; - - for (int i = 0; i < EMU10K_NUM_PORTC; i++) { - - portc = devc->portc[i]; - - portc->stop_port(portc); - - portc->dopos = 1; - if (portc->started) { - portc->reset_port(portc); - portc->start_port(portc); - } - } - mutex_exit(&devc->mutex); /* resume ac97 */ - ac97_resume(devc->ac97); + ac97_reset(devc->ac97); + + audio_dev_resume(devc->adev); return (DDI_SUCCESS); } @@ -2431,50 +2171,7 @@ int emu10k_suspend(emu10k_devc_t *devc) { - ac97_suspend(devc->ac97); - - mutex_enter(&devc->mutex); - - devc->suspended = B_TRUE; - - emu10k_write_reg(devc, CLIEL, 0, 0); - emu10k_write_reg(devc, CLIEH, 0, 0); - if (!(devc->feature_mask & SB_LIVE)) { - emu10k_write_reg(devc, HLIEL, 0, 0x0); - emu10k_write_reg(devc, HLIEH, 0, 0x0); - } - OUTL(devc, 0, devc->regs + IE); /* Intr enable (all off) */ - - for (int i = 0; i < EMU10K_NUM_PORTC; i++) { - emu10k_portc_t *portc = devc->portc[i]; - portc->stop_port(portc); - } - - /* stop all voices */ - for (int i = 0; i < 64; i++) { - emu10k_write_reg(devc, VEDS, i, 0); - } - for (int i = 0; i < 64; i++) { - emu10k_write_reg(devc, VTFT, i, 0); - emu10k_write_reg(devc, CVCF, i, 0); - emu10k_write_reg(devc, PTAB, i, 0); - emu10k_write_reg(devc, CPF, i, 0); - } - /* - * Turn off the hardware - */ - OUTL(devc, - HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | - HCFG_MUTEBUTTONENABLE, devc->regs + HCFG); - - /* stop ADC recording */ - emu10k_write_reg(devc, ADCSR, 0, 0x0); - emu10k_write_reg(devc, ADCBA, 0, 0x0); - emu10k_write_reg(devc, ADCBA, 0, 0x0); - - emu10k_write_reg(devc, PTBA, 0, 0); - - mutex_exit(&devc->mutex); + audio_dev_suspend(devc->adev); return (DDI_SUCCESS); } @@ -2593,7 +2290,6 @@ /* * Turn off the hardware */ - OUTL(devc, 0, devc->regs + IE); /* Intr enable (all off) */ OUTL(devc, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, devc->regs + HCFG);
--- a/usr/src/uts/common/io/audio/drv/audioemu10k/audioemu10k.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audioemu10k/audioemu10k.h Tue Mar 16 09:30:41 2010 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -362,7 +362,6 @@ boolean_t started; boolean_t active; - unsigned fragfr; unsigned nframes; unsigned nfrags; unsigned fragsz; @@ -384,13 +383,10 @@ struct _emu10k_devc_t { dev_info_t *dip; audio_dev_t *adev; - kstat_t *ksp; - boolean_t suspended; ddi_acc_handle_t pcih; ddi_acc_handle_t regsh; caddr_t regs; kmutex_t mutex; - ddi_intr_handle_t ih; /* * Page table @@ -446,8 +442,6 @@ #define INL(devc, reg) ddi_get32(devc->regsh, (void *)(reg)) #define OUTL(devc, val, reg) ddi_put32(devc->regsh, (void *)(reg), (val)) -#define EMU10K_KIOP(X) ((kstat_intr_t *)(X->ksp->ks_data)) - #endif /* _KERNEL */ #endif /* EMU10K_H */
--- a/usr/src/uts/common/io/audio/drv/audioens/audioens.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audioens/audioens.c Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* @@ -47,12 +47,6 @@ #include "audioens.h" /* - * The original OSS driver used a single duplex engine and a separate - * playback only engine. Instead, we expose three engines, one for input - * and two for output. - */ - -/* * Set the latency to 32, 64, 96, 128 clocks - some APCI97 devices exhibit * garbled audio in some cases and setting the latency to higer values fixes it * Values: 32, 64, 96, 128 - Default: 64 (or defined by bios) @@ -84,15 +78,11 @@ #define ECTIVA_ES1938 0x8938 #define DEFRATE 48000 -#define DEFINTS 75 #define DRVNAME "audioens" typedef struct audioens_port { /* Audio parameters */ - boolean_t trigger; - boolean_t suspended; - int speed; int num; @@ -105,8 +95,6 @@ ddi_acc_handle_t acch; ddi_dma_handle_t dmah; int nchan; - unsigned fragfr; - unsigned nfrags; unsigned nframes; unsigned frameno; uint64_t count; @@ -122,13 +110,6 @@ uint16_t devid; uint8_t revision; dev_info_t *dip; - boolean_t enabled; - - - int pintrs; - int rintrs; - - kstat_t *ksp; audioens_port_t port[PORT_MAX + 1]; @@ -136,7 +117,6 @@ caddr_t regs; ddi_acc_handle_t acch; - ddi_intr_handle_t ihandle[1]; } audioens_dev_t; static ddi_device_acc_attr_t acc_attr = { @@ -153,15 +133,9 @@ /* * The hardware appears to be able to address up to 16-bits worth of longwords, - * giving a total address space of 256K. Note, however, that we will restrict - * this further when we do fragment and memory allocation. At its very highest - * clock rate (48 kHz) and sample size (16-bit stereo), and lowest interrupt - * rate (32 Hz), we only need 6000 bytes per fragment. - * - * So with an allocated buffer size of 64K, we can support at least 10 frags, - * which is more than enough. (The legacy Sun driver used only 2 fragments.) + * giving a total address space of 256K. But we need substantially less. */ -#define AUDIOENS_BUF_LEN (65536) +#define AUDIOENS_BUF_LEN (16384) static ddi_dma_attr_t dma_attr = { DMA_ATTR_VERSION, /* dma_attr_version */ @@ -196,13 +170,7 @@ #define CLR32(dev, offset, v) PUT32(dev, offset, GET32(dev, offset) & ~(v)) #define SET32(dev, offset, v) PUT32(dev, offset, GET32(dev, offset) | (v)) -#define KSINTR(dev) ((kstat_intr_t *)((dev)->ksp->ks_data)) - static void audioens_init_hw(audioens_dev_t *); -static void audioens_init_port(audioens_port_t *); -static void audioens_start_port(audioens_port_t *); -static void audioens_stop_port(audioens_port_t *); -static void audioens_update_port(audioens_port_t *); static uint16_t audioens_rd97(void *dev_, uint8_t wAddr) @@ -422,105 +390,6 @@ return (GET32(dev, offs)); } -static uint_t -audioens_intr(caddr_t arg1, caddr_t arg2) -{ - audioens_dev_t *dev = (void *)arg1; - int stats; - int tmp; - unsigned char ackbits = 0; - audioens_port_t *port; - audio_engine_t *do_dac, *do_adc; - - _NOTE(ARGUNUSED(arg2)); - - /* - * NB: The old audioens didn't report spurious interrupts. On - * a system with shared interrupts (typical!) there will - * normally be lots of these (each time the "other" device - * interrupts). - * - * Also, because of the way the interrupt chain handling - * works, reporting of spurious interrupts is probably not - * terribly useful. - * - * However, we can count interrupts where the master interrupt - * bit is set but none of the ackbits that we are prepared to - * process is set. That is probably useful. - */ - mutex_enter(&dev->mutex); - if (!dev->enabled) { - - mutex_exit(&dev->mutex); - return (DDI_INTR_UNCLAIMED); - } - - stats = GET32(dev, CONC_dSTATUS_OFF); - - if (!(stats & CONC_STATUS_PENDING)) { /* No interrupt pending */ - mutex_exit(&dev->mutex); - return (DDI_INTR_UNCLAIMED); - } - - do_dac = do_adc = NULL; - - /* DAC1 (synth) interrupt */ - if (stats & CONC_STATUS_DAC1INT) { - - ackbits |= CONC_SERCTL_DAC1IE; - port = &dev->port[PORT_DAC]; - if (port->trigger) { - do_dac = port->engine; - } - } - - /* DAC2 interrupt */ - if (stats & CONC_STATUS_DAC2INT) { - - ackbits |= CONC_SERCTL_DAC2IE; - } - - /* ADC interrupt */ - if (stats & CONC_STATUS_ADCINT) { - - ackbits |= CONC_SERCTL_ADCIE; - port = &dev->port[PORT_ADC]; - - if (port->trigger) { - do_adc = port->engine; - } - } - - /* UART interrupt - we shouldn't get this! */ - if (stats & CONC_STATUS_UARTINT) { - uint8_t uart_stat = GET8(dev, CONC_bUARTCSTAT_OFF); - while (uart_stat & CONC_UART_RXRDY) - uart_stat = GET8(dev, CONC_bUARTCSTAT_OFF); - } - - /* Ack the interrupt */ - tmp = GET8(dev, CONC_bSERCTL_OFF); - PUT8(dev, CONC_bSERCTL_OFF, tmp & (~ackbits)); /* Clear bits */ - PUT8(dev, CONC_bSERCTL_OFF, tmp | ackbits); /* Turn them back on */ - - if (dev->ksp) { - if (ackbits == 0) { - KSINTR(dev)->intrs[KSTAT_INTR_SPURIOUS]++; - } else { - KSINTR(dev)->intrs[KSTAT_INTR_HARD]++; - } - } - - mutex_exit(&dev->mutex); - - if (do_dac) - audio_engine_consume(do_dac); - if (do_adc) - audio_engine_produce(do_adc); - - return (DDI_INTR_CLAIMED); -} - /* * Audio routines */ @@ -549,14 +418,34 @@ return (port->speed); } -static void -audioens_init_port(audioens_port_t *port) +static int +audioens_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp) { + audioens_port_t *port = arg; audioens_dev_t *dev = port->dev; - unsigned tmp; + + _NOTE(ARGUNUSED(flag)); + + mutex_enter(&dev->mutex); + + port->nframes = AUDIOENS_BUF_LEN / (port->nchan * sizeof (int16_t)); + port->count = 0; + + *nframes = port->nframes; + *bufp = port->kaddr; + mutex_exit(&dev->mutex); - if (port->suspended) - return; + return (0); +} + +static int +audioens_start(void *arg) +{ + audioens_port_t *port = arg; + audioens_dev_t *dev = port->dev; + uint32_t tmp; + + mutex_enter(&dev->mutex); switch (port->num) { case PORT_DAC: @@ -589,8 +478,12 @@ port->nframes - 1); /* Set # of frames between interrupts */ - PUT16(dev, CONC_wDAC1IC_OFF, port->fragfr - 1); - PUT16(dev, CONC_wDAC2IC_OFF, port->fragfr - 1); + PUT16(dev, CONC_wDAC1IC_OFF, port->nframes - 1); + PUT16(dev, CONC_wDAC2IC_OFF, port->nframes - 1); + + SET8(dev, CONC_bDEVCTL_OFF, + CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN); + break; case PORT_ADC: @@ -615,104 +508,13 @@ port->nframes - 1); /* Set # of frames between interrupts */ - PUT16(dev, CONC_wADCIC_OFF, port->fragfr - 1); + PUT16(dev, CONC_wADCIC_OFF, port->nframes - 1); + SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN); break; } port->frameno = 0; -} - -static int -audioens_open(void *arg, int flag, unsigned *fragfrp, unsigned *nfragsp, - caddr_t *bufp) -{ - audioens_port_t *port = arg; - audioens_dev_t *dev = port->dev; - int intrs; - - _NOTE(ARGUNUSED(flag)); - - mutex_enter(&dev->mutex); - - if (port->num == PORT_ADC) { - intrs = dev->rintrs; - } else { - intrs = dev->pintrs; - } - - /* interrupt at least at 25 Hz, and not more than 250 Hz */ - intrs = min(250, max(25, intrs)); - - port->fragfr = (port->speed / intrs); - port->nfrags = AUDIOENS_BUF_LEN / - (port->fragfr * port->nchan * sizeof (int16_t)); - port->nfrags = max(4, min(port->nfrags, 1024)); - port->nframes = port->nfrags * port->fragfr; - port->trigger = B_FALSE; - port->count = 0; - - audioens_init_port(port); - - *fragfrp = port->fragfr; - *nfragsp = port->nfrags; - *bufp = port->kaddr; - mutex_exit(&dev->mutex); - - return (0); -} - -static void -audioens_start_port(audioens_port_t *port) -{ - audioens_dev_t *dev = port->dev; - - if (!port->suspended) { - switch (port->num) { - case PORT_DAC: - SET8(dev, CONC_bDEVCTL_OFF, - CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN); - SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE); - break; - case PORT_ADC: - SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN); - SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE); - break; - } - } -} - -static void -audioens_stop_port(audioens_port_t *port) -{ - audioens_dev_t *dev = port->dev; - - if (!port->suspended) { - switch (port->num) { - case PORT_DAC: - CLR8(dev, CONC_bDEVCTL_OFF, - CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN); - CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE); - break; - case PORT_ADC: - CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN); - CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE); - break; - } - } -} - -static int -audioens_start(void *arg) -{ - audioens_port_t *port = arg; - audioens_dev_t *dev = port->dev; - - mutex_enter(&dev->mutex); - if (!port->trigger) { - port->trigger = B_TRUE; - audioens_start_port(port); - } mutex_exit(&dev->mutex); return (0); @@ -725,16 +527,24 @@ audioens_dev_t *dev = port->dev; mutex_enter(&dev->mutex); - if (port->trigger) { - port->trigger = B_FALSE; - audioens_stop_port(port); + switch (port->num) { + case PORT_DAC: + CLR8(dev, CONC_bDEVCTL_OFF, + CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN); + break; + case PORT_ADC: + CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN); + break; } mutex_exit(&dev->mutex); } -static void -audioens_update_port(audioens_port_t *port) +static uint64_t +audioens_count(void *arg) { + audioens_port_t *port = arg; + audioens_dev_t *dev = port->dev; + uint64_t val; uint32_t page, offs; int frameno, n; @@ -750,6 +560,7 @@ break; } + mutex_enter(&dev->mutex); /* * Note that the current frame counter is in the high nybble. */ @@ -759,30 +570,17 @@ frameno + port->nframes - port->frameno; port->frameno = frameno; port->count += n; -} -static uint64_t -audioens_count(void *arg) -{ - audioens_port_t *port = arg; - audioens_dev_t *dev = port->dev; - uint64_t val; - - mutex_enter(&dev->mutex); - if (!port->suspended) { - audioens_update_port(port); - } val = port->count; mutex_exit(&dev->mutex); + return (val); } static void audioens_close(void *arg) { - audioens_port_t *port = arg; - - audioens_stop(port); + _NOTE(ARGUNUSED(arg)); } static void @@ -793,7 +591,7 @@ _NOTE(ARGUNUSED(nframes)); if (port->num == PORT_ADC) { - (void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORCPU); + (void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL); } else { (void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORDEV); } @@ -850,12 +648,6 @@ SRCInit(dev); -#if 0 - PUT8(dev, CONC_bSERCTL_OFF, 0x00); - PUT8(dev, CONC_bNMIENA_OFF, 0x00); /* NMI off */ - PUT8(dev, CONC_wNMISTAT_OFF, 0x00); /* PUT8? */ -#endif - /* * Turn on CODEC (UART and joystick left disabled) */ @@ -889,8 +681,6 @@ /* we want to run each channel independently */ CLR32(dev, CONC_dSTATUS_OFF, CONC_STATUS_ECHO); } - - dev->enabled = B_TRUE; } static int @@ -919,12 +709,6 @@ return (DDI_FAILURE); } - dev->pintrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip, - DDI_PROP_DONTPASS, "play-interrupts", DEFINTS); - - dev->rintrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip, - DDI_PROP_DONTPASS, "record-interrupts", DEFINTS); - for (int i = 0; i <= PORT_MAX; i++) { audioens_port_t *port; unsigned caps; @@ -999,13 +783,6 @@ /* * Set up kstats for interrupt reporting. */ - dev->ksp = kstat_create(ddi_driver_name(dev->dip), - ddi_get_instance(dev->dip), ddi_driver_name(dev->dip), - "controller", KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT); - if (dev->ksp != NULL) { - kstat_install(dev->ksp); - } - if (audio_dev_register(dev->osdev) != DDI_SUCCESS) { audio_dev_warn(dev->osdev, "unable to register with audio framework"); @@ -1015,54 +792,12 @@ return (DDI_SUCCESS); } -int -audioens_setup_interrupts(audioens_dev_t *dev) -{ - int actual; - uint_t ipri; - - if ((ddi_intr_alloc(dev->dip, dev->ihandle, DDI_INTR_TYPE_FIXED, - 0, 1, &actual, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) || - (actual != 1)) { - audio_dev_warn(dev->osdev, "can't alloc intr handle"); - return (DDI_FAILURE); - } - - if (ddi_intr_get_pri(dev->ihandle[0], &ipri) != DDI_SUCCESS) { - audio_dev_warn(dev->osdev, "can't determine intr priority"); - (void) ddi_intr_free(dev->ihandle[0]); - dev->ihandle[0] = NULL; - return (DDI_FAILURE); - } - - if (ddi_intr_add_handler(dev->ihandle[0], audioens_intr, dev, - NULL) != DDI_SUCCESS) { - audio_dev_warn(dev->osdev, "can't add intr handler"); - (void) ddi_intr_free(dev->ihandle[0]); - dev->ihandle[0] = NULL; - return (DDI_FAILURE); - } - - mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - - return (DDI_SUCCESS); -} - void audioens_destroy(audioens_dev_t *dev) { int i; - if (dev->ihandle[0] != NULL) { - (void) ddi_intr_disable(dev->ihandle[0]); - (void) ddi_intr_remove_handler(dev->ihandle[0]); - (void) ddi_intr_free(dev->ihandle[0]); - mutex_destroy(&dev->mutex); - } - - if (dev->ksp != NULL) { - kstat_delete(dev->ksp); - } + mutex_destroy(&dev->mutex); /* free up ports, including DMA resources for ports */ for (i = 0; i <= PORT_MAX; i++) { @@ -1109,10 +844,12 @@ dev = kmem_zalloc(sizeof (*dev), KM_SLEEP); dev->dip = dip; ddi_set_driver_private(dip, dev); + mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL); if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) { audio_dev_warn(dev->osdev, "pci_config_setup failed"); kmem_free(dev, sizeof (*dev)); + mutex_destroy(&dev->mutex); return (DDI_FAILURE); } @@ -1197,19 +934,12 @@ goto err_exit; } - if (audioens_setup_interrupts(dev) != DDI_SUCCESS) { - audio_dev_warn(dev->osdev, "can't register interrupts"); - goto err_exit; - } - /* This allocates and configures the engines */ if (audioens_init(dev) != DDI_SUCCESS) { audio_dev_warn(dev->osdev, "can't init device"); goto err_exit; } - (void) ddi_intr_enable(dev->ihandle[0]); - pci_config_teardown(&pcih); ddi_report_dev(dip); @@ -1249,8 +979,6 @@ PUT8(dev, CONC_bDEVCTL_OFF, tmp); PUT8(dev, CONC_bDEVCTL_OFF, tmp); - dev->enabled = B_FALSE; - mutex_exit(&dev->mutex); audioens_destroy(dev); @@ -1258,68 +986,24 @@ return (DDI_SUCCESS); } -/*ARGSUSED*/ static int audioens_resume(audioens_dev_t *dev) { - /* ask framework to reset/relocate engine data */ - for (int i = 0; i <= PORT_MAX; i++) { - audio_engine_reset(dev->port[i].engine); - } - /* reinitialize hardware */ audioens_init_hw(dev); /* restore AC97 state */ - ac97_resume(dev->ac97); - - /* restart ports */ - mutex_enter(&dev->mutex); - for (int i = 0; i < PORT_MAX; i++) { - audioens_port_t *port = &dev->port[i]; - port->suspended = B_FALSE; - audioens_init_port(port); - /* possibly start it up if was going when we suspended */ - if (port->trigger) { - audioens_start_port(port); + ac97_reset(dev->ac97); - } - } - mutex_exit(&dev->mutex); - for (int i = 0; i < PORT_MAX; i++) { - audioens_port_t *port = &dev->port[i]; - /* signal callbacks on resume */ - if (!port->trigger) - continue; - if (port->num == PORT_ADC) { - audio_engine_produce(port->engine); - } else { - audio_engine_consume(port->engine); - } - } + audio_dev_resume(dev->osdev); + return (DDI_SUCCESS); } -/*ARGSUSED*/ static int audioens_suspend(audioens_dev_t *dev) { - /* - * Stop all engines/DMA data. - */ - mutex_enter(&dev->mutex); - for (int i = 0; i <= PORT_MAX; i++) { - audioens_stop_port(&dev->port[i]); - audioens_update_port(&dev->port[i]); - dev->port[i].suspended = B_TRUE; - } - dev->enabled = B_FALSE; - mutex_exit(&dev->mutex); - - /* - * Framework needs to save off AC'97 state. - */ - ac97_suspend(dev->ac97); + audio_dev_suspend(dev->osdev); return (DDI_SUCCESS); }
--- a/usr/src/uts/common/io/audio/drv/audioens/audioens.conf Tue Mar 16 09:43:38 2010 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# Configuration file for the audioens audio driver. -# -# WARNING: This is an UNSTABLE configuration file. Its contents -# may change at any time. - -# -# play-interrupts sets the number of interrupts per second when playing. -# This affects the resolution of various things, such as sample counts. -# record-interrupts does the same for record interrupts. -# -# These may be tuned to get more accurate information by increasing the -# count. However, the larger the interrupts per second the larger the -# load on the system. So use this capability cautiously. The audioens -# driver enforces a maximum and minimum count. -# -# It should also be understood that not all interrupt rates are legal. -# The hardware is restricted to DMA buffers being allocated on certain -# boundaries. If those boundaries are violated then the value specified -# will be ignored. -# -play-interrupts=75; -record-interrupts=75; - -# -# Uncomment reset-configuration to cause the audioens driver's state to -# be reset to the default when the driver is loaded. Otherwise this state -# is retained across driver unload/reload cycles, but not across reboots. -# -#reset-configuration=1;
--- a/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c Tue Mar 16 09:30:41 2010 -0700 @@ -29,8 +29,8 @@ #include <sys/pci.h> #include "audiohd.h" -#define DEFINTS 175 #define DRVNAME "audiohd" + /* * Module linkage routines for the kernel */ @@ -631,9 +631,7 @@ ddi_report_dev(dip); /* enable interrupt */ - AUDIOHD_REG_SET32(AUDIOHD_REG_INTCTL, - AUDIOHD_INTCTL_BIT_GIE | - AUDIOHD_INTCTL_BIT_SIE); + AUDIOHD_REG_SET32(AUDIOHD_REG_INTCTL, AUDIOHD_INTCTL_BIT_GIE); return (DDI_SUCCESS); error: audiohd_destroy(statep); @@ -984,14 +982,14 @@ if (!path) continue; switch (path->path_type) { - case PLAY: - audiohd_init_play_path(path); - break; - case RECORD: - audiohd_init_record_path(path); - break; - default: - break; + case PLAY: + audiohd_init_play_path(path); + break; + case RECORD: + audiohd_init_record_path(path); + break; + default: + break; } } statep->in_port = 0; @@ -1032,7 +1030,7 @@ if (!bTmp) { audio_dev_warn(statep->adev, "Failed to reset stream %d", port->index); - return (DDI_FAILURE); + return (EIO); } /* Empirical testing time, which works well */ @@ -1055,7 +1053,7 @@ audio_dev_warn(statep->adev, "Failed to exit reset state for" " stream %d, bTmp=0x%02x", port->index, bTmp); - return (DDI_FAILURE); + return (EIO); } AUDIOHD_REG_SET32(regbase + AUDIOHD_SDREG_OFFSET_BDLPL, @@ -1064,8 +1062,7 @@ (uint32_t)(port->bdl_paddr >> 32)); AUDIOHD_REG_SET16(regbase + AUDIOHD_SDREG_OFFSET_LVI, AUDIOHD_BDLE_NUMS - 1); - AUDIOHD_REG_SET32(regbase + AUDIOHD_SDREG_OFFSET_CBL, - port->samp_size * AUDIOHD_BDLE_NUMS); + AUDIOHD_REG_SET32(regbase + AUDIOHD_SDREG_OFFSET_CBL, port->bufsize); AUDIOHD_REG_SET16(regbase + AUDIOHD_SDREG_OFFSET_FORMAT, port->format << 4 | port->nchan - 1); @@ -1080,73 +1077,40 @@ AUDIOHD_PLAY_CTL_OFF, (port->index) << AUDIOHD_PLAY_TAG_OFF); - return (DDI_SUCCESS); + return (0); } static int -audiohd_engine_open(void *arg, int flag, - unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp) +audiohd_engine_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp) { audiohd_port_t *port = arg; - audiohd_state_t *statep = port->statep; _NOTE(ARGUNUSED(flag)); - mutex_enter(&statep->hda_mutex); - (void) audiohd_reset_port(port); - mutex_exit(&statep->hda_mutex); - - port->started = B_FALSE; port->count = 0; port->curpos = 0; - *fragfrp = port->fragfr; - *nfragsp = AUDIOHD_BDLE_NUMS; + *nframes = port->nframes; *bufp = port->samp_kaddr; return (0); } -static void -audiohd_start_port(audiohd_port_t *port) -{ - audiohd_state_t *statep = port->statep; - - ASSERT(mutex_owned(&statep->hda_mutex)); - /* if suspended, then do nothing else */ - if (statep->suspended) { - return; - } - - /* Enable interrupt and start DMA */ - AUDIOHD_REG_SET8(port->regoff + AUDIOHD_SDREG_OFFSET_CTL, - AUDIOHDR_SD_CTL_INTS | AUDIOHDR_SD_CTL_SRUN); -} - -static void -audiohd_stop_port(audiohd_port_t *port) -{ - audiohd_state_t *statep = port->statep; - - ASSERT(mutex_owned(&statep->hda_mutex)); - /* if suspended, then do nothing else */ - if (statep->suspended) { - return; - } - AUDIOHD_REG_SET8(port->regoff + AUDIOHD_SDREG_OFFSET_CTL, 0); -} - static int audiohd_engine_start(void *arg) { audiohd_port_t *port = arg; audiohd_state_t *statep = port->statep; + int rv; mutex_enter(&statep->hda_mutex); - if (!port->started) { - audiohd_start_port(port); - port->started = B_TRUE; - port->triggered = B_TRUE; + + if ((rv = audiohd_reset_port(port)) != 0) { + return (rv); } + /* Start DMA */ + AUDIOHD_REG_SET8(port->regoff + AUDIOHD_SDREG_OFFSET_CTL, + AUDIOHDR_SD_CTL_SRUN); + mutex_exit(&statep->hda_mutex); return (0); } @@ -1158,17 +1122,14 @@ audiohd_state_t *statep = port->statep; mutex_enter(&statep->hda_mutex); - if (port->started) { - audiohd_stop_port(port); - } - port->started = B_FALSE; + AUDIOHD_REG_SET8(port->regoff + AUDIOHD_SDREG_OFFSET_CTL, 0); mutex_exit(&statep->hda_mutex); } static void audiohd_update_port(audiohd_port_t *port) { - int pos; + uint32_t pos; uint32_t len; audiohd_state_t *statep = port->statep; @@ -1176,9 +1137,10 @@ /* Convert the position into a frame count */ pos /= (port->nchan * 2); - if (pos >= port->curpos) + ASSERT(pos <= port->nframes); + if (pos >= port->curpos) { len = (pos - port->curpos); - else { + } else { len = pos + port->nframes - port->curpos; } @@ -1195,8 +1157,7 @@ uint64_t val; mutex_enter(&statep->hda_mutex); - if (port->started && !statep->suspended) - audiohd_update_port(port); + audiohd_update_port(port); val = port->count; mutex_exit(&statep->hda_mutex); return (val); @@ -1205,14 +1166,7 @@ static void audiohd_engine_close(void *arg) { - audiohd_port_t *port = arg; - audiohd_state_t *statep = port->statep; - - mutex_enter(&statep->hda_mutex); - audiohd_stop_port(port); - port->started = B_FALSE; - port->triggered = B_FALSE; - mutex_exit(&statep->hda_mutex); + _NOTE(ARGUNUSED(arg)); } static void @@ -1222,8 +1176,7 @@ _NOTE(ARGUNUSED(nframes)); - (void) ddi_dma_sync(port->samp_dmah, 0, - 0, port->sync_dir); + (void) ddi_dma_sync(port->samp_dmah, 0, 0, port->sync_dir); } @@ -1247,11 +1200,8 @@ audiohd_get_value(void *arg, uint64_t *val) { audiohd_ctrl_t *pc = arg; - audiohd_state_t *statep = pc->statep; - - mutex_enter(&statep->hda_mutex); + *val = pc->val; - mutex_exit(&statep->hda_mutex); return (0); } @@ -5121,7 +5071,6 @@ audiohd_port_t *port; int dir; unsigned caps; - char *prop; int rc; audio_dev_t *adev; dev_info_t *dip; @@ -5156,13 +5105,10 @@ for (i = 0; i < PORT_MAX; i++) { port = kmem_zalloc(sizeof (*port), KM_SLEEP); - port->started = B_FALSE; - port->triggered = B_FALSE; statep->port[i] = port; port->statep = statep; switch (i) { case PORT_ADC: - prop = "record-interrupts"; dir = DDI_DMA_READ | DDI_DMA_CONSISTENT; caps = ENGINE_INPUT_CAP; port->sync_dir = DDI_DMA_SYNC_FORKERNEL; @@ -5171,7 +5117,6 @@ port->regoff = AUDIOHD_REG_SD_BASE; break; case PORT_DAC: - prop = "play-interrupts"; dir = DDI_DMA_WRITE | DDI_DMA_CONSISTENT; caps = ENGINE_OUTPUT_CAP; port->sync_dir = DDI_DMA_SYNC_FORDEV; @@ -5185,29 +5130,10 @@ return (DDI_FAILURE); } - port->intrs = ddi_prop_get_int(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, prop, AUDIOHD_INTS); - - /* make sure the values are good */ - if (port->intrs < AUDIOHD_MIN_INTS) { - audio_dev_warn(adev, "%s too low, %d, resetting to %d", - prop, port->intrs, AUDIOHD_INTS); - port->intrs = AUDIOHD_INTS; - } else if (port->intrs > AUDIOHD_MAX_INTS) { - audio_dev_warn(adev, "%s too high, %d, resetting to %d", - prop, port->intrs, AUDIOHD_INTS); - port->intrs = AUDIOHD_INTS; - } - port->format = AUDIOHD_FMT_PCM; - port->fragfr = 48000 / port->intrs; - port->fragfr = AUDIOHD_ROUNDUP(port->fragfr, - AUDIOHD_FRAGFR_ALIGN); - port->samp_size = port->fragfr * port->nchan * 2; - port->samp_size = AUDIOHD_ROUNDUP(port->samp_size, - AUDIOHD_BDLE_BUF_ALIGN); - port->nframes = port->samp_size * AUDIOHD_BDLE_NUMS / - (port->nchan * 2); + port->nframes = 1024 * AUDIOHD_BDLE_NUMS; + port->fragsize = 1024 * port->nchan * 2; + port->bufsize = port->nframes * port->nchan * 2; /* allocate dma handle */ rc = ddi_dma_alloc_handle(dip, &dma_attr, DDI_DMA_SLEEP, @@ -5228,17 +5154,13 @@ * complex architectures with nested IO caches, * reliance on this flag might lead to failure. */ - rc = ddi_dma_mem_alloc(port->samp_dmah, port->samp_size * - AUDIOHD_BDLE_NUMS, - &hda_dev_accattr, - DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED, + rc = ddi_dma_mem_alloc(port->samp_dmah, port->bufsize, + &hda_dev_accattr, DDI_DMA_CONSISTENT | IOMEM_DATA_UNCACHED, DDI_DMA_SLEEP, NULL, &port->samp_kaddr, &real_size, &port->samp_acch); if (rc == DDI_FAILURE) { - if (ddi_dma_mem_alloc(port->samp_dmah, - port->samp_size * AUDIOHD_BDLE_NUMS, - &hda_dev_accattr, - DDI_DMA_CONSISTENT, + if (ddi_dma_mem_alloc(port->samp_dmah, port->bufsize, + &hda_dev_accattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->samp_kaddr, &real_size, &port->samp_acch) != DDI_SUCCESS) { @@ -5302,9 +5224,9 @@ for (j = 0; j < AUDIOHD_BDLE_NUMS; j++) { entry->sbde_addr = buf_phys_addr; - entry->sbde_len = port->samp_size; + entry->sbde_len = port->fragsize; entry->sbde_ioc = 1; - buf_phys_addr += port->samp_size; + buf_phys_addr += port->fragsize; entry++; } (void) ddi_dma_sync(port->bdl_dmah, 0, sizeof (sd_bdle_t) * @@ -5415,32 +5337,6 @@ } /* - * restore_play_and_record() - */ -static void -audiohd_restore_play_and_record(audiohd_state_t *statep) -{ - int i; - audiohd_port_t *port; - - mutex_enter(&statep->hda_mutex); - for (i = 0; i < PORT_MAX; i++) { - port = statep->port[i]; - if (port == NULL) - continue; - - audio_engine_reset(port->engine); - if (port->triggered) { - (void) audiohd_reset_port(port); - audiohd_start_port(port); - } else { - audiohd_stop_port(port); - - } - } - mutex_exit(&statep->hda_mutex); -} -/* * audiohd_reset_pins_ur_cap() * Description: * Enable the unsolicited response of the pins which have the unsolicited @@ -5539,20 +5435,18 @@ /* reset to enable the capability of unsolicited response for pin */ audiohd_reset_pins_ur_cap(statep); /* Enable interrupt */ - AUDIOHD_REG_SET32(AUDIOHD_REG_INTCTL, - AUDIOHD_INTCTL_BIT_GIE | - AUDIOHD_INTCTL_BIT_SIE); + AUDIOHD_REG_SET32(AUDIOHD_REG_INTCTL, AUDIOHD_INTCTL_BIT_GIE); /* clear the unsolicited response interrupt */ rirbsts = AUDIOHD_REG_GET8(AUDIOHD_REG_RIRBSTS); AUDIOHD_REG_SET8(AUDIOHD_REG_RIRBSTS, rirbsts); - mutex_exit(&statep->hda_mutex); - - audiohd_restore_play_and_record(statep); + /* set widget power to D0 */ + audiohd_change_widget_power_state(statep, AUDIOHD_PW_D0); + audiohd_configure_output(statep); audiohd_configure_input(statep); - - /* set widget power to D0 */ - audiohd_change_widget_power_state(statep, AUDIOHD_PW_D0); + mutex_exit(&statep->hda_mutex); + + audio_dev_resume(statep->adev); return (DDI_SUCCESS); } /* audiohd_resume */ @@ -5563,6 +5457,8 @@ static int audiohd_suspend(audiohd_state_t *statep) { + audio_dev_suspend(statep->adev); + mutex_enter(&statep->hda_mutex); statep->suspended = B_TRUE; @@ -5580,22 +5476,38 @@ /* * audiohd_disable_pin() */ -static int +static void audiohd_disable_pin(audiohd_state_t *statep, int caddr, wid_t wid) { - AUDIOHD_DISABLE_PIN_OUT(statep, caddr, wid); - return (DDI_SUCCESS); + uint32_t tmp; + + tmp = audioha_codec_verb_get(statep, caddr, wid, + AUDIOHDC_VERB_GET_PIN_CTRL, 0); + if (tmp == AUDIOHD_CODEC_FAILURE) + return; + tmp = audioha_codec_verb_get(statep, caddr, wid, + AUDIOHDC_VERB_SET_PIN_CTRL, + (tmp & ~AUDIOHDC_PIN_CONTROL_OUT_ENABLE)); } /* * audiohd_enable_pin() */ -static int +static void audiohd_enable_pin(audiohd_state_t *statep, int caddr, wid_t wid) { - AUDIOHD_ENABLE_PIN_OUT(statep, caddr, wid); - return (DDI_SUCCESS); + uint32_t tmp; + + tmp = audioha_codec_verb_get(statep, caddr, wid, + AUDIOHDC_VERB_GET_PIN_CTRL, 0); + if (tmp == AUDIOHD_CODEC_FAILURE) + return; + tmp = audioha_codec_verb_get(statep, caddr, wid, + AUDIOHDC_VERB_SET_PIN_CTRL, + tmp | AUDIOHDC_PIN_CONTROL_OUT_ENABLE | + AUDIOHDC_PIN_CONTROL_HP_ENABLE); } + /* * audiohd_change_speaker_state() */ @@ -5618,8 +5530,7 @@ widget = path->codec->widget[wid]; pin = (audiohd_pin_t *)widget->priv; if (pin->device == DTYPE_SPEAKER) { - (void) audiohd_enable_pin( - statep, + audiohd_enable_pin(statep, path->codec->index, pin->wid); } @@ -5631,8 +5542,7 @@ widget = path->codec->widget[wid]; pin = (audiohd_pin_t *)widget->priv; if (pin->device == DTYPE_SPEAKER) { - (void) audiohd_disable_pin( - statep, + audiohd_disable_pin(statep, path->codec->index, pin->wid); } @@ -5830,15 +5740,11 @@ { audiohd_state_t *statep = (void *)arg1; uint32_t status; - uint32_t regbase; uint32_t resp, respex; - uint8_t sdstatus, rirbsts; + uint8_t rirbsts; int i, ret; _NOTE(ARGUNUSED(arg2)) - audio_engine_t *do_adc = NULL; - audio_engine_t *do_dac = NULL; - mutex_enter(&statep->hda_mutex); if (statep->suspended) { @@ -5889,22 +5795,6 @@ } } - /* stream intr */ - for (i = 0; i < statep->hda_streams_nums; i++) { - if ((status & (1<<i)) == 0) - continue; - - regbase = AUDIOHD_REG_SD_BASE + AUDIOHD_REG_SD_LEN * i; - sdstatus = AUDIOHD_REG_GET8(regbase + AUDIOHD_SDREG_OFFSET_STS); - - /* clear intrs */ - AUDIOHD_REG_SET8(regbase + AUDIOHD_SDREG_OFFSET_STS, sdstatus); - if (i < statep->hda_input_streams) - do_adc = statep->port[PORT_ADC]->engine; - else - do_dac = statep->port[PORT_DAC]->engine; - } - /* update the kernel interrupt statistics */ if (statep->hda_ksp) { ((kstat_intr_t *) @@ -5913,10 +5803,6 @@ mutex_exit(&statep->hda_mutex); - if (do_adc) - audio_engine_produce(do_adc); - if (do_dac) - audio_engine_consume(do_dac); return (DDI_INTR_CLAIMED); } /* audiohd_intr() */
--- a/usr/src/uts/common/io/audio/drv/audiohd/audiohd.conf Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiohd/audiohd.conf Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # @@ -28,22 +28,5 @@ # WARNING: This is an UNSTABLE configuration file. Its contents # may change at any time. -# -# play-interrupts sets the number of interrupts per second when playing. -# This affects the resolution of various things, such as sample counts. -# record-interrupts does the same for record interrupts. -# -# These may be tuned to get more accurate information by increasing the -# count. However, the larger the interrupts per second the larger the -# load on the system. So use this capability cautiously. The audiohd -# driver enforces a maximum and minimum count. -# -# It should also be understood that not all interrupt rates are legal. -# The hardware is restricted to DMA buffers being allocated on certain -# boundaries. If those boundaries are violated the driver will not be -# loaded and an error message is entered into the messages log -# -play-interrupts=175; -record-interrupts=175; audiohd_beep=1; msi_enable=1;
--- a/usr/src/uts/common/io/audio/drv/audiohd/audiohd.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiohd/audiohd.h Tue Mar 16 09:30:41 2010 -0700 @@ -56,10 +56,6 @@ #define NO_SPDIF 0x00000004 #define EN_PIN_BEEP 0x00000008 -#define AUDIOHD_INTS 50 -#define AUDIOHD_MAX_INTS 1500 -#define AUDIOHD_MIN_INTS 32 - #define AUDIOHD_DEV_CONFIG "onboard1" #define AUDIOHD_DEV_VERSION "a" @@ -179,7 +175,6 @@ #define AUDIOHD_CODEC_TYPE_MASK 0x000000ff #define AUDIOHD_ROUNDUP(x, algn) (((x) + ((algn) - 1)) & ~((algn) - 1)) -#define AUDIOHD_FRAGFR_ALIGN 64 #define AUDIOHD_BDLE_BUF_ALIGN 128 #define AUDIOHD_CMDIO_ENT_MASK 0x00ff /* 256 entries for CORB/RIRB */ #define AUDIOHD_CDBIO_CORB_LEN 1024 /* 256 entries for CORB, 1024B */ @@ -267,11 +262,6 @@ #define AUDIOHDR_SD_CTL_IOCE 0x000004 #define AUDIOHDR_SD_CTL_SRUN 0x000002 #define AUDIOHDR_SD_CTL_SRST 0x000001 -#define AUDIOHDR_SD_CTL_INTS \ - (AUDIOHDR_SD_CTL_DEIE | \ - AUDIOHDR_SD_CTL_FEIE | \ - AUDIOHDR_SD_CTL_IOCE) - /* bits for stream descriptor status register */ #define AUDIOHDR_SD_STS_BCIS 0x0004 @@ -680,21 +670,18 @@ uint8_t nchan; int index; uint16_t regoff; - boolean_t started; - boolean_t triggered; - unsigned fragfr; unsigned nframes; + size_t bufsize; + size_t fragsize; uint64_t count; int curpos; - int intrs; uint_t format; unsigned sync_dir; ddi_dma_handle_t samp_dmah; ddi_acc_handle_t samp_acch; - size_t samp_size; caddr_t samp_kaddr; uint64_t samp_paddr; @@ -817,10 +804,6 @@ audio_dev_t *adev; uint32_t devid; - - int hda_pint_freq; /* play intr frequence */ - int hda_rint_freq; /* record intr frequence */ - int hda_input_streams; /* # of input stream */ int hda_output_streams; /* # of output stream */ int hda_streams_nums; /* # of stream */ @@ -910,43 +893,6 @@ /* - * enable a pin widget to output - */ -#define AUDIOHD_ENABLE_PIN_OUT(statep, caddr, wid) \ -{ \ - uint32_t lTmp; \ -\ - lTmp = audioha_codec_verb_get(statep, caddr, wid, \ - AUDIOHDC_VERB_GET_PIN_CTRL, 0); \ - if (lTmp == AUDIOHD_CODEC_FAILURE) \ - return (DDI_FAILURE); \ - lTmp = audioha_codec_verb_get(statep, caddr, wid, \ - AUDIOHDC_VERB_SET_PIN_CTRL, \ - (lTmp | AUDIOHDC_PIN_CONTROL_OUT_ENABLE | \ - AUDIOHDC_PIN_CONTROL_HP_ENABLE)); \ - if (lTmp == AUDIOHD_CODEC_FAILURE) \ - return (DDI_FAILURE); \ -} - -/* - * disable output pin - */ -#define AUDIOHD_DISABLE_PIN_OUT(statep, caddr, wid) \ -{ \ - uint32_t lTmp; \ -\ - lTmp = audioha_codec_verb_get(statep, caddr, wid, \ - AUDIOHDC_VERB_GET_PIN_CTRL, 0); \ - if (lTmp == AUDIOHD_CODEC_FAILURE) \ - return (DDI_FAILURE); \ - lTmp = audioha_codec_verb_get(statep, caddr, wid, \ - AUDIOHDC_VERB_SET_PIN_CTRL, \ - (lTmp & ~AUDIOHDC_PIN_CONTROL_OUT_ENABLE)); \ - if (lTmp == AUDIOHD_CODEC_FAILURE) \ - return (DDI_FAILURE); \ -} - -/* * enable a pin widget to input */ #define AUDIOHD_ENABLE_PIN_IN(statep, caddr, wid) \
--- a/usr/src/uts/common/io/audio/drv/audioixp/audioixp.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audioixp/audioixp.c Tue Mar 16 09:30:41 2010 -0700 @@ -67,7 +67,7 @@ /* * Entry point routine prototypes */ -static int audioixp_open(void *, int, unsigned *, unsigned *, caddr_t *); +static int audioixp_open(void *, int, unsigned *, caddr_t *); static void audioixp_close(void *); static int audioixp_start(void *); static void audioixp_stop(void *); @@ -107,19 +107,11 @@ }; /* - * interrupt handler - */ -static uint_t audioixp_intr(caddr_t); - -/* * Local Routine Prototypes */ static int audioixp_attach(dev_info_t *); static int audioixp_detach(dev_info_t *); static int audioixp_alloc_port(audioixp_state_t *, int); -static void audioixp_start_port(audioixp_port_t *); -static void audioixp_stop_port(audioixp_port_t *); -static void audioixp_reset_port(audioixp_port_t *); static void audioixp_update_port(audioixp_port_t *); static int audioixp_codec_sync(audioixp_state_t *); @@ -357,20 +349,6 @@ } } -static void -audioixp_stop_dma(audioixp_state_t *statep) -{ - CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA); - CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA); -} - -static void -audioixp_disable_intr(audioixp_state_t *statep) -{ - PUT32(IXP_AUDIO_INT, GET32(IXP_AUDIO_INT)); - PUT32(IXP_AUDIO_INT_EN, 0); -} - /* * quiesce(9E) entry point. * @@ -388,11 +366,11 @@ statep = ddi_get_driver_private(dip); ASSERT(statep != NULL); - /* disable HW interrupt */ - audioixp_disable_intr(statep); - /* stop DMA engines */ - audioixp_stop_dma(statep); + CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT); + CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA); + CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN); + CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA); return (DDI_SUCCESS); } @@ -405,135 +383,28 @@ statep = ddi_get_driver_private(dip); ASSERT(statep != NULL); - ac97_suspend(statep->ac97); - mutex_enter(&statep->inst_lock); - - statep->suspended = B_TRUE; - - audioixp_disable_intr(statep); - audioixp_stop_dma(statep); - - mutex_exit(&statep->inst_lock); - return (DDI_SUCCESS); -} + audio_dev_suspend(statep->adev); -static void -audioixp_resume_port(audioixp_port_t *port) -{ - if (port != NULL) { - if (port->engine != NULL) { - audio_engine_reset(port->engine); - } - } - audioixp_reset_port(port); - if (port->started) { - audioixp_start_port(port); - } else { - audioixp_stop_port(port); - } + return (DDI_SUCCESS); } static int audioixp_resume(dev_info_t *dip) { audioixp_state_t *statep; - audio_dev_t *adev; - audioixp_port_t *rec_port, *play_port; statep = ddi_get_driver_private(dip); - adev = statep->adev; ASSERT(statep != NULL); if (audioixp_chip_init(statep) != DDI_SUCCESS) { - audio_dev_warn(adev, "DDI_RESUME failed to init chip"); + audio_dev_warn(statep->adev, "DDI_RESUME failed to init chip"); return (DDI_SUCCESS); } - ac97_resume(statep->ac97); - mutex_enter(&statep->inst_lock); - statep->suspended = B_FALSE; - - rec_port = statep->rec_port; - play_port = statep->play_port; - - audioixp_resume_port(rec_port); - audioixp_resume_port(play_port); - - mutex_exit(&statep->inst_lock); - return (DDI_SUCCESS); -} -/* - * audioixp_intr() - * - * Description: - * Interrupt service routine for both play and record. For play we - * get the next buffers worth of audio. For record we send it on to - * the mixer. - * - * There's a hardware pointer which indicate memory location where - * the hardware is processing. We check this pointer to decide whether - * to handle the buffer and how many buffers should be handled. - * Refer to ATI IXP400/450 Register Reference Manual, page 193,194. - * - * Arguments: - * caddr_t arg Pointer to the interrupting device's state - * structure - * - * Returns: - * DDI_INTR_CLAIMED Interrupt claimed and processed - * DDI_INTR_UNCLAIMED Interrupt not claimed, and thus ignored - */ -static uint_t -audioixp_intr(caddr_t arg) -{ - audioixp_state_t *statep; - uint32_t sr; - int claimed = DDI_INTR_UNCLAIMED; + ac97_reset(statep->ac97); + audio_dev_resume(statep->adev); - statep = (void *)arg; - mutex_enter(&statep->inst_lock); - - sr = GET32(IXP_AUDIO_INT); - - /* PCM in interrupt */ - if (sr & IXP_AUDIO_INT_IN_DMA) { - claimed = DDI_INTR_CLAIMED; - PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_IN_DMA); - } - - /* PCM out interrupt */ - if (sr & IXP_AUDIO_INT_OUT_DMA) { - claimed = DDI_INTR_CLAIMED; - PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_OUT_DMA); - } - - /* system is too busy to process the input stream, ignore it */ - if (sr & IXP_AUDIO_INT_IN_DMA_OVERFLOW) { - claimed = DDI_INTR_CLAIMED; - PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_IN_DMA_OVERFLOW); - } - - /* System is too busy, ignore it */ - if (sr & IXP_AUDIO_INT_OUT_DMA_UNDERFLOW) { - claimed = DDI_INTR_CLAIMED; - PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_OUT_DMA_UNDERFLOW); - } - - /* update the kernel interrupt statistics */ - if ((claimed == DDI_INTR_CLAIMED) && statep->ksp) { - IXP_KIOP(statep)->intrs[KSTAT_INTR_HARD]++; - } - - mutex_exit(&statep->inst_lock); - - if (sr & IXP_AUDIO_INT_IN_DMA) { - audio_engine_produce(statep->rec_port->engine); - } - if (sr & IXP_AUDIO_INT_OUT_DMA) { - audio_engine_consume(statep->play_port->engine); - } - - return (claimed); + return (DDI_SUCCESS); } /* @@ -545,8 +416,7 @@ * Arguments: * void *arg The DMA engine to set up * int flag Open flags - * unsigned *fragfrp Receives number of frames per fragment - * unsigned *nfragsp Receives number of fragments + * unsigned *nframesp Receives number of frames * caddr_t *bufp Receives kernel data buffer * * Returns: @@ -554,8 +424,7 @@ * errno on failure */ static int -audioixp_open(void *arg, int flag, - unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp) +audioixp_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp) { audioixp_port_t *port = arg; @@ -564,14 +433,9 @@ port->started = B_FALSE; port->count = 0; port->offset = 0; - *fragfrp = port->fragfr; - *nfragsp = IXP_BD_NUMS; + *nframesp = port->nframes; *bufp = port->samp_kaddr; - mutex_enter(&port->statep->inst_lock); - audioixp_reset_port(port); - mutex_exit(&port->statep->inst_lock); - return (0); } @@ -589,13 +453,7 @@ static void audioixp_close(void *arg) { - audioixp_port_t *port = arg; - audioixp_state_t *statep = port->statep; - - mutex_enter(&statep->inst_lock); - audioixp_stop_port(port); - port->started = B_FALSE; - mutex_exit(&statep->inst_lock); + _NOTE(ARGUNUSED(arg)); } /* @@ -615,10 +473,13 @@ audioixp_state_t *statep = port->statep; mutex_enter(&statep->inst_lock); - if (port->started) { - audioixp_stop_port(port); + if (port->num == IXP_REC) { + CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN); + CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA); + } else { + CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT); + CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA); } - port->started = B_FALSE; mutex_exit(&statep->inst_lock); } @@ -641,9 +502,53 @@ audioixp_state_t *statep = port->statep; mutex_enter(&statep->inst_lock); - if (!port->started) { - audioixp_start_port(port); - port->started = B_TRUE; + + port->offset = 0; + + if (port->num == IXP_REC) { + PUT32(IXP_AUDIO_FIFO_FLUSH, IXP_AUDIO_FIFO_FLUSH_IN); + SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_INTER_IN); + + SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA); + PUT32(IXP_AUDIO_IN_DMA_LINK_P, + port->bdl_paddr | IXP_AUDIO_IN_DMA_LINK_P_EN); + + SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN); + } else { + uint32_t slot = GET32(IXP_AUDIO_OUT_DMA_SLOT_EN_THRESHOLD); + PUT32(IXP_AUDIO_FIFO_FLUSH, IXP_AUDIO_FIFO_FLUSH_OUT); + /* clear all slots */ + slot &= ~ (IXP_AUDIO_OUT_DMA_SLOT_3 | + IXP_AUDIO_OUT_DMA_SLOT_4 | + IXP_AUDIO_OUT_DMA_SLOT_5 | + IXP_AUDIO_OUT_DMA_SLOT_6 | + IXP_AUDIO_OUT_DMA_SLOT_7 | + IXP_AUDIO_OUT_DMA_SLOT_8 | + IXP_AUDIO_OUT_DMA_SLOT_9 | + IXP_AUDIO_OUT_DMA_SLOT_10 | + IXP_AUDIO_OUT_DMA_SLOT_11 | + IXP_AUDIO_OUT_DMA_SLOT_12); + /* enable AC'97 output slots (depending on output channels) */ + slot |= IXP_AUDIO_OUT_DMA_SLOT_3 | + IXP_AUDIO_OUT_DMA_SLOT_4; + if (port->nchan >= 4) { + slot |= IXP_AUDIO_OUT_DMA_SLOT_6 | + IXP_AUDIO_OUT_DMA_SLOT_9; + } + if (port->nchan >= 6) { + slot |= IXP_AUDIO_OUT_DMA_SLOT_7 | + IXP_AUDIO_OUT_DMA_SLOT_8; + } + + PUT32(IXP_AUDIO_OUT_DMA_SLOT_EN_THRESHOLD, slot); + + SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_INTER_OUT); + + SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA); + PUT32(IXP_AUDIO_OUT_DMA_LINK_P, + port->bdl_paddr | IXP_AUDIO_OUT_DMA_LINK_P_EN); + + SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT); } mutex_exit(&statep->inst_lock); return (0); @@ -778,7 +683,6 @@ uint_t count; int dir; unsigned caps; - char *prop; audio_dev_t *adev; audioixp_port_t *port; uint32_t paddr; @@ -797,7 +701,6 @@ switch (num) { case IXP_REC: statep->rec_port = port; - prop = "record-interrupts"; dir = DDI_DMA_READ; caps = ENGINE_INPUT_CAP; port->sync_dir = DDI_DMA_SYNC_FORKERNEL; @@ -805,7 +708,6 @@ break; case IXP_PLAY: statep->play_port = port; - prop = "play-interrupts"; dir = DDI_DMA_WRITE; caps = ENGINE_OUTPUT_CAP; port->sync_dir = DDI_DMA_SYNC_FORDEV; @@ -836,29 +738,10 @@ return (DDI_FAILURE); } - port->intrs = ddi_prop_get_int(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, prop, IXP_INTS); - - /* make sure the values are good */ - if (port->intrs < IXP_MIN_INTS) { - audio_dev_warn(adev, "%s too low, %d, resetting to %d", - prop, port->intrs, IXP_INTS); - port->intrs = IXP_INTS; - } else if (port->intrs > IXP_MAX_INTS) { - audio_dev_warn(adev, "%s too high, %d, resetting to %d", - prop, port->intrs, IXP_INTS); - port->intrs = IXP_INTS; - } - - /* - * Figure out how much space we need. Sample rate is 48kHz, and - * we need to store 8 chunks. (Note that this means that low - * interrupt frequencies will require more RAM.) - */ - port->fragfr = 48000 / port->intrs; - port->fragfr = IXP_ROUNDUP(port->fragfr, IXP_MOD_SIZE); + port->nframes = 4096; + port->fragfr = port->nframes / IXP_BD_NUMS; port->fragsz = port->fragfr * port->nchan * 2; - port->samp_size = port->fragsz * IXP_BD_NUMS; + port->samp_size = port->nframes * port->nchan * 2; /* allocate dma handle */ rc = ddi_dma_alloc_handle(dip, &sample_buf_dma_attr, DDI_DMA_SLEEP, @@ -992,130 +875,6 @@ } /* - * audioixp_start_port() - * - * Description: - * This routine starts the DMA engine. - * - * Arguments: - * audioixp_port_t *port Port of DMA engine to start. - */ -static void -audioixp_start_port(audioixp_port_t *port) -{ - audioixp_state_t *statep = port->statep; - - ASSERT(mutex_owned(&statep->inst_lock)); - - /* if suspended, then do nothing else */ - if (statep->suspended) { - return; - } - - if (port->num == IXP_REC) { - SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN); - } else { - SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT); - } -} - -/* - * audioixp_stop_port() - * - * Description: - * This routine stops the DMA engine. - * - * Arguments: - * audioixp_port_t *port Port of DMA engine to stop. - */ -static void -audioixp_stop_port(audioixp_port_t *port) -{ - audioixp_state_t *statep = port->statep; - - ASSERT(mutex_owned(&statep->inst_lock)); - - /* if suspended, then do nothing else */ - if (statep->suspended) { - return; - } - if (port->num == IXP_REC) { - CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN); - } else { - CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT); - } -} - -/* - * audioixp_reset_port() - * - * Description: - * This routine resets the DMA engine pareparing it for work. - * - * Arguments: - * audioixp_port_t *port Port of DMA engine to reset. - */ -static void -audioixp_reset_port(audioixp_port_t *port) -{ - audioixp_state_t *statep = port->statep; - - ASSERT(mutex_owned(&statep->inst_lock)); - - port->offset = 0; - - if (statep->suspended) - return; - - /* - * Perform full reset of the engine, and enable its interrupts - * but leave it turned off. - */ - if (port->num == IXP_REC) { - PUT32(IXP_AUDIO_FIFO_FLUSH, IXP_AUDIO_FIFO_FLUSH_IN); - SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_INTER_IN); - - SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA); - PUT32(IXP_AUDIO_IN_DMA_LINK_P, - port->bdl_paddr | IXP_AUDIO_IN_DMA_LINK_P_EN); - - } else { - uint32_t slot = GET32(IXP_AUDIO_OUT_DMA_SLOT_EN_THRESHOLD); - PUT32(IXP_AUDIO_FIFO_FLUSH, IXP_AUDIO_FIFO_FLUSH_OUT); - /* clear all slots */ - slot &= ~ (IXP_AUDIO_OUT_DMA_SLOT_3 | - IXP_AUDIO_OUT_DMA_SLOT_4 | - IXP_AUDIO_OUT_DMA_SLOT_5 | - IXP_AUDIO_OUT_DMA_SLOT_6 | - IXP_AUDIO_OUT_DMA_SLOT_7 | - IXP_AUDIO_OUT_DMA_SLOT_8 | - IXP_AUDIO_OUT_DMA_SLOT_9 | - IXP_AUDIO_OUT_DMA_SLOT_10 | - IXP_AUDIO_OUT_DMA_SLOT_11 | - IXP_AUDIO_OUT_DMA_SLOT_12); - /* enable AC'97 output slots (depending on output channels) */ - slot |= IXP_AUDIO_OUT_DMA_SLOT_3 | - IXP_AUDIO_OUT_DMA_SLOT_4; - if (port->nchan >= 4) { - slot |= IXP_AUDIO_OUT_DMA_SLOT_6 | - IXP_AUDIO_OUT_DMA_SLOT_9; - } - if (port->nchan >= 6) { - slot |= IXP_AUDIO_OUT_DMA_SLOT_7 | - IXP_AUDIO_OUT_DMA_SLOT_8; - } - - PUT32(IXP_AUDIO_OUT_DMA_SLOT_EN_THRESHOLD, slot); - - SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_INTER_OUT); - - SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA); - PUT32(IXP_AUDIO_OUT_DMA_LINK_P, - port->bdl_paddr | IXP_AUDIO_OUT_DMA_LINK_P_EN); - } -} - -/* * audioixp_update_port() * * Description: @@ -1135,9 +894,6 @@ uint32_t offset; uint32_t paddr; - if (statep->suspended) { - return; - } if (port->num == IXP_REC) { regoff = IXP_AUDIO_IN_DMA_DT_CUR; } else { @@ -1453,13 +1209,6 @@ return (DDI_FAILURE); } - /* enable interrupts */ - PUT32(IXP_AUDIO_INT, 0xffffffff); - PUT32( - IXP_AUDIO_INT_EN, - IXP_AUDIO_INT_EN_IN_DMA_OVERFLOW | - IXP_AUDIO_INT_EN_STATUS | - IXP_AUDIO_INT_EN_OUT_DMA_UNDERFLOW); return (DDI_SUCCESS); } /* audioixp_chip_init() */ @@ -1489,27 +1238,11 @@ const char *name; const char *rev; - /* we don't support high level interrupts in the driver */ - if (ddi_intr_hilevel(dip, 0) != 0) { - cmn_err(CE_WARN, - "!%s%d: unsupported high level interrupt", - ddi_driver_name(dip), ddi_get_instance(dip)); - return (DDI_FAILURE); - } - /* allocate the soft state structure */ statep = kmem_zalloc(sizeof (*statep), KM_SLEEP); statep->dip = dip; ddi_set_driver_private(dip, statep); - - if (ddi_get_iblock_cookie(dip, 0, &statep->iblock) != DDI_SUCCESS) { - cmn_err(CE_WARN, - "!%s%d: cannot get iblock cookie", - ddi_driver_name(dip), ddi_get_instance(dip)); - kmem_free(statep, sizeof (*statep)); - return (DDI_FAILURE); - } - mutex_init(&statep->inst_lock, NULL, MUTEX_DRIVER, statep->iblock); + mutex_init(&statep->inst_lock, NULL, MUTEX_DRIVER, NULL); /* allocate framework audio device */ if ((adev = audio_dev_alloc(dip, 0)) == NULL) { @@ -1588,14 +1321,6 @@ } } - /* set up kernel statistics */ - if ((statep->ksp = kstat_create(IXP_NAME, ddi_get_instance(dip), - IXP_NAME, "controller", KSTAT_TYPE_INTR, 1, - KSTAT_FLAG_PERSISTENT)) != NULL) { - kstat_install(statep->ksp); - } - - if (audioixp_chip_init(statep) != DDI_SUCCESS) { audio_dev_warn(statep->adev, "failed to init chip"); goto error; @@ -1607,13 +1332,6 @@ goto error; } - /* set up the interrupt handler */ - if (ddi_add_intr(dip, 0, &statep->iblock, NULL, audioixp_intr, - (caddr_t)statep) != DDI_SUCCESS) { - audio_dev_warn(adev, "bad interrupt specification"); - } - statep->intr_added = B_TRUE; - if (audio_dev_register(adev) != DDI_SUCCESS) { audio_dev_warn(adev, "unable to register with framework"); goto error; @@ -1666,26 +1384,14 @@ * Arguments: * audioixp_state_t *state The device soft state. */ -void +static void audioixp_destroy(audioixp_state_t *statep) { - if (!statep->suspended) { - PUT32(IXP_AUDIO_INT, GET32(IXP_AUDIO_INT)); - PUT32(IXP_AUDIO_INT_EN, 0); - - /* - * put the audio controller into quiet state, everything off - */ - CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA); - CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA); - } - - if (statep->intr_added) { - ddi_remove_intr(statep->dip, 0, statep->iblock); - } - if (statep->ksp) { - kstat_delete(statep->ksp); - } + /* + * put the audio controller into quiet state, everything off + */ + CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT_DMA); + CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN_DMA); audioixp_free_port(statep->play_port); audioixp_free_port(statep->rec_port);
--- a/usr/src/uts/common/io/audio/drv/audioixp/audioixp.conf Tue Mar 16 09:43:38 2010 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# Configuration file for the audioixp audio driver. -# -# WARNING: This is an UNSTABLE configuration file. Its contents -# may change at any time. - - -# -# play-interrupts sets the number of interrupts per second when playing. -# This affects the resolution of various things, such as sample counts. -# record-interrupts does the same for record interrupts. -# -# These may be tuned to get more accurate information by increasing the -# count. However, the larger the interrupts per second the larger the -# load on the system. So use this capability cautiously. The audioixp -# driver enforces a maximum and minimum count. -# -# It should also be understood that not all interrupt rates are legal. -# The hardware is restricted to DMA buffers being allocated on certain -# boundaries. If those boundaries are violated the driver will not be -# loaded and an error message is entered into the messages log -# -play-interrupts=175; -record-interrupts=175;
--- a/usr/src/uts/common/io/audio/drv/audioixp/audioixp.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audioixp/audioixp.h Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -44,12 +44,7 @@ #define IXP_PLAY 0 #define IXP_REC 1 -#define IXP_ROUNDUP(x, algn) (((x) + ((algn) - 1)) & ~((algn) - 1)) -#define IXP_KIOP(X) ((kstat_intr_t *)(X->ksp->ks_data)) - #define IXP_BD_NUMS (8) -#define IXP_MOD_SIZE (32) - /* * PCI configuration registers and bits @@ -214,7 +209,7 @@ caddr_t bdl_kaddr; uint32_t bdl_paddr; - unsigned intrs; + unsigned nframes; unsigned fragfr; unsigned fragsz; uint64_t count; @@ -245,7 +240,6 @@ */ struct audioixp_state { kmutex_t inst_lock; /* state protection lock */ - ddi_iblock_cookie_t iblock; dev_info_t *dip; audio_dev_t *adev; /* audio handle */ ac97_t *ac97; @@ -257,11 +251,8 @@ caddr_t regsp; /* base of audio mixer regs */ boolean_t suspended; - boolean_t intr_added; boolean_t swap_out; /* swap line-out and sur-out */ - kstat_t *ksp; /* kernel statistics */ - uint32_t ixp_codec_not_ready_bits; /* for codec detect */ }; typedef struct audioixp_state audioixp_state_t;
--- a/usr/src/uts/common/io/audio/drv/audiols/audiols.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiols/audiols.c Tue Mar 16 09:30:41 2010 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -77,7 +77,7 @@ static int audigyls_detach(audigyls_dev_t *); static int audigyls_suspend(audigyls_dev_t *); -static int audigyls_open(void *, int, unsigned *, unsigned *, caddr_t *); +static int audigyls_open(void *, int, unsigned *, caddr_t *); static void audigyls_close(void *); static int audigyls_start(void *); static void audigyls_stop(void *); @@ -92,14 +92,8 @@ static uint16_t audigyls_read_ac97(void *, uint8_t); static void audigyls_write_ac97(void *, uint8_t, uint16_t); static int audigyls_alloc_port(audigyls_dev_t *, int); -static void audigyls_start_port(audigyls_port_t *); -static void audigyls_stop_port(audigyls_port_t *); -static void audigyls_update_port(audigyls_port_t *); -static void audigyls_reset_port(audigyls_port_t *); static void audigyls_destroy(audigyls_dev_t *); -static int audigyls_setup_intrs(audigyls_dev_t *); static void audigyls_hwinit(audigyls_dev_t *); -static uint_t audigyls_intr(caddr_t, caddr_t); static void audigyls_configure_mixer(audigyls_dev_t *dev); static audio_engine_ops_t audigyls_engine_ops = { @@ -309,92 +303,12 @@ return (1); } -static void -audigyls_update_port(audigyls_port_t *port) -{ - audigyls_dev_t *dev = port->dev; - uint32_t offset, n; - - if (dev->suspended) - return; - - if (port->direction == AUDIGYLS_PLAY_PORT) { - offset = read_chan(dev, CPFA, 0); - } else { - offset = read_chan(dev, CRFA, 2); - } - - - /* get the offset, and switch to frames */ - offset /= (2 * sizeof (uint16_t)); - - if (offset >= port->offset) { - n = offset - port->offset; - } else { - n = offset + (port->buf_frames - port->offset); - } - port->offset = offset; - port->count += n; -} - -static void -check_play_intr(audigyls_dev_t *dev) -{ - audigyls_port_t *port = dev->port[AUDIGYLS_PLAY_PORT]; - - if (!port->started) - return; - audio_engine_consume(port->engine); -} - -static void -check_rec_intr(audigyls_dev_t *dev) -{ - audigyls_port_t *port = dev->port[AUDIGYLS_REC_PORT]; - - if (!port->started) - return; - audio_engine_produce(port->engine); -} - -static uint_t -audigyls_intr(caddr_t argp, caddr_t nocare) -{ - audigyls_dev_t *dev = (void *)argp; - uint32_t status; - - _NOTE(ARGUNUSED(nocare)); - status = INL(dev, IPR); - - if (dev->suspended) { - OUTL(dev, IPR, status); /* Acknowledge */ - return (DDI_INTR_UNCLAIMED); - } - - if (!(status & (INTR_IT1 | INTR_PCI))) { - /* No audio interrupts pending */ - OUTL(dev, IPR, status); /* Acknowledge */ - return (DDI_INTR_UNCLAIMED); - } - - check_play_intr(dev); - check_rec_intr(dev); - - if (dev->ksp) { - AUDIGYLS_KIOP(dev)->intrs[KSTAT_INTR_HARD]++; - } - - OUTL(dev, IPR, status); /* Acknowledge */ - return (DDI_INTR_CLAIMED); -} - /* * Audio routines */ int -audigyls_open(void *arg, int flag, - unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp) +audigyls_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp) { audigyls_port_t *port = arg; audigyls_dev_t *dev = port->dev; @@ -403,12 +317,9 @@ mutex_enter(&dev->mutex); - port->started = B_FALSE; port->count = 0; - *fragfrp = port->fragfr; - *nfragsp = AUDIGYLS_NUM_FRAGS; + *nframesp = port->buf_frames; *bufp = port->buf_kaddr; - audigyls_reset_port(port); mutex_exit(&dev->mutex); return (0); @@ -417,13 +328,7 @@ void audigyls_close(void *arg) { - audigyls_port_t *port = arg; - audigyls_dev_t *dev = port->dev; - - mutex_enter(&dev->mutex); - audigyls_stop_port(port); - port->started = B_FALSE; - mutex_exit(&dev->mutex); + _NOTE(ARGUNUSED(arg)); } int @@ -431,12 +336,41 @@ { audigyls_port_t *port = arg; audigyls_dev_t *dev = port->dev; + uint32_t tmp; mutex_enter(&dev->mutex); - if (!port->started) { - audigyls_start_port(port); - port->started = B_TRUE; + + port->offset = 0; + + switch (port->direction) { + case AUDIGYLS_PLAY_PORT: + write_chan(dev, PTCA, 0, 0); + write_chan(dev, CPFA, 0, 0); + write_chan(dev, CPCAV, 0, 0); + write_chan(dev, PTCA, 1, 0); + write_chan(dev, CPFA, 1, 0); + write_chan(dev, CPCAV, 1, 0); + write_chan(dev, PTCA, 3, 0); + write_chan(dev, CPFA, 3, 0); + write_chan(dev, CPCAV, 3, 0); + + tmp = read_reg(dev, SA); + tmp |= SA_SPA(0); + tmp |= SA_SPA(1); + tmp |= SA_SPA(3); + write_reg(dev, SA, tmp); + break; + + case AUDIGYLS_REC_PORT: + write_chan(dev, CRFA, 2, 0); + write_chan(dev, CRCAV, 2, 0); + + tmp = read_reg(dev, SA); + tmp |= SA_SRA(2); + write_reg(dev, SA, tmp); + break; } + mutex_exit(&dev->mutex); return (0); } @@ -446,12 +380,26 @@ { audigyls_port_t *port = arg; audigyls_dev_t *dev = port->dev; + uint32_t tmp; mutex_enter(&dev->mutex); - if (port->started) { - audigyls_stop_port(port); - port->started = B_FALSE; + + switch (port->direction) { + case AUDIGYLS_PLAY_PORT: + tmp = read_reg(dev, SA); + tmp &= ~SA_SPA(0); + tmp &= ~SA_SPA(1); + tmp &= ~SA_SPA(3); + write_reg(dev, SA, tmp); + break; + + case AUDIGYLS_REC_PORT: + tmp = read_reg(dev, SA); + tmp &= ~SA_SRA(2); + write_reg(dev, SA, tmp); + break; } + mutex_exit(&dev->mutex); } @@ -494,10 +442,27 @@ audigyls_port_t *port = arg; audigyls_dev_t *dev = port->dev; uint64_t count; + uint32_t offset, n; mutex_enter(&dev->mutex); - if (!dev->suspended) - audigyls_update_port(port); + + if (port->direction == AUDIGYLS_PLAY_PORT) { + offset = read_chan(dev, CPFA, 0); + } else { + offset = read_chan(dev, CRFA, 2); + } + + /* get the offset, and switch to frames */ + offset /= (2 * sizeof (uint16_t)); + + if (offset >= port->offset) { + n = offset - port->offset; + } else { + n = offset + (port->buf_frames - port->offset); + } + port->offset = offset; + port->count += n; + count = port->count; mutex_exit(&dev->mutex); return (count); @@ -519,107 +484,6 @@ /* private implementation bits */ -void -audigyls_start_port(audigyls_port_t *port) -{ - audigyls_dev_t *dev = port->dev; - uint32_t tmp; - - ASSERT(mutex_owned(&dev->mutex)); - - if (dev->suspended || port->active) - return; - - port->active = B_TRUE; - port->offset = 0; - dev->nactive++; - - if (dev->nactive == 1) { - write_reg(dev, IT, dev->timer); - OUTL(dev, IER, INL(dev, IER) | INTR_IT1); - } - - switch (port->direction) { - case AUDIGYLS_PLAY_PORT: - tmp = read_reg(dev, SA); - tmp |= SA_SPA(0); - tmp |= SA_SPA(1); - tmp |= SA_SPA(3); - write_reg(dev, SA, tmp); - break; - - case AUDIGYLS_REC_PORT: - tmp = read_reg(dev, SA); - tmp |= SA_SRA(2); - write_reg(dev, SA, tmp); - break; - } - -} - -void -audigyls_stop_port(audigyls_port_t *port) -{ - audigyls_dev_t *dev = port->dev; - uint32_t tmp; - - if (dev->suspended || !port->active) - return; - - port->active = B_FALSE; - dev->nactive--; - - switch (port->direction) { - case AUDIGYLS_PLAY_PORT: - tmp = read_reg(dev, SA); - tmp &= ~SA_SPA(0); - tmp &= ~SA_SPA(1); - tmp &= ~SA_SPA(3); - write_reg(dev, SA, tmp); - break; - - case AUDIGYLS_REC_PORT: - tmp = read_reg(dev, SA); - tmp &= ~SA_SRA(2); - write_reg(dev, SA, tmp); - break; - } - - if (dev->nactive == 0) { - OUTL(dev, IER, INL(dev, IER) & ~INTR_IT1); - } -} - -void -audigyls_reset_port(audigyls_port_t *port) -{ - audigyls_dev_t *dev = port->dev; - - ASSERT(mutex_owned(&dev->mutex)); - - if (dev->suspended) - return; - - switch (port->direction) { - case AUDIGYLS_PLAY_PORT: - write_chan(dev, PTCA, 0, 0); - write_chan(dev, CPFA, 0, 0); - write_chan(dev, CPCAV, 0, 0); - write_chan(dev, PTCA, 1, 0); - write_chan(dev, CPFA, 1, 0); - write_chan(dev, CPCAV, 1, 0); - write_chan(dev, PTCA, 3, 0); - write_chan(dev, CPFA, 3, 0); - write_chan(dev, CPCAV, 3, 0); - break; - - case AUDIGYLS_REC_PORT: - write_chan(dev, CRFA, 2, 0); - write_chan(dev, CRCAV, 2, 0); - break; - } -} - int audigyls_alloc_port(audigyls_dev_t *dev, int num) { @@ -635,7 +499,6 @@ port = kmem_zalloc(sizeof (*port), KM_SLEEP); dev->port[num] = port; port->dev = dev; - port->started = B_FALSE; port->direction = num; switch (num) { @@ -655,15 +518,8 @@ return (DDI_FAILURE); } - /* figure out fragment configuration */ - port->fragfr = 48000 / dev->intrs; - /* we want to make sure that we are aligning on reasonable chunks */ - port->fragfr = (port->fragfr + 63) & ~(63); - - /* 16 bit frames */ - port->fragsz = port->fragfr * 2 * port->nchan; - port->buf_size = port->fragsz * AUDIGYLS_NUM_FRAGS; - port->buf_frames = port->fragfr * AUDIGYLS_NUM_FRAGS; + port->buf_frames = 2048; + port->buf_size = port->buf_frames * port->nchan * sizeof (int16_t); /* Alloc buffers */ if (ddi_dma_alloc_handle(dev->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL, @@ -699,42 +555,6 @@ return (DDI_SUCCESS); } -int -audigyls_setup_intrs(audigyls_dev_t *dev) -{ - uint_t ipri; - int actual; - int rv; - ddi_intr_handle_t ih[1]; - - rv = ddi_intr_alloc(dev->dip, ih, DDI_INTR_TYPE_FIXED, - 0, 1, &actual, DDI_INTR_ALLOC_STRICT); - if ((rv != DDI_SUCCESS) || (actual != 1)) { - audio_dev_warn(dev->adev, - "Can't alloc interrupt handle (rv %d actual %d)", - rv, actual); - return (DDI_FAILURE); - } - - if (ddi_intr_get_pri(ih[0], &ipri) != DDI_SUCCESS) { - audio_dev_warn(dev->adev, "Can't get interrupt priority"); - (void) ddi_intr_free(ih[0]); - return (DDI_FAILURE); - } - - if (ddi_intr_add_handler(ih[0], audigyls_intr, dev, NULL) != - DDI_SUCCESS) { - audio_dev_warn(dev->adev, "Can't add interrupt handler"); - (void) ddi_intr_free(ih[0]); - return (DDI_FAILURE); - } - - dev->ih = ih[0]; - mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - mutex_init(&dev->low_mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - return (DDI_SUCCESS); -} - void audigyls_del_controls(audigyls_dev_t *dev) { @@ -749,17 +569,8 @@ void audigyls_destroy(audigyls_dev_t *dev) { - if (dev->ih != NULL) { - (void) ddi_intr_disable(dev->ih); - (void) ddi_intr_remove_handler(dev->ih); - (void) ddi_intr_free(dev->ih); - mutex_destroy(&dev->mutex); - mutex_destroy(&dev->low_mutex); - } - - if (dev->ksp) { - kstat_delete(dev->ksp); - } + mutex_destroy(&dev->mutex); + mutex_destroy(&dev->low_mutex); for (int i = 0; i < AUDIGYLS_NUM_PORT; i++) { audigyls_port_t *port = dev->port[i]; @@ -868,7 +679,7 @@ } } - OUTL(dev, IER, INTR_PCI); + OUTL(dev, IER, 0); OUTL(dev, HC, 0x00000009); /* Enable audio, use 48 kHz */ tmp = read_chan(dev, SRCTL, 0); @@ -1051,7 +862,7 @@ if (dev->controls[CTL_LOOP].val) { r = 0; v1 = RECSEL_I2SOUT; - r |= (v1 << 28) | (v1 << 24) | (v1 << 20) | (v1 << 16) | v2; + r |= (v1 << 28) | (v1 << 24) | (v1 << 20) | (v1 << 16) | v1; } else { /* * You'd think this would be the same as the logic @@ -1119,9 +930,8 @@ mutex_enter(&dev->mutex); pc->val = val; - if (!dev->suspended) { - audigyls_configure_mixer(dev); - } + audigyls_configure_mixer(dev); + mutex_exit(&dev->mutex); return (0); @@ -1300,6 +1110,8 @@ dev = kmem_zalloc(sizeof (*dev), KM_SLEEP); dev->dip = dip; ddi_set_driver_private(dip, dev); + mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL); + mutex_init(&dev->low_mutex, NULL, MUTEX_DRIVER, NULL); if ((dev->adev = audio_dev_alloc(dip, 0)) == NULL) { cmn_err(CE_WARN, "audio_dev_alloc failed"); @@ -1338,27 +1150,6 @@ dev->digital_enable = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip, DDI_PROP_DONTPASS, "digital-enable", 0); - if (audigyls_setup_intrs(dev) != DDI_SUCCESS) - goto error; - - dev->intrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip, - DDI_PROP_DONTPASS, "interrupt-rate", AUDIGYLS_INTRS); - - /* make sure the values are good */ - if (dev->intrs < AUDIGYLS_MIN_INTRS) { - audio_dev_warn(dev->adev, - "interrupt-rate too low, %d, reset to %d", - dev->intrs, AUDIGYLS_INTRS); - dev->intrs = AUDIGYLS_INTRS; - } else if (dev->intrs > AUDIGYLS_MAX_INTRS) { - audio_dev_warn(dev->adev, - "interrupt-rate too high, %d, reset to %d", - dev->intrs, AUDIGYLS_INTRS); - dev->intrs = AUDIGYLS_INTRS; - } - - dev->timer = (192000 / dev->intrs) << 16; - switch (subdevice) { case 0x11021001: /* SB0310 */ case 0x11021002: /* SB0310 */ @@ -1453,19 +1244,11 @@ audigyls_configure_mixer(dev); - /* set up kernel statistics */ - if ((dev->ksp = kstat_create(AUDIGYLS_NAME, ddi_get_instance(dip), - AUDIGYLS_NAME, "controller", KSTAT_TYPE_INTR, 1, - KSTAT_FLAG_PERSISTENT)) != NULL) { - kstat_install(dev->ksp); - } - if (audio_dev_register(dev->adev) != DDI_SUCCESS) { audio_dev_warn(dev->adev, "unable to register with framework"); goto error; } - (void) ddi_intr_enable(dev->ih); ddi_report_dev(dip); return (DDI_SUCCESS); @@ -1479,38 +1262,17 @@ audigyls_resume(dev_info_t *dip) { audigyls_dev_t *dev; - audigyls_port_t *port; dev = ddi_get_driver_private(dip); - for (int i = 0; i < AUDIGYLS_NUM_PORT; i++) { - port = dev->port[i]; - audio_engine_reset(port->engine); - } - audigyls_hwinit(dev); /* allow ac97 operations again */ if (dev->ac97) - ac97_resume(dev->ac97); - - audigyls_configure_mixer(dev); - - mutex_enter(&dev->mutex); - dev->suspended = B_FALSE; - - for (int i = 0; i < AUDIGYLS_NUM_PORT; i++) { + ac97_reset(dev->ac97); - port = dev->port[i]; - - audigyls_reset_port(port); + audio_dev_resume(dev->adev); - if (port->started) { - audigyls_start_port(port); - } - } - - mutex_exit(&dev->mutex); return (DDI_SUCCESS); } @@ -1527,17 +1289,8 @@ int audigyls_suspend(audigyls_dev_t *dev) { - if (dev->ac97) - ac97_suspend(dev->ac97); - - mutex_enter(&dev->mutex); - for (int i = 0; i < AUDIGYLS_NUM_PORT; i++) { + audio_dev_suspend(dev->adev); - audigyls_port_t *port = dev->port[i]; - audigyls_stop_port(port); - } - dev->suspended = B_TRUE; - mutex_exit(&dev->mutex); return (DDI_SUCCESS); } @@ -1640,17 +1393,10 @@ audigyls_dev_t *dev; uint32_t status; - dev = ddi_get_driver_private(dip); - - for (int i = 0; i < AUDIGYLS_NUM_PORT; i++) { - - audigyls_port_t *port = dev->port[i]; - audigyls_stop_port(port); - } - /* * Turn off the hardware */ + dev = ddi_get_driver_private(dip); write_reg(dev, SA, 0); OUTL(dev, IER, 0); /* Interrupt disable */
--- a/usr/src/uts/common/io/audio/drv/audiols/audiols.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiols/audiols.h Tue Mar 16 09:30:41 2010 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -45,20 +45,9 @@ #define AUDIGYLS_PLAY_PORT 0 #define AUDIGYLS_REC_PORT 1 -/* - * Number of fragments must be multiple of 2 because the - * hardware supports only full and half buffer interrupts. In - * addition it looks like 8 fragments is the minimum. - */ -#define AUDIGYLS_NUM_FRAGS (8*2) - #define PCI_VENDOR_ID_CREATIVE 0x1102 #define PCI_DEVICE_ID_CREATIVE_AUDIGYLS 0x0007 -#define AUDIGYLS_MAX_INTRS 256 -#define AUDIGYLS_MIN_INTRS 24 -#define AUDIGYLS_INTRS 100 - /* * PCI registers */ @@ -216,11 +205,7 @@ audio_engine_t *engine; int direction; - int started; - boolean_t active; - unsigned fragfr; - unsigned fragsz; unsigned nchan; ddi_dma_handle_t buf_dmah; /* dma for buffers */ @@ -239,20 +224,15 @@ dev_info_t *dip; audio_dev_t *adev; ac97_t *ac97; - kstat_t *ksp; - unsigned intrs; - unsigned timer; int nactive; /* Num active ports */ char digital_enable; /* Orange combo-jack mode */ - boolean_t suspended; ddi_acc_handle_t pcih; ddi_acc_handle_t regsh; caddr_t base; kmutex_t mutex; /* For normal locking */ kmutex_t low_mutex; /* For low level routines */ - ddi_intr_handle_t ih; audigyls_port_t *port[AUDIGYLS_NUM_PORT]; audigyls_ctrl_t controls[CTL_NUM]; @@ -277,6 +257,4 @@ #define OUTL(dev, reg, val) \ ddi_put32(dev->regsh, (void *)(dev->base + reg), (val)) -#define AUDIGYLS_KIOP(X) ((kstat_intr_t *)(X->ksp->ks_data)) - #endif /* AUDIGYLS_H */
--- a/usr/src/uts/common/io/audio/drv/audiop16x/audiop16x.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiop16x/audiop16x.c Tue Mar 16 09:30:41 2010 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -95,7 +95,7 @@ static int p16x_detach(p16x_dev_t *); static int p16x_suspend(p16x_dev_t *); -static int p16x_open(void *, int, unsigned *, unsigned *, caddr_t *); +static int p16x_open(void *, int, unsigned *, caddr_t *); static void p16x_close(void *); static int p16x_start(void *); static void p16x_stop(void *); @@ -109,13 +109,8 @@ static uint16_t p16x_read_ac97(void *, uint8_t); static void p16x_write_ac97(void *, uint8_t, uint16_t); static int p16x_alloc_port(p16x_dev_t *, int); -static void p16x_update_port(p16x_port_t *); -static void p16x_start_port(p16x_port_t *); -static void p16x_stop_port(p16x_port_t *); static void p16x_destroy(p16x_dev_t *); -static int p16x_setup_intrs(p16x_dev_t *); static void p16x_hwinit(p16x_dev_t *); -static uint_t p16x_intr(caddr_t, caddr_t); static audio_engine_ops_t p16x_engine_ops = { AUDIO_ENGINE_VERSION, @@ -138,10 +133,10 @@ { unsigned int val; - mutex_enter(&dev->low_mutex); + mutex_enter(&dev->mutex); OUTL(dev, (reg << 16) | (chn & 0xffff), PTR); /* Pointer */ val = INL(dev, DR); /* Data */ - mutex_exit(&dev->low_mutex); + mutex_exit(&dev->mutex); return (val); } @@ -150,10 +145,34 @@ write_reg(p16x_dev_t *dev, int reg, int chn, unsigned int value) { - mutex_enter(&dev->low_mutex); + mutex_enter(&dev->mutex); OUTL(dev, (reg << 16) | (chn & 0xffff), PTR); /* Pointer */ OUTL(dev, value, DR); /* Data */ - mutex_exit(&dev->low_mutex); + mutex_exit(&dev->mutex); +} + +static void +set_reg_bits(p16x_dev_t *dev, int reg, int chn, unsigned int mask) +{ + unsigned int val; + mutex_enter(&dev->mutex); + OUTL(dev, (reg << 16) | (chn & 0xffff), PTR); /* Pointer */ + val = INL(dev, DR); /* Data */ + val |= mask; + OUTL(dev, val, DR); /* Data */ + mutex_exit(&dev->mutex); +} + +static void +clear_reg_bits(p16x_dev_t *dev, int reg, int chn, unsigned int mask) +{ + unsigned int val; + mutex_enter(&dev->mutex); + OUTL(dev, (reg << 16) | (chn & 0xffff), PTR); /* Pointer */ + val = INL(dev, DR); /* Data */ + val &= ~(mask); + OUTL(dev, val, DR); /* Data */ + mutex_exit(&dev->mutex); } static uint16_t @@ -163,13 +182,11 @@ uint16_t value; int i; - mutex_enter(&dev->low_mutex); OUTB(dev, index, AC97A); for (i = 0; i < 10000; i++) if (INB(dev, AC97A) & 0x80) break; value = INW(dev, AC97D); - mutex_exit(&dev->low_mutex); return (value); } @@ -179,83 +196,51 @@ p16x_dev_t *dev = arg; unsigned int i; - mutex_enter(&dev->low_mutex); OUTB(dev, index, AC97A); for (i = 0; i < 10000; i++) if (INB(dev, AC97A) & 0x80) break; OUTW(dev, data, AC97D); - mutex_exit(&dev->low_mutex); -} - -static uint_t -p16x_intr(caddr_t argp, caddr_t nocare) -{ - p16x_dev_t *dev = (void *)argp; - unsigned int status; - audio_engine_t *consume = NULL; - audio_engine_t *produce = NULL; - - _NOTE(ARGUNUSED(nocare)); - - mutex_enter(&dev->mutex); - if (dev->suspended) { - mutex_exit(&dev->mutex); - return (DDI_INTR_UNCLAIMED); - } - /* Read the interrupt status */ - status = INL(dev, IP); - OUTL(dev, status, IP); /* Acknowledge */ - - if (!(status & INTR_ALL)) { - mutex_exit(&dev->mutex); - return (DDI_INTR_UNCLAIMED); - } - - if (status & INTR_PCI) { - audio_dev_warn(dev->adev, "PCI error triggered, PCI status %x", - pci_config_get16(dev->pcih, PCI_CONF_STAT)); - } - - if ((status & (INTR_PFF | INTR_PFH)) && - (dev->port[P16X_PLAY]->started)) { - consume = dev->port[P16X_PLAY]->engine; - } - - if ((status & (INTR_RFF | INTR_RFH)) && - (dev->port[P16X_REC]->started)) { - produce = dev->port[P16X_REC]->engine; - } - - mutex_exit(&dev->mutex); - - if (consume) { - audio_engine_consume(consume); - } - - if (produce) { - audio_engine_produce(produce); - } - - return (DDI_INTR_CLAIMED); } /* * Audio routines */ -static void -p16x_init_port(p16x_port_t *port) +int +p16x_open(void *arg, int flag, uint_t *nframes, caddr_t *bufp) { + p16x_port_t *port = arg; + + _NOTE(ARGUNUSED(flag)); + + port->count = 0; + *nframes = port->buf_frames; + *bufp = port->buf_kaddr; + + return (0); +} + +void +p16x_close(void *arg) +{ + _NOTE(ARGUNUSED(arg)); +} + +int +p16x_start(void *arg) +{ + p16x_port_t *port = arg; p16x_dev_t *dev = port->dev; - if (port->suspended) - return; + port->offset = 0; if (port->port_num == P16X_REC) { write_reg(dev, CRFA, 0, 0); write_reg(dev, CRCAV, 0, 0); + /* Enable rec channel */ + set_reg_bits(dev, SA, 0, 0x100); } else { for (int i = 0; i < 3; i++) { write_reg(dev, PTBA, i, 0); @@ -266,73 +251,27 @@ write_reg(dev, CPCAV, i, 0); } + /* Enable play channel */ + set_reg_bits(dev, SA, 0, 0x7); } -} - - -static int -p16x_open(void *arg, int flag, uint_t *fragfrp, uint_t *nfp, caddr_t *bufp) -{ - p16x_port_t *port = arg; - p16x_dev_t *dev = port->dev; - - _NOTE(ARGUNUSED(flag)); - - mutex_enter(&dev->mutex); - - port->started = B_FALSE; - port->count = 0; - port->offset = 0; - - p16x_init_port(port); - - *fragfrp = port->fragfr; - *nfp = port->nfrags; - *bufp = port->buf_kaddr; - mutex_exit(&dev->mutex); return (0); } void -p16x_close(void *arg) -{ - p16x_port_t *port = arg; - p16x_dev_t *dev = port->dev; - - mutex_enter(&dev->mutex); - p16x_stop_port(port); - port->started = B_FALSE; - mutex_exit(&dev->mutex); -} - -int -p16x_start(void *arg) -{ - p16x_port_t *port = arg; - p16x_dev_t *dev = port->dev; - - mutex_enter(&dev->mutex); - if (!port->started) { - p16x_start_port(port); - port->started = B_TRUE; - } - mutex_exit(&dev->mutex); - return (0); -} - -void p16x_stop(void *arg) { p16x_port_t *port = arg; p16x_dev_t *dev = port->dev; - mutex_enter(&dev->mutex); - if (port->started) { - p16x_stop_port(port); - port->started = B_FALSE; + if (port->port_num == P16X_REC) { + /* Disable rec channel */ + clear_reg_bits(dev, SA, 0, 0x100); + + } else { + /* Disable Play channel */ + clear_reg_bits(dev, SA, 0, 0x7); } - mutex_exit(&dev->mutex); } int @@ -374,12 +313,25 @@ p16x_port_t *port = arg; p16x_dev_t *dev = port->dev; uint64_t val; + uint32_t offset, n; - mutex_enter(&dev->mutex); - if (port->started && !dev->suspended) - p16x_update_port(port); + if (port->port_num == P16X_PLAY) { + offset = read_reg(dev, CPFA, 0); + } else { + offset = read_reg(dev, CRFA, 0); + } + + /* get the offset, and switch to frames */ + offset /= (2 * sizeof (uint16_t)); + + if (offset >= port->offset) { + n = offset - port->offset; + } else { + n = offset + (port->buf_frames - port->offset); + } + port->offset = offset; + port->count += n; val = port->count; - mutex_exit(&dev->mutex); return (val); } @@ -415,93 +367,6 @@ /* private implementation bits */ -void -p16x_update_port(p16x_port_t *port) -{ - p16x_dev_t *dev = port->dev; - uint32_t offset, n; - - if (dev->suspended) - return; - - if (port->port_num == P16X_PLAY) { - offset = read_reg(dev, CPFA, 0); - } else { - offset = read_reg(dev, CRFA, 0); - } - - /* get the offset, and switch to frames */ - offset /= (2 * sizeof (uint16_t)); - - if (offset >= port->offset) { - n = offset - port->offset; - } else { - n = offset + (port->buf_frames - port->offset); - } - port->offset = offset; - port->count += n; -} - -void -p16x_start_port(p16x_port_t *port) -{ - p16x_dev_t *dev = port->dev; - unsigned int tmp; - - ASSERT(mutex_owned(&dev->mutex)); - - if (dev->suspended) - return; - - if (port->port_num == P16X_REC) { - /* Enable Rec Channel */ - tmp = read_reg(dev, SA, 0); - tmp |= 0x100; - write_reg(dev, SA, 0, tmp); - tmp = INL(dev, IE); - tmp |= INTR_REC; - OUTL(dev, tmp, IE); - } else { - /* Enable play channel and go */ - tmp = read_reg(dev, SA, 0); - tmp |= 7; - write_reg(dev, SA, 0, tmp); - tmp = INL(dev, IE); - tmp |= INTR_PLAY; - OUTL(dev, tmp, IE); - } -} - -void -p16x_stop_port(p16x_port_t *port) -{ - p16x_dev_t *dev = port->dev; - unsigned int tmp; - - - if (dev->suspended) - return; - - if (port->port_num == P16X_REC) { - /* Disable rec channel */ - tmp = read_reg(dev, SA, 0); - tmp &= ~0x100; - write_reg(dev, SA, 0, tmp); - tmp = INL(dev, IE); - tmp &= ~INTR_REC; - OUTL(dev, tmp, IE); - - } else { - /* Disable Play channel */ - tmp = read_reg(dev, SA, 0); - tmp &= ~7; - write_reg(dev, SA, 0, tmp); - tmp = INL(dev, IE); - tmp &= ~INTR_PLAY; - OUTL(dev, tmp, IE); - } -} - int p16x_alloc_port(p16x_dev_t *dev, int num) { @@ -510,7 +375,6 @@ ddi_dma_cookie_t cookie; uint_t count; int dir; - char *prop; unsigned caps; audio_dev_t *adev; @@ -518,11 +382,9 @@ port = kmem_zalloc(sizeof (*port), KM_SLEEP); dev->port[num] = port; port->dev = dev; - port->started = B_FALSE; switch (num) { case P16X_REC: - prop = "record-interrupts"; port->syncdir = DDI_DMA_SYNC_FORKERNEL; caps = ENGINE_INPUT_CAP; dir = DDI_DMA_READ; @@ -530,7 +392,6 @@ port->nchan = 2; break; case P16X_PLAY: - prop = "play-interrupts"; port->syncdir = DDI_DMA_SYNC_FORDEV; caps = ENGINE_OUTPUT_CAP; dir = DDI_DMA_WRITE; @@ -541,37 +402,16 @@ return (DDI_FAILURE); } - /* figure out fragment configuration */ - port->intrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip, - DDI_PROP_DONTPASS, prop, P16X_DEF_INTRS); - - /* make sure the values are good */ - if (port->intrs < P16X_MIN_INTRS) { - audio_dev_warn(adev, "%s too low, %d, reset to %d", - prop, port->intrs, P16X_MIN_INTRS); - port->intrs = P16X_MIN_INTRS; - } else if (port->intrs > P16X_MAX_INTRS) { - audio_dev_warn(adev, "%s too high, %d, reset to %d", - prop, port->intrs, P16X_DEF_INTRS); - port->intrs = P16X_DEF_INTRS; - } - /* - * This needs the very latest Boomer changes. + * NB: The device operates in pairs of dwords at a time, for + * performance reasons. So make sure that our buffer is + * arranged as a whole number of these. The value below gives + * a reasonably large buffer so we can support a deep + * playahead if we need to (and we should avoid input + * overruns.) */ - port->nfrags = 2; - port->fragfr = 48000 / port->intrs; - /* - * The device operates in pairs of dwords at a time, for - * performance reasons. So make sure that our buffer is - * arranged as a whole number of these. We could probably - * fine tune by just ensuring that the overall buffer was 128 - * (64 for half and 64 for full), but this is simpler. - */ - port->fragfr = (port->fragfr + 63) & ~(63); - port->fragsz = port->fragfr * port->nchan * 2; /* 16 bit frames */ - port->buf_size = port->nfrags * port->fragsz; - port->buf_frames = port->fragfr * port->nfrags; + port->buf_frames = 4096; + port->buf_size = port->buf_frames * port->nchan * sizeof (uint16_t); /* now allocate buffers */ if (ddi_dma_alloc_handle(dev->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL, @@ -610,17 +450,7 @@ void p16x_destroy(p16x_dev_t *dev) { - if (dev->ih != NULL) { - (void) ddi_intr_disable(dev->ih); - (void) ddi_intr_remove_handler(dev->ih); - (void) ddi_intr_free(dev->ih); - mutex_destroy(&dev->mutex); - mutex_destroy(&dev->low_mutex); - } - - if (dev->ksp) { - kstat_delete(dev->ksp); - } + mutex_destroy(&dev->mutex); for (int i = 0; i < P16X_NUM_PORT; i++) { p16x_port_t *port = dev->port[i]; @@ -709,42 +539,6 @@ } int -p16x_setup_intrs(p16x_dev_t *dev) -{ - uint_t ipri; - int actual; - int rv; - ddi_intr_handle_t ih[1]; - - rv = ddi_intr_alloc(dev->dip, ih, DDI_INTR_TYPE_FIXED, - 0, 1, &actual, DDI_INTR_ALLOC_STRICT); - if ((rv != DDI_SUCCESS) || (actual != 1)) { - audio_dev_warn(dev->adev, - "Can't alloc interrupt handle (rv %d actual %d)", - rv, actual); - return (DDI_FAILURE); - } - - if (ddi_intr_get_pri(ih[0], &ipri) != DDI_SUCCESS) { - audio_dev_warn(dev->adev, "Can't get interrupt priority"); - (void) ddi_intr_free(ih[0]); - return (DDI_FAILURE); - } - - if (ddi_intr_add_handler(ih[0], p16x_intr, dev, NULL) != - DDI_SUCCESS) { - audio_dev_warn(dev->adev, "Can't add interrupt handler"); - (void) ddi_intr_free(ih[0]); - return (DDI_FAILURE); - } - - dev->ih = ih[0]; - mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - mutex_init(&dev->low_mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - return (DDI_SUCCESS); -} - -int p16x_attach(dev_info_t *dip) { uint16_t vendor, device; @@ -755,23 +549,7 @@ dev->dip = dip; ddi_set_driver_private(dip, dev); - /* we don't support high level interrupts in the driver */ - if (ddi_intr_hilevel(dip, 0) != 0) { - cmn_err(CE_WARN, - "!%s%d: unsupported high level interrupt", - ddi_driver_name(dip), ddi_get_instance(dip)); - return (DDI_FAILURE); - } - - if (ddi_get_iblock_cookie(dip, 0, &dev->iblock) != DDI_SUCCESS) { - cmn_err(CE_WARN, - "!%s%d: cannot get iblock cookie", - ddi_driver_name(dip), ddi_get_instance(dip)); - kmem_free(dev, sizeof (*dev)); - return (DDI_FAILURE); - } - mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, dev->iblock); - mutex_init(&dev->low_mutex, NULL, MUTEX_DRIVER, dev->iblock); + mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL); if ((dev->adev = audio_dev_alloc(dip, 0)) == NULL) { cmn_err(CE_WARN, "audio_dev_alloc failed"); @@ -815,14 +593,6 @@ p16x_hwinit(dev); - /* set up the interrupt handler */ - if (p16x_setup_intrs(dev) != DDI_SUCCESS) { - goto error; - } - - /* Enable PCI interrupts */ - OUTL(dev, INTR_PCI, IE); - dev->ac97 = ac97_allocate(dev->adev, dip, p16x_read_ac97, p16x_write_ac97, dev); if (dev->ac97 == NULL) { @@ -843,19 +613,11 @@ ac97_register_controls(dev->ac97); - /* set up kernel statistics */ - if ((dev->ksp = kstat_create(P16X_NAME, ddi_get_instance(dip), - P16X_NAME, "controller", KSTAT_TYPE_INTR, 1, - KSTAT_FLAG_PERSISTENT)) != NULL) { - kstat_install(dev->ksp); - } - if (audio_dev_register(dev->adev) != DDI_SUCCESS) { audio_dev_warn(dev->adev, "unable to register with framework"); goto error; } - (void) ddi_intr_enable(dev->ih); ddi_report_dev(dip); return (DDI_SUCCESS); @@ -874,29 +636,10 @@ p16x_hwinit(dev); - /* allow ac97 operations again */ - ac97_resume(dev->ac97); - - mutex_enter(&dev->mutex); - dev->suspended = B_FALSE; - - for (int i = 0; i < P16X_NUM_PORT; i++) { - - p16x_port_t *port = dev->port[i]; + ac97_reset(dev->ac97); - if (port->engine != NULL) - audio_engine_reset(port->engine); - - /* reset the port */ - p16x_init_port(port); + audio_dev_resume(dev->adev); - if (port->started) { - p16x_start_port(port); - } else { - p16x_stop_port(port); - } - } - mutex_exit(&dev->mutex); return (DDI_SUCCESS); } @@ -913,21 +656,8 @@ int p16x_suspend(p16x_dev_t *dev) { - ac97_suspend(dev->ac97); - - mutex_enter(&dev->mutex); - for (int i = 0; i < P16X_NUM_PORT; i++) { + audio_dev_suspend(dev->adev); - p16x_port_t *port = dev->port[i]; - p16x_stop_port(port); - } - - write_reg(dev, SA, 0, 0); - OUTL(dev, 0x00, IE); /* Interrupt disable */ - OUTL(dev, 0x01, HC); - - dev->suspended = B_TRUE; - mutex_exit(&dev->mutex); return (DDI_SUCCESS); } @@ -1031,14 +761,7 @@ dev = ddi_get_driver_private(dip); - for (int i = 0; i < P16X_NUM_PORT; i++) { - - p16x_port_t *port = dev->port[i]; - p16x_stop_port(port); - } - write_reg(dev, SA, 0, 0); - OUTL(dev, 0x00, IE); /* Interrupt disable */ OUTL(dev, 0x01, HC); return (DDI_SUCCESS);
--- a/usr/src/uts/common/io/audio/drv/audiop16x/audiop16x.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiop16x/audiop16x.h Tue Mar 16 09:30:41 2010 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -46,10 +46,6 @@ #define CREATIVE_VENDOR_ID 0x1102 #define SB_P16X_ID 0x0006 -#define P16X_MAX_INTRS 256 -#define P16X_MIN_INTRS 24 -#define P16X_DEF_INTRS 175 - typedef struct _p16x_dev_t p16x_dev_t; typedef struct _p16x_port_t p16x_port_t; @@ -58,10 +54,7 @@ p16x_dev_t *dev; audio_engine_t *engine; - unsigned intrs; caddr_t base; - boolean_t started; - boolean_t suspended; int port_num; #define P16X_PLAY 0 @@ -74,10 +67,6 @@ uint32_t buf_frames; int syncdir; int nchan; - unsigned fragfr; - unsigned nfrags; - unsigned fragsz; - unsigned nframes; uint64_t count; uint32_t offset; }; @@ -87,16 +76,11 @@ dev_info_t *dip; audio_dev_t *adev; ac97_t *ac97; - kstat_t *ksp; - ddi_iblock_cookie_t iblock; boolean_t suspended; - boolean_t intr_added; ddi_acc_handle_t pcih; ddi_acc_handle_t regsh; caddr_t base; - kmutex_t mutex; /* For normal locking */ - kmutex_t low_mutex; /* For low level routines */ - ddi_intr_handle_t ih; + kmutex_t mutex; /* For low level routines */ p16x_port_t *port[P16X_NUM_PORT]; }; @@ -115,8 +99,6 @@ #define OUTB(dev, val, reg) \ ddi_put8(dev->regsh, (void *)((char *)dev->base+(reg)), (val)) -#define P16X_KIOP(X) ((kstat_intr_t *)(X->ksp->ks_data)) - /* * SB P16X Registers */
--- a/usr/src/uts/common/io/audio/drv/audiopci/audiopci.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiopci/audiopci.c Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* @@ -53,7 +53,6 @@ #define CREATIVE_VENDOR_ID 0x1102 #define ENSONIQ_ES1370 0x5000 -#define DEFINTS 75 #define DRVNAME "audiopci" #define INPUT_MIC 0 @@ -75,8 +74,6 @@ typedef struct audiopci_port { /* Audio parameters */ - boolean_t trigger; - int speed; int fmt; @@ -90,14 +87,12 @@ uint32_t paddr; ddi_acc_handle_t acch; ddi_dma_handle_t dmah; - unsigned fragfr; - unsigned nfrags; unsigned nframes; unsigned frameno; uint64_t count; struct audiopci_dev *dev; - audio_engine_t *engine; + audio_engine_t *engine; } audiopci_port_t; typedef enum { @@ -130,11 +125,6 @@ kmutex_t mutex; uint16_t devid; dev_info_t *dip; - boolean_t enabled; - boolean_t suspended; - - int pintrs; - int rintrs; uint8_t ak_regs[0x20]; int micbias; @@ -147,14 +137,11 @@ audiopci_ctrl_t *micbias; #endif - kstat_t *ksp; - audiopci_port_t port[PORT_MAX + 1]; caddr_t regs; ddi_acc_handle_t acch; - ddi_intr_handle_t ihandle[1]; } audiopci_dev_t; static ddi_device_acc_attr_t acc_attr = { @@ -171,15 +158,9 @@ /* * The hardware appears to be able to address up to 16-bits worth of longwords, - * giving a total address space of 256K. Note, however, that we will restrict - * this further when we do fragment and memory allocation. At its very highest - * clock rate (48 kHz) and sample size (16-bit stereo), and lowest interrupt - * rate (32 Hz), we only need 6000 bytes per fragment. - * - * So with an allocated buffer size of 64K, we can support at least 10 frags, - * which is more than enough. (The legacy Sun driver used only 2 fragments.) + * giving a total address space of 256K. But we need substantially less. */ -#define AUDIOPCI_BUF_LEN (65536) +#define AUDIOPCI_BUF_LEN (16384) static ddi_dma_attr_t dma_attr = { DMA_ATTR_VERSION, /* dma_attr_version */ @@ -214,13 +195,8 @@ #define CLR16(dev, offset, v) PUT16(dev, offset, GET16(dev, offset) & ~(v)) #define SET16(dev, offset, v) PUT16(dev, offset, GET16(dev, offset) | (v)) -#define KSINTR(dev) ((kstat_intr_t *)((dev)->ksp->ks_data)) - static void audiopci_init_hw(audiopci_dev_t *); static void audiopci_init_port(audiopci_port_t *); -static void audiopci_start_port(audiopci_port_t *); -static void audiopci_stop_port(audiopci_port_t *); -static void audiopci_update_port(audiopci_port_t *); static uint16_t audiopci_dac_rate(int); static int audiopci_add_controls(audiopci_dev_t *); static void audiopci_del_controls(audiopci_dev_t *); @@ -252,9 +228,6 @@ { uint8_t wstat; - if (dev->suspended) - return; - /* shadow the value */ dev->ak_regs[addr] = data; @@ -284,111 +257,6 @@ return (GET32(dev, offs)); } -static uint_t -audiopci_intr(caddr_t arg1, caddr_t arg2) -{ - audiopci_dev_t *dev = (void *)arg1; - int stats; - int tmp; - unsigned char ackbits = 0; - audiopci_port_t *port; - audio_engine_t *do_syn, *do_dac, *do_adc; - - _NOTE(ARGUNUSED(arg2)); - - /* - * NB: The old driver didn't report spurious interrupts. On - * a system with shared interrupts (typical!) there will - * normally be lots of these (each time the "other" device - * interrupts). - * - * Also, because of the way the interrupt chain handling - * works, reporting of spurious interrupts is probably not - * terribly useful. - * - * However, we can count interrupts where the master interrupt - * bit is set but none of the ackbits that we are prepared to - * process is set. That is probably useful. - */ - mutex_enter(&dev->mutex); - if (!dev->enabled) { - - mutex_exit(&dev->mutex); - return (DDI_INTR_UNCLAIMED); - } - - stats = GET32(dev, CONC_dSTATUS_OFF); - - if (!(stats & CONC_INTSTAT_PENDING)) { /* No interrupt pending */ - mutex_exit(&dev->mutex); - return (DDI_INTR_UNCLAIMED); - } - - do_syn = do_dac = do_adc = NULL; - - /* synth interrupt */ - if (stats & CONC_INTSTAT_SYNINT) { - - ackbits |= CONC_SERCTL_SYNIE; - port = &dev->port[PORT_SYN]; - if (port->trigger) { - do_syn = port->engine; - } - } - - /* DAC interrupt */ - if (stats & CONC_INTSTAT_DACINT) { - - ackbits |= CONC_SERCTL_DACIE; - port = &dev->port[PORT_DAC]; - if (port->trigger) { - do_dac = port->engine; - } - } - - /* ADC interrupt */ - if (stats & CONC_INTSTAT_ADCINT) { - - ackbits |= CONC_SERCTL_ADCIE; - port = &dev->port[PORT_ADC]; - - if (port->trigger) { - do_adc = port->engine; - } - } - - /* UART interrupt - we shouldn't get this! */ - if (stats & CONC_INTSTAT_UARTINT) { - uint8_t uart_stat = GET8(dev, CONC_bUARTCSTAT_OFF); - while (uart_stat & CONC_UART_RXRDY) - uart_stat = GET8(dev, CONC_bUARTCSTAT_OFF); - } - - /* Ack the interrupt */ - tmp = GET8(dev, CONC_bSERCTL_OFF); - PUT8(dev, CONC_bSERCTL_OFF, tmp & (~ackbits)); /* Clear bits */ - PUT8(dev, CONC_bSERCTL_OFF, tmp | ackbits); /* Turn them back on */ - - if (dev->ksp) { - if (ackbits == 0) { - KSINTR(dev)->intrs[KSTAT_INTR_SPURIOUS]++; - } else { - KSINTR(dev)->intrs[KSTAT_INTR_HARD]++; - } - } - - mutex_exit(&dev->mutex); - - if (do_syn) - audio_engine_consume(do_syn); - if (do_dac) - audio_engine_consume(do_dac); - if (do_adc) - audio_engine_produce(do_adc); - - return (DDI_INTR_CLAIMED); -} - /* * Audio routines */ @@ -421,9 +289,6 @@ audiopci_dev_t *dev = port->dev; unsigned tmp; - if (dev->suspended) - return; - switch (port->num) { case PORT_DAC: @@ -447,7 +312,7 @@ port->nframes - 1); /* Set # of frames between interrupts */ - PUT16(dev, CONC_wDACIC_OFF, port->fragfr - 1); + PUT16(dev, CONC_wDACIC_OFF, port->nframes - 1); break; @@ -472,7 +337,7 @@ port->nframes - 1); /* Set # of frames between interrupts */ - PUT16(dev, CONC_wSYNIC_OFF, port->fragfr - 1); + PUT16(dev, CONC_wSYNIC_OFF, port->nframes - 1); break; @@ -498,7 +363,7 @@ port->nframes - 1); /* Set # of frames between interrupts */ - PUT16(dev, CONC_wADCIC_OFF, port->fragfr - 1); + PUT16(dev, CONC_wADCIC_OFF, port->nframes - 1); break; } @@ -507,96 +372,22 @@ } static int -audiopci_open(void *arg, int flag, unsigned *fragfrp, unsigned *nfragsp, - caddr_t *bufp) +audiopci_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp) { audiopci_port_t *port = arg; - audiopci_dev_t *dev = port->dev; - int intrs; _NOTE(ARGUNUSED(flag)); - - mutex_enter(&dev->mutex); - switch (port->num) { - case PORT_ADC: - intrs = dev->rintrs; - break; - case PORT_DAC: - intrs = dev->pintrs; - break; - case PORT_SYN: - intrs = dev->pintrs; - break; - } - - /* interrupt at least at 25 Hz, and not more than 250 Hz */ - intrs = min(250, max(25, intrs)); - /* NB: frame size = 4 (16-bit stereo) */ - port->fragfr = (port->speed / intrs); - port->nfrags = AUDIOPCI_BUF_LEN / (port->fragfr * 4); - port->nfrags = max(4, min(port->nfrags, 1024)); - port->nframes = port->nfrags * port->fragfr; - port->trigger = B_FALSE; + port->nframes = AUDIOPCI_BUF_LEN / 4; port->count = 0; - audiopci_init_port(port); - - *fragfrp = port->fragfr; - *nfragsp = port->nfrags; + *nframes = port->nframes; *bufp = port->kaddr; - mutex_exit(&dev->mutex); return (0); } -static void -audiopci_start_port(audiopci_port_t *port) -{ - audiopci_dev_t *dev = port->dev; - - if (!dev->suspended) { - switch (port->num) { - case PORT_DAC: - SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_DAC_EN); - SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DACIE); - break; - case PORT_SYN: - SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_SYN_EN); - SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_SYNIE); - break; - case PORT_ADC: - SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN); - SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE); - break; - } - } -} - -static void -audiopci_stop_port(audiopci_port_t *port) -{ - audiopci_dev_t *dev = port->dev; - - if (!dev->suspended) { - switch (port->num) { - case PORT_DAC: - CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_DAC_EN); - CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DACIE); - break; - case PORT_SYN: - CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_SYN_EN); - CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_SYNIE); - break; - case PORT_ADC: - CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN); - CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE); - break; - } - } -} - static int audiopci_start(void *arg) { @@ -604,9 +395,19 @@ audiopci_dev_t *dev = port->dev; mutex_enter(&dev->mutex); - if (!port->trigger) { - port->trigger = B_TRUE; - audiopci_start_port(port); + + audiopci_init_port(port); + + switch (port->num) { + case PORT_DAC: + SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_DAC_EN); + break; + case PORT_SYN: + SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_SYN_EN); + break; + case PORT_ADC: + SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN); + break; } mutex_exit(&dev->mutex); @@ -620,16 +421,26 @@ audiopci_dev_t *dev = port->dev; mutex_enter(&dev->mutex); - if (port->trigger) { - port->trigger = B_FALSE; - audiopci_stop_port(port); + switch (port->num) { + case PORT_DAC: + CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_DAC_EN); + break; + case PORT_SYN: + CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_SYN_EN); + break; + case PORT_ADC: + CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN); + break; } mutex_exit(&dev->mutex); } -static void -audiopci_update_port(audiopci_port_t *port) +static uint64_t +audiopci_count(void *arg) { + audiopci_port_t *port = arg; + audiopci_dev_t *dev = port->dev; + uint64_t val; uint32_t page, offs; int frameno, n; @@ -653,36 +464,24 @@ /* * Note that the current frame counter is in the high nybble. */ + mutex_enter(&dev->mutex); frameno = audiopci_readmem(port->dev, page, offs) >> 16; + mutex_exit(&dev->mutex); + n = frameno >= port->frameno ? frameno - port->frameno : frameno + port->nframes - port->frameno; port->frameno = frameno; port->count += n; -} -static uint64_t -audiopci_count(void *arg) -{ - audiopci_port_t *port = arg; - audiopci_dev_t *dev = port->dev; - uint64_t val; - - mutex_enter(&dev->mutex); - if (!dev->suspended) { - audiopci_update_port(port); - } val = port->count; - mutex_exit(&dev->mutex); return (val); } static void audiopci_close(void *arg) { - audiopci_port_t *port = arg; - - audiopci_stop(port); + _NOTE(ARGUNUSED(arg)); } static void @@ -797,8 +596,6 @@ if (dev->micbias) { SET16(dev, 2, CONC_DEVCTL_MICBIAS); } - - dev->enabled = B_TRUE; } static int @@ -808,12 +605,6 @@ audiopci_init_hw(dev); - dev->pintrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip, - DDI_PROP_DONTPASS, "play-interrupts", DEFINTS); - - dev->rintrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip, - DDI_PROP_DONTPASS, "record-interrupts", DEFINTS); - for (int i = 0; i <= PORT_MAX; i++) { audiopci_port_t *port; unsigned caps; @@ -896,15 +687,6 @@ return (DDI_FAILURE); } - /* - * Set up kstats for interrupt reporting. - */ - dev->ksp = kstat_create(ddi_driver_name(dev->dip), - ddi_get_instance(dev->dip), ddi_driver_name(dev->dip), - "controller", KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT); - if (dev->ksp != NULL) { - kstat_install(dev->ksp); - } if (audio_dev_register(dev->adev) != DDI_SUCCESS) { audio_dev_warn(dev->adev, @@ -915,54 +697,12 @@ return (DDI_SUCCESS); } -int -audiopci_setup_interrupts(audiopci_dev_t *dev) -{ - int actual; - uint_t ipri; - - if ((ddi_intr_alloc(dev->dip, dev->ihandle, DDI_INTR_TYPE_FIXED, - 0, 1, &actual, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) || - (actual != 1)) { - audio_dev_warn(dev->adev, "can't alloc intr handle"); - return (DDI_FAILURE); - } - - if (ddi_intr_get_pri(dev->ihandle[0], &ipri) != DDI_SUCCESS) { - audio_dev_warn(dev->adev, "can't determine intr priority"); - (void) ddi_intr_free(dev->ihandle[0]); - dev->ihandle[0] = NULL; - return (DDI_FAILURE); - } - - if (ddi_intr_add_handler(dev->ihandle[0], audiopci_intr, dev, - NULL) != DDI_SUCCESS) { - audio_dev_warn(dev->adev, "can't add intr handler"); - (void) ddi_intr_free(dev->ihandle[0]); - dev->ihandle[0] = NULL; - return (DDI_FAILURE); - } - - mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - - return (DDI_SUCCESS); -} - -void +static void audiopci_destroy(audiopci_dev_t *dev) { int i; - if (dev->ihandle[0] != NULL) { - (void) ddi_intr_disable(dev->ihandle[0]); - (void) ddi_intr_remove_handler(dev->ihandle[0]); - (void) ddi_intr_free(dev->ihandle[0]); - mutex_destroy(&dev->mutex); - } - - if (dev->ksp != NULL) { - kstat_delete(dev->ksp); - } + mutex_destroy(&dev->mutex); /* free up ports, including DMA resources for ports */ for (i = 0; i <= PORT_MAX; i++) { @@ -1046,11 +786,9 @@ audiopci_get_value(void *arg, uint64_t *val) { audiopci_ctrl_t *pc = arg; - audiopci_dev_t *dev = pc->dev; - mutex_enter(&dev->mutex); *val = pc->val; - mutex_exit(&dev->mutex); + return (0); } @@ -1425,7 +1163,7 @@ return (DDI_SUCCESS); } -void +static void audiopci_del_controls(audiopci_dev_t *dev) { for (int i = 0; i < CTL_NUM; i++) { @@ -1435,7 +1173,7 @@ } } -int +static int audiopci_attach(dev_info_t *dip) { uint16_t pci_command, vendor, device; @@ -1446,8 +1184,11 @@ dev->dip = dip; ddi_set_driver_private(dip, dev); + mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL); + if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) { audio_dev_warn(dev->adev, "pci_config_setup failed"); + mutex_destroy(&dev->mutex); kmem_free(dev, sizeof (*dev)); return (DDI_FAILURE); } @@ -1482,10 +1223,6 @@ goto err_exit; } - if (audiopci_setup_interrupts(dev) != DDI_SUCCESS) { - audio_dev_warn(dev->adev, "can't register interrupts"); - goto err_exit; - } /* This allocates and configures the engines */ if (audiopci_init(dev) != DDI_SUCCESS) { @@ -1493,8 +1230,6 @@ goto err_exit; } - (void) ddi_intr_enable(dev->ihandle[0]); - pci_config_teardown(&pcih); ddi_report_dev(dip); @@ -1502,6 +1237,7 @@ return (DDI_SUCCESS); err_exit: + mutex_destroy(&dev->mutex); pci_config_teardown(&pcih); audiopci_destroy(dev); @@ -1509,7 +1245,7 @@ return (DDI_FAILURE); } -int +static int audiopci_detach(audiopci_dev_t *dev) { int tmp; @@ -1534,8 +1270,6 @@ PUT8(dev, CONC_bDEVCTL_OFF, tmp); PUT8(dev, CONC_bDEVCTL_OFF, tmp); - dev->enabled = B_FALSE; - mutex_exit(&dev->mutex); audiopci_destroy(dev); @@ -1546,55 +1280,17 @@ static int audiopci_resume(audiopci_dev_t *dev) { - /* ask framework to reset/relocate engine data */ - for (int i = 0; i <= PORT_MAX; i++) { - audio_engine_reset(dev->port[i].engine); - } - - mutex_enter(&dev->mutex); - dev->suspended = B_FALSE; - /* reinitialize hardware */ audiopci_init_hw(dev); - /* restore mixer settings */ - audiopci_configure_output(dev); - audiopci_configure_input(dev); - - /* restart ports */ - for (int i = 0; i < PORT_MAX; i++) { - audiopci_port_t *port = &dev->port[i]; - audiopci_init_port(port); - /* possibly start it up if was going when we suspended */ - if (port->trigger) { - audiopci_start_port(port); - - /* signal callbacks on resume */ - if (port->num == PORT_ADC) { - audio_engine_produce(port->engine); - } else { - audio_engine_consume(port->engine); - } - } - } - mutex_exit(&dev->mutex); + audio_dev_resume(dev->adev); return (DDI_SUCCESS); } static int audiopci_suspend(audiopci_dev_t *dev) { - /* - * Stop all engines/DMA data. - */ - mutex_enter(&dev->mutex); - for (int i = 0; i <= PORT_MAX; i++) { - audiopci_stop_port(&dev->port[i]); - audiopci_update_port(&dev->port[i]); - } - dev->suspended = B_TRUE; - dev->enabled = B_FALSE; - mutex_exit(&dev->mutex); + audio_dev_suspend(dev->adev); return (DDI_SUCCESS); }
--- a/usr/src/uts/common/io/audio/drv/audiopci/audiopci.conf Tue Mar 16 09:43:38 2010 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# Configuration file for the audiopci audio driver. -# -# WARNING: This is an UNSTABLE configuration file. Its contents -# may change at any time. - -# -# play-interrupts sets the number of interrupts per second when playing. -# This affects the resolution of various things, such as sample counts. -# record-interrupts does the same for record interrupts. -# -# These may be tuned to get more accurate information by increasing the -# count. However, the larger the interrupts per second the larger the -# load on the system. So use this capability cautiously. The audiopci -# driver enforces a maximum and minimum count. -# -# It should also be understood that not all interrupt rates are legal. -# The hardware is restricted to DMA buffers being allocated on certain -# boundaries. If those boundaries are violated then the value specified -# will be ignored. -# -play-interrupts=75; -record-interrupts=75;
--- a/usr/src/uts/common/io/audio/drv/audiosolo/audiosolo.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiosolo/audiosolo.c Tue Mar 16 09:30:41 2010 -0700 @@ -24,7 +24,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -101,14 +101,11 @@ /* * For the sake of simplicity, this driver fixes a few parameters with - * constants. If you want these values to be tunable, upgrade to a - * nicer and newer device. This is all tuned for 100 Hz (10 - * millisecs) latency. + * constants. */ #define SOLO_RATE 48000 -#define SOLO_INTRS 100 -#define SOLO_FRAGFR (SOLO_RATE / SOLO_INTRS) -#define SOLO_NFRAGS 8 +#define SOLO_FRAGFR 1024 +#define SOLO_NFRAGS 2 #define SOLO_NCHAN 2 #define SOLO_SAMPSZ 2 #define SOLO_FRAGSZ (SOLO_FRAGFR * (SOLO_NCHAN * SOLO_SAMPSZ)) @@ -147,6 +144,7 @@ uint32_t paddr; bool started; + bool trigger; uint64_t count; uint16_t offset; int syncdir; @@ -340,8 +338,6 @@ solo_intr(caddr_t arg1, caddr_t arg2) { solo_dev_t *dev = (void *)arg1; - audio_engine_t *prod = NULL; - audio_engine_t *cons = NULL; uint8_t status; uint_t rv = DDI_INTR_UNCLAIMED; @@ -357,27 +353,17 @@ status = PORT_RD8(dev->io, 0x7); if (status & 0x20) { rv = DDI_INTR_CLAIMED; - cons = dev->play.engine; /* ack the interrupt */ solo_setmixer(dev, 0x7a, solo_getmixer(dev, 0x7a) & ~0x80); } if (status & 0x10) { rv = DDI_INTR_CLAIMED; - prod = dev->rec.engine; /* ack the interrupt */ (void) PORT_RD8(dev->sb, 0xe); } mutex_exit(&dev->mutex); - if (cons) { - audio_engine_consume(cons); - } - - if (prod) { - audio_engine_produce(prod); - } - return (rv); } @@ -488,8 +474,7 @@ mutex_enter(&dev->mutex); pc->val = val; - if (!dev->suspended) - solo_configure_mixer(dev); + solo_configure_mixer(dev); mutex_exit(&dev->mutex); return (0); } @@ -508,8 +493,7 @@ mutex_enter(&dev->mutex); pc->val = val; - if (!dev->suspended) - solo_configure_mixer(dev); + solo_configure_mixer(dev); mutex_exit(&dev->mutex); return (0); } @@ -530,8 +514,7 @@ mutex_enter(&dev->mutex); pc->val = val; - if (!dev->suspended) - solo_configure_mixer(dev); + solo_configure_mixer(dev); mutex_exit(&dev->mutex); return (0); } @@ -544,8 +527,7 @@ mutex_enter(&dev->mutex); pc->val = val; - if (!dev->suspended) - solo_configure_mixer(dev); + solo_configure_mixer(dev); mutex_exit(&dev->mutex); return (0); } @@ -751,23 +733,45 @@ uint32_t ptr; uint32_t count; uint32_t diff; + int tries; ASSERT(mutex_owned(&dev->mutex)); /* * During recording, this register is known to give back * garbage if it's not quiescent while being read. This hack - * attempts to work around it. + * attempts to work around it. We also suspend the DMA + * while we do this, to minimize record distortion. */ - ptr = PORT_RD32(dev->vc, 0); - count = PORT_RD16(dev->vc, 4); - diff = e->paddr + SOLO_BUFSZ - ptr - count; - if ((diff > 3) || (ptr < e->paddr) || - (ptr >= (e->paddr + SOLO_BUFSZ))) { - ptr = dev->last_capture; - } else { - dev->last_capture = ptr; + if (e->trigger) { + drv_usecwait(20); + } + for (tries = 10; tries; tries--) { + drv_usecwait(10); + ptr = PORT_RD32(dev->vc, 0); + count = PORT_RD16(dev->vc, 4); + diff = e->paddr + SOLO_BUFSZ - ptr - count; + if ((diff > 3) || (ptr < e->paddr) || + (ptr >= (e->paddr + SOLO_BUFSZ))) { + ptr = dev->last_capture; + } else { + break; + } } + if (e->trigger) { + PORT_WR8(dev->vc, 0xf, 0); /* restart DMA */ + } + if (!tries) { + /* + * Note, this is a pretty bad situation, because we'll + * not have an accurate idea of our position. But its + * better than making a bad alteration. If we had FMA + * for audio devices, this would be a good point to + * raise a fault. + */ + return; + } + dev->last_capture = ptr; offset = ptr - e->paddr; offset /= (SOLO_NCHAN * SOLO_SAMPSZ); @@ -831,6 +835,7 @@ PORT_WR8(dev->vc, 0xf, 0); /* start DMA */ dev->last_capture = e->paddr; + e->trigger = true; } static void @@ -840,6 +845,7 @@ /* NB: We might be in quiesce, without a lock held */ solo_write(dev, 0xb8, solo_read(dev, 0xb8) & ~0x01); + e->trigger = false; } static void @@ -900,6 +906,8 @@ v = solo_mixer_scale(dev, CTL_VOLUME); v = v | (v << 4); solo_setmixer(dev, 0x7c, v & 0xff); + + e->trigger = true; } static void @@ -910,6 +918,8 @@ /* NB: We might be in quiesce, without a lock held */ PORT_WR8(dev->io, 0x6, 0); solo_setmixer(dev, 0x78, solo_getmixer(dev, 0x78) & ~0x03); + + e->trigger = false; } /* @@ -968,8 +978,7 @@ uint64_t count; mutex_enter(&dev->mutex); - if (!dev->suspended) - e->update(e); + e->update(e); count = e->count; mutex_exit(&dev->mutex); @@ -977,16 +986,14 @@ } static int -solo_open(void *arg, int f, unsigned *ffr, unsigned *nfr, caddr_t *buf) +solo_open(void *arg, int f, unsigned *nframes, caddr_t *buf) { solo_engine_t *e = arg; solo_dev_t *dev = e->dev; _NOTE(ARGUNUSED(f)); - /* NB: For simplicity, we just fix the interrupt rate at 100 Hz */ - *ffr = SOLO_FRAGFR; - *nfr = SOLO_NFRAGS; + *nframes = SOLO_NFRAGS * SOLO_FRAGFR; *buf = e->kaddr; mutex_enter(&dev->mutex); @@ -1004,8 +1011,7 @@ solo_dev_t *dev = e->dev; mutex_enter(&dev->mutex); - if (!dev->suspended) - e->stop(e); + e->stop(e); e->started = false; mutex_exit(&dev->mutex); } @@ -1019,8 +1025,7 @@ mutex_enter(&dev->mutex); if (!e->started) { - if (!dev->suspended) - e->start(e); + e->start(e); e->started = true; } mutex_exit(&dev->mutex); @@ -1036,8 +1041,7 @@ mutex_enter(&dev->mutex); if (e->started) { - if (!dev->suspended) - e->stop(e); + e->stop(e); e->started = false; } mutex_exit(&dev->mutex); @@ -1332,14 +1336,9 @@ static int solo_suspend(solo_dev_t *dev) { + audio_dev_suspend(dev->adev); + mutex_enter(&dev->mutex); - /* play */ - solo_aud2_stop(&dev->play); - solo_aud2_update(&dev->play); - /* record */ - solo_aud1_stop(&dev->rec); - solo_aud1_update(&dev->rec); - dev->suspended = true; mutex_exit(&dev->mutex); @@ -1349,43 +1348,16 @@ static int solo_resume(solo_dev_t *dev) { - solo_engine_t *e; - audio_engine_t *prod = NULL; - audio_engine_t *cons = NULL; - - audio_engine_reset(dev->rec.engine); - audio_engine_reset(dev->play.engine); - mutex_enter(&dev->mutex); if (!solo_init_hw(dev)) { /* yikes! */ audio_dev_warn(dev->adev, "unable to resume audio!"); audio_dev_warn(dev->adev, "reboot or reload driver to reset"); - mutex_exit(&dev->mutex); - return (DDI_SUCCESS); } dev->suspended = false; - - /* record - audio 1 */ - e = &dev->rec; - if (e->started) { - e->start(e); - prod = e->engine; - } - - /* play - audio 2 */ - e = &dev->play; - if (e->started) { - e->start(e); - cons = e->engine; - } - mutex_exit(&dev->mutex); - if (cons) - audio_engine_consume(cons); - if (prod) - audio_engine_produce(prod); + audio_dev_resume(dev->adev); return (DDI_SUCCESS); }
--- a/usr/src/uts/common/io/audio/drv/audiots/audiots.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiots/audiots.c Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -103,6 +103,30 @@ * audio subsystem. All PM support is now removed. */ +/* + * Synchronization notes: + * + * The audio framework guarantees that our entry points are exclusive + * with suspend and resume. This includes data flow and control entry + * points alike. + * + * The audio framework guarantees that only one control is being + * accessed on any given audio device at a time. + * + * The audio framework guarantees that entry points are themselves + * serialized for a given engine. + * + * We have no interrupt routine or other internal asynchronous routines. + * + * Our device uses completely separate registers for each engine, + * except for the start/stop registers, which are implemented in a + * manner that allows for them to be accessed concurrently safely from + * different threads. + * + * Hence, it turns out that we simply don't need any locking in this + * driver. + */ + #include <sys/modctl.h> #include <sys/kmem.h> #include <sys/pci.h> @@ -124,7 +148,7 @@ /* * Entry point routine prototypes */ -static int audiots_open(void *, int, unsigned *, unsigned *, caddr_t *); +static int audiots_open(void *, int, unsigned *, caddr_t *); static void audiots_close(void *); static int audiots_start(void *); static void audiots_stop(void *); @@ -159,16 +183,11 @@ static uint16_t audiots_get_ac97(void *, uint8_t); static void audiots_set_ac97(void *, uint8_t, uint16_t); static int audiots_init_state(audiots_state_t *, dev_info_t *); -static uint_t audiots_intr(caddr_t); static int audiots_map_regs(dev_info_t *, audiots_state_t *); -static void audiots_update_port(audiots_port_t *); -static void audiots_start_port(audiots_port_t *); -static void audiots_stop_port(audiots_port_t *); static uint16_t audiots_read_ac97(audiots_state_t *, int); static void audiots_stop_everything(audiots_state_t *); static void audiots_destroy(audiots_state_t *); static int audiots_alloc_port(audiots_state_t *, int); -static void audiots_reset_port(audiots_port_t *); /* * Global variables, but viewable only by this file. @@ -177,9 +196,6 @@ /* anchor for soft state structures */ static void *audiots_statep; -/* driver name, so we don't have to call ddi_driver_name() or hard code strs */ -static char *audiots_name = TS_NAME; - /* * DDI Structures */ @@ -370,15 +386,7 @@ case DDI_RESUME: /* we've already allocated the state structure so get ptr */ - if ((state = ddi_get_soft_state(audiots_statep, instance)) == - NULL) { - /* this will probably panic */ - cmn_err(CE_WARN, - "!%s%d: RESUME get soft state failed", - audiots_name, instance); - return (DDI_FAILURE); - } - + state = ddi_get_soft_state(audiots_statep, instance); ASSERT(dip == state->ts_dip); /* suspend/resume resets the chip, so we have no more faults */ @@ -394,59 +402,29 @@ audiots_power_up(state); audiots_chip_init(state); - ac97_resume(state->ts_ac97); - mutex_enter(&state->ts_lock); - /* - * Initialize/reset ports. Done under the lock, to - * avoid race with interrupt service routine. - */ - state->ts_suspended = B_FALSE; - for (int i = 0; i < TS_NUM_PORTS; i++) { - audiots_port_t *port = state->ts_ports[i]; - if (port != NULL) { - /* relocate any streams properly */ - if (port->tp_engine) - audio_engine_reset(port->tp_engine); + ac97_reset(state->ts_ac97); - /* do a hardware reset on the port */ - audiots_reset_port(port); - if (port->tp_started) { - audiots_start_port(port); - } else { - audiots_stop_port(port); - } - } - } - mutex_exit(&state->ts_lock); + audio_dev_resume(state->ts_adev); return (DDI_SUCCESS); default: - cmn_err(CE_WARN, "!%s%d: attach() unknown command: 0x%x", - audiots_name, instance, cmd); return (DDI_FAILURE); } /* before we do anything make sure that we haven't had a h/w failure */ if (ddi_get_devstate(dip) == DDI_DEVSTATE_DOWN) { cmn_err(CE_WARN, "%s%d: The audio hardware has " - "been disabled.", audiots_name, instance); + "been disabled.", ddi_driver_name(dip), instance); cmn_err(CE_CONT, "Please reboot to restore audio."); return (DDI_FAILURE); } - /* we don't support high level interrupts in this driver */ - if (ddi_intr_hilevel(dip, 0) != 0) { - cmn_err(CE_WARN, "!%s%d: unsupported high level interrupt", - audiots_name, instance); - return (DDI_FAILURE); - } - /* allocate the state structure */ if (ddi_soft_state_zalloc(audiots_statep, instance) == DDI_FAILURE) { cmn_err(CE_WARN, "!%s%d: soft state allocate failed", - audiots_name, instance); + ddi_driver_name(dip), instance); return (DDI_FAILURE); } @@ -497,22 +475,6 @@ goto error; } - /* set up kernel statistics */ - state->ts_ksp = kstat_create(TS_NAME, instance, TS_NAME, - "controller", KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT); - if (state->ts_ksp != NULL) { - kstat_install(state->ts_ksp); - } - - /* set up the interrupt handler */ - if (ddi_add_intr(dip, 0, NULL, NULL, audiots_intr, - (caddr_t)state) != DDI_SUCCESS) { - audio_dev_warn(state->ts_adev, - "failed to register interrupt handler"); - goto error; - } - state->ts_flags |= TS_INTR_INSTALLED; - /* everything worked out, so report the device */ ddi_report_dev(dip); @@ -548,7 +510,7 @@ /* get the state structure */ if ((state = ddi_get_soft_state(audiots_statep, instance)) == NULL) { cmn_err(CE_WARN, "!%s%d: detach get soft state failed", - audiots_name, instance); + ddi_driver_name(dip), instance); return (DDI_FAILURE); } @@ -557,19 +519,11 @@ break; case DDI_SUSPEND: - ac97_suspend(state->ts_ac97); - - mutex_enter(&state->ts_lock); - - state->ts_suspended = B_TRUE; /* stop new ops */ - - /* we may already be powered down, so only save state if up */ + audio_dev_suspend(state->ts_adev); /* stop playing and recording */ (void) audiots_stop_everything(state); - mutex_exit(&state->ts_lock); - return (DDI_SUCCESS); default: @@ -687,9 +641,6 @@ * * Arguments: * audiots_state_t *state The device's state structure - * - * Returns: - * void */ static void audiots_chip_init(audiots_state_t *state) @@ -865,105 +816,17 @@ /* save the device info pointer */ state->ts_dip = dip; - /* get the iblock cookie needed for interrupt context */ - if (ddi_get_iblock_cookie(dip, 0, &state->ts_iblock) != DDI_SUCCESS) { - audio_dev_warn(state->ts_adev, - "cannot get iblock cookie"); - return (DDI_FAILURE); - } - - /* initialize the state mutexes and condition variables */ - mutex_init(&state->ts_lock, NULL, MUTEX_DRIVER, state->ts_iblock); - state->ts_flags |= TS_MUTEX_INIT; - for (int i = 0; i < TS_NUM_PORTS; i++) { if (audiots_alloc_port(state, i) != DDI_SUCCESS) { return (DDI_FAILURE); } } - /* init power management state */ - state->ts_suspended = B_FALSE; return (DDI_SUCCESS); } /* - * audiots_intr() - * - * Description: - * Interrupt service routine for both play and record. For play we - * get the next buffers worth of audio. For record we send it on to - * the mixer. - * - * NOTE: This device needs to make sure any PIO access required to clear - * its interrupt has made it out on the PCI bus before returning from its - * interrupt handler so that the interrupt has been deasserted. This is - * done by rereading the address engine interrupt register. - * - * Arguments: - * caddr_t T Pointer to the interrupting device's state - * structure - * - * Returns: - * DDI_INTR_CLAIMED Interrupt claimed and processed - * DDI_INTR_UNCLAIMED Interrupt not claimed, and thus ignored - */ -static uint_t -audiots_intr(caddr_t T) -{ - audiots_state_t *state = (void *)T; - audiots_regs_t *regs = state->ts_regs; - ddi_acc_handle_t handle = state->ts_acch; - uint32_t interrupts; - - mutex_enter(&state->ts_lock); - - if (state->ts_suspended) { - mutex_exit(&state->ts_lock); - return (DDI_INTR_UNCLAIMED); - } - - interrupts = ddi_get32(handle, ®s->aud_regs.ap_aint); - if (interrupts == 0) { - mutex_exit(&state->ts_lock); - /* no interrupts to process, so it's not us */ - return (DDI_INTR_UNCLAIMED); - } - - /* - * Clear the interrupts to acknowledge. Also, reread the - * interrupt reg to ensure that PIO write has completed. - */ - ddi_put32(handle, ®s->aud_regs.ap_aint, interrupts); - (void) ddi_get32(handle, ®s->aud_regs.ap_aint); - - /* update the kernel interrupt statistics */ - if (state->ts_ksp) { - TS_KIOP(state)->intrs[KSTAT_INTR_HARD]++; - } - - mutex_exit(&state->ts_lock); - - for (int i = 0; i < TS_NUM_PORTS; i++) { - audiots_port_t *port = state->ts_ports[i]; - - if (((interrupts & port->tp_int_mask) == 0) || - (!port->tp_started)) - continue; - - if (i == TS_INPUT_PORT) { - audio_engine_produce(port->tp_engine); - } else { - audio_engine_consume(port->tp_engine); - } - } - - return (DDI_INTR_CLAIMED); - -} - -/* * audiots_map_regs() * * Description: @@ -1055,7 +918,6 @@ audiots_port_t *port; dev_info_t *dip = state->ts_dip; audio_dev_t *adev = state->ts_adev; - char *prop; int dir; unsigned caps; ddi_dma_cookie_t cookie; @@ -1068,49 +930,23 @@ state->ts_ports[num] = port; port->tp_num = num; port->tp_state = state; - port->tp_started = B_FALSE; - port->tp_rate = 48000; + port->tp_rate = TS_RATE; if (num == TS_INPUT_PORT) { - prop = "record-interrupts"; dir = DDI_DMA_READ; caps = ENGINE_INPUT_CAP; port->tp_dma_stream = 31; - port->tp_int_stream = 2; port->tp_sync_dir = DDI_DMA_SYNC_FORKERNEL; } else { - prop = "play-interrupts"; dir = DDI_DMA_WRITE; caps = ENGINE_OUTPUT_CAP; port->tp_dma_stream = 0; - port->tp_int_stream = 1; port->tp_sync_dir = DDI_DMA_SYNC_FORDEV; } - port->tp_int_mask = (1U << port->tp_int_stream); + port->tp_dma_mask = (1U << port->tp_dma_stream); - - /* get the number of interrupts per second */ - port->tp_intrs = ddi_prop_get_int(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, prop, TS_INTS); - - /* make sure the values are good */ - if (port->tp_intrs < TS_MIN_INTS) { - audio_dev_warn(adev, "%s too low, %d, resetting to %d", - prop, port->tp_intrs, TS_INTS); - port->tp_intrs = TS_INTS; - } else if (port->tp_intrs > TS_MAX_INTS) { - audio_dev_warn(adev, "%s too high, %d, resetting to %d", - prop, port->tp_intrs, TS_INTS); - port->tp_intrs = TS_INTS; - } - - /* - * Now allocate space. We configure for the worst case. The - * worst (biggest) case is 48000 kHz, at 4 bytes per frame - * (16-bit stereo), with the lowest interrupt frequency. We - * need two fragments though, and each half has to be rounded - * up to allow for alignment considerations. - */ + port->tp_nframes = 4096; + port->tp_size = port->tp_nframes * TS_FRAMESZ; /* allocate dma handle */ rc = ddi_dma_alloc_handle(dip, &audiots_attr, DDI_DMA_SLEEP, @@ -1120,7 +956,7 @@ return (DDI_FAILURE); } /* allocate DMA buffer */ - rc = ddi_dma_mem_alloc(port->tp_dmah, TS_BUFSZ, &ts_acc_attr, + rc = ddi_dma_mem_alloc(port->tp_dmah, port->tp_size, &ts_acc_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->tp_kaddr, &port->tp_size, &port->tp_acch); if (rc == DDI_FAILURE) { @@ -1305,9 +1141,6 @@ * audiots_state_t *state The device's state structure * int reg AC-97 register number * uint16_t value The value to write - * - * Returns: - * void */ static void audiots_set_ac97(void *arg, uint8_t reg8, uint16_t data) @@ -1369,99 +1202,6 @@ } /* audiots_set_ac97() */ /* - * audiots_reset_port() - * - * Description: - * Initializes the hardware for a DMA engine. - * We only support stereo 16-bit linear PCM (signed native endian). - * - * The audio core uses a single DMA buffer which is divided into two - * halves. An interrupt is generated when the middle of the buffer has - * been reached and at the end. The audio core resets the pointer back - * to the beginning automatically. After the interrupt the driver clears - * the buffer and asks the mixer for more audio samples. If there aren't - * enough then silence is played out. - * - * Arguments: - * audiots_port_t *port The DMA engine to reset - * - * Returns: - * void - */ -static void -audiots_reset_port(audiots_port_t *port) -{ - audiots_state_t *state = port->tp_state; - ddi_acc_handle_t handle = state->ts_acch; - audiots_regs_t *regs = state->ts_regs; - audiots_aram_t *aram; - audiots_eram_t *eram; - unsigned delta; - uint16_t ctrl; - uint16_t gvsel; - uint16_t eso; - - if (state->ts_suspended) - return; - - port->tp_cso = 0; - - gvsel = ERAM_WAVE_VOL | ERAM_PAN_0dB | ERAM_VOL_DEFAULT; - ctrl = ERAM_16_BITS | ERAM_STEREO | ERAM_LOOP_MODE | ERAM_SIGNED_PCM; - for (int i = 0; i < 2; i++) { - - delta = (port->tp_rate << TS_SRC_SHIFT) / TS_RATE; - - if (i == 0) { - /* first do the DMA stream */ - aram = ®s->aud_ram[port->tp_dma_stream].aram; - eram = ®s->aud_ram[port->tp_dma_stream].eram; - if (port->tp_num == TS_INPUT_PORT) { - delta = (TS_RATE << TS_SRC_SHIFT) / - port->tp_rate; - } - eso = port->tp_nframes - 1; - } else { - /* else do the interrupt stream */ - aram = ®s->aud_ram[port->tp_int_stream].aram; - eram = ®s->aud_ram[port->tp_int_stream].eram; - /* interrupt stream is silent */ - gvsel |= ERAM_VOL_MAX_ATTEN; - eso = port->tp_fragfr - 1; - } - - /* program the sample rate */ - ddi_put16(handle, &aram->aram_delta, (uint16_t)delta); - - /* program the precision, number of channels and loop mode */ - ddi_put16(handle, &eram->eram_ctrl_ec, ctrl); - - /* program the volume settings */ - ddi_put16(handle, &eram->eram_gvsel_pan_vol, gvsel); - - /* set ALPHA and FMS to 0 */ - ddi_put16(handle, &aram->aram_alpha_fms, 0x0); - - /* set CSO to 0 */ - ddi_put16(handle, &aram->aram_cso, 0x0); - - /* set LBA */ - ddi_put32(handle, &aram->aram_cptr_lba, - port->tp_paddr & ARAM_LBA_MASK); - - /* set ESO */ - ddi_put16(handle, &aram->aram_eso, eso); - } - - /* stop the DMA & interrupt engines */ - ddi_put32(handle, ®s->aud_regs.ap_stop, - port->tp_int_mask | port->tp_dma_mask); - - /* enable interrupts */ - OR_SET_WORD(handle, ®s->aud_regs.ap_ainten, port->tp_int_mask); -} - -/* * audiots_open() * * Description: @@ -1471,8 +1211,7 @@ * Arguments: * void *arg The DMA engine to set up * int flag Open flags - * unsigned *fragfrp Receives number of frames per fragment - * unsigned *nfragsp Receives number of fragments + * unsigned *nframesp Receives number of frames * caddr_t *bufp Receives kernel data buffer * * Returns: @@ -1480,40 +1219,17 @@ * errno on failure */ static int -audiots_open(void *arg, int flag, - unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp) +audiots_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp) { audiots_port_t *port = arg; - unsigned nfrag; _NOTE(ARGUNUSED(flag)); - /* - * Round up - we have to have a sample that is a whole number - * of 64-bit words. Since our frames are 4 bytes wide, we - * just need an even number of frames. - */ - port->tp_fragfr = port->tp_rate / port->tp_intrs; - port->tp_fragfr = (port->tp_fragfr + 1) & ~1; - nfrag = port->tp_size / (port->tp_fragfr * TS_FRAMESZ); - port->tp_nframes = nfrag * port->tp_fragfr; - port->tp_started = B_FALSE; port->tp_count = 0; port->tp_cso = 0; - *fragfrp = port->tp_fragfr; - *nfragsp = nfrag; + *nframesp = port->tp_nframes; *bufp = port->tp_kaddr; - /* - * This should always be true because we used a worst case - * assumption when calculating the port->tp_size. - */ - ASSERT((port->tp_fragfr * nfrag) <= port->tp_size); - - mutex_enter(&port->tp_state->ts_lock); - audiots_reset_port(port); - mutex_exit(&port->tp_state->ts_lock); - return (0); } @@ -1522,25 +1238,16 @@ * * Description: * Closes an audio DMA engine that was previously opened. Since - * nobody is using it, we take this opportunity to possibly power - * down the entire device. + * nobody is using it, we could take this opportunity to possibly power + * down the entire device, or at least the DMA engine. * * Arguments: * void *arg The DMA engine to shut down - * - * Returns: - * void */ static void audiots_close(void *arg) { - audiots_port_t *port = arg; - audiots_state_t *state = port->tp_state; - - mutex_enter(&state->ts_lock); - audiots_stop_port(port); - port->tp_started = B_FALSE; - mutex_exit(&state->ts_lock); + _NOTE(ARGUNUSED(arg)); } /* @@ -1552,9 +1259,6 @@ * * Arguments: * void *arg The DMA engine to stop - * - * Returns: - * void */ static void audiots_stop(void *arg) @@ -1562,12 +1266,8 @@ audiots_port_t *port = arg; audiots_state_t *state = port->tp_state; - mutex_enter(&state->ts_lock); - if (port->tp_started) { - audiots_stop_port(port); - } - port->tp_started = B_FALSE; - mutex_exit(&state->ts_lock); + ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_stop, + port->tp_dma_mask); } /* @@ -1585,15 +1285,60 @@ static int audiots_start(void *arg) { - audiots_port_t *port = arg; - audiots_state_t *state = port->tp_state; + audiots_port_t *port = arg; + audiots_state_t *state = port->tp_state; + ddi_acc_handle_t handle = state->ts_acch; + audiots_regs_t *regs = state->ts_regs; + audiots_aram_t *aram; + audiots_eram_t *eram; + unsigned delta; + uint16_t ctrl; + uint16_t gvsel; + uint16_t eso; + + aram = ®s->aud_ram[port->tp_dma_stream].aram; + eram = ®s->aud_ram[port->tp_dma_stream].eram; + + port->tp_cso = 0; + + gvsel = ERAM_WAVE_VOL | ERAM_PAN_0dB | ERAM_VOL_DEFAULT; + ctrl = ERAM_16_BITS | ERAM_STEREO | ERAM_LOOP_MODE | ERAM_SIGNED_PCM; + + delta = (port->tp_rate << TS_SRC_SHIFT) / TS_RATE; + + if (port->tp_num == TS_INPUT_PORT) { + delta = (TS_RATE << TS_SRC_SHIFT) / port->tp_rate; + } + eso = port->tp_nframes - 1; - mutex_enter(&state->ts_lock); - if (!port->tp_started) { - audiots_start_port(port); - port->tp_started = B_TRUE; - } - mutex_exit(&state->ts_lock); + /* program the sample rate */ + ddi_put16(handle, &aram->aram_delta, (uint16_t)delta); + + /* program the precision, number of channels and loop mode */ + ddi_put16(handle, &eram->eram_ctrl_ec, ctrl); + + /* program the volume settings */ + ddi_put16(handle, &eram->eram_gvsel_pan_vol, gvsel); + + /* set ALPHA and FMS to 0 */ + ddi_put16(handle, &aram->aram_alpha_fms, 0x0); + + /* set CSO to 0 */ + ddi_put16(handle, &aram->aram_cso, 0x0); + + /* set LBA */ + ddi_put32(handle, &aram->aram_cptr_lba, + port->tp_paddr & ARAM_LBA_MASK); + + /* set ESO */ + ddi_put16(handle, &aram->aram_eso, eso); + + /* stop the DMA engines */ + ddi_put32(handle, ®s->aud_regs.ap_stop, port->tp_dma_mask); + + /* now make sure it starts playing */ + ddi_put32(handle, ®s->aud_regs.ap_start, port->tp_dma_mask); + return (0); } @@ -1701,131 +1446,8 @@ audiots_port_t *port = arg; audiots_state_t *state = port->tp_state; uint64_t val; - - mutex_enter(&state->ts_lock); - audiots_update_port(port); - - val = port->tp_count; - mutex_exit(&state->ts_lock); - return (val); -} - -/* - * audiots_sync() - * - * Description: - * This is called by the framework to synchronize DMA caches. - * We also leverage this do some endian swapping, because on SPARC - * the chip accesses the DMA region using 32-bit little-endian - * accesses. Its not enough to just use the framework's sample - * conversion logic, because the channels will also be backwards. - * - * Arguments: - * void *arg The DMA engine to sync - * - * Returns: - * void - */ -static void -audiots_sync(void *arg, unsigned nframes) -{ - audiots_port_t *port = arg; - _NOTE(ARGUNUSED(nframes)); - - (void) ddi_dma_sync(port->tp_dmah, 0, 0, port->tp_sync_dir); -} - -/* - * audiots_start_port() - * - * Description: - * The audio core uses a single DMA buffer which is divided into two - * halves. An interrupt is generated when the middle of the buffer has - * been reached and at the end. The audio core resets the pointer back - * to the beginning automatically. After the interrupt the driver clears - * the buffer and asks the mixer for more audio samples. If there aren't - * enough then silence is played out. - * - * Arguments: - * audiots_port_t *port The DMA engine to start up - * - * Returns: - * void - */ -static void -audiots_start_port(audiots_port_t *port) -{ - audiots_state_t *state = port->tp_state; - audiots_regs_t *regs = state->ts_regs; - ddi_acc_handle_t handle = state->ts_acch; - - ASSERT(mutex_owned(&state->ts_lock)); - - /* if suspended then do nothing else */ - if (state->ts_suspended) { - return; - } - - /* make sure it starts playing */ - ddi_put32(handle, ®s->aud_regs.ap_start, - port->tp_dma_mask | port->tp_int_mask); - - ASSERT(mutex_owned(&state->ts_lock)); -} - -/* - * audiots_stop_port() - * - * Description: - * This routine stops a DMA engine. - * - * Arguments: - * audiots_port_t *port The port to stop - * - * Returns: - * void - */ -static void -audiots_stop_port(audiots_port_t *port) -{ - audiots_state_t *state = port->tp_state; - - ASSERT(mutex_owned(&state->ts_lock)); - - if (state->ts_suspended) - return; - - ddi_put32(state->ts_acch, &state->ts_regs->aud_regs.ap_stop, - port->tp_int_mask | port->tp_dma_mask); - - ASSERT(mutex_owned(&state->ts_lock)); -} - -/* - * audiots_update_port() - * - * Description: - * This routine updates the ports frame counter from hardware, and - * gracefully handles wraps. - * - * Arguments: - * audiots_port_t *port The port to stop - * - * Returns: - * void - */ -static void -audiots_update_port(audiots_port_t *port) -{ - audiots_state_t *state = port->tp_state; - - uint16_t cso; - unsigned n; - - ASSERT(mutex_owned(&state->ts_lock)); - - if (state->ts_suspended) - return; + uint16_t cso; + unsigned n; cso = ddi_get16(state->ts_acch, &state->ts_regs->aud_ram[port->tp_dma_stream].aram.aram_cso); @@ -1836,6 +1458,27 @@ port->tp_cso = cso; port->tp_count += n; + val = port->tp_count; + + return (val); +} + +/* + * audiots_sync() + * + * Description: + * This is called by the framework to synchronize DMA caches. + * + * Arguments: + * void *arg The DMA engine to sync + */ +static void +audiots_sync(void *arg, unsigned nframes) +{ + audiots_port_t *port = arg; + _NOTE(ARGUNUSED(nframes)); + + (void) ddi_dma_sync(port->tp_dmah, 0, 0, port->tp_sync_dir); } /* @@ -1858,9 +1501,6 @@ * * Arguments: * audiots_state_t *state The device's state structure - * - * Returns: - * void */ static void audiots_stop_everything(audiots_state_t *state) @@ -1887,9 +1527,6 @@ * * Arguments: * audiots_port_t *port The port structure for a device stream. - * - * Returns: - * None */ void audiots_free_port(audiots_port_t *port) @@ -1923,21 +1560,12 @@ * * Arguments: * audiots_state_t *state The device soft state. - * - * Returns: - * None */ void audiots_destroy(audiots_state_t *state) { audiots_stop_everything(state); - if (state->ts_flags & TS_INTR_INSTALLED) - ddi_remove_intr(state->ts_dip, 0, NULL); - - if (state->ts_ksp) - kstat_delete(state->ts_ksp); - for (int i = 0; i < TS_NUM_PORTS; i++) audiots_free_port(state->ts_ports[i]); @@ -1953,9 +1581,5 @@ if (state->ts_adev) audio_dev_free(state->ts_adev); - if (state->ts_flags & TS_MUTEX_INIT) { - mutex_destroy(&state->ts_lock); - } - ddi_soft_state_free(audiots_statep, ddi_get_instance(state->ts_dip)); }
--- a/usr/src/uts/common/io/audio/drv/audiots/audiots.conf Tue Mar 16 09:43:38 2010 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# Configuration file for the audiots audio driver. -# -# WARNING: This is an UNSTABLE configuration file. Its contents -# may change at any time. -# - -# -# play-interrupts sets the number of interrupts per second when playing. -# This affects the resolution of various things, such as sample counts. -# record-interrupts does the same for record interrupts. -# -# These may be tuned to get more accurate information by increasing the -# count. However, the larger the interrupts per second the larger the -# load on the system. So use this capability cautiously. The audiots -# driver enforces a maximum and minimum count. -# -# It should also be understood that not all interrupt rates are legal. -# The hardware is restricted to DMA buffers being allocated on certain -# boundaries. If those boundaries are violated the driver will not be -# loaded and an error message is entered into the messages log -# -#play-interrupts=175; -#record-interrupts=175; - -# -# ac97-amplifier enables the use of any onboard external amplifier. -# -#ac97-amplifier=1;
--- a/usr/src/uts/common/io/audio/drv/audiots/audiots.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiots/audiots.h Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -47,17 +47,6 @@ #define TS_RATE (48000) #define TS_STEREO (2) /* stereo */ #define TS_FRAMESZ (4) /* 16-bit stereo */ -/* - * This is how much buffer space we at minimum need. The worst - * (biggest) case is 48000 kHz, at 4 bytes per frame (16-bit stereo), - * with the lowest interrupt frequency. 48000 x 4 bytes per sample / - * 24 is 8000 bytes per fragment. Two such fragments would be 16000. - * (At higher data rates, we get better buffering, with more frags.) - */ -#define TS_BUFSZ (16000) /* maximum buffer size */ -#define TS_INTS (175) /* default interrupt rate */ -#define TS_MIN_INTS (24) /* minimum interrupt rate */ -#define TS_MAX_INTS (2000) /* maximum interrupt rate */ /* * Misc. defines @@ -77,7 +66,6 @@ #define TS_MAX_HW_CHANNELS (32) -#define TS_KIOP(X) ((kstat_intr_t *)(X->ts_ksp->ks_data)) #define TS_WAIT_CNT (512) #define TS_LOOP_CNT (10) #define TS_DELAY_CNT (25) @@ -439,14 +427,10 @@ struct audiots_state *tp_state; int tp_num; int tp_dma_stream; - int tp_int_stream; uint32_t tp_dma_mask; - uint32_t tp_int_mask; boolean_t tp_started; - unsigned tp_intrs; - unsigned tp_fragfr; unsigned tp_nframes; unsigned tp_rate; uint64_t tp_count; @@ -467,10 +451,7 @@ * audiots_state_t - per instance state and operation data */ struct audiots_state { - kmutex_t ts_lock; /* state protection lock */ - ddi_iblock_cookie_t ts_iblock; /* iblock cookie */ uint_t ts_flags; /* flags */ - kstat_t *ts_ksp; /* kernel statistics */ dev_info_t *ts_dip; /* used by ts_getinfo() */ audio_dev_t *ts_adev; /* audio device handle */ ac97_t *ts_ac97; /* ac97 common handle */ @@ -480,7 +461,6 @@ ddi_acc_handle_t ts_pcih; /* handle to config regs */ ddi_acc_handle_t ts_acch; /* handle to mapped regs */ - boolean_t ts_suspended; /* power management state */ uint32_t ts_devid; uint8_t ts_revid; /* SB Chip Revision ID */ @@ -488,14 +468,7 @@ }; typedef struct audiots_state audiots_state_t; -_NOTE(MUTEX_PROTECTS_DATA(audiots_state::ts_lock, audiots_state)) -_NOTE(READ_ONLY_DATA(audiots_state::ts_instance)) -_NOTE(READ_ONLY_DATA(audiots_state::ts_dip)) -_NOTE(READ_ONLY_DATA(audiots_state::ts_adev)) - /* audiots_state.ts_flags defines */ -#define TS_MUTEX_INIT 0x0001u /* mutex initialized */ -#define TS_INTR_INSTALLED 0x0002u /* intr handler installeld */ #define TS_AUDIO_READ_FAILED 0x0020u /* reading the AC97 register */ /* has stopped working */ #define TS_READ_FAILURE_PRINTED 0x0040u /* Flag to avoid flooding the */
--- a/usr/src/uts/common/io/audio/drv/audiovia823x/audiovia823x.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiovia823x/audiovia823x.c Tue Mar 16 09:30:41 2010 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -97,7 +97,7 @@ static int auvia_detach(auvia_devc_t *); static int auvia_suspend(auvia_devc_t *); -static int auvia_open(void *, int, unsigned *, unsigned *, caddr_t *); +static int auvia_open(void *, int, unsigned *, caddr_t *); static void auvia_close(void *); static int auvia_start(void *); static void auvia_stop(void *); @@ -110,15 +110,10 @@ static uint16_t auvia_read_ac97(void *, uint8_t); static void auvia_write_ac97(void *, uint8_t, uint16_t); static int auvia_alloc_port(auvia_devc_t *, int); -static void auvia_start_port(auvia_portc_t *); -static void auvia_stop_port(auvia_portc_t *); -static void auvia_update_port(auvia_portc_t *); static void auvia_reset_input(auvia_portc_t *); static void auvia_reset_output(auvia_portc_t *); static void auvia_destroy(auvia_devc_t *); -static int auvia_setup_intrs(auvia_devc_t *); static void auvia_hwinit(auvia_devc_t *); -static uint_t auvia_intr(caddr_t, caddr_t); static audio_engine_ops_t auvia_engine_ops = { AUDIO_ENGINE_VERSION, @@ -143,8 +138,6 @@ uint32_t val = 0; int i; - mutex_enter(&devc->low_mutex); - val = ((uint32_t)index << 16) | CODEC_RD; OUTL(devc, devc->base + REG_CODEC, val); drv_usecwait(100); @@ -166,12 +159,10 @@ val = INL(devc, devc->base + REG_CODEC); OUTB(devc, devc->base + REG_CODEC + 3, 0x02); if (((val & CODEC_INDEX) >> 16) == index) { - mutex_exit(&devc->low_mutex); return (val & CODEC_DATA); } failed: - mutex_exit(&devc->low_mutex); return (0xffff); } @@ -182,8 +173,6 @@ uint32_t val = 0; int i = 0; - mutex_enter(&devc->low_mutex); - val = ((uint32_t)index << 16) | data | CODEC_WR; OUTL(devc, devc->base + REG_CODEC, val); drv_usecwait(100); @@ -196,70 +185,6 @@ drv_usecwait(50); } - mutex_exit(&devc->low_mutex); -} - -static uint_t -auvia_intr(caddr_t argp, caddr_t nocare) -{ - auvia_devc_t *devc = (void *)argp; - auvia_portc_t *portc; - uint8_t status; - unsigned intrs = 0; - boolean_t claimed = B_FALSE; - - _NOTE(ARGUNUSED(nocare)); - - mutex_enter(&devc->mutex); - if (devc->suspended) { - mutex_exit(&devc->mutex); - return (DDI_INTR_UNCLAIMED); - } - - for (int i = 0; i < AUVIA_NUM_PORTC; i++) { - - portc = devc->portc[i]; - - status = INB(devc, portc->base + OFF_STATUS); - if ((status & STATUS_INTR) == 0) { - /* clear any other interrupts */ - continue; - } - - /* - * NB: The old code did some goofy things to update - * the last valid SGD. However, since we don't ever - * reach the last valid SGD (because we loop first), I - * don't believe we need to do that. It would appear - * that NetBSD does the same. - */ - /* port interrupt */ - if (portc->started) { - intrs |= (1U << i); - } - /* let the chip know we are acking the interrupt */ - OUTB(devc, portc->base + OFF_STATUS, status); - - claimed = B_TRUE; - } - - mutex_exit(&devc->mutex); - - if (!claimed) { - return (DDI_INTR_UNCLAIMED); - } - - if (intrs & (1U << AUVIA_PLAY_SGD_NUM)) { - audio_engine_consume(devc->portc[AUVIA_PLAY_SGD_NUM]->engine); - } - if (intrs & (1U << AUVIA_REC_SGD_NUM)) { - audio_engine_produce(devc->portc[AUVIA_REC_SGD_NUM]->engine); - } - if (devc->ksp) { - AUVIA_KIOP(devc)->intrs[KSTAT_INTR_HARD]++; - } - - return (DDI_INTR_CLAIMED); } /* @@ -267,37 +192,23 @@ */ int -auvia_open(void *arg, int flag, - unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp) +auvia_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp) { auvia_portc_t *portc = arg; - auvia_devc_t *devc = portc->devc; _NOTE(ARGUNUSED(flag)); - portc->started = B_FALSE; portc->count = 0; - *fragfrp = portc->fragfr; - *nfragsp = AUVIA_NUM_SGD; + *nframesp = portc->nframes; *bufp = portc->buf_kaddr; - mutex_enter(&devc->mutex); - portc->reset(portc); - mutex_exit(&devc->mutex); - return (0); } void auvia_close(void *arg) { - auvia_portc_t *portc = arg; - auvia_devc_t *devc = portc->devc; - - mutex_enter(&devc->mutex); - auvia_stop_port(portc); - portc->started = B_FALSE; - mutex_exit(&devc->mutex); + _NOTE(ARGUNUSED(arg)); } int @@ -306,12 +217,8 @@ auvia_portc_t *portc = arg; auvia_devc_t *devc = portc->devc; - mutex_enter(&devc->mutex); - if (!portc->started) { - auvia_start_port(portc); - portc->started = B_TRUE; - } - mutex_exit(&devc->mutex); + portc->reset(portc); + OUTB(devc, portc->base + OFF_CTRL, CTRL_START | CTRL_AUTOSTART); return (0); } @@ -321,12 +228,7 @@ auvia_portc_t *portc = arg; auvia_devc_t *devc = portc->devc; - mutex_enter(&devc->mutex); - if (portc->started) { - auvia_stop_port(portc); - portc->started = B_FALSE; - } - mutex_exit(&devc->mutex); + OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE); } int @@ -367,96 +269,34 @@ { auvia_portc_t *portc = arg; auvia_devc_t *devc = portc->devc; - uint64_t val; + uint32_t pos; + uint32_t n; + + pos = INL(devc, portc->base + OFF_COUNT); + pos &= 0xffffff; + pos /= (sizeof (int16_t) * portc->nchan); - mutex_enter(&devc->mutex); - auvia_update_port(portc); - /* - * The residual is in bytes. We have to convert to frames, - * and then subtract it from the fragment size to get the - * number of frames processed. It is somewhat unfortunate thta - * this (the division) has to happen under the lock. If we - * restricted ourself to stereo out, this would be a simple - * shift. - */ - val = portc->count + - (portc->fragfr - (portc->resid / (portc->nchan * 2))); - mutex_exit(&devc->mutex); + if (pos >= portc->pos) { + n = portc->nframes - (pos - portc->pos); + } else { + n = portc->pos - pos; + } + portc->pos = pos; + portc->count += n; - return (val); + return (portc->count); } /* private implementation bits */ void -auvia_start_port(auvia_portc_t *portc) -{ - auvia_devc_t *devc = portc->devc; - - ASSERT(mutex_owned(&devc->mutex)); - - if (devc->suspended) - return; - - /* - * Start with autoinit and SGD flag - * interrupts enabled. - */ - OUTB(devc, portc->base + OFF_CTRL, - CTRL_START | CTRL_AUTOSTART | CTRL_FLAG); -} - -void -auvia_stop_port(auvia_portc_t *portc) -{ - auvia_devc_t *devc = portc->devc; - - if (devc->suspended) - return; - - OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE); -} - -void -auvia_update_port(auvia_portc_t *portc) -{ - auvia_devc_t *devc = portc->devc; - uint32_t frag; - uint32_t n; - - ASSERT(mutex_owned(&devc->mutex)); - if (devc->suspended) { - portc->cur_frag = 0; - portc->resid = portc->fragsz; - n = 0; - } else { - frag = INL(devc, portc->base + OFF_COUNT); - portc->resid = (frag & 0xffffff); - frag >>= 24; - frag &= 0xff; - - if (frag >= portc->cur_frag) { - n = frag - portc->cur_frag; - } else { - n = frag + AUVIA_NUM_SGD - portc->cur_frag; - } - portc->count += (n * portc->fragfr); - portc->cur_frag = frag; - } -} - -void auvia_reset_output(auvia_portc_t *portc) { auvia_devc_t *devc = portc->devc; uint32_t cmap; - portc->cur_frag = 0; - portc->resid = portc->fragsz; - - if (devc->suspended) - return; + portc->pos = 0; OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE); /* Stop */ OUTL(devc, portc->base + OFF_DMA, portc->sgd_paddr); @@ -504,11 +344,7 @@ auvia_devc_t *devc = portc->devc; uint32_t fmt; - portc->cur_frag = 0; - portc->resid = portc->fragsz; - - if (devc->suspended) - return; + portc->pos = 0; OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE); /* Stop */ OUTL(devc, portc->base + OFF_DMA, portc->sgd_paddr); @@ -531,21 +367,17 @@ ddi_dma_cookie_t cookie; uint_t count; int dir; - char *prop; unsigned caps; audio_dev_t *adev; uint32_t *desc; - uint32_t paddr; adev = devc->adev; portc = kmem_zalloc(sizeof (*portc), KM_SLEEP); devc->portc[num] = portc; portc->devc = devc; - portc->started = B_FALSE; switch (num) { case AUVIA_REC_SGD_NUM: - prop = "record-interrupts"; portc->base = devc->base + REG_RECBASE; portc->syncdir = DDI_DMA_SYNC_FORKERNEL; portc->nchan = 2; @@ -554,7 +386,6 @@ dir = DDI_DMA_READ; break; case AUVIA_PLAY_SGD_NUM: - prop = "play-interrupts"; portc->base = devc->base + REG_PLAYBASE; portc->syncdir = DDI_DMA_SYNC_FORDEV; portc->nchan = 6; @@ -569,24 +400,8 @@ /* make sure port is shut down */ OUTB(portc->devc, portc->base + OFF_CTRL, CTRL_TERMINATE); - /* figure out fragment configuration */ - portc->intrs = ddi_prop_get_int(DDI_DEV_T_ANY, devc->dip, - DDI_PROP_DONTPASS, prop, AUVIA_INTRS); - - /* make sure the values are good */ - if (portc->intrs < AUVIA_MIN_INTRS) { - audio_dev_warn(adev, "%s too low, %d, reset to %d", - prop, portc->intrs, AUVIA_INTRS); - portc->intrs = AUVIA_INTRS; - } else if (portc->intrs > AUVIA_MAX_INTRS) { - audio_dev_warn(adev, "%s too high, %d, reset to %d", - prop, portc->intrs, AUVIA_INTRS); - portc->intrs = AUVIA_INTRS; - } - - portc->fragfr = 48000 / portc->intrs; - portc->fragsz = portc->fragfr * portc->nchan * 2; - portc->buf_size = portc->fragsz * AUVIA_NUM_SGD; + portc->nframes = 4096; + portc->buf_size = portc->nframes * portc->nchan * sizeof (int16_t); /* first allocate up space for SGD list */ if (ddi_dma_alloc_handle(devc->dip, &dma_attr_sgd, @@ -595,8 +410,7 @@ return (DDI_FAILURE); } - if (ddi_dma_mem_alloc(portc->sgd_dmah, - AUVIA_NUM_SGD * 2 * sizeof (uint32_t), &dev_attr, + if (ddi_dma_mem_alloc(portc->sgd_dmah, 2 * sizeof (uint32_t), &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &portc->sgd_kaddr, &len, &portc->sgd_acch) != DDI_SUCCESS) { audio_dev_warn(adev, "failed to allocate SGD memory"); @@ -633,21 +447,11 @@ } portc->buf_paddr = cookie.dmac_address; - /* now wire descriptors up */ + /* now wire up descriptor -- just one */ desc = (void *)portc->sgd_kaddr; - paddr = portc->buf_paddr; - for (int i = 0; i < AUVIA_NUM_SGD; i++) { - uint32_t flags; - - flags = AUVIA_SGD_FLAG | portc->fragsz; - if (i == (AUVIA_NUM_SGD - 1)) { - flags |= AUVIA_SGD_EOL; - } - ddi_put32(portc->sgd_acch, desc++, paddr); - ddi_put32(portc->sgd_acch, desc++, flags); - paddr += portc->fragsz; - } + ddi_put32(portc->sgd_acch, desc++, portc->buf_paddr); + ddi_put32(portc->sgd_acch, desc++, AUVIA_SGD_EOL | portc->buf_size); (void) ddi_dma_sync(portc->sgd_dmah, 0, 0, DDI_DMA_SYNC_FORDEV); @@ -663,57 +467,9 @@ return (DDI_SUCCESS); } -int -auvia_setup_intrs(auvia_devc_t *devc) -{ - uint_t ipri; - int actual; - int rv; - ddi_intr_handle_t ih[1]; - - rv = ddi_intr_alloc(devc->dip, ih, DDI_INTR_TYPE_FIXED, - 0, 1, &actual, DDI_INTR_ALLOC_STRICT); - if ((rv != DDI_SUCCESS) || (actual != 1)) { - audio_dev_warn(devc->adev, - "Can't alloc interrupt handle (rv %d actual %d)", - rv, actual); - return (DDI_FAILURE); - } - - if (ddi_intr_get_pri(ih[0], &ipri) != DDI_SUCCESS) { - audio_dev_warn(devc->adev, "Can't get interrupt priority"); - (void) ddi_intr_free(ih[0]); - return (DDI_FAILURE); - } - - if (ddi_intr_add_handler(ih[0], auvia_intr, devc, NULL) != - DDI_SUCCESS) { - audio_dev_warn(devc->adev, "Can't add interrupt handler"); - (void) ddi_intr_free(ih[0]); - return (DDI_FAILURE); - } - - devc->ih = ih[0]; - mutex_init(&devc->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - mutex_init(&devc->low_mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - return (DDI_SUCCESS); -} - void auvia_destroy(auvia_devc_t *devc) { - if (devc->ih != NULL) { - (void) ddi_intr_disable(devc->ih); - (void) ddi_intr_remove_handler(devc->ih); - (void) ddi_intr_free(devc->ih); - mutex_destroy(&devc->mutex); - mutex_destroy(&devc->low_mutex); - } - - if (devc->ksp) { - kstat_delete(devc->ksp); - } - for (int i = 0; i < AUVIA_NUM_PORTC; i++) { auvia_portc_t *portc = devc->portc[i]; if (!portc) @@ -856,10 +612,6 @@ goto error; } - if (auvia_setup_intrs(devc) != DDI_SUCCESS) { - goto error; - } - devc->ac97 = ac97_alloc(dip, auvia_read_ac97, auvia_write_ac97, devc); if (devc->ac97 == NULL) { audio_dev_warn(devc->adev, "failed to allocate ac97 handle"); @@ -871,19 +623,11 @@ goto error; } - /* set up kernel statistics */ - if ((devc->ksp = kstat_create(AUVIA_NAME, ddi_get_instance(dip), - AUVIA_NAME, "controller", KSTAT_TYPE_INTR, 1, - KSTAT_FLAG_PERSISTENT)) != NULL) { - kstat_install(devc->ksp); - } - if (audio_dev_register(devc->adev) != DDI_SUCCESS) { audio_dev_warn(devc->adev, "unable to register with framework"); goto error; } - (void) ddi_intr_enable(devc->ih); ddi_report_dev(dip); return (DDI_SUCCESS); @@ -902,28 +646,10 @@ auvia_hwinit(devc); - /* allow ac97 operations again */ - ac97_resume(devc->ac97); - - mutex_enter(&devc->mutex); - devc->suspended = B_TRUE; - for (int i = 0; i < AUVIA_NUM_PORTC; i++) { - - auvia_portc_t *portc = devc->portc[i]; + ac97_reset(devc->ac97); - if (portc->engine != NULL) - audio_engine_reset(portc->engine); - - /* reset the port */ - portc->reset(portc); + audio_dev_resume(devc->adev); - if (portc->started) { - auvia_start_port(portc); - } else { - auvia_stop_port(portc); - } - } - mutex_exit(&devc->mutex); return (DDI_SUCCESS); } @@ -941,16 +667,8 @@ int auvia_suspend(auvia_devc_t *devc) { - ac97_suspend(devc->ac97); - - mutex_enter(&devc->mutex); - for (int i = 0; i < AUVIA_NUM_PORTC; i++) { + audio_dev_suspend(devc->adev); - auvia_portc_t *portc = devc->portc[i]; - auvia_stop_port(portc); - } - devc->suspended = B_TRUE; - mutex_exit(&devc->mutex); return (DDI_SUCCESS); } @@ -1057,7 +775,7 @@ for (int i = 0; i < AUVIA_NUM_PORTC; i++) { auvia_portc_t *portc = devc->portc[i]; - auvia_stop_port(portc); + OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE); } return (DDI_SUCCESS); }
--- a/usr/src/uts/common/io/audio/drv/audiovia823x/audiovia823x.conf Tue Mar 16 09:43:38 2010 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# Configuration file for the audiovia823x audio driver. -# -# WARNING: This is an UNSTABLE configuration file. Its contents -# may change at any time. - -# -# play-interrupts sets the number of interrupts per second when playing. -# This affects the resolution of various things, such as sample counts. -# record-interrupts does the same for record interrupts. -# -# These may be tuned to get more accurate information by increasing the -# count. However, the larger the interrupts per second the larger the -# load on the system. So use this capability cautiously. The audiovia823x -# driver enforces a maximum and minimum count. -# -play-interrupts=175; -record-interrupts=175;
--- a/usr/src/uts/common/io/audio/drv/audiovia823x/audiovia823x.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiovia823x/audiovia823x.h Tue Mar 16 09:30:41 2010 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -62,10 +62,6 @@ #define AUVIA_NUM_PORTC 2 #define AUVIA_NUM_SGD 16 /* number of fragments */ -#define AUVIA_MAX_INTRS 256 -#define AUVIA_MIN_INTRS 24 -#define AUVIA_INTRS 175 - #define AUVIA_SGD_EOL 0x80000000 #define AUVIA_SGD_FLAG 0x40000000 @@ -127,7 +123,6 @@ auvia_devc_t *devc; audio_engine_t *engine; caddr_t base; /* base for registers */ - boolean_t started; int nchan; ddi_dma_handle_t sgd_dmah; /* dma for descriptors */ @@ -142,11 +137,8 @@ size_t buf_size; int syncdir; - unsigned intrs; - unsigned fragfr; - unsigned fragsz; - unsigned cur_frag; - unsigned resid; + unsigned nframes; + unsigned pos; uint64_t count; @@ -159,8 +151,6 @@ dev_info_t *dip; audio_dev_t *adev; ac97_t *ac97; - kstat_t *ksp; - boolean_t suspended; char *chip_name; int chip_type; @@ -172,9 +162,6 @@ ddi_acc_handle_t regsh; caddr_t base; - kmutex_t mutex; /* For normal locking */ - kmutex_t low_mutex; /* For low level routines */ - ddi_intr_handle_t ih; auvia_portc_t *portc[AUVIA_NUM_PORTC]; };
--- a/usr/src/uts/common/io/audio/drv/audiovia97/audiovia97.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiovia97/audiovia97.c Tue Mar 16 09:30:41 2010 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -92,7 +92,7 @@ static int via97_detach(via97_devc_t *); static int via97_suspend(via97_devc_t *); -static int via97_open(void *, int, unsigned *, unsigned *, caddr_t *); +static int via97_open(void *, int, unsigned *, caddr_t *); static void via97_close(void *); static int via97_start(void *); static void via97_stop(void *); @@ -101,18 +101,13 @@ static int via97_rate(void *); static uint64_t via97_count(void *); static void via97_sync(void *, unsigned); +static uint_t via97_playahead(void *); static uint16_t via97_read_ac97(void *, uint8_t); static void via97_write_ac97(void *, uint8_t, uint16_t); static int via97_alloc_port(via97_devc_t *, int); -static void via97_start_port(via97_portc_t *); -static void via97_stop_port(via97_portc_t *); -static void via97_update_port(via97_portc_t *); -static void via97_reset_port(via97_portc_t *); static void via97_destroy(via97_devc_t *); -static int via97_setup_intrs(via97_devc_t *); static void via97_hwinit(via97_devc_t *); -static uint_t via97_intr(caddr_t, caddr_t); static audio_engine_ops_t via97_engine_ops = { AUDIO_ENGINE_VERSION, @@ -127,7 +122,7 @@ via97_sync, NULL, NULL, - NULL + via97_playahead }; static uint16_t @@ -140,7 +135,6 @@ if (index > 0x7F) return (0xffff); - mutex_enter(&devc->low_mutex); addr = (index << 16) + CODEC_RD; OUTL(devc, devc->base + AC97CODEC, addr); drv_usecwait(100); @@ -153,7 +147,6 @@ drv_usecwait(50); } if (i == CODEC_TIMEOUT_COUNT) { - mutex_exit(&devc->low_mutex); return (0xffff); } @@ -161,10 +154,8 @@ tmp = INL(devc, devc->base + AC97CODEC); OUTB(devc, devc->base + AC97CODEC + 3, 0x02); if (((tmp & CODEC_INDEX) >> 16) == index) { - mutex_exit(&devc->low_mutex); return ((int)tmp & CODEC_DATA); } - mutex_exit(&devc->low_mutex); return (0xffff); } @@ -175,7 +166,6 @@ int value = 0; unsigned int i = 0; - mutex_enter(&devc->low_mutex); value = (index << 16) + data; OUTL(devc, devc->base + AC97CODEC, value); drv_usecwait(100); @@ -187,61 +177,6 @@ break; drv_usecwait(50); } - mutex_exit(&devc->low_mutex); -} - -static uint_t -via97_recintr(via97_devc_t *devc) -{ - int status; - - status = INB(devc, devc->base + 0x10); - - if (!(status & 0x01)) /* No interrupt */ - return (B_FALSE); - - audio_engine_produce(devc->portc[VIA97_REC_SGD_NUM]->engine); - - OUTB(devc, devc->base + 0x10, status | 0x01); /* Ack */ - return (B_TRUE); -} - -static uint_t -via97_playintr(via97_devc_t *devc) -{ - int status; - - status = INB(devc, devc->base + 0x00); - - if (!(status & 0x01)) /* No interrupt */ - return (B_FALSE); - - audio_engine_consume(devc->portc[VIA97_PLAY_SGD_NUM]->engine); - - OUTB(devc, devc->base + 0x00, status | 0x01); /* Ack */ - return (B_TRUE); -} - -static uint_t -via97_intr(caddr_t argp, caddr_t nocare) -{ - via97_devc_t *devc = (void *)argp; - - _NOTE(ARGUNUSED(nocare)); - - if (devc->suspended) { - return (DDI_INTR_UNCLAIMED); - } - - if (!via97_recintr(devc) && !via97_playintr(devc)) { - return (DDI_INTR_UNCLAIMED); - } - - if (devc->ksp) { - VIA97_KIOP(devc)->intrs[KSTAT_INTR_HARD]++; - } - - return (DDI_INTR_CLAIMED); } /* @@ -249,37 +184,23 @@ */ int -via97_open(void *arg, int flag, - unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp) +via97_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp) { via97_portc_t *portc = arg; - via97_devc_t *devc = portc->devc; _NOTE(ARGUNUSED(flag)); - portc->started = B_FALSE; portc->count = 0; - *fragfrp = portc->fragfr; - *nfragsp = VIA97_NUM_SGD; + *nframesp = portc->nframes; *bufp = portc->buf_kaddr; - mutex_enter(&devc->mutex); - via97_reset_port(portc); - mutex_exit(&devc->mutex); - return (0); } void via97_close(void *arg) { - via97_portc_t *portc = arg; - via97_devc_t *devc = portc->devc; - - mutex_enter(&devc->mutex); - via97_stop_port(portc); - portc->started = B_FALSE; - mutex_exit(&devc->mutex); + _NOTE(ARGUNUSED(arg)); } int @@ -288,12 +209,18 @@ via97_portc_t *portc = arg; via97_devc_t *devc = portc->devc; - mutex_enter(&devc->mutex); - if (!portc->started) { - via97_start_port(portc); - portc->started = B_TRUE; - } - mutex_exit(&devc->mutex); + portc->pos = 0; + + OUTB(devc, portc->base + 0x01, 0x40); /* Stop */ + OUTL(devc, portc->base + 4, portc->sgd_paddr); + /* Set autostart at EOL, stereo, 16 bits */ + OUTB(devc, portc->base + 0x02, + 0x80 | /* Set autostart at EOL */ + 0x20 | /* 16 bits */ + 0x10); /* Stereo */ + + OUTB(devc, portc->base + 0x01, 0x80); /* Start */ + return (0); } @@ -303,12 +230,7 @@ via97_portc_t *portc = arg; via97_devc_t *devc = portc->devc; - mutex_enter(&devc->mutex); - if (portc->started) { - via97_stop_port(portc); - portc->started = B_FALSE; - } - mutex_exit(&devc->mutex); + OUTB(devc, portc->base + 0x01, 0x40); /* Stop */ } int @@ -344,112 +266,45 @@ (void) ddi_dma_sync(portc->buf_dmah, 0, 0, portc->syncdir); } +uint_t +via97_playahead(void *arg) +{ + _NOTE(ARGUNUSED(arg)); + + /* + * We see some situations where the default 1.5 fragments from + * the framework is not enough. 800-900 frame jitter is not + * uncommon. Especially at startup. + */ + return (1024); +} + uint64_t via97_count(void *arg) { via97_portc_t *portc = arg; via97_devc_t *devc = portc->devc; - uint64_t val; + uint32_t pos; + uint32_t n; + + pos = INL(devc, portc->base + 0x0c) & 0xffffff; + /* convert from bytes to 16-bit stereo frames */ + pos /= (sizeof (int16_t) * 2); - mutex_enter(&devc->mutex); - via97_update_port(portc); - /* - * The residual is in bytes. We have to convert to frames, - * and then subtract it from the fragment size to get the - * number of frames processed. Note that we have 16 bit - * stereo frames. - */ - val = portc->count + - (portc->fragfr - (portc->resid / (2 * 2))); - mutex_exit(&devc->mutex); + if (pos >= portc->pos) { + n = portc->nframes - (pos - portc->pos); + } else { + n = portc->pos - pos; + } + portc->pos = pos; + portc->count += n; - return (val); + return (portc->count); } /* private implementation bits */ -void -via97_start_port(via97_portc_t *portc) -{ - via97_devc_t *devc = portc->devc; - - ASSERT(mutex_owned(&devc->mutex)); - - if (devc->suspended) - return; - OUTB(devc, portc->base + 0x01, 0x80); /* Start */ -} - -void -via97_stop_port(via97_portc_t *portc) -{ - via97_devc_t *devc = portc->devc; - - if (devc->suspended) - return; - - OUTB(devc, portc->base + 0x01, 0x40); /* Stop */ -} - -void -via97_update_port(via97_portc_t *portc) -{ -/* - * Unfortunately the controller seems to raise interrupt about 32 bytes before - * the DMA pointer moves to a new fragment. This means that the bytes value - * returned will be bogus during few samples before - * the pointer wraps back to the beginning of buffer. - */ - via97_devc_t *devc = portc->devc; - uint32_t frag, resid; - uint32_t n; - - ASSERT(mutex_owned(&devc->mutex)); - if (devc->suspended) { - portc->cur_frag = 0; - portc->resid = portc->fragsz; - n = 0; - } else { - resid = INL(devc, portc->base + 0x0c) & 0xffffff; - resid = portc->fragsz - resid; - - frag = - ((INL(devc, portc->base + 0x04) - portc->sgd_paddr) / 8) - - 1; - - portc->resid = resid; - - if (frag >= portc->cur_frag) { - n = frag - portc->cur_frag; - } else { - n = frag + VIA97_NUM_SGD - portc->cur_frag; - } - portc->count += (n * portc->fragfr); - portc->cur_frag = frag; - } -} - -void -via97_reset_port(via97_portc_t *portc) -{ - via97_devc_t *devc = portc->devc; - - portc->cur_frag = 0; - portc->resid = portc->fragsz; - - if (devc->suspended) - return; - - OUTB(devc, portc->base + 0x01, 0x40); /* Stop */ - OUTL(devc, portc->base + 4, portc->sgd_paddr); - /* Set autostart at EOL, interrupt on FLAG, stereo, 16 bits */ - OUTB(devc, portc->base + 0x02, - 0x81 | /* Set autostart at EOL, interrupt on FLAG */ - 0x20 | /* 16 bits */ - 0x10); /* Stereo */ -} - int via97_alloc_port(via97_devc_t *devc, int num) { @@ -458,28 +313,23 @@ ddi_dma_cookie_t cookie; uint_t count; int dir; - char *prop; unsigned caps; audio_dev_t *adev; uint32_t *desc; - uint32_t paddr; adev = devc->adev; portc = kmem_zalloc(sizeof (*portc), KM_SLEEP); devc->portc[num] = portc; portc->devc = devc; - portc->started = B_FALSE; portc->base = devc->base + num * 0x10; switch (num) { case VIA97_REC_SGD_NUM: - prop = "record-interrupts"; portc->syncdir = DDI_DMA_SYNC_FORKERNEL; caps = ENGINE_INPUT_CAP; dir = DDI_DMA_READ; break; case VIA97_PLAY_SGD_NUM: - prop = "play-interrupts"; portc->syncdir = DDI_DMA_SYNC_FORDEV; caps = ENGINE_OUTPUT_CAP; dir = DDI_DMA_WRITE; @@ -488,24 +338,9 @@ return (DDI_FAILURE); } - /* figure out fragment configuration */ - portc->intrs = ddi_prop_get_int(DDI_DEV_T_ANY, devc->dip, - DDI_PROP_DONTPASS, prop, VIA97_INTRS); - - /* make sure the values are good */ - if (portc->intrs < VIA97_MIN_INTRS) { - audio_dev_warn(adev, "%s too low, %d, reset to %d", - prop, portc->intrs, VIA97_INTRS); - portc->intrs = VIA97_INTRS; - } else if (portc->intrs > VIA97_MAX_INTRS) { - audio_dev_warn(adev, "%s too high, %d, reset to %d", - prop, portc->intrs, VIA97_INTRS); - portc->intrs = VIA97_INTRS; - } - - portc->fragfr = 48000 / portc->intrs; - portc->fragsz = portc->fragfr * 2 * 2; /* 16 bit stereo frames */ - portc->buf_size = portc->fragsz * VIA97_NUM_SGD; + /* Simplicity -- a single contiguous looping buffer */ + portc->nframes = 2048; + portc->buf_size = portc->nframes * sizeof (int16_t) * 2; /* first allocate up space for SGD list */ if (ddi_dma_alloc_handle(devc->dip, &dma_attr_sgd, @@ -514,8 +349,8 @@ return (DDI_FAILURE); } - if (ddi_dma_mem_alloc(portc->sgd_dmah, - VIA97_NUM_SGD * 2 *sizeof (uint32_t), &dev_attr, + /* a single SGD entry is only 8 bytes long */ + if (ddi_dma_mem_alloc(portc->sgd_dmah, 8, &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &portc->sgd_kaddr, &len, &portc->sgd_acch) != DDI_SUCCESS) { audio_dev_warn(adev, "failed to allocate SGD memory"); @@ -552,22 +387,10 @@ } portc->buf_paddr = cookie.dmac_address; - /* now wire descriptors up */ + /* now wire descriptor up -- we only use one (which has EOL set)! */ desc = (void *)portc->sgd_kaddr; - paddr = portc->buf_paddr; - for (int i = 0; i < VIA97_NUM_SGD; i++) { - uint32_t flags; - - flags = 0x40000000 | portc->fragsz; - - if (i == (VIA97_NUM_SGD - 1)) { - flags |= 0x80000000; /* EOL */ - } - - ddi_put32(portc->sgd_acch, desc++, paddr); - ddi_put32(portc->sgd_acch, desc++, flags); - paddr += portc->fragsz; - } + ddi_put32(portc->sgd_acch, desc++, portc->buf_paddr); + ddi_put32(portc->sgd_acch, desc++, 0x80000000U | portc->buf_size); OUTL(devc, portc->base + 4, portc->sgd_paddr); (void) ddi_dma_sync(portc->sgd_dmah, 0, 0, DDI_DMA_SYNC_FORDEV); @@ -584,57 +407,9 @@ return (DDI_SUCCESS); } -int -via97_setup_intrs(via97_devc_t *devc) -{ - uint_t ipri; - int actual; - int rv; - ddi_intr_handle_t ih[1]; - - rv = ddi_intr_alloc(devc->dip, ih, DDI_INTR_TYPE_FIXED, - 0, 1, &actual, DDI_INTR_ALLOC_STRICT); - if ((rv != DDI_SUCCESS) || (actual != 1)) { - audio_dev_warn(devc->adev, - "Can't alloc interrupt handle (rv %d actual %d)", - rv, actual); - return (DDI_FAILURE); - } - - if (ddi_intr_get_pri(ih[0], &ipri) != DDI_SUCCESS) { - audio_dev_warn(devc->adev, "Can't get interrupt priority"); - (void) ddi_intr_free(ih[0]); - return (DDI_FAILURE); - } - - if (ddi_intr_add_handler(ih[0], via97_intr, devc, NULL) != - DDI_SUCCESS) { - audio_dev_warn(devc->adev, "Can't add interrupt handler"); - (void) ddi_intr_free(ih[0]); - return (DDI_FAILURE); - } - - devc->ih = ih[0]; - mutex_init(&devc->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - mutex_init(&devc->low_mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri)); - return (DDI_SUCCESS); -} - void via97_destroy(via97_devc_t *devc) { - if (devc->ih != NULL) { - (void) ddi_intr_disable(devc->ih); - (void) ddi_intr_remove_handler(devc->ih); - (void) ddi_intr_free(devc->ih); - mutex_destroy(&devc->mutex); - mutex_destroy(&devc->low_mutex); - } - - if (devc->ksp) { - kstat_delete(devc->ksp); - } - for (int i = 0; i < VIA97_NUM_PORTC; i++) { via97_portc_t *portc = devc->portc[i]; if (!portc) @@ -753,10 +528,6 @@ goto error; } - if (via97_setup_intrs(devc) != DDI_SUCCESS) { - goto error; - } - devc->ac97 = ac97_alloc(dip, via97_read_ac97, via97_write_ac97, devc); if (devc->ac97 == NULL) { audio_dev_warn(devc->adev, "failed to allocate ac97 handle"); @@ -768,19 +539,11 @@ goto error; } - /* set up kernel statistics */ - if ((devc->ksp = kstat_create(VIA97_NAME, ddi_get_instance(dip), - VIA97_NAME, "controller", KSTAT_TYPE_INTR, 1, - KSTAT_FLAG_PERSISTENT)) != NULL) { - kstat_install(devc->ksp); - } - if (audio_dev_register(devc->adev) != DDI_SUCCESS) { audio_dev_warn(devc->adev, "unable to register with framework"); goto error; } - (void) ddi_intr_enable(devc->ih); ddi_report_dev(dip); return (DDI_SUCCESS); @@ -799,28 +562,9 @@ via97_hwinit(devc); - /* allow ac97 operations again */ - ac97_resume(devc->ac97); - - mutex_enter(&devc->mutex); - devc->suspended = B_FALSE; - for (int i = 0; i < VIA97_NUM_PORTC; i++) { - - via97_portc_t *portc = devc->portc[i]; + ac97_reset(devc->ac97); - if (portc->engine != NULL) - audio_engine_reset(portc->engine); - - /* reset the port */ - via97_reset_port(portc); - - if (portc->started) { - via97_start_port(portc); - } else { - via97_stop_port(portc); - } - } - mutex_exit(&devc->mutex); + audio_dev_resume(devc->adev); return (DDI_SUCCESS); } @@ -837,16 +581,7 @@ int via97_suspend(via97_devc_t *devc) { - ac97_suspend(devc->ac97); - - mutex_enter(&devc->mutex); - for (int i = 0; i < VIA97_NUM_PORTC; i++) { - - via97_portc_t *portc = devc->portc[i]; - via97_stop_port(portc); - } - devc->suspended = B_TRUE; - mutex_exit(&devc->mutex); + audio_dev_suspend(devc->adev); return (DDI_SUCCESS); } @@ -950,12 +685,6 @@ devc = ddi_get_driver_private(dip); - for (int i = 0; i < VIA97_NUM_PORTC; i++) { - - via97_portc_t *portc = devc->portc[i]; - via97_stop_port(portc); - } - /* * Turn off the hardware */
--- a/usr/src/uts/common/io/audio/drv/audiovia97/audiovia97.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/drv/audiovia97/audiovia97.h Tue Mar 16 09:30:41 2010 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -38,15 +38,10 @@ #define VIA97_NUM_PORTC 2 #define VIA97_PLAY_SGD_NUM 0 #define VIA97_REC_SGD_NUM 1 -#define VIA97_NUM_SGD 512 /* Max number of SGD entries (4k/8) */ #define VIA_VENDOR_ID 0x1106 #define VIA_82C686 0x3058 -#define VIA97_MAX_INTRS 256 -#define VIA97_MIN_INTRS 24 -#define VIA97_INTRS 175 - #define CODEC_TIMEOUT_COUNT 500 #define AC97CODEC 0x80 /* Access AC97 Codec */ #define IN_CMD 0x01000000 /* busy in sending */ @@ -63,12 +58,9 @@ via97_devc_t *devc; audio_engine_t *engine; - int started; - unsigned intrs; - unsigned fragfr; - unsigned fragsz; - unsigned cur_frag; - unsigned resid; + int started; + unsigned nframes; + unsigned pos; caddr_t base; ddi_dma_handle_t sgd_dmah; /* dma for descriptors */ @@ -90,15 +82,10 @@ dev_info_t *dip; audio_dev_t *adev; ac97_t *ac97; - kstat_t *ksp; - boolean_t suspended; ddi_acc_handle_t pcih; ddi_acc_handle_t regsh; caddr_t base; - kmutex_t mutex; /* For normal locking */ - kmutex_t low_mutex; /* For low level routines */ - ddi_intr_handle_t ih; via97_portc_t *portc[VIA97_NUM_PORTC]; };
--- a/usr/src/uts/common/io/audio/impl/audio_client.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/impl/audio_client.c Tue Mar 16 09:30:41 2010 -0700 @@ -21,7 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -109,32 +109,32 @@ return (sp->s_user_parms->p_rate); } -unsigned +uint_t auclnt_get_fragsz(audio_stream_t *sp) { return (sp->s_fragbytes); } -unsigned +uint_t auclnt_get_framesz(audio_stream_t *sp) { return (sp->s_framesz); } -unsigned +uint_t auclnt_get_nfrags(audio_stream_t *sp) { return (sp->s_nfrags); } -unsigned +uint_t auclnt_get_nframes(audio_stream_t *sp) { return (sp->s_nframes); } void -auclnt_set_latency(audio_stream_t *sp, unsigned frags, unsigned bytes) +auclnt_set_latency(audio_stream_t *sp, uint_t frags, uint_t bytes) { mutex_enter(&sp->s_lock); sp->s_hintfrags = (uint16_t)frags; @@ -154,13 +154,13 @@ return (sp->s_tail); } -unsigned +uint_t auclnt_get_hidx(audio_stream_t *sp) { return (sp->s_hidx); } -unsigned +uint_t auclnt_get_tidx(audio_stream_t *sp) { return (sp->s_tidx); @@ -178,21 +178,21 @@ return (&c->c_ostream); } -unsigned +uint_t auclnt_get_count(audio_stream_t *sp) { - unsigned count; + uint_t count; mutex_enter(&sp->s_lock); ASSERT((sp->s_head - sp->s_tail) <= sp->s_nframes); - count = (unsigned)(sp->s_head - sp->s_tail); + count = (uint_t)(sp->s_head - sp->s_tail); mutex_exit(&sp->s_lock); return (count); } -unsigned -auclnt_consume(audio_stream_t *sp, unsigned n) +uint_t +auclnt_consume(audio_stream_t *sp, uint_t n) { mutex_enter(&sp->s_lock); @@ -212,12 +212,12 @@ return (n); } -unsigned -auclnt_consume_data(audio_stream_t *sp, caddr_t dst, unsigned n) +uint_t +auclnt_consume_data(audio_stream_t *sp, caddr_t dst, uint_t n) { - unsigned nframes; - unsigned framesz; - unsigned cnt; + uint_t nframes; + uint_t framesz; + uint_t cnt; caddr_t data; mutex_enter(&sp->s_lock); @@ -233,7 +233,7 @@ cnt = n = min(n, sp->s_head - sp->s_tail); data = sp->s_data + (sp->s_tidx * framesz); do { - unsigned nf, nb; + uint_t nf, nb; nf = min(nframes - sp->s_tidx, n); nb = nf * framesz; @@ -259,8 +259,8 @@ return (cnt); } -unsigned -auclnt_produce(audio_stream_t *sp, unsigned n) +uint_t +auclnt_produce(audio_stream_t *sp, uint_t n) { mutex_enter(&sp->s_lock); @@ -280,12 +280,12 @@ return (n); } -unsigned -auclnt_produce_data(audio_stream_t *sp, caddr_t src, unsigned n) +uint_t +auclnt_produce_data(audio_stream_t *sp, caddr_t src, uint_t n) { - unsigned nframes; - unsigned framesz; - unsigned cnt; + uint_t nframes; + uint_t framesz; + uint_t cnt; caddr_t data; mutex_enter(&sp->s_lock); @@ -301,7 +301,7 @@ cnt = n = min(n, nframes - (sp->s_head - sp->s_tail)); data = sp->s_data + (sp->s_hidx * framesz); do { - unsigned nf, nb; + uint_t nf, nb; nf = min(nframes - sp->s_hidx, n); nb = nf * framesz; @@ -332,10 +332,12 @@ auclnt_read(audio_client_t *c, struct uio *uio) { audio_stream_t *sp = &c->c_istream; - unsigned cnt; + uint_t cnt; int rv = 0; offset_t loff; int eagain; + uint_t tidx; + uint_t framesz; loff = uio->uio_loffset; eagain = EAGAIN; @@ -348,11 +350,13 @@ mutex_enter(&sp->s_lock); } + + framesz = sp->s_framesz; + ASSERT(sp->s_head >= sp->s_tail); ASSERT(sp->s_tidx < sp->s_nframes); - ASSERT(sp->s_hidx < sp->s_nframes); - while (uio->uio_resid >= sp->s_framesz) { + while (uio->uio_resid >= framesz) { while ((cnt = (sp->s_head - sp->s_tail)) == 0) { if (uio->uio_fmode & (FNONBLOCK|FNDELAY)) { @@ -365,19 +369,22 @@ } } - cnt = min(cnt, sp->s_nframes - sp->s_tidx); - cnt = min(cnt, (uio->uio_resid / sp->s_framesz)); + tidx = sp->s_tidx; + cnt = min(cnt, sp->s_nframes - tidx); + cnt = min(cnt, (uio->uio_resid / framesz)); - rv = uiomove(sp->s_data + (sp->s_tidx * sp->s_framesz), - cnt * sp->s_framesz, UIO_READ, uio); + mutex_exit(&sp->s_lock); + rv = uiomove(sp->s_data + (tidx * framesz), + cnt * framesz, UIO_READ, uio); + uio->uio_loffset = loff; eagain = 0; if (rv != 0) { - mutex_exit(&sp->s_lock); return (rv); } + mutex_enter(&sp->s_lock); sp->s_tail += cnt; sp->s_tidx += cnt; if (sp->s_tidx == sp->s_nframes) { @@ -400,21 +407,24 @@ auclnt_write(audio_client_t *c, struct uio *uio) { audio_stream_t *sp = &c->c_ostream; - unsigned cnt; + uint_t cnt; int rv = 0; offset_t loff; int eagain; + uint_t framesz; + uint_t hidx; loff = uio->uio_loffset; eagain = EAGAIN; mutex_enter(&sp->s_lock); + framesz = sp->s_framesz; + ASSERT(sp->s_head >= sp->s_tail); - ASSERT(sp->s_tidx < sp->s_nframes); ASSERT(sp->s_hidx < sp->s_nframes); - while (uio->uio_resid >= sp->s_framesz) { + while (uio->uio_resid >= framesz) { while ((cnt = sp->s_nframes - (sp->s_head - sp->s_tail)) == 0) { if (uio->uio_fmode & (FNONBLOCK|FNDELAY)) { @@ -427,19 +437,31 @@ } } - cnt = min(cnt, sp->s_nframes - sp->s_hidx); - cnt = min(cnt, (uio->uio_resid / sp->s_framesz)); + hidx = sp->s_hidx; + cnt = min(cnt, sp->s_nframes - hidx); + cnt = min(cnt, (uio->uio_resid / framesz)); - rv = uiomove(sp->s_data + (sp->s_hidx * sp->s_framesz), - cnt * sp->s_framesz, UIO_WRITE, uio); + /* + * We have to drop the stream lock, because the + * uiomove might require doing a page in, which could + * get blocked behind the PIL of the audio processing + * thread which also grabs the s_lock. (Hence, there + * is a risk of deadlock due to priority inversion.) + */ + mutex_exit(&sp->s_lock); + + rv = uiomove(sp->s_data + (hidx * framesz), + cnt * framesz, UIO_WRITE, uio); + uio->uio_loffset = loff; eagain = 0; if (rv != 0) { - mutex_exit(&sp->s_lock); return (rv); } + mutex_enter(&sp->s_lock); + sp->s_head += cnt; sp->s_hidx += cnt; if (sp->s_hidx == sp->s_nframes) { @@ -509,12 +531,12 @@ } void -auclnt_get_output_qlen(audio_client_t *c, unsigned *slen, unsigned *flen) +auclnt_get_output_qlen(audio_client_t *c, uint_t *slen, uint_t *flen) { audio_stream_t *sp = &c->c_ostream; audio_engine_t *e = sp->s_engine; uint64_t el, sl; - unsigned cnt, er, sr; + uint_t cnt, er, sr; if (e == NULL) { /* if no output engine, can't do it! */ @@ -533,13 +555,13 @@ er = e->e_rate; sl = sp->s_cnv_cnt; sr = sp->s_user_parms->p_rate; - cnt = (unsigned)(sp->s_head - sp->s_tail); + cnt = (uint_t)(sp->s_head - sp->s_tail); mutex_exit(&sp->s_lock); mutex_exit(&e->e_lock); /* engine frames converted to stream rate, plus stream frames */ *slen = cnt; - *flen = ((unsigned)(((el * sr) / er) + sl)); + *flen = ((uint_t)(((el * sr) / er) + sl)); } int @@ -1197,6 +1219,39 @@ return (c); } +int +auclnt_serialize(audio_client_t *c) +{ + mutex_enter(&c->c_lock); + while (c->c_serialize) { + if (cv_wait_sig(&c->c_cv, &c->c_lock) == 0) { + mutex_exit(&c->c_lock); + return (EINTR); + } + } + c->c_serialize = B_TRUE; + mutex_exit(&c->c_lock); + return (0); +} + +void +auclnt_unserialize(audio_client_t *c) +{ + mutex_enter(&c->c_lock); + ASSERT(c->c_serialize); + c->c_serialize = B_FALSE; + cv_broadcast(&c->c_cv); + mutex_exit(&c->c_lock); +} + +void +auclnt_hold(audio_client_t *c) +{ + mutex_enter(&c->c_lock); + c->c_refcnt++; + mutex_exit(&c->c_lock); +} + void auclnt_release(audio_client_t *c) { @@ -1208,7 +1263,7 @@ mutex_exit(&c->c_lock); } -unsigned +uint_t auclnt_dev_get_serial(audio_dev_t *d) { return (d->d_serial); @@ -1240,7 +1295,7 @@ int -auclnt_open(audio_client_t *c, unsigned fmts, int oflag) +auclnt_open(audio_client_t *c, uint_t fmts, int oflag) { audio_stream_t *sp; audio_dev_t *d = c->c_dev; @@ -1412,11 +1467,11 @@ return (dev->d_vers); } -unsigned +uint_t auclnt_get_dev_capab(audio_dev_t *dev) { uint32_t flags; - unsigned caps = 0; + uint_t caps = 0; flags = dev->d_flags; @@ -1577,13 +1632,13 @@ { audio_ctrl_t *ctrl; - rw_enter(&d->d_ctrl_lock, RW_READER); + mutex_enter(&d->d_ctrl_lock); for (ctrl = list_head(&d->d_controls); ctrl; ctrl = list_next(&d->d_controls, ctrl)) { if (walker(ctrl, arg) == AUDIO_WALK_STOP) break; } - rw_exit(&d->d_ctrl_lock); + mutex_exit(&d->d_ctrl_lock); } /* @@ -1604,15 +1659,15 @@ /* Verify argument */ ASSERT(d); - rw_enter(&d->d_ctrl_lock, RW_READER); + mutex_enter(&d->d_ctrl_lock); for (ctrl = list_head(&d->d_controls); ctrl; ctrl = list_next(&d->d_controls, ctrl)) { if (strcmp(ctrl->ctrl_name, name) == 0) { - rw_exit(&d->d_ctrl_lock); + mutex_exit(&d->d_ctrl_lock); return (ctrl); } } - rw_exit(&d->d_ctrl_lock); + mutex_exit(&d->d_ctrl_lock); return (NULL); }
--- a/usr/src/uts/common/io/audio/impl/audio_client.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/impl/audio_client.h Tue Mar 16 09:30:41 2010 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -94,26 +94,26 @@ void auclnt_flush(audio_stream_t *); -void auclnt_get_output_qlen(audio_client_t *, unsigned *, unsigned *); +void auclnt_get_output_qlen(audio_client_t *, uint_t *, uint_t *); -unsigned auclnt_get_fragsz(audio_stream_t *); -unsigned auclnt_get_framesz(audio_stream_t *); -unsigned auclnt_get_nfrags(audio_stream_t *); -unsigned auclnt_get_nframes(audio_stream_t *); -unsigned auclnt_get_count(audio_stream_t *); +uint_t auclnt_get_fragsz(audio_stream_t *); +uint_t auclnt_get_framesz(audio_stream_t *); +uint_t auclnt_get_nfrags(audio_stream_t *); +uint_t auclnt_get_nframes(audio_stream_t *); +uint_t auclnt_get_count(audio_stream_t *); uint64_t auclnt_get_head(audio_stream_t *); uint64_t auclnt_get_tail(audio_stream_t *); -unsigned auclnt_get_hidx(audio_stream_t *); -unsigned auclnt_get_tidx(audio_stream_t *); +uint_t auclnt_get_hidx(audio_stream_t *); +uint_t auclnt_get_tidx(audio_stream_t *); -void auclnt_set_latency(audio_stream_t *, unsigned, unsigned); +void auclnt_set_latency(audio_stream_t *, uint_t, uint_t); audio_stream_t *auclnt_input_stream(audio_client_t *); audio_stream_t *auclnt_output_stream(audio_client_t *); int auclnt_get_oflag(audio_client_t *); -int auclnt_open(audio_client_t *, unsigned, int); +int auclnt_open(audio_client_t *, uint_t, int); void auclnt_close(audio_client_t *); void auclnt_register_ops(minor_t, audio_client_ops_t *); @@ -124,10 +124,10 @@ queue_t *auclnt_get_rq(audio_client_t *); queue_t *auclnt_get_wq(audio_client_t *); -unsigned auclnt_produce(audio_stream_t *, unsigned); -unsigned auclnt_produce_data(audio_stream_t *, caddr_t, unsigned); -unsigned auclnt_consume(audio_stream_t *, unsigned); -unsigned auclnt_consume_data(audio_stream_t *, caddr_t, unsigned); +uint_t auclnt_produce(audio_stream_t *, uint_t); +uint_t auclnt_produce_data(audio_stream_t *, caddr_t, uint_t); +uint_t auclnt_consume(audio_stream_t *, uint_t); +uint_t auclnt_consume_data(audio_stream_t *, caddr_t, uint_t); int auclnt_read(audio_client_t *, struct uio *); int auclnt_write(audio_client_t *, struct uio *); int auclnt_chpoll(audio_client_t *, short, int, short *, struct pollhead **); @@ -159,7 +159,7 @@ const char *auclnt_get_dev_description(audio_dev_t *); const char *auclnt_get_dev_version(audio_dev_t *); const char *auclnt_get_dev_hw_info(audio_dev_t *, void **); -unsigned auclnt_get_dev_capab(audio_dev_t *); +uint_t auclnt_get_dev_capab(audio_dev_t *); #define AUDIO_CLIENT_CAP_PLAY (1U << 0) #define AUDIO_CLIENT_CAP_RECORD (1U << 1) #define AUDIO_CLIENT_CAP_DUPLEX (1U << 2) @@ -181,7 +181,7 @@ * need, and its far lighter weight than forcing an asynchronous * callback on everything. */ -unsigned auclnt_dev_get_serial(audio_dev_t *); +uint_t auclnt_dev_get_serial(audio_dev_t *); /* * Audio control functions for use by clients. @@ -246,6 +246,9 @@ audio_client_t *auclnt_hold_by_devt(dev_t); void auclnt_release(audio_client_t *); +void auclnt_hold(audio_client_t *); +int auclnt_serialize(audio_client_t *); +void auclnt_unserialize(audio_client_t *); /* * Engine rlated accesses. Note that normally clients don't need this level @@ -256,7 +259,7 @@ int auclnt_engine_get_format(audio_engine_t *); int auclnt_engine_get_rate(audio_engine_t *); int auclnt_engine_get_channels(audio_engine_t *); -unsigned auclnt_engine_get_capab(audio_engine_t *); +uint_t auclnt_engine_get_capab(audio_engine_t *); /* * Retrieve minor-specific data for the instance. This allows for
--- a/usr/src/uts/common/io/audio/impl/audio_ctrl.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/impl/audio_ctrl.c Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -139,16 +139,15 @@ * Also by doing this we can use the normal add code to do * what it normally does below. */ - rw_enter(&d->d_ctrl_lock, RW_WRITER); + mutex_enter(&d->d_ctrl_lock); list_remove(&d->d_controls, ctrl); - rw_exit(&d->d_ctrl_lock); + mutex_exit(&d->d_ctrl_lock); audio_control_freenames(ctrl); ctrl->ctrl_read_fn = NULL; ctrl->ctrl_write_fn = NULL; ctrl->ctrl_arg = NULL; ctrl->ctrl_dev = NULL; - mutex_destroy(&ctrl->ctrl_lock); } new_desc = &ctrl->ctrl_des; @@ -198,11 +197,9 @@ ctrl->ctrl_arg = arg; } - mutex_init(&ctrl->ctrl_lock, NULL, MUTEX_DRIVER, NULL); - - rw_enter(&d->d_ctrl_lock, RW_WRITER); + mutex_enter(&d->d_ctrl_lock); list_insert_tail(&d->d_controls, ctrl); - rw_exit(&d->d_ctrl_lock); + mutex_exit(&d->d_ctrl_lock); return (ctrl); @@ -230,11 +227,9 @@ d = ctrl->ctrl_dev; ASSERT(d); - rw_enter(&d->d_ctrl_lock, RW_WRITER); + mutex_enter(&d->d_ctrl_lock); list_remove(&d->d_controls, ctrl); - rw_exit(&d->d_ctrl_lock); - - mutex_destroy(&ctrl->ctrl_lock); + mutex_exit(&d->d_ctrl_lock); audio_control_freenames(ctrl); kmem_free(ctrl, sizeof (*ctrl)); @@ -289,26 +284,31 @@ int audio_control_read(audio_ctrl_t *ctrl, uint64_t *value) { - uint64_t my_value; - int ret; + audio_dev_t *d = ctrl->ctrl_dev; + uint64_t my_value; + int ret; - /* Verify arguments */ - ASSERT(ctrl); ASSERT(value); - ASSERT(ctrl->ctrl_dev); + + mutex_enter(&d->d_ctrl_lock); + while (d->d_suspended) { + cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock); + } if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_READABLE)) { + mutex_exit(&d->d_ctrl_lock); return (ENXIO); } ASSERT(ctrl->ctrl_read_fn); - if ((ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &my_value)) != 0) { - return (ret); + ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &my_value); + mutex_exit(&d->d_ctrl_lock); + + if (ret == 0) { + *value = my_value; } - *value = my_value; - return (ret); } @@ -328,20 +328,85 @@ int ret; audio_dev_t *d = ctrl->ctrl_dev; - /* Verify arguments */ - ASSERT(ctrl); - ASSERT(d); + mutex_enter(&d->d_ctrl_lock); + while (d->d_suspended) { + cv_wait(&d->d_ctrl_cv, &d->d_ctrl_lock); + } if (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) { + mutex_exit(&d->d_ctrl_lock); return (ENXIO); } ASSERT(ctrl->ctrl_write_fn); ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, value); + if (ret == 0) { + ctrl->ctrl_saved = value; + ctrl->ctrl_saved_ok = B_TRUE; + } + mutex_exit(&d->d_ctrl_lock); - if (ret == 0) + if (ret == 0) { audio_dev_update_controls(d); + } return (ret); } + +/* + * This is used to save control values. + */ +int +auimpl_save_controls(audio_dev_t *d) +{ + audio_ctrl_t *ctrl; + list_t *l; + int ret; + + ASSERT(mutex_owned(&d->d_ctrl_lock)); + l = &d->d_controls; + + for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) { + if ((!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE)) || + (!(ctrl->ctrl_flags & AUDIO_CTRL_FLAG_WRITEABLE))) { + continue; + } + ret = ctrl->ctrl_read_fn(ctrl->ctrl_arg, &ctrl->ctrl_saved); + if (ret != 0) { + audio_dev_warn(d, + "Unable to save value of control %s", + ctrl->ctrl_name); + return (ret); + } else { + ctrl->ctrl_saved_ok = B_TRUE; + } + } + return (0); +} + +int +auimpl_restore_controls(audio_dev_t *d) +{ + audio_ctrl_t *ctrl; + list_t *l; + int ret; + int rv = 0; + + ASSERT(mutex_owned(&d->d_ctrl_lock)); + l = &d->d_controls; + + for (ctrl = list_head(l); ctrl; ctrl = list_next(l, ctrl)) { + if (!ctrl->ctrl_saved_ok) { + continue; + } + ret = ctrl->ctrl_write_fn(ctrl->ctrl_arg, ctrl->ctrl_saved); + if (ret != 0) { + audio_dev_warn(d, + "Unable to restore value of control %s", + ctrl->ctrl_name); + rv = ret; + } + } + return (rv); +}
--- a/usr/src/uts/common/io/audio/impl/audio_ddi.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/impl/audio_ddi.c Tue Mar 16 09:30:41 2010 -0700 @@ -21,7 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -275,7 +275,7 @@ * avoids leaving record data stuck in queues. */ if (c->c_istream.s_engine != NULL) - audio_engine_produce(c->c_istream.s_engine); + auimpl_input_callback(c->c_istream.s_engine); /* get a local hold on the device */ d = c->c_dev; @@ -322,7 +322,7 @@ * avoids leaving record data stuck in queues. */ if (c->c_istream.s_engine != NULL) - audio_engine_produce(c->c_istream.s_engine); + auimpl_input_callback(c->c_istream.s_engine); /* get a local hold on the device */ d = c->c_dev; @@ -358,7 +358,10 @@ if ((c = auclnt_hold_by_devt(dev)) == NULL) { return (ENXIO); } - rv = (c->c_write == NULL) ? ENXIO : c->c_write(c, uio, credp); + if ((rv = auclnt_serialize(c)) == 0) { + rv = (c->c_write == NULL) ? ENXIO : c->c_write(c, uio, credp); + auclnt_unserialize(c); + } auclnt_release(c); return (rv); @@ -373,7 +376,10 @@ if ((c = auclnt_hold_by_devt(dev)) == NULL) { return (ENXIO); } - rv = (c->c_read == NULL) ? ENXIO : c->c_read(c, uio, credp); + if ((rv = auclnt_serialize(c)) == 0) { + rv = (c->c_read == NULL) ? ENXIO : c->c_read(c, uio, credp); + auclnt_unserialize(c); + } auclnt_release(c); return (rv);
--- a/usr/src/uts/common/io/audio/impl/audio_engine.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/impl/audio_engine.c Tue Mar 16 09:30:41 2010 -0700 @@ -40,6 +40,16 @@ * Audio Engine functions. */ +/* + * Globals + */ +uint_t audio_intrhz = AUDIO_INTRHZ; +/* + * We need to operate at fairly high interrupt priority to avoid + * underruns due to other less time sensitive processing. + */ +int audio_priority = DDI_IPL_8; + audio_dev_t * audio_dev_alloc(dev_info_t *dip, int instance) { @@ -74,7 +84,8 @@ d->d_pcmvol = 100; mutex_init(&d->d_lock, NULL, MUTEX_DRIVER, NULL); cv_init(&d->d_cv, NULL, CV_DRIVER, NULL); - rw_init(&d->d_ctrl_lock, NULL, RW_DRIVER, NULL); + mutex_init(&d->d_ctrl_lock, NULL, MUTEX_DRIVER, NULL); + cv_init(&d->d_ctrl_cv, NULL, CV_DRIVER, NULL); list_create(&d->d_clients, sizeof (struct audio_client), offsetof(struct audio_client, c_dev_linkage)); list_create(&d->d_engines, sizeof (struct audio_engine), @@ -93,6 +104,7 @@ audio_dev_free(audio_dev_t *d) { struct audio_infostr *isp; + while ((isp = list_remove_head(&d->d_hwinfo)) != NULL) { kmem_free(isp, sizeof (*isp)); } @@ -103,9 +115,10 @@ list_destroy(&d->d_engines); list_destroy(&d->d_controls); list_destroy(&d->d_clients); - rw_destroy(&d->d_ctrl_lock); + mutex_destroy(&d->d_ctrl_lock); mutex_destroy(&d->d_lock); cv_destroy(&d->d_cv); + cv_destroy(&d->d_ctrl_cv); kmem_free(d, sizeof (*d)); } @@ -136,72 +149,36 @@ } } -void -audio_engine_consume(audio_engine_t *e) -{ - mutex_enter(&e->e_lock); - e->e_tail = ENG_COUNT(e); - if (e->e_tail > e->e_head) { - /* want more data than we have, not much we can do */ - e->e_errors++; - e->e_underruns++; - } - auimpl_output_callback(e); - mutex_exit(&e->e_lock); -} - -void -audio_engine_produce(audio_engine_t *e) -{ - mutex_enter(&e->e_lock); - e->e_head = ENG_COUNT(e); - if ((e->e_head - e->e_tail) > e->e_nframes) { - /* no room for engine data, not much we can do */ - e->e_errors++; - e->e_overruns++; - } - auimpl_input_callback(e); - mutex_exit(&e->e_lock); -} - -void -audio_engine_reset(audio_engine_t *e) +static void +auimpl_engine_reset(audio_engine_t *e) { char *buf; char *ptr; - int nfr; - int tail; + int nfr, resid, cnt; + int tidx; + tidx = e->e_tidx; + nfr = min(e->e_head - e->e_tail, e->e_nframes); + buf = kmem_alloc(nfr * e->e_framesz, KM_SLEEP); + ptr = buf; + cnt = 0; + + ASSERT(e->e_nframes); - if ((e->e_flags & (ENGINE_INPUT | ENGINE_OUTPUT)) == 0) { - /* engine not open, nothing to do */ - return; + for (resid = nfr; resid; resid -= cnt) { + int nbytes; + + cnt = min((e->e_nframes - tidx), resid); + nbytes = cnt * e->e_framesz; + + bcopy(e->e_data + (tidx * e->e_framesz), ptr, nbytes); + ptr += nbytes; + tidx += cnt; + if (tidx == e->e_nframes) { + tidx = 0; + } } - buf = kmem_alloc(e->e_nbytes, KM_SLEEP); - ptr = buf; - - mutex_enter(&e->e_lock); - - tail = e->e_tidx; - nfr = min(e->e_head - e->e_tail, e->e_nframes); - while (nfr) { - int cnt; - int nbytes; - - cnt = min((e->e_nframes - tail), nfr); - nbytes = cnt * e->e_framesz; - - bcopy(e->e_data + (tail * e->e_framesz), ptr, nbytes); - ptr += nbytes; - tail += cnt; - if (tail >= e->e_framesz) { - tail -= e->e_framesz; - } - nfr -= cnt; - } - - nfr = min(e->e_head - e->e_tail, e->e_nframes); if (e->e_flags & ENGINE_INPUT) { /* record */ e->e_hidx = 0; @@ -214,16 +191,18 @@ /* relocate from scratch area to destination */ bcopy(buf, e->e_data + (e->e_tidx * e->e_framesz), nfr * e->e_framesz); - mutex_exit(&e->e_lock); - - kmem_free(buf, e->e_nbytes); + kmem_free(buf, nfr * e->e_framesz); } +static volatile uint_t auimpl_engno = 0; + audio_engine_t * -audio_engine_alloc(audio_engine_ops_t *ops, unsigned flags) +audio_engine_alloc(audio_engine_ops_t *ops, uint_t flags) { int i; audio_engine_t *e; + char tname[32]; + int num; if (ops->audio_engine_version != AUDIO_ENGINE_VERSION) { audio_dev_warn(NULL, "audio engine version mismatch: %d != %d", @@ -238,7 +217,9 @@ return (NULL); } e->e_ops = *ops; - mutex_init(&e->e_lock, NULL, MUTEX_DRIVER, NULL); + mutex_init(&e->e_lock, NULL, MUTEX_DRIVER, + DDI_INTR_PRI(audio_priority)); + cv_init(&e->e_cv, NULL, CV_DRIVER, NULL); list_create(&e->e_streams, sizeof (struct audio_stream), offsetof(struct audio_stream, s_eng_linkage)); @@ -252,6 +233,10 @@ } } + num = atomic_inc_uint_nv(&auimpl_engno); + + (void) snprintf(tname, sizeof (tname), "audio_engine_%d", num); + e->e_flags = flags & ENGINE_DRIVER_FLAGS; return (e); } @@ -267,8 +252,10 @@ sizeof (int32_t) * AUDIO_CHBUFS); } } + list_destroy(&e->e_streams); mutex_destroy(&e->e_lock); + cv_destroy(&e->e_cv); kmem_free(e, sizeof (*e)); } @@ -377,11 +364,12 @@ { audio_engine_t *e = NULL; list_t *list; - unsigned caps; + uint_t caps; int priority = 0; int rv = ENODEV; int sampsz; int i; + int fragfr; /* * Engine selection: @@ -411,6 +399,11 @@ * and output engines. */ + /* if engine suspended, wait for it not to be */ + while (d->d_suspended) { + cv_wait(&d->d_ctrl_cv, &d->d_lock); + } + again: for (audio_engine_t *t = list_head(list); t; t = list_next(list, t)) { @@ -418,12 +411,20 @@ /* make sure the engine can do what we want it to */ mutex_enter(&t->e_lock); + if ((((t->e_flags & caps) & caps) == 0) || ((ENG_FORMAT(t) & fmts) == 0)) { mutex_exit(&t->e_lock); continue; } + /* if in failed state, don't assign a new stream here */ + if (t->e_failed) { + mutex_exit(&t->e_lock); + rv = EIO; + continue; + } + /* if engine is in exclusive use, can't do it */ if (t->e_flags & ENGINE_EXCLUSIVE) { mutex_exit(&t->e_lock); @@ -558,6 +559,13 @@ goto done; } + fragfr = e->e_rate / audio_intrhz; + if ((fragfr > AUDIO_CHBUFS) || (fragfr < 1)) { + audio_dev_warn(d, "invalid fragment configration"); + rv = EINVAL; + goto done; + } + /* sanity test a few values */ if ((e->e_nchan < 0) || (e->e_nchan > AUDIO_MAX_CHANNELS) || (e->e_rate < 5000) || (e->e_rate > 192000)) { @@ -566,52 +574,44 @@ goto done; } - rv = ENG_OPEN(e, &e->e_fragfr, &e->e_nfrags, &e->e_data); + rv = ENG_OPEN(e, &e->e_nframes, &e->e_data); if (rv != 0) { audio_dev_warn(d, "unable to open engine"); goto done; } - if ((e->e_fragfr < 1) || (e->e_data == NULL)) { + if ((e->e_nframes <= (fragfr * 2)) || (e->e_data == NULL)) { audio_dev_warn(d, "improper engine configuration"); rv = EINVAL; goto done; } - if ((e->e_fragfr > AUDIO_CHBUFS) || (e->e_nfrags < 2)) { - rv = EINVAL; - audio_dev_warn(d, "invalid fragment configuration"); - goto done; - } - e->e_framesz = e->e_nchan * sampsz; - e->e_fragbytes = e->e_fragfr * e->e_framesz; - e->e_nframes = e->e_nfrags * e->e_fragfr; - e->e_intrs = e->e_rate / e->e_fragfr; - e->e_nbytes = e->e_nframes * e->e_framesz; + e->e_intrs = audio_intrhz; + e->e_fragfr = fragfr; e->e_head = 0; e->e_tail = 0; e->e_hidx = 0; e->e_tidx = 0; e->e_limiter_state = 0x10000; - bzero(e->e_data, e->e_nbytes); + bzero(e->e_data, e->e_nframes * e->e_framesz); if (e->e_ops.audio_engine_playahead == NULL) { - e->e_playahead = (e->e_fragfr * 3) / 2; + e->e_playahead = (fragfr * 3) / 2; } else { e->e_playahead = ENG_PLAYAHEAD(e); /* * Need to have at least a fragment plus some extra to * avoid underruns. */ - if (e->e_playahead < ((e->e_fragfr * 3) / 2)) { - e->e_playahead = (e->e_fragfr * 3) / 2; + if (e->e_playahead < ((fragfr * 3) / 2)) { + e->e_playahead = (fragfr * 3) / 2; } /* * Impossible to queue more frames than FIFO can hold. */ if (e->e_playahead > e->e_nframes) { - e->e_playahead = (e->e_fragfr * 3) / 2; + e->e_playahead = (fragfr * 3) / 2; } } @@ -632,24 +632,26 @@ * starting up. */ if (flags & ENGINE_OUTPUT) { - auimpl_output_callback(e); + auimpl_output_preload(e); } /* - * Start the engine up now. - * - * AC3: Note that this will need to be modified for AC3, since - * for AC3 we can't start the device until we actually have - * some data for it from the application. Probably the best - * way to do this would be to add a flag, ENGINE_DEFERRED or - * somesuch. + * Arrange for the engine to be started. We defer this to the + * periodic callback, to ensure that the start happens near + * the edge of the periodic callback. This is necessary to + * ensure that the first fragment processed is about the same + * size as the usual fragment size. (Basically, the problem + * is that we have only 10 msec resolution with the periodic + * interface, whch is rather unfortunate.) */ - if (e->e_ops.audio_engine_start != NULL) { - rv = ENG_START(e); - if (rv != 0) { - ENG_CLOSE(e); - goto done; - } + e->e_need_start = B_TRUE; + + if (e->e_flags & ENGINE_OUTPUT) { + e->e_periodic = ddi_periodic_add(auimpl_output_callback, e, + NANOSEC / audio_intrhz, audio_priority); + } else { + e->e_periodic = ddi_periodic_add(auimpl_input_callback, e, + NANOSEC / audio_intrhz, audio_priority); } ok: @@ -670,6 +672,7 @@ { audio_engine_t *e = sp->s_engine; audio_dev_t *d; + ddi_periodic_t p = 0; if (e == NULL) return; @@ -677,20 +680,24 @@ d = e->e_dev; mutex_enter(&d->d_lock); + while (d->d_suspended) { + cv_wait(&d->d_ctrl_cv, &d->d_lock); + } mutex_enter(&e->e_lock); sp->s_engine = NULL; list_remove(&e->e_streams, sp); if (list_is_empty(&e->e_streams)) { - /* if last client holding engine open, close it all down */ - if (e->e_ops.audio_engine_stop != NULL) - ENG_STOP(e); + ENG_STOP(e); + p = e->e_periodic; e->e_flags &= ENGINE_DRIVER_FLAGS; ENG_CLOSE(e); } mutex_exit(&e->e_lock); - cv_broadcast(&d->d_cv); mutex_exit(&d->d_lock); + if (p != 0) { + ddi_periodic_delete(p); + } } int @@ -720,6 +727,7 @@ start = 1; } d->d_index = start; + rw_enter(&auimpl_dev_lock, RW_WRITER); l = &auimpl_devs_by_index; for (srch = list_head(l); srch; srch = list_next(l, srch)) { @@ -798,10 +806,8 @@ st->st_head.value.ui64 = e->e_head; st->st_tail.value.ui64 = e->e_tail; st->st_flags.value.ui32 = e->e_flags; - st->st_fragfr.value.ui32 = e->e_fragfr; - st->st_nfrags.value.ui32 = e->e_nfrags; + st->st_nbytes.value.ui32 = e->e_framesz * e->e_nframes; st->st_framesz.value.ui32 = e->e_framesz; - st->st_nbytes.value.ui32 = e->e_nbytes; st->st_hidx.value.ui32 = e->e_hidx; st->st_tidx.value.ui32 = e->e_tidx; st->st_format.value.ui32 = e->e_format; @@ -814,6 +820,8 @@ st->st_stream_underruns.value.ui32 = e->e_stream_underruns; st->st_stream_overruns.value.ui32 = e->e_stream_overruns; st->st_suspended.value.ui32 = e->e_suspended; + st->st_failed.value.ui32 = e->e_failed; + st->st_playahead.value.ui32 = e->e_playahead; mutex_exit(&e->e_lock); return (0); @@ -844,10 +852,8 @@ kstat_named_init(&st->st_head, "head", KSTAT_DATA_UINT64); kstat_named_init(&st->st_tail, "tail", KSTAT_DATA_UINT64); kstat_named_init(&st->st_flags, "flags", KSTAT_DATA_UINT32); - kstat_named_init(&st->st_fragfr, "fragfr", KSTAT_DATA_UINT32); - kstat_named_init(&st->st_nfrags, "nfrags", KSTAT_DATA_UINT32); + kstat_named_init(&st->st_nbytes, "nbytes", KSTAT_DATA_UINT32); kstat_named_init(&st->st_framesz, "framesz", KSTAT_DATA_UINT32); - kstat_named_init(&st->st_nbytes, "nbytes", KSTAT_DATA_UINT32); kstat_named_init(&st->st_hidx, "hidx", KSTAT_DATA_UINT32); kstat_named_init(&st->st_tidx, "tidx", KSTAT_DATA_UINT32); kstat_named_init(&st->st_format, "format", KSTAT_DATA_UINT32); @@ -863,16 +869,18 @@ KSTAT_DATA_UINT32); kstat_named_init(&st->st_stream_underruns, "stream_underruns", KSTAT_DATA_UINT32); + kstat_named_init(&st->st_playahead, "playahead", KSTAT_DATA_UINT32); kstat_named_init(&st->st_suspended, "suspended", KSTAT_DATA_UINT32); + kstat_named_init(&st->st_failed, "failed", KSTAT_DATA_UINT32); kstat_install(e->e_ksp); } void audio_dev_add_engine(audio_dev_t *d, audio_engine_t *e) { - e->e_num = d->d_engno++; + mutex_enter(&d->d_lock); - mutex_enter(&d->d_lock); + e->e_num = d->d_engno++; auimpl_engine_ksinit(d, e); @@ -941,9 +949,7 @@ l = &auimpl_devs_by_index; rw_enter(&auimpl_dev_lock, RW_READER); for (d = list_head(l); d; d = list_next(l, d)) { - mutex_enter(&d->d_lock); cont = walker(d, arg); - mutex_exit(&d->d_lock); if (cont == AUDIO_WALK_STOP) break; } @@ -960,9 +966,7 @@ l = &auimpl_devs_by_number; rw_enter(&auimpl_dev_lock, RW_READER); for (d = list_head(l); d; d = list_next(l, d)) { - mutex_enter(&d->d_lock); cont = walker(d, arg); - mutex_exit(&d->d_lock); if (cont == AUDIO_WALK_STOP) break; } @@ -1004,10 +1008,10 @@ return (ENG_RATE(e)); } -unsigned +uint_t auclnt_engine_get_capab(audio_engine_t *e) { - unsigned capab = 0; + uint_t capab = 0; if (e->e_flags & ENGINE_INPUT_CAP) { capab |= AUDIO_CLIENT_CAP_RECORD; @@ -1018,33 +1022,6 @@ return (capab); } -static void -auimpl_walk_engines(int (*walker)(audio_engine_t *, void *), void *arg) -{ - audio_dev_t *d; - audio_engine_t *e; - list_t *l1; - list_t *l2; - boolean_t done = B_FALSE; - - rw_enter(&auimpl_dev_lock, RW_READER); - l1 = &auimpl_devs_by_index; - for (d = list_head(l1); d; d = list_next(l1, d)) { - mutex_enter(&d->d_lock); - l2 = &d->d_engines; - for (e = list_head(l2); e; e = list_next(l2, e)) { - if (walker(e, arg) == AUDIO_WALK_STOP) { - done = B_TRUE; - break; - } - } - mutex_exit(&d->d_lock); - if (done) - break; - } - rw_exit(&auimpl_dev_lock); -} - /* * This function suspends an engine. The intent is to pause the * engine temporarily so that it does not underrun while user threads @@ -1063,25 +1040,116 @@ * driver gets resumed well in advance of the time when user threads * are ready to start operation. */ +static void +auimpl_engine_suspend(audio_engine_t *e) +{ + ASSERT(mutex_owned(&e->e_lock)); + + if (e->e_failed || e->e_suspended) { + e->e_suspended = B_TRUE; + return; + } + e->e_suspended = B_TRUE; + if (e->e_flags & ENGINE_INPUT) { + e->e_head = ENG_COUNT(e); + ENG_STOP(e); + } + if (e->e_flags & ENGINE_OUTPUT) { + e->e_tail = ENG_COUNT(e); + ENG_STOP(e); + } +} + +static void +auimpl_engine_resume(audio_engine_t *e) +{ + ASSERT(mutex_owned(&e->e_lock)); + ASSERT(e->e_suspended); + + if (e->e_failed) { + /* No longer suspended, but still failed! */ + e->e_suspended = B_FALSE; + return; + } + + if (e->e_flags & (ENGINE_INPUT | ENGINE_OUTPUT)) { + + auimpl_engine_reset(e); + + if (e->e_flags & ENGINE_OUTPUT) { + auimpl_output_preload(e); + } + + e->e_need_start = B_TRUE; + } + e->e_suspended = B_FALSE; + cv_broadcast(&e->e_cv); +} + static int -auimpl_engine_suspend(audio_engine_t *e, void *dontcare) +auimpl_dev_suspend(audio_dev_t *d, void *dontcare) { + list_t *l; + audio_engine_t *e; + _NOTE(ARGUNUSED(dontcare)); - mutex_enter(&e->e_lock); - e->e_suspended = B_TRUE; - mutex_exit(&e->e_lock); + mutex_enter(&d->d_lock); + mutex_enter(&d->d_ctrl_lock); + if (d->d_suspended) { + d->d_suspended++; + mutex_exit(&d->d_ctrl_lock); + mutex_exit(&d->d_lock); + return (AUDIO_WALK_CONTINUE); + } + + d->d_suspended++; + + (void) auimpl_save_controls(d); + mutex_exit(&d->d_ctrl_lock); + + l = &d->d_engines; + for (e = list_head(l); e != NULL; e = list_next(l, e)) { + mutex_enter(&e->e_lock); + auimpl_engine_suspend(e); + mutex_exit(&e->e_lock); + } + mutex_exit(&d->d_lock); return (AUDIO_WALK_CONTINUE); } static int -auimpl_engine_resume(audio_engine_t *e, void *dontcare) +auimpl_dev_resume(audio_dev_t *d, void *dontcare) { + list_t *l; + audio_engine_t *e; + _NOTE(ARGUNUSED(dontcare)); - mutex_enter(&e->e_lock); - e->e_suspended = B_FALSE; - mutex_exit(&e->e_lock); + + mutex_enter(&d->d_lock); + mutex_enter(&d->d_ctrl_lock); + + ASSERT(d->d_suspended); + d->d_suspended--; + if (d->d_suspended) { + mutex_exit(&d->d_ctrl_lock); + mutex_exit(&d->d_lock); + return (AUDIO_WALK_CONTINUE); + } + + (void) auimpl_restore_controls(d); + cv_broadcast(&d->d_ctrl_cv); + mutex_exit(&d->d_ctrl_lock); + + l = &d->d_engines; + for (e = list_head(l); e != NULL; e = list_next(l, e)) { + mutex_enter(&e->e_lock); + auimpl_engine_resume(e); + mutex_exit(&e->e_lock); + } + mutex_exit(&d->d_lock); + return (AUDIO_WALK_CONTINUE); } @@ -1092,11 +1160,11 @@ switch (code) { case CB_CODE_CPR_CHKPT: - auimpl_walk_engines(auimpl_engine_suspend, NULL); + auclnt_walk_devs(auimpl_dev_suspend, NULL); return (B_TRUE); case CB_CODE_CPR_RESUME: - auimpl_walk_engines(auimpl_engine_resume, NULL); + auclnt_walk_devs(auimpl_dev_resume, NULL); return (B_TRUE); default: @@ -1104,6 +1172,18 @@ } } +void +audio_dev_suspend(audio_dev_t *d) +{ + (void) auimpl_dev_suspend(d, NULL); +} + +void +audio_dev_resume(audio_dev_t *d) +{ + (void) auimpl_dev_resume(d, NULL); +} + static callb_id_t auimpl_cpr_id = 0; void @@ -1236,22 +1316,3 @@ cmn_err(CE_NOTE, "%08x:%s", i - (i % wrap), line); } } - -/* - * The following two functions are a temporary workaround for CR6924018 - * in usb audio, and are not to be used for anything else, they WILL be - * removed in the future. - */ -void -audio_engine_lock(audio_engine_t *e) -{ - mutex_enter(&e->e_lock); - -} - -void -audio_engine_unlock(audio_engine_t *e) -{ - mutex_exit(&e->e_lock); - -}
--- a/usr/src/uts/common/io/audio/impl/audio_impl.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/impl/audio_impl.h Tue Mar 16 09:30:41 2010 -0700 @@ -21,7 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -42,6 +42,10 @@ #define AUDIO_VOL_SCALE 256 #define AUDIO_DB_SIZE 50 +#define AUDIO_INTRHZ 100 +#define AUDIO_INTRHZ_MIN 50 /* 20 msec max */ +#define AUDIO_INTRHZ_MAX 500 + struct audio_parms { int p_format; int p_rate; @@ -54,14 +58,10 @@ caddr_t b_data; uint64_t b_head; uint64_t b_tail; - unsigned b_hidx; /* head % nframes */ - unsigned b_tidx; /* tail % nframes */ - unsigned b_fragfr; /* frames per frag */ - unsigned b_fragbytes; /* bytes per frag */ - unsigned b_nframes; /* total frames */ - unsigned b_nbytes; /* total bytes */ - unsigned b_nfrags; /* total frags */ - unsigned b_framesz; /* bytes per frame */ + uint_t b_hidx; /* head % nframes */ + uint_t b_tidx; /* tail % nframes */ + uint_t b_nframes; /* total frames */ + uint_t b_framesz; /* bytes per frame */ }; /* @@ -75,14 +75,14 @@ #define s_bufsz s_buf.b_size #define s_head s_buf.b_head #define s_tail s_buf.b_tail -#define s_nfrags s_buf.b_nfrags #define s_framesz s_buf.b_framesz -#define s_fragfr s_buf.b_fragfr -#define s_fragbytes s_buf.b_fragbytes #define s_nframes s_buf.b_nframes -#define s_nbytes s_buf.b_nbytes #define s_tidx s_buf.b_tidx #define s_hidx s_buf.b_hidx + uint_t s_nfrags; + uint_t s_fragfr; + uint_t s_nbytes; + uint_t s_fragbytes; ddi_umem_cookie_t s_cookie; uint32_t s_allocsz; uint32_t s_hintsz; /* latency hints */ @@ -102,7 +102,7 @@ * Sample rate conversion (SRC) and format conversion details. */ struct grc3state *s_src_state[AUDIO_MAX_CHANNELS]; - unsigned s_src_quality; + uint_t s_src_quality; int s_cnv_max; audio_cnv_func_t s_converter; uint32_t *s_cnv_buf0; @@ -152,7 +152,7 @@ /* * Other bits. */ - unsigned s_engcap; /* ENGINE_xxx_CAP */ + uint_t s_engcap; /* ENGINE_xxx_CAP */ }; /* @@ -166,6 +166,14 @@ void *c_private; /* + * We can keep a linked list of clients to "notify" so that + * we can do this outside of locked context. + */ + audio_client_t *c_next_input; + audio_client_t *c_next_output; + audio_client_t *c_next_drain; + + /* * DDI support. */ major_t c_major; @@ -180,6 +188,7 @@ list_node_t c_global_linkage; list_node_t c_dev_linkage; int c_refcnt; + boolean_t c_serialize; kmutex_t c_lock; kcondvar_t c_cv; @@ -188,7 +197,7 @@ /* * Client wide settings... e.g. ops vector, etc. */ - unsigned c_omode; /* open mode */ + uint_t c_omode; /* open mode */ pid_t c_pid; /* opening process id */ audio_dev_t *c_dev; cred_t *c_cred; @@ -220,7 +229,6 @@ kstat_named_t st_head; kstat_named_t st_tail; kstat_named_t st_flags; - kstat_named_t st_fragfr; kstat_named_t st_nfrags; kstat_named_t st_framesz; kstat_named_t st_nbytes; @@ -235,9 +243,14 @@ kstat_named_t st_engine_overruns; kstat_named_t st_stream_underruns; kstat_named_t st_stream_overruns; + kstat_named_t st_playahead; kstat_named_t st_suspended; + kstat_named_t st_failed; }; +typedef void (*audio_import_fn_t)(audio_engine_t *, uint_t, audio_stream_t *); +typedef void (*audio_export_fn_t)(audio_engine_t *, uint_t, uint_t); + /* * An audio engine corresponds to a single DMA transfer channel. It can * represent either record or playback, but not both at the same time. @@ -247,17 +260,17 @@ struct audio_engine { audio_engine_ops_t e_ops; void *e_private; - unsigned e_flags; + uint_t e_flags; /* * Mixing related fields. */ - unsigned e_limiter_state; + uint_t e_limiter_state; int32_t *e_chbufs[AUDIO_MAX_CHANNELS]; - unsigned e_choffs[AUDIO_MAX_CHANNELS]; - unsigned e_chincr[AUDIO_MAX_CHANNELS]; - void (*e_export)(audio_engine_t *); - void (*e_import)(audio_engine_t *, audio_stream_t *); + uint_t e_choffs[AUDIO_MAX_CHANNELS]; + uint_t e_chincr[AUDIO_MAX_CHANNELS]; + audio_export_fn_t e_export; + audio_import_fn_t e_import; /* * Underlying physical buffer shared with device driver. @@ -266,16 +279,12 @@ #define e_head e_buf.b_head #define e_tail e_buf.b_tail #define e_data e_buf.b_data -#define e_fragfr e_buf.b_fragfr -#define e_fragbytes e_buf.b_fragbytes #define e_framesz e_buf.b_framesz -#define e_nbytes e_buf.b_nbytes #define e_nframes e_buf.b_nframes -#define e_nfrags e_buf.b_nfrags #define e_hidx e_buf.b_hidx #define e_tidx e_buf.b_tidx - - unsigned e_playahead; + uint_t e_fragfr; + uint_t e_playahead; int e_intrs; int e_errors; @@ -300,6 +309,8 @@ * Synchronization. */ kmutex_t e_lock; + kcondvar_t e_cv; + ddi_periodic_t e_periodic; /* * Linkage for per-device list. @@ -313,7 +324,10 @@ */ list_t e_streams; int e_nrunning; - boolean_t e_suspended; + int e_suspended; + boolean_t e_failed; + + boolean_t e_need_start; }; struct audio_dev { @@ -342,9 +356,12 @@ */ kmutex_t d_lock; kcondvar_t d_cv; - krwlock_t d_ctrl_lock; /* leaf lock */ + kmutex_t d_ctrl_lock; /* leaf lock */ + kcondvar_t d_ctrl_cv; krwlock_t d_clnt_lock; - unsigned d_refcnt; + uint_t d_refcnt; + int d_suspended; + boolean_t d_failed; /* * Lists of virtual clients, controls and engines. Protected by @@ -356,7 +373,7 @@ audio_ctrl_t *d_pcmvol_ctrl; uint64_t d_pcmvol; - volatile unsigned d_serial; + volatile uint_t d_serial; /* * Linkage onto global list of devices. @@ -403,8 +420,9 @@ audio_ctrl_rd_t ctrl_read_fn; audio_ctrl_wr_t ctrl_write_fn; list_node_t ctrl_linkage; - kmutex_t ctrl_lock; void *ctrl_arg; + uint64_t ctrl_saved; /* the saved value */ + boolean_t ctrl_saved_ok; }; @@ -418,22 +436,23 @@ int auimpl_format_setup(audio_stream_t *, audio_parms_t *); /* audio_output.c */ -void auimpl_export_16ne(audio_engine_t *); -void auimpl_export_16oe(audio_engine_t *); -void auimpl_export_24ne(audio_engine_t *); -void auimpl_export_24oe(audio_engine_t *); -void auimpl_export_32ne(audio_engine_t *); -void auimpl_export_32oe(audio_engine_t *); -void auimpl_output_callback(audio_engine_t *); +void auimpl_export_16ne(audio_engine_t *, uint_t, uint_t); +void auimpl_export_16oe(audio_engine_t *, uint_t, uint_t); +void auimpl_export_24ne(audio_engine_t *, uint_t, uint_t); +void auimpl_export_24oe(audio_engine_t *, uint_t, uint_t); +void auimpl_export_32ne(audio_engine_t *, uint_t, uint_t); +void auimpl_export_32oe(audio_engine_t *, uint_t, uint_t); +void auimpl_output_callback(void *); +void auimpl_output_preload(audio_engine_t *); /* audio_input.c */ -void auimpl_import_16ne(audio_engine_t *, audio_stream_t *); -void auimpl_import_16oe(audio_engine_t *, audio_stream_t *); -void auimpl_import_24ne(audio_engine_t *, audio_stream_t *); -void auimpl_import_24oe(audio_engine_t *, audio_stream_t *); -void auimpl_import_32ne(audio_engine_t *, audio_stream_t *); -void auimpl_import_32oe(audio_engine_t *, audio_stream_t *); -void auimpl_input_callback(audio_engine_t *); +void auimpl_import_16ne(audio_engine_t *, uint_t, audio_stream_t *); +void auimpl_import_16oe(audio_engine_t *, uint_t, audio_stream_t *); +void auimpl_import_24ne(audio_engine_t *, uint_t, audio_stream_t *); +void auimpl_import_24oe(audio_engine_t *, uint_t, audio_stream_t *); +void auimpl_import_32ne(audio_engine_t *, uint_t, audio_stream_t *); +void auimpl_import_32oe(audio_engine_t *, uint_t, audio_stream_t *); +void auimpl_input_callback(void *); int auimpl_input_drain(audio_stream_t *); /* audio_client.c */ @@ -448,7 +467,12 @@ int auimpl_set_pcmvol(void *, uint64_t); int auimpl_get_pcmvol(void *, uint64_t *); +/* audio_ctrl.c */ +int auimpl_save_controls(audio_dev_t *); +int auimpl_restore_controls(audio_dev_t *); + /* audio_engine.c */ +extern int audio_priority; void auimpl_dev_init(void); void auimpl_dev_fini(void); void auimpl_dev_hold(audio_dev_t *); @@ -478,7 +502,7 @@ #define ENG_QLEN(e) E_OP(e, qlen)(E_PRV(e)) #define ENG_PLAYAHEAD(e) E_OP(e, playahead)(E_PRV(e)) #define ENG_CLOSE(e) E_OP(e, close)(E_PRV(e)) -#define ENG_OPEN(e, s, nf, d) E_OP(e, open)(E_PRV(e), e->e_flags, s, nf, d) +#define ENG_OPEN(e, nf, d) E_OP(e, open)(E_PRV(e), e->e_flags, nf, d) #define ENG_CHINFO(e, c, o, i) E_OP(e, chinfo(E_PRV(e), c, o, i)) /* audio_sun.c */
--- a/usr/src/uts/common/io/audio/impl/audio_input.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/impl/audio_input.c Tue Mar 16 09:30:41 2010 -0700 @@ -21,7 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -34,17 +34,16 @@ #include <sys/ddi.h> #include <sys/sunddi.h> #include <sys/sysmacros.h> +#include <sys/sdt.h> #include "audio_impl.h" #define DECL_AUDIO_IMPORT(NAME, TYPE, SWAP, SHIFT) \ void \ -auimpl_import_##NAME(audio_engine_t *eng, audio_stream_t *sp) \ +auimpl_import_##NAME(audio_engine_t *e, uint_t nfr, audio_stream_t *sp) \ { \ - int fragfr = eng->e_fragfr; \ - int nch = eng->e_nchan; \ - unsigned tidx = eng->e_tidx; \ - int32_t *out = (void *)sp->s_cnv_src; \ - TYPE *in = (void *)eng->e_data; \ + int nch = e->e_nchan; \ + int32_t *out = (void *)sp->s_cnv_src; \ + TYPE *in = (void *)e->e_data; \ int ch = 0; \ int vol = sp->s_gain_eff; \ \ @@ -52,13 +51,14 @@ TYPE *ip; \ int32_t *op; \ int i; \ - int incr = eng->e_chincr[ch]; \ + int incr = e->e_chincr[ch]; \ + uint_t tidx = e->e_tidx; \ \ /* get value and adjust next channel offset */ \ op = out++; \ - ip = in + eng->e_choffs[ch] + (tidx * incr); \ + ip = in + e->e_choffs[ch] + (tidx * incr); \ \ - i = fragfr; \ + i = nfr; \ \ do { /* for each frame */ \ int32_t sample = (TYPE)SWAP(*ip); \ @@ -68,9 +68,13 @@ scaled /= AUDIO_VOL_SCALE; \ \ *op = scaled; \ - ip += incr; \ op += nch; \ \ + ip += incr; \ + if (++tidx == e->e_nframes) { \ + tidx = 0; \ + ip = in + e->e_choffs[ch]; \ + } \ } while (--i); \ ch++; \ } while (ch < nch); \ @@ -84,18 +88,16 @@ DECL_AUDIO_IMPORT(24oe, int32_t, ddi_swap32, /* nop */) /* - * Produce a fragment's worth of data. This is called when the data in - * the conversion buffer is exhausted, and we need to refill it from the - * source buffer. We always consume data from the client in quantities of - * a fragment at a time (assuming that a fragment is available.) + * Produce capture data. This takes data from the conversion buffer + * and copies it into the stream data buffer. */ static void -auimpl_produce_fragment(audio_stream_t *sp, unsigned count) +auimpl_produce_data(audio_stream_t *sp, uint_t count) { - unsigned nframes; - unsigned framesz; - caddr_t cnvsrc; - caddr_t data; + uint_t nframes; + uint_t framesz; + caddr_t cnvsrc; + caddr_t data; nframes = sp->s_nframes; framesz = sp->s_framesz; @@ -114,7 +116,6 @@ unsigned nf; unsigned nb; - ASSERT(sp->s_hidx < nframes); nf = min(nframes - sp->s_hidx, count); nb = nf * framesz; @@ -125,50 +126,92 @@ sp->s_head += nf; count -= nf; sp->s_samples += nf; - if (sp->s_hidx >= nframes) { - sp->s_hidx -= nframes; - data -= sp->s_nbytes; + if (sp->s_hidx == nframes) { + sp->s_hidx = 0; + data = sp->s_data; } } while (count); ASSERT(sp->s_tail <= sp->s_head); ASSERT(sp->s_hidx < nframes); - ASSERT(sp->s_tail <= sp->s_head); - ASSERT(sp->s_hidx < nframes); } void -auimpl_input_callback(audio_engine_t *eng) +auimpl_input_callback(void *arg) { - int fragfr = eng->e_fragfr; + audio_engine_t *e = arg; + uint_t fragfr = e->e_fragfr; + audio_stream_t *sp; audio_client_t *c; + audio_client_t *clist = NULL; + list_t *l = &e->e_streams; + uint64_t h; + + mutex_enter(&e->e_lock); + + if (e->e_suspended || e->e_failed) { + mutex_exit(&e->e_lock); + return; + } + + if (e->e_need_start) { + int rv; + if ((rv = ENG_START(e)) != 0) { + e->e_failed = B_TRUE; + mutex_exit(&e->e_lock); + audio_dev_warn(e->e_dev, + "failed starting input, rv = %d", rv); + return; + } + e->e_need_start = B_FALSE; + } + + h = ENG_COUNT(e); + ASSERT(h >= e->e_head); + if (h < e->e_head) { + /* + * This is a sign of a serious bug. We should + * probably offline the device via FMA, if we ever + * support FMA for audio devices. + */ + e->e_failed = B_TRUE; + ENG_STOP(e); + mutex_exit(&e->e_lock); + audio_dev_warn(e->e_dev, + "device malfunction: broken capture sample counter"); + return; + } + e->e_head = h; + ASSERT(e->e_head >= e->e_tail); + + if ((e->e_head - e->e_tail) > e->e_nframes) { + /* no room for data, not much we can do */ + e->e_errors++; + e->e_overruns++; + } /* consume all fragments in the buffer */ - while ((eng->e_head - eng->e_tail) > fragfr) { + while ((e->e_head - e->e_tail) > fragfr) { /* * Consider doing the SYNC outside of the lock. */ - ENG_SYNC(eng, fragfr); + ENG_SYNC(e, fragfr); - for (audio_stream_t *sp = list_head(&eng->e_streams); - sp != NULL; - sp = list_next(&eng->e_streams, sp)) { + for (sp = list_head(l); sp != NULL; sp = list_next(l, sp)) { int space; int count; - c = sp->s_client; - mutex_enter(&sp->s_lock); /* skip over streams paused or not running */ - if (sp->s_paused || (!sp->s_running) || - eng->e_suspended) { + if (sp->s_paused || !sp->s_running) { mutex_exit(&sp->s_lock); continue; } sp->s_cnv_src = sp->s_cnv_buf0; sp->s_cnv_dst = sp->s_cnv_buf1; - eng->e_import(eng, sp); + + e->e_import(e, fragfr, sp); /* * Optionally convert fragment to requested sample @@ -180,33 +223,56 @@ count = fragfr; } + ASSERT(sp->s_head >= sp->s_tail); space = sp->s_nframes - (sp->s_head - sp->s_tail); if (count > space) { - eng->e_stream_overruns++; - eng->e_errors++; + e->e_stream_overruns++; + e->e_errors++; sp->s_errors += count - space; count = space; } - auimpl_produce_fragment(sp, count); + auimpl_produce_data(sp, count); /* wake blocked threads (blocking reads, etc.) */ cv_broadcast(&sp->s_cv); mutex_exit(&sp->s_lock); - if (c->c_input != NULL) { - c->c_input(c); + /* + * Add client to notification list. We'll + * process it after dropping the lock. + */ + c = sp->s_client; + + if ((c->c_input != NULL) && + (c->c_next_input == NULL)) { + auclnt_hold(c); + c->c_next_input = clist; + clist = c; } } /* * Update the tail pointer, and the data pointer. */ - eng->e_tail += fragfr; - eng->e_tidx += fragfr; - if (eng->e_tidx >= eng->e_nframes) { - eng->e_tidx -= eng->e_nframes; + e->e_tail += fragfr; + e->e_tidx += fragfr; + if (e->e_tidx >= e->e_nframes) { + e->e_tidx -= e->e_nframes; } } + + mutex_exit(&e->e_lock); + + /* + * Notify client personalities. + */ + + while ((c = clist) != NULL) { + clist = c->c_next_input; + c->c_next_input = NULL; + c->c_input(c); + auclnt_release(c); + } }
--- a/usr/src/uts/common/io/audio/impl/audio_output.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/audio/impl/audio_output.c Tue Mar 16 09:30:41 2010 -0700 @@ -21,7 +21,7 @@ /* * Copyright (C) 4Front Technologies 1996-2008. * - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -38,11 +38,10 @@ #define DECL_AUDIO_EXPORT(NAME, TYPE, SAMPLE) \ void \ -auimpl_export_##NAME(audio_engine_t *eng) \ +auimpl_export_##NAME(audio_engine_t *eng, uint_t nfr, uint_t froff) \ { \ - int fragfr = eng->e_fragfr; \ int nch = eng->e_nchan; \ - unsigned hidx = eng->e_hidx; \ + uint_t hidx = eng->e_hidx; \ TYPE *out = (void *)eng->e_data; \ int ch = 0; \ \ @@ -55,8 +54,9 @@ /* get value and adjust next channel offset */ \ op = out + eng->e_choffs[ch] + (hidx * incr); \ ip = eng->e_chbufs[ch]; \ + ip += froff; \ \ - i = fragfr; \ + i = nfr; \ \ do { /* for each frame */ \ int32_t sample = *ip; \ @@ -85,18 +85,18 @@ auimpl_output_limiter(audio_engine_t *eng) { int k, t; - unsigned int q, amp, amp2; + uint_t q, amp, amp2; int nchan = eng->e_nchan; - int fragfr = eng->e_fragfr; + uint_t fragfr = eng->e_fragfr; int32_t **chbufs = eng->e_chbufs; - unsigned int statevar = eng->e_limiter_state; + uint_t statevar = eng->e_limiter_state; for (t = 0; t < fragfr; t++) { - amp = (unsigned)ABS(chbufs[0][t]); + amp = (uint_t)ABS(chbufs[0][t]); for (k = 1; k < nchan; k++) { - amp2 = (unsigned)ABS(chbufs[k][t]); + amp2 = (uint_t)ABS(chbufs[k][t]); if (amp2 > amp) amp = amp2; } @@ -130,7 +130,7 @@ for (k = 0; k < nchan; k++) { int32_t in = chbufs[k][t]; int32_t out = 0; - unsigned int p; + uint_t p; if (in >= 0) { p = in; @@ -221,12 +221,12 @@ static void auimpl_consume_fragment(audio_stream_t *sp) { - unsigned count; - unsigned avail; - unsigned nframes; - unsigned fragfr; - unsigned framesz; - caddr_t cnvbuf; + uint_t count; + uint_t avail; + uint_t nframes; + uint_t fragfr; + uint_t framesz; + caddr_t cnvbuf; sp->s_cnv_src = sp->s_cnv_buf0; sp->s_cnv_dst = sp->s_cnv_buf1; @@ -247,8 +247,8 @@ * do...while to minimize the number of tests. */ do { - unsigned n; - unsigned nbytes; + uint_t n; + uint_t nbytes; n = min(nframes - sp->s_tidx, count); nbytes = framesz * n; @@ -272,9 +272,11 @@ } static void -auimpl_output_callback_impl(audio_engine_t *eng) +auimpl_output_callback_impl(audio_engine_t *eng, audio_client_t **output, + audio_client_t **drain) { - int fragfr = eng->e_fragfr; + uint_t fragfr = eng->e_fragfr; + uint_t resid; /* clear any preexisting mix results */ for (int i = 0; i < eng->e_nchan; i++) @@ -303,7 +305,7 @@ mutex_enter(&sp->s_lock); /* skip over streams not running or paused */ - if ((!sp->s_running) || (sp->s_paused) || eng->e_suspended) { + if ((!sp->s_running) || (sp->s_paused)) { mutex_exit(&sp->s_lock); continue; } @@ -402,12 +404,18 @@ * the client's stream from engine. So we're safe. */ - if (c->c_output != NULL) { - c->c_output(c); + if (output && (c->c_output != NULL) && + (c->c_next_output == NULL)) { + auclnt_hold(c); + c->c_next_output = *output; + *output = c; } - if (drained && (c->c_drain != NULL)) { - c->c_drain(c); + if (drain && drained && (c->c_drain != NULL) && + (c->c_next_drain == NULL)) { + auclnt_hold(c); + c->c_next_drain = *drain; + *drain = c; } } @@ -417,19 +425,20 @@ auimpl_output_limiter(eng); /* - * Export the data (a whole fragment) to the device. + * Export the data (a whole fragment) to the device. Deal + * properly with wraps. Note that the test and subtraction is + * faster for dealing with wrap than modulo. */ - eng->e_export(eng); - - /* - * Update the head and offset. The head counts without - * wrapping, whereas the offset wraps. Note that the test + - * subtraction is faster for dealing with wrap than modulo. - */ - eng->e_head += fragfr; - eng->e_hidx += fragfr; - if (eng->e_hidx >= eng->e_nframes) - eng->e_hidx -= eng->e_nframes; + resid = fragfr; + do { + uint_t part = min(resid, eng->e_nframes - eng->e_hidx); + eng->e_export(eng, part, fragfr - resid); + eng->e_head += part; + eng->e_hidx += part; + if (eng->e_hidx == eng->e_nframes) + eng->e_hidx = 0; + resid -= part; + } while (resid); /* * Consider doing the SYNC outside of the lock. @@ -442,15 +451,105 @@ */ void -auimpl_output_callback(audio_engine_t *eng) +auimpl_output_callback(void *arg) { - int64_t cnt; + audio_engine_t *e = arg; + int64_t cnt; + audio_client_t *c; + audio_client_t *output = NULL; + audio_client_t *drain = NULL; + uint64_t t; + + mutex_enter(&e->e_lock); + + if (e->e_suspended || e->e_failed) { + mutex_exit(&e->e_lock); + return; + } - cnt = eng->e_head - eng->e_tail; + if (e->e_need_start) { + int rv; + if ((rv = ENG_START(e)) != 0) { + e->e_failed = B_TRUE; + mutex_exit(&e->e_lock); + audio_dev_warn(e->e_dev, + "failed starting output, rv = %d", rv); + return; + } + e->e_need_start = B_FALSE; + } + + t = ENG_COUNT(e); + if (t < e->e_tail) { + /* + * This is a sign of a serious bug. We should + * probably offline the device via FMA, if we ever + * support FMA for audio devices. + */ + e->e_failed = B_TRUE; + ENG_STOP(e); + mutex_exit(&e->e_lock); + audio_dev_warn(e->e_dev, + "device malfunction: broken play back sample counter"); + return; + + } + e->e_tail = t; + + if (e->e_tail > e->e_head) { + /* want more than we have */ + e->e_errors++; + e->e_underruns++; + } + + cnt = e->e_head - e->e_tail; /* stay a bit ahead */ - while (cnt < eng->e_playahead) { - auimpl_output_callback_impl(eng); - cnt = eng->e_head - eng->e_tail; + while (cnt < e->e_playahead) { + auimpl_output_callback_impl(e, &output, &drain); + cnt = e->e_head - e->e_tail; + } + mutex_exit(&e->e_lock); + + /* + * Notify client personalities. + */ + while ((c = output) != NULL) { + + output = c->c_next_output; + c->c_next_output = NULL; + c->c_output(c); + auclnt_release(c); + } + + while ((c = drain) != NULL) { + + drain = c->c_next_drain; + c->c_next_drain = NULL; + c->c_drain(c); + auclnt_release(c); + } + +} + +void +auimpl_output_preload(audio_engine_t *e) +{ + int64_t cnt; + + ASSERT(mutex_owned(&e->e_lock)); + + if (e->e_tail > e->e_head) { + /* want more than we have */ + e->e_errors++; + e->e_underruns++; + e->e_tail = e->e_head; + } + cnt = e->e_head - e->e_tail; + + /* stay a bit ahead */ + while (cnt < e->e_playahead) { + auimpl_output_callback_impl(e, NULL, NULL); + cnt = e->e_head - e->e_tail; } }
--- a/usr/src/uts/common/io/usb/clients/audio/usb_ac/usb_ac.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/io/usb/clients/audio/usb_ac/usb_ac.c Tue Mar 16 09:30:41 2010 -0700 @@ -326,7 +326,7 @@ static int usb_audio_register(usb_ac_state_t *); static int usb_audio_unregister(usb_ac_state_t *); -static int usb_engine_open(void *, int, unsigned *, unsigned *, caddr_t *); +static int usb_engine_open(void *, int, unsigned *, caddr_t *); static void usb_engine_close(void *); static uint64_t usb_engine_count(void *); static int usb_engine_start(void *); @@ -5340,7 +5340,6 @@ unsigned frames; unsigned i; size_t sz; - int bufcnt = 0; caddr_t bp = buf; mutex_enter(&engp->lock); @@ -5362,15 +5361,12 @@ sz = (frames << engp->frsmshift) << engp->smszshift; - bufcnt++; - /* must move data before updating framework */ usb_eng_bufio(engp, bp, sz); engp->frames += frames; bp += sz; mutex_exit(&engp->lock); - audio_engine_consume(engp->af_engp); } mutex_enter(&engp->lock); @@ -5393,7 +5389,6 @@ unsigned frames; unsigned i; size_t sz; - int bufcnt = 0; caddr_t bp = buf; mutex_enter(&engp->lock); @@ -5416,15 +5411,12 @@ sz = (frames << engp->frsmshift) << engp->smszshift; - bufcnt++; - /* must move data before updating framework */ usb_eng_bufio(engp, bp, sz); engp->frames += frames; bp += sz; mutex_exit(&engp->lock); - audio_engine_produce(engp->af_engp); } mutex_enter(&engp->lock); @@ -5439,15 +5431,14 @@ * ************************************************************************** * audio framework engine callbacks */ -/*ARGSUSED*/ static int -usb_engine_open(void *arg, int flag, - unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp) +usb_engine_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp) { usb_audio_eng_t *engp = (usb_audio_eng_t *)arg; usb_ac_state_t *statep = engp->statep; int rv = EIO; + _NOTE(ARGUNUSED(flag)); if (usb_ac_open(statep->usb_ac_dip) != USB_SUCCESS) { @@ -5504,8 +5495,7 @@ engp->started = B_FALSE; engp->busy = B_FALSE; - *fragfrp = engp->fragfr; - *nfragsp = engp->nfrags; + *nframesp = engp->nfrags * engp->fragfr; *bufp = engp->bufp; mutex_exit(&engp->lock); @@ -5539,14 +5529,12 @@ usb_audio_eng_t *engp = (usb_audio_eng_t *)arg; usb_ac_state_t *statep = engp->statep; - audio_engine_unlock(engp->af_engp); mutex_enter(&engp->lock); while (engp->busy) { cv_wait(&engp->usb_audio_cv, &engp->lock); } mutex_exit(&engp->lock); - audio_engine_lock(engp->af_engp); if (statep->flags & AD_SETUP) { usb_ac_teardown(statep, engp);
--- a/usr/src/uts/common/sys/audio/ac97.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/sys/audio/ac97.h Tue Mar 16 09:30:41 2010 -0700 @@ -20,7 +20,7 @@ */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -653,8 +653,6 @@ * Bits common to both new style and old style initialization. */ void ac97_free(ac97_t *); -void ac97_suspend(ac97_t *); -void ac97_resume(ac97_t *); void ac97_reset(ac97_t *); int ac97_num_channels(ac97_t *);
--- a/usr/src/uts/common/sys/audio/audio_driver.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/common/sys/audio/audio_driver.h Tue Mar 16 09:30:41 2010 -0700 @@ -43,15 +43,14 @@ struct audio_engine_ops { int audio_engine_version; -#define AUDIO_ENGINE_VERSION 1 +#define AUDIO_ENGINE_VERSION 2 /* * Initialize engine, including buffer allocation. Arguments * that are pointers are hints. On return, they are updated with * the actual values configured by the driver. */ - int (*audio_engine_open)(void *, int flags, - unsigned *fragfr, unsigned *nfrags, caddr_t *buf); + int (*audio_engine_open)(void *, int, uint_t *, caddr_t *); void (*audio_engine_close)(void *); /* @@ -98,13 +97,13 @@ * flags passed to ae_open()), and dealing with any partial * synchronization if any is needed. */ - void (*audio_engine_sync)(void *, unsigned); + void (*audio_engine_sync)(void *, uint_t); /* * The framework may like to know how deep the device queues data. * This can be used to provide a more accurate latency calculation. */ - unsigned (*audio_engine_qlen)(void *); + uint_t (*audio_engine_qlen)(void *); /* * If the driver doesn't use simple interleaving, then we need to @@ -114,8 +113,8 @@ * samples. If this entry point is NULL, the framework assumes * that simple interlevaing is used instead. */ - void (*audio_engine_chinfo)(void *, int chan, unsigned *offset, - unsigned *incr); + void (*audio_engine_chinfo)(void *, int chan, uint_t *offset, + uint_t *incr); /* * The following entry point is used to determine the play ahead @@ -123,9 +122,12 @@ * or with a need for deeper queuing, implement this. If not * implemented, the framework assumes 1.5 * fragfr. */ - unsigned (*audio_engine_playahead)(void *); + uint_t (*audio_engine_playahead)(void *); }; +/* + * Drivers call these. + */ void audio_init_ops(struct dev_ops *, const char *); void audio_fini_ops(struct dev_ops *); @@ -136,7 +138,7 @@ void audio_dev_set_version(audio_dev_t *, const char *); void audio_dev_add_info(audio_dev_t *, const char *); -audio_engine_t *audio_engine_alloc(audio_engine_ops_t *, unsigned); +audio_engine_t *audio_engine_alloc(audio_engine_ops_t *, uint_t); void audio_engine_set_private(audio_engine_t *, void *); void *audio_engine_get_private(audio_engine_t *); void audio_engine_free(audio_engine_t *); @@ -145,20 +147,15 @@ void audio_dev_remove_engine(audio_dev_t *, audio_engine_t *); int audio_dev_register(audio_dev_t *); int audio_dev_unregister(audio_dev_t *); +void audio_dev_suspend(audio_dev_t *); +void audio_dev_resume(audio_dev_t *); void audio_dev_warn(audio_dev_t *, const char *, ...); + /* DEBUG ONLY */ void audio_dump_bytes(const uint8_t *w, int dcount); void audio_dump_words(const uint16_t *w, int dcount); void audio_dump_dwords(const uint32_t *w, int dcount); -/* - * Drivers call these. - */ -void audio_engine_consume(audio_engine_t *); -void audio_engine_produce(audio_engine_t *); -void audio_engine_reset(audio_engine_t *); -void audio_engine_lock(audio_engine_t *); -void audio_engine_unlock(audio_engine_t *); /* Engine flags */ #define ENGINE_OUTPUT_CAP (1U << 2) @@ -172,7 +169,6 @@ #define ENGINE_RUNNING (1U << 19) #define ENGINE_EXCLUSIVE (1U << 20) /* exclusive use, e.g. AC3 */ #define ENGINE_NDELAY (1U << 21) /* non-blocking open */ -#define ENGINE_WAKE (1U << 22) /* wakeup tq running */ /* * entry points used by legacy SADA drivers
--- a/usr/src/uts/intel/audio1575/Makefile Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/intel/audio1575/Makefile Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # uts/intel/audio1575/Makefile @@ -40,7 +40,6 @@ OBJECTS = $(AUDIO1575_OBJS:%=$(OBJS_DIR)/%) LINTS = $(AUDIO1575_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/io/audio/drv/audio1575 # # Include common rules. @@ -55,9 +54,9 @@ # # Define targets # -ALL_TARGET = $(BINARY) $(SRC_CONFILE) +ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) # # Default build targets.
--- a/usr/src/uts/intel/audioens/Makefile Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/intel/audioens/Makefile Tue Mar 16 09:30:41 2010 -0700 @@ -21,7 +21,7 @@ # # uts/intel/audioens/Makefile # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This makefile drives the production of the audioens driver. @@ -39,7 +39,6 @@ OBJECTS = $(AUDIOENS_OBJS:%=$(OBJS_DIR)/%) LINTS = $(AUDIOENS_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/io/audio/drv/audioens # # Include common rules. @@ -49,9 +48,9 @@ # # Define targets # -ALL_TARGET = $(BINARY) $(SRC_CONFFILE) +ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) LDFLAGS += -dy -Ndrv/audio -Nmisc/ac97
--- a/usr/src/uts/intel/audioixp/Makefile Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/intel/audioixp/Makefile Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # uts/intel/audioixp/Makefile @@ -40,7 +40,6 @@ OBJECTS = $(AUDIOIXP_OBJS:%=$(OBJS_DIR)/%) LINTS = $(AUDIOIXP_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/io/audio/drv/audioixp # # Include common rules. @@ -51,8 +50,6 @@ # Overrides, lint pass one enforcement # CFLAGS += $(CCVERBOSE) -DEBUG_FLGS = -$(NOT_RELEASE_BUILD)DEBUG_DEFS += $(DEBUG_FLGS) # # Depends on misc/audiosup @@ -62,9 +59,9 @@ # # Define targets # -ALL_TARGET = $(BINARY) $(SRC_CONFILE) +ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) # # Default build targets.
--- a/usr/src/uts/intel/audiopci/Makefile Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/intel/audiopci/Makefile Tue Mar 16 09:30:41 2010 -0700 @@ -21,7 +21,7 @@ # # uts/intel/audiopci/Makefile # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This makefile drives the production of the audiopci driver. @@ -39,7 +39,6 @@ OBJECTS = $(AUDIOPCI_OBJS:%=$(OBJS_DIR)/%) LINTS = $(AUDIOPCI_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/io/audio/drv/audiopci # # Include common rules. @@ -49,9 +48,9 @@ # # Define targets # -ALL_TARGET = $(BINARY) $(SRC_CONFFILE) +ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) LDFLAGS += -dy -Ndrv/audio
--- a/usr/src/uts/intel/audiots/Makefile Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/intel/audiots/Makefile Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # uts/intel/audiots/Makefile @@ -39,7 +39,6 @@ OBJECTS = $(AUDIOTS_OBJS:%=$(OBJS_DIR)/%) LINTS = $(AUDIOTS_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/io/audio/drv/audiots # # Include common rules. @@ -59,9 +58,9 @@ # # Define targets # -ALL_TARGET = $(BINARY) $(SRC_CONFILE) +ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) # # Default build targets.
--- a/usr/src/uts/intel/audiovia823x/Makefile Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/intel/audiovia823x/Makefile Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # uts/intel/audiovia823x/Makefile @@ -40,7 +40,6 @@ OBJECTS = $(AUDIOVIA823X_OBJS:%=$(OBJS_DIR)/%) LINTS = $(AUDIOVIA823X_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/io/audio/drv/audiovia823x # # Include common rules. @@ -55,9 +54,9 @@ # # Define targets # -ALL_TARGET = $(BINARY) $(SRC_CONFILE) +ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) # # Default build targets.
--- a/usr/src/uts/sparc/audio1575/Makefile Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/sparc/audio1575/Makefile Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # uts/sparc/audio1575/Makefile @@ -39,7 +39,6 @@ OBJECTS = $(AUDIO1575_OBJS:%=$(OBJS_DIR)/%) LINTS = $(AUDIO1575_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/io/audio/drv/audio1575 # # Include common rules. @@ -59,16 +58,15 @@ # # Define targets # -ALL_TARGET = $(BINARY) $(SRC_CONFILE) +ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE_64) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) # # Overrides # ALL_BUILDS = $(ALL_BUILDSONLY64) DEF_BUILDS = $(DEF_BUILDSONLY64) -CLEANLINTFILES += $(LINT32_FILES) # # Default build targets. @@ -85,7 +83,7 @@ lint: $(LINT_DEPS) -modlintlib: $(MODLINTLIB_DEPS) lint32 +modlintlib: $(MODLINTLIB_DEPS) clean.lint: $(CLEAN_LINT_DEPS)
--- a/usr/src/uts/sparc/audiocs/Makefile Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/sparc/audiocs/Makefile Tue Mar 16 09:30:41 2010 -0700 @@ -21,7 +21,7 @@ # # uts/sparc/audiocs/Makefile # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This makefile drives the production of the Crystal 4231 @@ -40,7 +40,6 @@ OBJECTS = $(AUDIOCS_OBJS:%=$(OBJS_DIR)/%) LINTS = $(AUDIOCS_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/sun/io/audio/drv/audiocs # # Include common rules. @@ -50,9 +49,9 @@ # # Define targets # -ALL_TARGET = $(BINARY) $(SRC_CONFILE) +ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) # # Overrides
--- a/usr/src/uts/sparc/audioens/Makefile Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/sparc/audioens/Makefile Tue Mar 16 09:30:41 2010 -0700 @@ -21,7 +21,7 @@ # # uts/sparc/audioens/Makefile # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This makefile drives the production of the audioens driver. @@ -39,7 +39,6 @@ OBJECTS = $(AUDIOENS_OBJS:%=$(OBJS_DIR)/%) LINTS = $(AUDIOENS_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/io/audio/drv/audioens # # Include common rules. @@ -49,9 +48,9 @@ # # Define targets # -ALL_TARGET = $(BINARY) $(SRC_CONFFILE) +ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) LDFLAGS += -dy -Ndrv/audio -Nmisc/ac97
--- a/usr/src/uts/sparc/audiopci/Makefile Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/sparc/audiopci/Makefile Tue Mar 16 09:30:41 2010 -0700 @@ -21,7 +21,7 @@ # # uts/sparc/audiopci/Makefile # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This makefile drives the production of the audiopci driver. @@ -39,7 +39,6 @@ OBJECTS = $(AUDIOPCI_OBJS:%=$(OBJS_DIR)/%) LINTS = $(AUDIOPCI_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/io/audio/drv/audiopci # # Include common rules. @@ -49,9 +48,9 @@ # # Define targets # -ALL_TARGET = $(BINARY) $(SRC_CONFFILE) +ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) LDFLAGS += -dy -Ndrv/audio
--- a/usr/src/uts/sparc/audiots/Makefile Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/sparc/audiots/Makefile Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # uts/sparc/audiots/Makefile @@ -39,7 +39,6 @@ OBJECTS = $(AUDIOTS_OBJS:%=$(OBJS_DIR)/%) LINTS = $(AUDIOTS_OBJS:%.o=$(LINTS_DIR)/%.ln) ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/io/audio/drv/audiots # # Include common rules. @@ -59,16 +58,15 @@ # # Define targets # -ALL_TARGET = $(BINARY) $(SRC_CONFILE) +ALL_TARGET = $(BINARY) LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE_64) +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) # # Overrides # ALL_BUILDS = $(ALL_BUILDSONLY64) DEF_BUILDS = $(DEF_BUILDSONLY64) -CLEANLINTFILES += $(LINT32_FILES) # # Default build targets. @@ -85,7 +83,7 @@ lint: $(LINT_DEPS) -modlintlib: $(MODLINTLIB_DEPS) lint32 +modlintlib: $(MODLINTLIB_DEPS) clean.lint: $(CLEAN_LINT_DEPS)
--- a/usr/src/uts/sun/io/audio/drv/audiocs/audio_4231.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/sun/io/audio/drv/audiocs/audio_4231.c Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -73,7 +73,7 @@ /* * Entry point routine prototypes */ -static int audiocs_open(void *, int, unsigned *, unsigned *, caddr_t *); +static int audiocs_open(void *, int, unsigned *, caddr_t *); static void audiocs_close(void *); static int audiocs_start(void *); static void audiocs_stop(void *); @@ -82,7 +82,6 @@ static int audiocs_rate(void *); static uint64_t audiocs_count(void *); static void audiocs_sync(void *, unsigned); -static unsigned audiocs_qlen(void *); /* * Control callbacks. @@ -106,9 +105,6 @@ static int audiocs_chip_init(CS_state_t *); static int audiocs_alloc_engine(CS_state_t *, int); static void audiocs_free_engine(CS_engine_t *); -static void audiocs_reset_engine(CS_engine_t *); -static int audiocs_start_engine(CS_engine_t *); -static void audiocs_stop_engine(CS_engine_t *); static void audiocs_get_ports(CS_state_t *); static void audiocs_configure_input(CS_state_t *); static void audiocs_configure_output(CS_state_t *); @@ -130,6 +126,7 @@ #define SELIDX(s, idx) audiocs_sel_index(s, idx) #define PUTIDX(s, val, mask) audiocs_put_index(s, val, mask) #endif +#define GETIDX(s) ddi_get8((handle), &CS4231_IDR) #define ORIDX(s, val, mask) \ PUTIDX(s, \ @@ -151,9 +148,9 @@ audiocs_channels, audiocs_rate, audiocs_sync, - audiocs_qlen, + NULL, NULL, - NULL + NULL, }; #define OUTPUT_SPEAKER 0 @@ -470,22 +467,11 @@ if (state == NULL) return; - /* - * Unregister any interrupts. That way we can't get called by and - * interrupt after the audio framework is removed. - */ - CS4231_DMA_REM_INTR(state); - for (int i = CS4231_PLAY; i <= CS4231_REC; i++) { audiocs_free_engine(state->cs_engines[i]); } audiocs_del_controls(state); - /* free the kernel statistics structure */ - if (state->cs_ksp) { - kstat_delete(state->cs_ksp); - } - if (state->cs_adev) { audio_dev_free(state->cs_adev); } @@ -524,15 +510,8 @@ state->cs_dip = dip; ddi_set_driver_private(dip, state); - /* get the iblock cookie needed for interrupt context */ - if (ddi_get_iblock_cookie(dip, 0, &state->cs_iblock) != DDI_SUCCESS) { - audio_dev_warn(NULL, "cannot get iblock cookie"); - kmem_free(state, sizeof (*state)); - return (DDI_FAILURE); - } - /* now fill it in, initialize the state mutexs first */ - mutex_init(&state->cs_lock, NULL, MUTEX_DRIVER, state->cs_iblock); + mutex_init(&state->cs_lock, NULL, MUTEX_DRIVER, NULL); /* * audio state initialization... should always succeed, @@ -562,18 +541,6 @@ /* chip init will have powered us up */ state->cs_powered = B_TRUE; - /* set up kernel statistics */ - if ((state->cs_ksp = kstat_create(ddi_driver_name(dip), - ddi_get_instance(dip), ddi_driver_name(dip), - "controller", KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT)) != NULL) { - kstat_install(state->cs_ksp); - } - - /* we're ready, set up the interrupt handler */ - if (CS4231_DMA_ADD_INTR(state) != DDI_SUCCESS) { - mutex_exit(&state->cs_lock); - goto error; - } mutex_exit(&state->cs_lock); /* finally register with framework to kick everything off */ @@ -636,16 +603,6 @@ state->cs_suspended = B_FALSE; - for (int i = CS4231_PLAY; i <= CS4231_REC; i++) { - CS_engine_t *eng = state->cs_engines[i]; - - audiocs_reset_engine(eng); - if (eng->ce_started) { - (void) audiocs_start_engine(eng); - } else { - audiocs_stop_engine(eng); - } - } mutex_exit(&state->cs_lock); /* @@ -655,6 +612,8 @@ (void) pm_raise_power(dip, CS4231_COMPONENT, CS4231_PWR_ON); (void) pm_idle_component(state->cs_dip, CS4231_COMPONENT); + audio_dev_resume(state->cs_adev); + return (DDI_SUCCESS); } @@ -731,17 +690,13 @@ /* get the state structure */ state = ddi_get_driver_private(dip); - ASSERT(!mutex_owned(&state->cs_lock)); - mutex_enter(&state->cs_lock); ASSERT(!state->cs_suspended); + audio_dev_suspend(state->cs_adev); + if (state->cs_powered) { - /* stop playing and recording */ - CS4231_DMA_STOP(state, state->cs_engines[CS4231_PLAY]); - CS4231_DMA_STOP(state, state->cs_engines[CS4231_REC]); - /* now we can power down the Codec */ audiocs_power_down(state); state->cs_powered = B_FALSE; @@ -749,7 +704,6 @@ state->cs_suspended = B_TRUE; /* stop new ops */ mutex_exit(&state->cs_lock); - ASSERT(!mutex_owned(&state->cs_lock)); return (DDI_SUCCESS); } @@ -1034,6 +988,7 @@ PUTIDX(state, 0, AFE2_VALID_MASK); } + /* clear the play and capture interrupt flags */ SELIDX(state, AFS_REG); ddi_put8(handle, &CS4231_STATUS, (AFS_RESET_STATUS)); @@ -1371,9 +1326,6 @@ ASSERT(mutex_owned(&state->cs_lock)); - if (state->cs_suspended) - return; - inputs = state->cs_inputs->cc_val; micboost = state->cs_micboost->cc_val; r = (state->cs_igain->cc_val & 0xff); @@ -1439,9 +1391,6 @@ ASSERT(mutex_owned(&state->cs_lock)); - if (state->cs_suspended) - return; - outputs = state->cs_outputs->cc_val; /* port selection */ @@ -1710,8 +1659,7 @@ * Arguments: * void *arg The DMA engine to set up * int flag Open flags - * unsigned *fragfrp Receives number of frames per fragment - * unsigned *nfragsp Receives number of fragments + * unsigned *nframesp Receives number of frames * caddr_t *bufp Receives kernel data buffer * * Returns: @@ -1719,8 +1667,7 @@ * errno on failure */ static int -audiocs_open(void *arg, int flag, - unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp) +audiocs_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp) { CS_engine_t *eng = arg; CS_state_t *state = eng->ce_state; @@ -1738,16 +1685,10 @@ audio_dev_warn(state->cs_adev, "power up failed"); } - eng->ce_started = B_FALSE; eng->ce_count = 0; - - *fragfrp = eng->ce_fragfr; - *nfragsp = CS4231_NFRAGS; + *nframesp = CS4231_NFRAMES; *bufp = eng->ce_kaddr; - mutex_enter(&state->cs_lock); - audiocs_reset_engine(eng); - mutex_exit(&state->cs_lock); return (0); } @@ -1768,11 +1709,6 @@ CS_engine_t *eng = arg; CS_state_t *state = eng->ce_state; - mutex_enter(&state->cs_lock); - audiocs_stop_engine(eng); - eng->ce_started = B_FALSE; - mutex_exit(&state->cs_lock); - (void) pm_idle_component(state->cs_dip, CS4231_COMPONENT); } @@ -1789,14 +1725,21 @@ static void audiocs_stop(void *arg) { - CS_engine_t *eng = arg; - CS_state_t *state = eng->ce_state; + CS_engine_t *eng = arg; + CS_state_t *state = eng->ce_state; + ddi_acc_handle_t handle = CODEC_HANDLE; mutex_enter(&state->cs_lock); - if (eng->ce_started) { - audiocs_stop_engine(eng); - eng->ce_started = B_FALSE; - } + /* + * Stop the DMA engine. + */ + CS4231_DMA_STOP(state, eng); + + /* + * Stop the codec. + */ + SELIDX(state, INTC_REG); + ANDIDX(state, ~(eng->ce_codec_en), INTC_VALID_MASK); mutex_exit(&state->cs_lock); } @@ -1815,18 +1758,45 @@ static int audiocs_start(void *arg) { - CS_engine_t *eng = arg; - CS_state_t *state = eng->ce_state; - int rv = 0; + CS_engine_t *eng = arg; + CS_state_t *state = eng->ce_state; + ddi_acc_handle_t handle = CODEC_HANDLE; + uint8_t mask; + uint8_t value; + uint8_t reg; + int rv; mutex_enter(&state->cs_lock); - if (!eng->ce_started) { - if (audiocs_start_engine(eng) == DDI_SUCCESS) { - eng->ce_started = B_TRUE; - } else { - rv = EIO; - } + + if (eng->ce_num == CS4231_PLAY) { + /* sample rate only set on play side */ + value = FS_48000 | PDF_STEREO | PDF_LINEAR16NE; + reg = FSDF_REG; + mask = FSDF_VALID_MASK; + } else { + value = CDF_STEREO | CDF_LINEAR16NE; + reg = CDF_REG; + mask = CDF_VALID_MASK; } + eng->ce_curoff = 0; + eng->ce_curidx = 0; + + SELIDX(state, reg | IAR_MCE); + PUTIDX(state, value, mask); + + if (audiocs_poll_ready(state) != DDI_SUCCESS) { + rv = EIO; + } else if (CS4231_DMA_START(state, eng) != DDI_SUCCESS) { + rv = EIO; + } else { + /* + * Start the codec. + */ + SELIDX(state, INTC_REG); + ORIDX(state, eng->ce_codec_en, INTC_VALID_MASK); + rv = 0; + } + mutex_exit(&state->cs_lock); return (rv); } @@ -1906,12 +1876,40 @@ static uint64_t audiocs_count(void *arg) { - CS_engine_t *eng = arg; - CS_state_t *state = eng->ce_state; - uint64_t val; + CS_engine_t *eng = arg; + CS_state_t *state = eng->ce_state; + uint64_t val; + uint32_t off; mutex_enter(&state->cs_lock); + + off = CS4231_DMA_ADDR(state, eng); + ASSERT(off >= eng->ce_paddr); + off -= eng->ce_paddr; + + /* + * Every now and then, we get a value that is just a wee bit + * too large. This seems to be a small value related to + * prefetch. Rather than believe it, we just assume the last + * offset in the buffer. This should allow us to handle + * wraps, but without inserting bogus sample counts. + */ + if (off >= CS4231_BUFSZ) { + off = CS4231_BUFSZ - 4; + } + + off /= 4; + + val = (off >= eng->ce_curoff) ? + off - eng->ce_curoff : + off + CS4231_NFRAMES - eng->ce_curoff; + + eng->ce_count += val; + eng->ce_curoff = off; val = eng->ce_count; + + /* while here, possibly reload the next address */ + CS4231_DMA_RELOAD(state, eng); mutex_exit(&state->cs_lock); return (val); @@ -1936,61 +1934,6 @@ } /* - * audiocs_qlen() - * - * Description: - * This is called by the framework to determine on-device queue length. - * - * Arguments: - * void *arg The DMA engine to query - * - * Returns: - * hardware queue length not reported by count (0 for this device) - */ -static unsigned -audiocs_qlen(void *arg) -{ - CS_engine_t *eng = arg; - - return (eng->ce_fragfr); -} - - -/* - * audiocs_reset_engine() - * - * Description: - * This routine resets the DMA engine pareparing it for work. - * - * Arguments: - * CS_engine_t *engine DMA engine to stop. - */ -void -audiocs_reset_engine(CS_engine_t *eng) -{ - CS_state_t *state = eng->ce_state; - uint8_t mask; - uint8_t value; - uint8_t reg; - - if (eng->ce_num == CS4231_PLAY) { - /* sample rate only set on play side */ - value = FS_48000 | PDF_STEREO | PDF_LINEAR16NE; - reg = FSDF_REG; - mask = FSDF_VALID_MASK; - } else { - value = CDF_STEREO | CDF_LINEAR16NE; - reg = CDF_REG; - mask = CDF_VALID_MASK; - } - - SELIDX(state, reg | IAR_MCE); - PUTIDX(state, value, mask); - - (void) audiocs_poll_ready(state); -} - -/* * audiocs_alloc_engine() * * Description: @@ -2007,7 +1950,6 @@ int audiocs_alloc_engine(CS_state_t *state, int num) { - char *prop; unsigned caps; int dir; int rc; @@ -2016,6 +1958,7 @@ CS_engine_t *eng; uint_t ccnt; ddi_dma_cookie_t dmac; + size_t bufsz; static ddi_device_acc_attr_t buf_attr = { DDI_DEVICE_ATTR_V0, @@ -2033,14 +1976,12 @@ switch (num) { case CS4231_REC: - prop = "record-interrupts"; dir = DDI_DMA_READ; caps = ENGINE_INPUT_CAP; eng->ce_syncdir = DDI_DMA_SYNC_FORKERNEL; eng->ce_codec_en = INTC_CEN; break; case CS4231_PLAY: - prop = "play-interrupts"; dir = DDI_DMA_WRITE; caps = ENGINE_OUTPUT_CAP; eng->ce_syncdir = DDI_DMA_SYNC_FORDEV; @@ -2053,32 +1994,6 @@ } state->cs_engines[num] = eng; - eng->ce_intrs = ddi_prop_get_int(DDI_DEV_T_ANY, dip, - DDI_PROP_DONTPASS, prop, CS4231_INTS); - - /* make sure the values are good */ - if (eng->ce_intrs < CS4231_MIN_INTS) { - audio_dev_warn(adev, "%s too low, %d, resetting to %d", - prop, eng->ce_intrs, CS4231_INTS); - eng->ce_intrs = CS4231_INTS; - } else if (eng->ce_intrs > CS4231_MAX_INTS) { - audio_dev_warn(adev, "%s too high, %d, resetting to %d", - prop, eng->ce_intrs, CS4231_INTS); - eng->ce_intrs = CS4231_INTS; - } - - /* - * Figure out how much space we need. Sample rate is 48kHz, and - * we need to store 8 chunks. (Note that this means that low - * interrupt frequencies will require more RAM. We could probably - * do some cleverness to use a more dynamic list.) - */ - eng->ce_fragfr = 48000 / eng->ce_intrs; - eng->ce_fragfr &= ~(64 - 1); /* align @ 64B boundaries */ - eng->ce_fragfr = max(eng->ce_fragfr, 64); - eng->ce_fragsz = eng->ce_fragfr * 4; /* each frame is 4 bytes */ - eng->ce_size = eng->ce_fragsz * CS4231_NFRAGS; - /* allocate dma handle */ rc = ddi_dma_alloc_handle(dip, CS4231_DMA_ATTR(state), DDI_DMA_SLEEP, NULL, &eng->ce_dmah); @@ -2087,9 +2002,9 @@ return (DDI_FAILURE); } /* allocate DMA buffer */ - rc = ddi_dma_mem_alloc(eng->ce_dmah, eng->ce_size, &buf_attr, + rc = ddi_dma_mem_alloc(eng->ce_dmah, CS4231_BUFSZ, &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &eng->ce_kaddr, - &eng->ce_size, &eng->ce_acch); + &bufsz, &eng->ce_acch); if (rc == DDI_FAILURE) { audio_dev_warn(adev, "dma_mem_alloc failed"); return (DDI_FAILURE); @@ -2097,7 +2012,7 @@ /* bind DMA buffer */ rc = ddi_dma_addr_bind_handle(eng->ce_dmah, NULL, - eng->ce_kaddr, eng->ce_size, dir | DDI_DMA_CONSISTENT, + eng->ce_kaddr, CS4231_BUFSZ, dir | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &dmac, &ccnt); if ((rc != DDI_DMA_MAPPED) || (ccnt != 1)) { audio_dev_warn(adev, @@ -2105,11 +2020,7 @@ return (DDI_FAILURE); } - /* save off phys addresses for each frag */ - for (int i = 0; i < CS4231_NFRAGS; i++) { - eng->ce_paddr[i] = dmac.dmac_address; - dmac.dmac_address += eng->ce_fragsz; - } + eng->ce_paddr = dmac.dmac_address; eng->ce_engine = audio_engine_alloc(&audiocs_engine_ops, caps); if (eng->ce_engine == NULL) { @@ -2143,7 +2054,7 @@ audio_dev_remove_engine(adev, eng->ce_engine); audio_engine_free(eng->ce_engine); } - if (eng->ce_paddr[0]) { + if (eng->ce_paddr) { (void) ddi_dma_unbind_handle(eng->ce_dmah); } if (eng->ce_acch) { @@ -2156,83 +2067,6 @@ } /* - * audiocs_start_port() - * - * Description: - * This routine starts the DMA engine. - * - * Arguments: - * CS_engine_t *eng Port of DMA engine to start. - * - * Returns: - * DDI_SUCCESS DMA engine started. - * DDI_FAILURE DMA engine not started. - */ -int -audiocs_start_engine(CS_engine_t *eng) -{ - CS_state_t *state = eng->ce_state; - ddi_acc_handle_t handle = CODEC_HANDLE; - - ASSERT(mutex_owned(&state->cs_lock)); - - /* - * If we are suspended, we can't touch hardware. - */ - if (state->cs_suspended) - return (DDI_SUCCESS); - - /* - * Start the DMA engine. - */ - if (CS4231_DMA_START(state, eng) != DDI_SUCCESS) - return (DDI_FAILURE); - - /* - * Start the codec. - */ - SELIDX(state, INTC_REG); - ORIDX(state, eng->ce_codec_en, INTC_VALID_MASK); - - return (DDI_SUCCESS); -} - -/* - * audiocs_stop_engine() - * - * Description: - * This routine stop the DMA engine. - * - * Arguments: - * CS_engine_t *eng DMA engine to stop. - */ -void -audiocs_stop_engine(CS_engine_t *eng) -{ - CS_state_t *state = eng->ce_state; - ddi_acc_handle_t handle = CODEC_HANDLE; - - ASSERT(mutex_owned(&state->cs_lock)); - - /* - * If we are suspended, we can't touch hardware. - */ - if (state->cs_suspended) - return; - - /* - * Stop the DMA engine. - */ - CS4231_DMA_STOP(state, eng); - - /* - * Stop the codec. - */ - SELIDX(state, INTC_REG); - ANDIDX(state, ~(eng->ce_codec_en), INTC_VALID_MASK); -} - -/* * audiocs_poll_ready() * * Description: @@ -2311,9 +2145,6 @@ * ddi_acc_handle_t handle A handle to the device's registers * uint8_t addr The register address to program * int reg The register to select - * - * Returns: - * void */ void #ifdef DEBUG @@ -2364,9 +2195,6 @@ * CS_state_t state Handle to this device * uint8_t mask Mask to not set reserved register bits * int val The value to program - * - * Returns: - * void */ void #ifdef DEBUG
--- a/usr/src/uts/sun/io/audio/drv/audiocs/audio_4231.h Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/sun/io/audio/drv/audiocs/audio_4231.h Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -55,10 +55,6 @@ #define CS4231_NAME "audiocs" #define CS4231_MOD_NAME "CS4231 audio driver" -#define CS4231_INTS (175) /* default interrupt rate */ -#define CS4231_MIN_INTS (10) /* minimum interrupt rate */ -#define CS4231_MAX_INTS (2000) /* maximum interrupt rate */ - /* * Implementation specific header file for the audiocs device driver. */ @@ -148,15 +144,10 @@ #define APC_CINTR_ENABLE (APC_CIE|APC_CXI_EN) #define APC_COMMON_ENABLE (APC_IE|APC_EIE) -#define APC_PLAY_ENABLE (APC_PINTR_MASK|APC_COMMON_MASK|\ - APC_PINTR_ENABLE|APC_COMMON_ENABLE|APC_PDMA_GO) - -#define APC_PLAY_DISABLE (APC_PINTR_MASK|APC_PINTR_ENABLE|APC_PDMA_GO) - -#define APC_CAP_ENABLE (APC_CINTR_MASK|APC_COMMON_MASK|\ - APC_CINTR_ENABLE|APC_COMMON_ENABLE|APC_CDMA_GO) - -#define APC_CAP_DISABLE (APC_CINTR_MASK|APC_CINTR_ENABLE|APC_CDMA_GO) +#define APC_PLAY_ENABLE (APC_PDMA_GO) +#define APC_PLAY_DISABLE (APC_PDMA_GO) +#define APC_CAP_ENABLE (APC_CDMA_GO) +#define APC_CAP_DISABLE (APC_CDMA_GO) /* * These are the registers for the EBUS2 DMA channel interface to the @@ -219,10 +210,10 @@ #define EB2_PCLEAR_RESET_VALUE (EB2_READ|EB2_EN_NEXT|EB2_EN_CNT) #define EB2_RCLEAR_RESET_VALUE (EB2_WRITE|EB2_EN_NEXT|EB2_EN_CNT) -#define EB2_PLAY_ENABLE (EB2_INT_EN|EB2_EN_DMA|EB2_EN_CNT|EB2_64|\ +#define EB2_PLAY_ENABLE (EB2_EN_DMA|EB2_EN_CNT|EB2_64|\ EB2_PCLEAR_RESET_VALUE) -#define EB2_REC_ENABLE (EB2_INT_EN|EB2_EN_DMA|EB2_EN_CNT|EB2_64|\ +#define EB2_REC_ENABLE (EB2_EN_DMA|EB2_EN_CNT|EB2_64|\ EB2_RCLEAR_RESET_VALUE) #define EB2_FIFO_DRAIN (EB2_DRAIN|EB2_CYC_PENDING) @@ -239,7 +230,10 @@ #define CS4231_300MS (300*1000) #define CS4231_PLAY 0 #define CS4231_REC 1 -#define CS4231_NFRAGS 8 +#define CS4231_NFRAMES 4096 +#define CS4231_NFRAGS 2 +#define CS4231_FRAGSZ ((CS4231_NFRAMES / CS4231_NFRAGS) * 4) +#define CS4231_BUFSZ (CS4231_NFRAMES * 4) /* * Supported dma engines and the ops vector @@ -305,19 +299,15 @@ audio_engine_t *ce_engine; int ce_num; unsigned ce_syncdir; - unsigned ce_intrs; - unsigned ce_fragfr; - unsigned ce_fragsz; - unsigned ce_nframes; - unsigned ce_cfrag; boolean_t ce_started; uint64_t ce_count; - size_t ce_size; caddr_t ce_kaddr; ddi_dma_handle_t ce_dmah; ddi_acc_handle_t ce_acch; - uint32_t ce_paddr[CS4231_NFRAGS]; + uint32_t ce_paddr; + uint32_t ce_curoff; + int ce_curidx; /* registers (EB2 only) */ ddi_acc_handle_t ce_regsh; @@ -340,8 +330,6 @@ struct CS_state { kmutex_t cs_lock; /* state protection lock */ kcondvar_t cs_cv; /* suspend/resume cond. var. */ - ddi_iblock_cookie_t cs_iblock; /* iblock cookie */ - kstat_t *cs_ksp; /* kernel statistics */ dev_info_t *cs_dip; /* used by cs4231_getinfo() */ audio_dev_t *cs_adev; /* audio device state */ @@ -374,8 +362,6 @@ CS_ctrl_t *cs_inputs; }; -#define KIOP(X) ((kstat_intr_t *)(X->cs_ksp->ks_data)) - /* * DMA ops vector definition */ @@ -385,11 +371,11 @@ int (*cs_dma_map_regs)(CS_state_t *); void (*cs_dma_unmap_regs)(CS_state_t *); void (*cs_dma_reset)(CS_state_t *); - int (*cs_dma_add_intr)(CS_state_t *); - void (*cs_dma_rem_intr)(CS_state_t *); int (*cs_dma_start)(CS_engine_t *); void (*cs_dma_stop)(CS_engine_t *); void (*cs_dma_power)(CS_state_t *, int); + void (*cs_dma_reload)(CS_engine_t *); + uint32_t (*cs_dma_addr)(CS_engine_t *); }; typedef struct cs4231_dma_ops cs4231_dma_ops_t; @@ -399,12 +385,12 @@ #define CS4231_DMA_MAP_REGS(S) ((S)->cs_dma_ops->cs_dma_map_regs)(S) #define CS4231_DMA_UNMAP_REGS(S) ((S)->cs_dma_ops->cs_dma_unmap_regs)(S) #define CS4231_DMA_RESET(S) ((S)->cs_dma_ops->cs_dma_reset)(S) -#define CS4231_DMA_ADD_INTR(S) ((S)->cs_dma_ops->cs_dma_add_intr)(S) -#define CS4231_DMA_REM_INTR(S) ((S)->cs_dma_ops->cs_dma_rem_intr)(S) #define CS4231_DMA_START(S, E) ((S)->cs_dma_ops->cs_dma_start)(E) #define CS4231_DMA_STOP(S, E) ((S)->cs_dma_ops->cs_dma_stop)(E) #define CS4231_DMA_POWER(S, L) ((S)->cs_dma_ops->cs_dma_power)(S, L) #define CS4231_DMA_ATTR(S) ((S)->cs_dma_ops->cs_dma_attr) +#define CS4231_DMA_RELOAD(S, E) ((S)->cs_dma_ops->cs_dma_reload)(E) +#define CS4231_DMA_ADDR(S, E) ((S)->cs_dma_ops->cs_dma_addr)(E) /* * Useful bit twiddlers
--- a/usr/src/uts/sun/io/audio/drv/audiocs/audio_4231_apcdma.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/sun/io/audio/drv/audiocs/audio_4231_apcdma.c Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -61,22 +61,16 @@ }; /* - * Local routines - */ -static uint_t apc_intr(caddr_t); -static void apc_load_fragment(CS_engine_t *); - -/* * DMA ops vector functions */ static int apc_map_regs(CS_state_t *); static void apc_unmap_regs(CS_state_t *); static void apc_reset(CS_state_t *); -static int apc_add_intr(CS_state_t *); -static void apc_rem_intr(CS_state_t *); static int apc_start_engine(CS_engine_t *); static void apc_stop_engine(CS_engine_t *); static void apc_power(CS_state_t *, int); +static void apc_reload(CS_engine_t *); +static uint32_t apc_addr(CS_engine_t *); cs4231_dma_ops_t cs4231_apcdma_ops = { "APC DMA controller", @@ -84,11 +78,11 @@ apc_map_regs, apc_unmap_regs, apc_reset, - apc_add_intr, - apc_rem_intr, apc_start_engine, apc_stop_engine, apc_power, + apc_reload, + apc_addr, }; /* @@ -182,69 +176,6 @@ } /* apc_reset() */ /* - * apc_add_intr() - * - * Description: - * Register the APC interrupts with the kernel. - * - * NOTE: This does NOT turn on interrupts. - * - * CAUTION: While the interrupts are added, the Codec interrupts are - * not enabled. - * - * Arguments: - * CS_state_t *state Pointer to the device's state structure - * - * Returns: - * DDI_SUCCESS Registers successfully mapped - * DDI_FAILURE Registers not successfully mapped - */ -static int -apc_add_intr(CS_state_t *state) -{ - dev_info_t *dip = state->cs_dip; - - /* first we make sure this isn't a high level interrupt */ - if (ddi_intr_hilevel(dip, 0) != 0) { - audio_dev_warn(state->cs_adev, - "unsupported high level interrupt"); - return (DDI_FAILURE); - } - - /* okay to register the interrupt */ - if (ddi_add_intr(dip, 0, NULL, NULL, apc_intr, (caddr_t)state) != - DDI_SUCCESS) { - audio_dev_warn(state->cs_adev, "bad interrupt specification"); - return (DDI_FAILURE); - } - - return (DDI_SUCCESS); - -} /* apc_add_intr() */ - -/* - * apc_rem_intr() - * - * Description: - * Unregister the APC interrupts from the kernel. - * - * CAUTION: While the interrupts are removed, the Codec interrupts are - * not disabled, but then, they never should have been on in - * the first place. - * - * Arguments: - * CS_state_t *state Pointer to the device's state structure - * - * Returns: - * void - */ -static void -apc_rem_intr(CS_state_t *state) -{ - ddi_remove_intr(state->cs_dip, 0, NULL); -} /* apc_rem_intr() */ - -/* * apc_start_engine() * * Description: @@ -279,10 +210,10 @@ ASSERT(mutex_owned(&state->cs_lock)); if (eng->ce_num == CS4231_PLAY) { - enable = APC_PLAY_ENABLE; + enable = APC_PDMA_GO; dirty = APC_PD; } else { - enable = APC_CAP_ENABLE; + enable = APC_CDMA_GO; dirty = APC_CD; } @@ -301,7 +232,7 @@ /* * Program the first fragment. */ - apc_load_fragment(eng); + apc_reload(eng); /* * Start the DMA engine, including interrupts. @@ -309,9 +240,9 @@ OR_SET_WORD(handle, &APC_DMACSR, enable); /* - * Program the second fragment. + * Program the double buffering. */ - apc_load_fragment(eng); + apc_reload(eng); return (DDI_SUCCESS); } @@ -338,7 +269,6 @@ CS_state_t *state = eng->ce_state; ddi_acc_handle_t handle = APC_HANDLE; uint32_t reg; - uint32_t intren; uint32_t abort; uint32_t drainbit; uint32_t disable; @@ -346,20 +276,15 @@ ASSERT(mutex_owned(&state->cs_lock)); if (eng->ce_num == CS4231_PLAY) { - intren = APC_PINTR_ENABLE; abort = APC_P_ABORT; drainbit = APC_PM; disable = APC_PLAY_DISABLE; } else { - intren = APC_CINTR_ENABLE; abort = APC_C_ABORT; drainbit = APC_CX; disable = APC_CAP_DISABLE; } - /* clear the interrupts so the ISR doesn't get involved */ - AND_SET_WORD(handle, &APC_DMACSR, ~intren); - /* first, abort the DMA engine */ OR_SET_WORD(handle, &APC_DMACSR, abort); @@ -391,9 +316,6 @@ * Arguments: * CS_state_t *state Ptr to the device's state structure * int level Power level to set - * - * Returns: - * void */ static void apc_power(CS_state_t *state, int level) @@ -417,119 +339,8 @@ } /* apc_power() */ -/* ******* Local Routines ************************************************** */ - -/* - * apc_intr() - * - * Description: - * APC interrupt service routine, which services both play and capture - * interrupts. First we find out why there was an interrupt, then we - * take the appropriate action. - * - * Because this ISR deals with both play and record interrupts we have - * to be careful to not lose an interrupt. So we service the record - * interrupt first and save the incoming data until later. This is all - * done without releasing the lock, thus there can be no race conditions. - * Then we process the play interrupt. While processing the play interrupt - * we have to release the lock. When this happens we send recorded data - * to the mixer and then get the next chunk of data to play. If there - * wasn't a play interrupt then we finish by sending the recorded data, - * if any. - * - * Arguments: - * caddr_t T Pointer to the interrupting device's state - * structure - * - * Returns: - * DDI_INTR_CLAIMED Interrupt claimed and processed - * DDI_INTR_UNCLAIMED Interrupt not claimed, and thus ignored - */ -static uint_t -apc_intr(caddr_t T) -{ - CS_state_t *state = (void *)T; - ddi_acc_handle_t handle = APC_HANDLE; - uint_t csr; - int rc = DDI_INTR_UNCLAIMED; - - /* the state must be protected */ - mutex_enter(&state->cs_lock); - - if (state->cs_suspended) { - mutex_exit(&state->cs_lock); - return (DDI_INTR_UNCLAIMED); - } - - /* get the APC CSR */ - csr = ddi_get32(handle, &APC_DMACSR); - - /* make sure this device sent the interrupt */ - if (!(csr & APC_IP)) { - if (csr & APC_PMI_EN) { - /* - * Clear device generated interrupt while play is - * active (Only seen while playing and insane mode - * switching) - */ - mutex_exit(&state->cs_lock); - return (DDI_INTR_CLAIMED); - } else { - /* nope, this isn't our interrupt */ - mutex_exit(&state->cs_lock); - return (DDI_INTR_UNCLAIMED); - } - } - - /* clear all interrupts we captured this time */ - ddi_put32(handle, &APC_DMACSR, csr); - - if (csr & APC_CINTR_MASK) { - /* try to load the next record buffer */ - apc_load_fragment(state->cs_engines[CS4231_REC]); - rc = DDI_INTR_CLAIMED; - } - - if (csr & APC_PINTR_MASK) { - /* try to load the next play buffer */ - apc_load_fragment(state->cs_engines[CS4231_PLAY]); - rc = DDI_INTR_CLAIMED; - } - -done: - - /* APC error interrupt, not sure what to do here */ - if (csr & APC_EI) { - audio_dev_warn(state->cs_adev, "error interrupt: 0x%x", csr); - rc = DDI_INTR_CLAIMED; - } - - /* update the kernel interrupt statistics */ - if (state->cs_ksp) { - if (rc == DDI_INTR_CLAIMED) { - KIOP(state)->intrs[KSTAT_INTR_HARD]++; - } - } - - mutex_exit(&state->cs_lock); - - if (csr & APC_CINTR_MASK) { - CS_engine_t *eng = state->cs_engines[CS4231_REC]; - if (eng->ce_started) - audio_engine_produce(eng->ce_engine); - } - if (csr & APC_PINTR_MASK) { - CS_engine_t *eng = state->cs_engines[CS4231_PLAY]; - if (eng->ce_started) - audio_engine_consume(eng->ce_engine); - } - - return (rc); - -} /* apc_intr() */ - static void -apc_load_fragment(CS_engine_t *eng) +apc_reload(CS_engine_t *eng) { CS_state_t *state = eng->ce_state; ddi_acc_handle_t handle = APC_HANDLE; @@ -556,12 +367,40 @@ (void) ddi_get32(handle, nva); /* write the address of the next fragment */ - ddi_put32(handle, nva, eng->ce_paddr[eng->ce_cfrag]); + ddi_put32(handle, nva, + eng->ce_paddr + (CS4231_FRAGSZ * eng->ce_curidx)); + eng->ce_curidx++; + eng->ce_curidx %= CS4231_NFRAGS; /* now program the NC reg., which enables the state machine */ - ddi_put32(handle, nc, eng->ce_fragsz); + ddi_put32(handle, nc, CS4231_FRAGSZ); +} - eng->ce_cfrag++; - eng->ce_cfrag %= CS4231_NFRAGS; - eng->ce_count += eng->ce_fragfr; +/* + * apc_addr() + * + * Description: + * This routine returns the current DMA address for the engine (the + * next address being accessed). + * + * Arguments: + * CS_engine_t *eng The engine + * + * Returns: + * Physical DMA address for current transfer. + */ +static uint32_t +apc_addr(CS_engine_t *eng) +{ + CS_state_t *state = eng->ce_state; + ddi_acc_handle_t handle = APC_HANDLE; + uint32_t *va; /* VA reg */ + + if (eng->ce_num == CS4231_PLAY) { + va = &APC_DMAPVA; + } else { + va = &APC_DMACVA; + } + + return (ddi_get32(handle, va)); }
--- a/usr/src/uts/sun/io/audio/drv/audiocs/audio_4231_eb2dma.c Tue Mar 16 09:43:38 2010 -0600 +++ b/usr/src/uts/sun/io/audio/drv/audiocs/audio_4231_eb2dma.c Tue Mar 16 09:30:41 2010 -0700 @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -67,22 +67,16 @@ }; /* - * Local routines - */ -static uint_t eb2_intr(caddr_t); -static void eb2_load_fragment(CS_engine_t *); - -/* * DMA ops vector functions */ static int eb2_map_regs(CS_state_t *); static void eb2_unmap_regs(CS_state_t *); static void eb2_reset(CS_state_t *); -static int eb2_add_intr(CS_state_t *); -static void eb2_rem_intr(CS_state_t *); static int eb2_start_engine(CS_engine_t *); static void eb2_stop_engine(CS_engine_t *); static void eb2_power(CS_state_t *, int); +static void eb2_reload(CS_engine_t *); +static uint32_t eb2_addr(CS_engine_t *); cs4231_dma_ops_t cs4231_eb2dma_ops = { "EB2 DMA controller", @@ -90,11 +84,11 @@ eb2_map_regs, eb2_unmap_regs, eb2_reset, - eb2_add_intr, - eb2_rem_intr, eb2_start_engine, eb2_stop_engine, eb2_power, + eb2_reload, + eb2_addr, }; /* @@ -177,9 +171,6 @@ * * Arguments: * CS_state_t *state The device's state - * - * Returns: - * void */ static void eb2_unmap_regs(CS_state_t *state) @@ -205,9 +196,6 @@ * Arguments: * dev_info_t *dip Pointer to the device's devinfo structure * CS_state_t *state The device's state structure - * - * Returns: - * void */ static void eb2_reset(CS_state_t *state) @@ -242,80 +230,6 @@ } /* eb2_reset() */ /* - * eb2_add_intr() - * - * Description: - * Register the EB2 interrupts with the kernel. - * - * NOTE: This does NOT turn on interrupts. - * - * CAUTION: While the interrupts are added, the Codec interrupts are - * not enabled. - * - * Arguments: - * CS_state_t *state Pointer to the device's state structure - * - * Returns: - * DDI_SUCCESS Interrupts added - * DDI_FAILURE Interrupts not added - */ -static int -eb2_add_intr(CS_state_t *state) -{ - dev_info_t *dip = state->cs_dip; - - /* first we make sure these aren't high level interrupts */ - if (ddi_intr_hilevel(dip, 0) != 0) { - audio_dev_warn(state->cs_adev, "unsupported hi level intr 0"); - return (DDI_FAILURE); - } - if (ddi_intr_hilevel(dip, 1) != 0) { - audio_dev_warn(state->cs_adev, "unsupported hi level intr 1"); - return (DDI_FAILURE); - } - - /* okay to register the interrupts */ - if (ddi_add_intr(dip, 0, NULL, NULL, eb2_intr, - (caddr_t)state->cs_engines[CS4231_REC]) != DDI_SUCCESS) { - audio_dev_warn(state->cs_adev, "bad record interrupt spec"); - return (DDI_FAILURE); - } - - if (ddi_add_intr(dip, 1, NULL, NULL, eb2_intr, - (caddr_t)state->cs_engines[CS4231_PLAY]) != DDI_SUCCESS) { - audio_dev_warn(state->cs_adev, "play interrupt spec"); - ddi_remove_intr(dip, 0, NULL); - return (DDI_FAILURE); - } - - return (DDI_SUCCESS); - -} /* eb2_add_intr() */ - -/* - * eb2_rem_intr() - * - * Description: - * Unregister the EB2 interrupts from the kernel. - * - * CAUTION: While the interrupts are removed, the Codec interrupts are - * not disabled, but then, they never should have been on in - * the first place. - * - * Arguments: - * CS_state_t *state Pointer to the device's soft state - * - * Returns: - * void - */ -static void -eb2_rem_intr(CS_state_t *state) -{ - ddi_remove_intr(state->cs_dip, 0, NULL); - ddi_remove_intr(state->cs_dip, 1, NULL); -} /* eb2_rem_intr() */ - -/* * eb2_start_engine() * * Description: @@ -375,7 +289,7 @@ /* * Program the DMA engine. */ - eb2_load_fragment(eng); + eb2_reload(eng); /* * Start playing before we load the next fragment. @@ -383,9 +297,9 @@ OR_SET_WORD(handle, ®s->eb2csr, enable); /* - * Program a 2nd fragment. + * Program the next address, too. */ - eb2_load_fragment(eng); + eb2_reload(eng); return (DDI_SUCCESS); @@ -401,9 +315,6 @@ * * Arguments: * CS_engine_t *eng The engine to stop - * - * Returns: - * void */ static void eb2_stop_engine(CS_engine_t *eng) @@ -449,9 +360,6 @@ * Arguments: * CS_state_t *state Ptr to the device's state structure * int level Power level to set - * - * Returns: - * void */ static void eb2_power(CS_state_t *state, int level) @@ -466,90 +374,18 @@ } /* eb2_power() */ - -/* ******* Local Routines ************************************************** */ - /* - * eb2_intr() + * eb2_reload() * * Description: - * EB2 interrupt serivce routine. First we find out why there was an - * interrupt, then we take the appropriate action. + * This routine reloads the DMA address, so that we can continue + * double buffer round-robin fashion. * * Arguments: - * caddr_t T Pointer to the interrupting device's state - * structure - * - * Returns: - * DDI_INTR_CLAIMED Interrupt claimed and processed - * DDI_INTR_UNCLAIMED Interrupt not claimed, and thus ignored + * CS_engine_t *eng The engine */ -static uint_t -eb2_intr(caddr_t T) -{ - CS_engine_t *eng = (void *)T; - CS_state_t *state = eng->ce_state; - cs4231_eb2regs_t *regs = eng->ce_eb2regs; - ddi_acc_handle_t handle = eng->ce_regsh; - uint32_t csr; - boolean_t doit = B_FALSE; - - /* the state must be protected */ - mutex_enter(&state->cs_lock); - if (state->cs_suspended) { - mutex_exit(&state->cs_lock); - return (DDI_INTR_UNCLAIMED); - } - - /* get the EB2 CSR */ - csr = ddi_get32(handle, ®s->eb2csr); - - /* make sure this device sent the interrupt */ - if (!(csr & EB2_INT_PEND)) { - mutex_exit(&state->cs_lock); - /* nope, this isn't our interrupt */ - return (DDI_INTR_UNCLAIMED); - } - - /* clear all interrupts we captured at this time */ - ddi_put32(handle, ®s->eb2csr, (csr|EB2_TC)); - - if (csr & EB2_TC) { - - /* try to load the next audio buffer */ - eb2_load_fragment(eng); - - /* if engine was started, then we want to consume later */ - doit = eng->ce_started; - - } else if (csr & EB2_ERR_PEND) { - audio_dev_warn(state->cs_adev, "error intr: 0x%x", csr); - - } else { - audio_dev_warn(state->cs_adev, "unknown intr: 0x%x", csr); - } - - /* update the kernel interrupt statisitcs */ - if (state->cs_ksp) { - KIOP(state)->intrs[KSTAT_INTR_HARD]++; - } - - mutex_exit(&state->cs_lock); - - if (doit) { - if (eng->ce_num == CS4231_PLAY) { - audio_engine_consume(eng->ce_engine); - } else { - audio_engine_produce(eng->ce_engine); - } - } - - return (DDI_INTR_CLAIMED); - -} /* eb2_intr() */ - static void -eb2_load_fragment(CS_engine_t *eng) +eb2_reload(CS_engine_t *eng) { ddi_acc_handle_t handle = eng->ce_regsh; cs4231_eb2regs_t *regs = eng->ce_eb2regs; @@ -562,12 +398,34 @@ /* * For eb2 we first program the Next Byte Count Register. */ - ddi_put32(handle, ®s->eb2bcr, eng->ce_fragsz); + ddi_put32(handle, ®s->eb2bcr, CS4231_FRAGSZ); /* now program the Next Address Register */ - ddi_put32(handle, ®s->eb2acr, eng->ce_paddr[eng->ce_cfrag]); + ddi_put32(handle, ®s->eb2acr, + eng->ce_paddr + (CS4231_FRAGSZ * eng->ce_curidx)); + + eng->ce_curidx++; + eng->ce_curidx %= CS4231_NFRAGS; +} - eng->ce_cfrag++; - eng->ce_cfrag %= CS4231_NFRAGS; - eng->ce_count += eng->ce_fragfr; +/* + * eb2_addr() + * + * Description: + * This routine returns the current DMA address for the engine (the + * next address being accessed). + * + * Arguments: + * CS_engine_t *eng The engine + * + * Returns: + * Physical DMA address for current transfer. + */ +static uint32_t +eb2_addr(CS_engine_t *eng) +{ + ddi_acc_handle_t handle = eng->ce_regsh; + cs4231_eb2regs_t *regs = eng->ce_eb2regs; + + return (ddi_get32(handle, ®s->eb2acr)); }
--- a/usr/src/uts/sun/io/audio/drv/audiocs/audiocs.conf Tue Mar 16 09:43:38 2010 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# Configuration file for the audiocs audio driver. -# -# WARNING: This is an UNSTABLE configuration file. Its contents -# may change at any time. -# - -# -# play-interrupts sets the number of interrupts per second when playing. -# This affects the resolution of various things, such as sample counts. -# record-interrupts does the same for record interrupts. -# -# These may be tuned to get more accurate information by increasing the -# count. However, the larger the interrupt rate the larger the load on -# the system. So use this cautiously. The audiocs driver enforces a -# maximum and minimum count. -# -# It should also be understood that not all interrupt rates are legal. -# The hardware is restricted to DMA buffers being allocated on certain -# boundaries. If those boundaries are violated the driver will not be -# loaded and an error message is entered into the messages log -# -play-interrupts=175; -record-interrupts=175;