static int audio_stropen(queue_t *rq, dev_t *devp, int oflag, int sflag, cred_t *credp) { int rv; audio_client_t *c; if (sflag != 0) { /* no direct clone or module opens */ return (ENXIO); } /* * Make sure its a STREAMS personality - only legacy Sun API uses * STREAMS. */ switch (AUDIO_MN_TYPE_MASK & getminor(*devp)) { case AUDIO_MINOR_DEVAUDIO: case AUDIO_MINOR_DEVAUDIOCTL: break; default: return (ENOSTR); } if ((c = auimpl_client_create(*devp)) == NULL) { audio_dev_warn(NULL, "client create failed"); return (ENXIO); } rq->q_ptr = WR(rq)->q_ptr = c; c->c_omode = oflag; c->c_pid = ddi_get_pid(); c->c_cred = credp; c->c_rq = rq; c->c_wq = WR(rq); /* * Call client/personality specific open handler. Note that * we "insist" that there is an open. The personality layer * will initialize/allocate any engines required. * * Hmm... do we need to pass in the cred? */ if ((rv = c->c_open(c, oflag)) != 0) { audio_dev_warn(c->c_dev, "open failed (rv %d)", rv); auimpl_client_destroy(c); return (rv); } /* we do device cloning! */ *devp = makedevice(c->c_major, c->c_minor); qprocson(rq); /* now we can receive upcalls */ auimpl_client_activate(c); atomic_inc_uint(&c->c_dev->d_serial); return (0); }
/* * audioixp_chip_init() * * Description: * This routine initializes ATI IXP audio controller and the AC97 * codec. * * Arguments: * audioixp_state_t *state The device's state structure * * Returns: * DDI_SUCCESS The hardware was initialized properly * DDI_FAILURE The hardware couldn't be initialized properly */ static int audioixp_chip_init(audioixp_state_t *statep) { /* * 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); /* AC97 reset */ if (audioixp_reset_ac97(statep) != DDI_SUCCESS) { audio_dev_warn(statep->adev, "AC97 codec reset failed"); return (DDI_FAILURE); } if (audioixp_codec_ready(statep) != DDI_SUCCESS) { audio_dev_warn(statep->adev, "AC97 codec not ready"); 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() */
/* * audioixp_rd97() * * Description: * Get the specific AC97 Codec register. * * Arguments: * void *arg The device's state structure * uint8_t reg AC97 register number * * Returns: * Register value. */ static uint16_t audioixp_rd97(void *arg, uint8_t reg) { audioixp_state_t *statep = arg; uint32_t value; uint32_t result; if (audioixp_codec_sync(statep) != DDI_SUCCESS) return (0xffff); value = IXP_AUDIO_OUT_PHY_PRIMARY_CODEC | IXP_AUDIO_OUT_PHY_READ | IXP_AUDIO_OUT_PHY_EN | ((unsigned)reg << IXP_AUDIO_OUT_PHY_ADDR_SHIFT); PUT32(IXP_AUDIO_OUT_PHY_ADDR_DATA, value); if (audioixp_codec_sync(statep) != DDI_SUCCESS) return (0xffff); for (int i = 0; i < 300; i++) { result = GET32(IXP_AUDIO_IN_PHY_ADDR_DATA); if (result & IXP_AUDIO_IN_PHY_READY) { return (result >> IXP_AUDIO_IN_PHY_DATA_SHIFT); } drv_usecwait(10); } done: audio_dev_warn(statep->adev, "time out reading codec reg %d", reg); return (0xffff); }
/* * audio1575_resume() * * Description: * Resume operation of the device after sleeping or hibernating. * Note that this should never fail, even if hardware goes wonky, * because the current PM framework will panic if it does. * * Arguments: * dev_info_t *dip Pointer to the device's dev_info struct * * Returns: * DDI_SUCCESS The driver was resumed */ static int audio1575_resume(dev_info_t *dip) { audio1575_state_t *statep; audio_dev_t *adev; /* we've already allocated the state structure so get ptr */ statep = ddi_get_driver_private(dip); adev = statep->adev; ASSERT(!mutex_owned(&statep->lock)); if (audio1575_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. */ audio_dev_warn(adev, "DDI_RESUME failed to init chip"); return (DDI_SUCCESS); } /* allow ac97 operations again */ ac97_reset(statep->ac97); audio_dev_resume(adev); 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"); 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_map_regs() * * Description: * The registers are mapped in. * * Arguments: * audioixp_state_t *state The device's state structure * * Returns: * DDI_SUCCESS Registers successfully mapped * DDI_FAILURE Registers not successfully mapped */ static int audioixp_map_regs(audioixp_state_t *statep) { dev_info_t *dip = statep->dip; /* map PCI config space */ if (pci_config_setup(statep->dip, &statep->pcih) == DDI_FAILURE) { audio_dev_warn(statep->adev, "unable to map PCI config space"); return (DDI_FAILURE); } /* map audio mixer register */ if ((ddi_regs_map_setup(dip, IXP_IO_AM_REGS, &statep->regsp, 0, 0, &dev_attr, &statep->regsh)) != DDI_SUCCESS) { audio_dev_warn(statep->adev, "unable to map audio registers"); return (DDI_FAILURE); } return (DDI_SUCCESS); }
static int audio_open(dev_t *devp, int oflag, int otyp, cred_t *credp) { int rv; audio_client_t *c; if (otyp == OTYP_BLK) { return (ENXIO); } if ((c = auimpl_client_create(*devp)) == NULL) { audio_dev_warn(NULL, "client create failed"); return (ENXIO); } c->c_omode = oflag; c->c_pid = ddi_get_pid(); c->c_cred = credp; /* * Call client/personality specific open handler. Note that * we "insist" that there is an open. The personality layer * will initialize/allocate any engines required. * * Hmm... do we need to pass in the cred? */ if ((rv = c->c_open(c, oflag)) != 0) { audio_dev_warn(c->c_dev, "open failed (rv %d)", rv); auimpl_client_destroy(c); return (rv); } /* we do device cloning! */ *devp = makedevice(c->c_major, c->c_minor); /* now we can receive upcalls */ auimpl_client_activate(c); atomic_inc_uint(&c->c_dev->d_serial); return (0); }
static int audio_close(dev_t dev, int flag, int otyp, cred_t *credp) { audio_client_t *c; audio_dev_t *d; _NOTE(ARGUNUSED(flag)); _NOTE(ARGUNUSED(credp)); _NOTE(ARGUNUSED(otyp)); if ((c = auclnt_hold_by_devt(dev)) == NULL) { audio_dev_warn(NULL, "close on bogus devt %x,%x", getmajor(dev), getminor(dev)); return (ENXIO); } /* we don't want any upcalls anymore */ auimpl_client_deactivate(c); /* * Pick up any data sitting around in input buffers. This * avoids leaving record data stuck in queues. */ if (c->c_istream.s_engine != NULL) audio_engine_produce(c->c_istream.s_engine); /* get a local hold on the device */ d = c->c_dev; auimpl_dev_hold(c->c_dev); /* * NB: This must be done before c->c_close, since it calls * auclnt_close which will block waiting for the refence count * to drop to zero. */ auclnt_release(c); /* Call personality specific close handler */ c->c_close(c); auimpl_client_destroy(c); /* notify peers that a change has occurred */ atomic_inc_uint(&d->d_serial); /* now we can drop the release we had on the device */ auimpl_dev_release(d); return (0); }
/* * audio1575_map_regs() * * Description: * The registers are mapped in. * * Arguments: * dev_info_t *dip Pointer to the device's devinfo * * Returns: * DDI_SUCCESS Registers successfully mapped * DDI_FAILURE Registers not successfully mapped */ static int audio1575_map_regs(audio1575_state_t *statep) { dev_info_t *dip = statep->dip; /* map the M1575 Audio PCI Cfg Space */ if (pci_config_setup(dip, &statep->pcih) != DDI_SUCCESS) { audio_dev_warn(statep->adev, "PCI config map failure"); goto error; } /* map the M1575 Audio registers in PCI IO Space */ if ((ddi_regs_map_setup(dip, M1575_AUDIO_IO_SPACE, &statep->regsp, 0, 0, &dev_attr, &statep->regsh)) != DDI_SUCCESS) { audio_dev_warn(statep->adev, "Audio IO mapping failure"); goto error; } return (DDI_SUCCESS); error: audio1575_unmap_regs(statep); return (DDI_FAILURE); }
/* * audioixp_update_port() * * Description: * This routine updates the ports frame counter from hardware, and * gracefully handles wraps. * * Arguments: * audioixp_port_t *port The port to update. */ static void audioixp_update_port(audioixp_port_t *port) { audioixp_state_t *statep = port->statep; unsigned regoff; unsigned n; int loop; uint32_t offset; uint32_t paddr; if (statep->suspended) { return; } if (port->num == IXP_REC) { regoff = IXP_AUDIO_IN_DMA_DT_CUR; } else { regoff = IXP_AUDIO_OUT_DMA_DT_CUR; } /* * Apparently it may take several tries to get an update on the * position. Is this a hardware bug? */ for (loop = 100; loop; loop--) { paddr = GET32(regoff); /* make sure address is reasonable */ if ((paddr < port->samp_paddr) || (paddr >= (port->samp_paddr + port->samp_size))) { continue; } offset = paddr - port->samp_paddr; if (offset >= port->offset) { n = offset - port->offset; } else { n = offset + (port->samp_size - port->offset); } port->offset = offset; port->count += (n / (port->nchan * sizeof (uint16_t))); return; } audio_dev_warn(statep->adev, "Unable to update count (h/w bug?)"); }
/* * audioixp_codec_sync() * * Description: * Serialize access to the AC97 audio mixer registers. * * Arguments: * audioixp_state_t *state The device's state structure * * Returns: * DDI_SUCCESS Ready for an I/O access to the codec * DDI_FAILURE An I/O access is currently in progress, can't * perform another I/O access. */ static int audioixp_codec_sync(audioixp_state_t *statep) { int i; uint32_t cmd; for (i = 0; i < 300; i++) { cmd = GET32(IXP_AUDIO_OUT_PHY_ADDR_DATA); if (!(cmd & IXP_AUDIO_OUT_PHY_EN)) { return (DDI_SUCCESS); } drv_usecwait(10); } audio_dev_warn(statep->adev, "unable to synchronize codec"); return (DDI_FAILURE); }
/* * audioixp_codec_ready() * * Description: * This routine checks the state of codecs. It checks the flag to confirm * that primary codec is ready. * * Arguments: * audioixp_state_t *state The device's state structure * * Returns: * DDI_SUCCESS codec is ready * DDI_FAILURE codec is not ready */ static int audioixp_codec_ready(audioixp_state_t *statep) { uint32_t sr; PUT32(IXP_AUDIO_INT, 0xffffffff); drv_usecwait(1000); sr = GET32(IXP_AUDIO_INT); if (sr & IXP_AUDIO_INT_CODEC0_NOT_READY) { PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_CODEC0_NOT_READY); audio_dev_warn(statep->adev, "primary codec not ready"); return (DDI_FAILURE); } return (DDI_SUCCESS); }
/* * audioixp_reset_ac97() * * Description: * Reset AC97 Codec register. * * Arguments: * audioixp_state_t *state The device's state structure * * Returns: * DDI_SUCCESS Reset the codec successfully * DDI_FAILURE Failed to reset the codec */ static int audioixp_reset_ac97(audioixp_state_t *statep) { uint32_t cmd; int i; CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_POWER_DOWN); drv_usecwait(10); /* register reset */ SET32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_AC_SOFT_RESET); /* force a read to flush caches */ (void) GET32(IXP_AUDIO_CMD); drv_usecwait(10); CLR32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_AC_SOFT_RESET); /* cold reset */ for (i = 0; i < 300; i++) { cmd = GET32(IXP_AUDIO_CMD); if (cmd & IXP_AUDIO_CMD_AC_ACTIVE) { cmd |= IXP_AUDIO_CMD_AC_RESET | IXP_AUDIO_CMD_AC_SYNC; PUT32(IXP_AUDIO_CMD, cmd); return (DDI_SUCCESS); } cmd &= ~IXP_AUDIO_CMD_AC_RESET; cmd |= IXP_AUDIO_CMD_AC_SYNC; PUT32(IXP_AUDIO_CMD, cmd); (void) GET32(IXP_AUDIO_CMD); drv_usecwait(10); cmd |= IXP_AUDIO_CMD_AC_RESET; PUT32(IXP_AUDIO_CMD, cmd); drv_usecwait(10); } audio_dev_warn(statep->adev, "AC'97 reset timed out"); return (DDI_FAILURE); }
/* * audioixp_alloc_port() * * Description: * This routine allocates the DMA handles and the memory for the * DMA engines to use. It also configures the BDL lists properly * for use. * * Arguments: * dev_info_t *dip Pointer to the device's devinfo * * Returns: * DDI_SUCCESS Registers successfully mapped * DDI_FAILURE Registers not successfully mapped */ static int audioixp_alloc_port(audioixp_state_t *statep, int num) { ddi_dma_cookie_t cookie; uint_t count; int dir; unsigned caps; char *prop; audio_dev_t *adev; audioixp_port_t *port; uint32_t paddr; int rc; dev_info_t *dip; audioixp_bd_entry_t *bdentry; adev = statep->adev; dip = statep->dip; port = kmem_zalloc(sizeof (*port), KM_SLEEP); port->statep = statep; port->started = B_FALSE; port->num = num; 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; port->nchan = 2; 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; /* This could possibly be conditionalized */ port->nchan = 6; break; default: audio_dev_warn(adev, "bad port number (%d)!", num); 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->fragsz = port->fragfr * port->nchan * 2; port->samp_size = port->fragsz * IXP_BD_NUMS; /* allocate dma handle */ rc = ddi_dma_alloc_handle(dip, &sample_buf_dma_attr, DDI_DMA_SLEEP, NULL, &port->samp_dmah); if (rc != DDI_SUCCESS) { audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc); return (DDI_FAILURE); } /* allocate DMA buffer */ rc = ddi_dma_mem_alloc(port->samp_dmah, port->samp_size, &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->samp_kaddr, &port->samp_size, &port->samp_acch); if (rc == DDI_FAILURE) { audio_dev_warn(adev, "dma_mem_alloc failed"); return (DDI_FAILURE); } /* bind DMA buffer */ rc = ddi_dma_addr_bind_handle(port->samp_dmah, NULL, port->samp_kaddr, port->samp_size, dir|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &cookie, &count); if ((rc != DDI_DMA_MAPPED) || (count != 1)) { audio_dev_warn(adev, "ddi_dma_addr_bind_handle failed: %d", rc); return (DDI_FAILURE); } port->samp_paddr = cookie.dmac_address; /* * now, from here we allocate DMA memory for buffer descriptor list. * we allocate adjacent DMA memory for all DMA engines. */ rc = ddi_dma_alloc_handle(dip, &bdlist_dma_attr, DDI_DMA_SLEEP, NULL, &port->bdl_dmah); if (rc != DDI_SUCCESS) { audio_dev_warn(adev, "ddi_dma_alloc_handle(bdlist) failed"); return (DDI_FAILURE); } /* * we allocate all buffer descriptors lists in continuous dma memory. */ port->bdl_size = sizeof (audioixp_bd_entry_t) * IXP_BD_NUMS; rc = ddi_dma_mem_alloc(port->bdl_dmah, port->bdl_size, &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->bdl_kaddr, &port->bdl_size, &port->bdl_acch); if (rc != DDI_SUCCESS) { audio_dev_warn(adev, "ddi_dma_mem_alloc(bdlist) failed"); return (DDI_FAILURE); } rc = ddi_dma_addr_bind_handle(port->bdl_dmah, NULL, port->bdl_kaddr, port->bdl_size, DDI_DMA_WRITE|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &cookie, &count); if ((rc != DDI_DMA_MAPPED) || (count != 1)) { audio_dev_warn(adev, "addr_bind_handle failed"); return (DDI_FAILURE); } port->bdl_paddr = cookie.dmac_address; /* * Wire up the BD list. */ paddr = port->samp_paddr; bdentry = (void *)port->bdl_kaddr; for (int i = 0; i < IXP_BD_NUMS; i++) { /* set base address of buffer */ ddi_put32(port->bdl_acch, &bdentry->buf_base, paddr); ddi_put16(port->bdl_acch, &bdentry->status, 0); ddi_put16(port->bdl_acch, &bdentry->buf_len, port->fragsz / 4); ddi_put32(port->bdl_acch, &bdentry->next, port->bdl_paddr + (((i + 1) % IXP_BD_NUMS) * sizeof (audioixp_bd_entry_t))); paddr += port->fragsz; bdentry++; } (void) ddi_dma_sync(port->bdl_dmah, 0, 0, DDI_DMA_SYNC_FORDEV); port->engine = audio_engine_alloc(&audioixp_engine_ops, caps); if (port->engine == NULL) { audio_dev_warn(adev, "audio_engine_alloc failed"); return (DDI_FAILURE); } audio_engine_set_private(port->engine, port); audio_dev_add_engine(adev, port->engine); return (DDI_SUCCESS); }
/* * audio1575_dma_stop() * * Description: * This routine is used to put each DMA engine into the quiet state. * * Arguments: * audio1575_state_t *statep The device's state structure */ static void audio1575_dma_stop(audio1575_state_t *statep, boolean_t quiesce) { uint32_t intrsr; int i; if (statep->regsh == NULL) { return; } /* pause bus master (needed for the following reset register) */ for (i = 0; i < M1575_LOOP_CTR; i++) { SET32(M1575_DMACR_REG, M1575_DMACR_PAUSE_ALL); if (GET32(M1575_DMACR_REG) & M1575_DMACR_PAUSE_ALL) { break; } drv_usecwait(10); } if (i >= M1575_LOOP_CTR) { if (!quiesce) audio_dev_warn(statep->adev, "failed to stop DMA"); return; } /* Pause bus master (needed for the following reset register) */ PUT8(M1575_PCMICR_REG, 0); PUT8(M1575_PCMOCR_REG, 0); PUT8(M1575_MICICR_REG, 0); PUT8(M1575_CSPOCR_REG, 0); PUT8(M1575_PCMI2CR_RR, 0); PUT8(M1575_MICI2CR_RR, 0); /* Reset the bus master registers for all DMA engines */ PUT8(M1575_PCMICR_REG, M1575_PCMICR_RR); PUT8(M1575_PCMOCR_REG, M1575_PCMOCR_RR); PUT8(M1575_MICICR_REG, M1575_MICICR_RR); PUT8(M1575_CSPOCR_REG, M1575_CSPOCR_RR); PUT8(M1575_PCMI2CR_REG, M1575_PCMI2CR_RR); PUT8(M1575_MICI2CR_REG, M1575_MICI2CR_RR); /* Reset FIFOS */ PUT32(M1575_FIFOCR1_REG, 0x81818181); PUT32(M1575_FIFOCR2_REG, 0x81818181); PUT32(M1575_FIFOCR3_REG, 0x81818181); /* Clear Interrupts */ SET16(M1575_PCMISR_REG, M1575_SR_CLR); SET16(M1575_PCMOSR_REG, M1575_SR_CLR); SET16(M1575_MICISR_REG, M1575_SR_CLR); SET16(M1575_CSPOSR_REG, M1575_SR_CLR); SET16(M1575_PCMI2SR_REG, M1575_SR_CLR); SET16(M1575_MICI2SR_REG, M1575_SR_CLR); /* * clear the interrupt control and status register * READ/WRITE/READ workaround required to flush PCI caches */ PUT32(M1575_INTRCR_REG, 0); (void) GET32(M1575_INTRCR_REG); intrsr = GET32(M1575_INTRSR_REG); PUT32(M1575_INTRSR_REG, (intrsr & M1575_INTR_MASK)); (void) GET32(M1575_INTRSR_REG); }
/* * audioixp_attach() * * Description: * Attach an instance of the audioixp driver. This routine does * the device dependent attach tasks. * * Arguments: * dev_info_t *dip Pointer to the device's dev_info struct * ddi_attach_cmd_t cmd Attach command * * Returns: * DDI_SUCCESS The driver was initialized properly * DDI_FAILURE The driver couldn't be initialized properly */ static int audioixp_attach(dev_info_t *dip) { uint16_t cmdeg; audioixp_state_t *statep; audio_dev_t *adev; uint32_t devid; 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); /* allocate framework audio device */ if ((adev = audio_dev_alloc(dip, 0)) == NULL) { cmn_err(CE_WARN, "!%s%d: unable to allocate audio dev", ddi_driver_name(dip), ddi_get_instance(dip)); goto error; } statep->adev = adev; /* map in the registers */ if (audioixp_map_regs(statep) != DDI_SUCCESS) { audio_dev_warn(adev, "couldn't map registers"); goto error; } /* set device information -- this could be smarter */ devid = ((pci_config_get16(statep->pcih, PCI_CONF_VENID)) << 16) | pci_config_get16(statep->pcih, PCI_CONF_DEVID); name = "ATI AC'97"; switch (devid) { case IXP_PCI_ID_200: rev = "IXP150"; break; case IXP_PCI_ID_300: rev = "SB300"; break; case IXP_PCI_ID_400: if (pci_config_get8(statep->pcih, PCI_CONF_REVID) & 0x80) { rev = "SB450"; } else { rev = "SB400"; } break; case IXP_PCI_ID_SB600: rev = "SB600"; break; default: rev = "Unknown"; break; } audio_dev_set_description(adev, name); audio_dev_set_version(adev, rev); /* allocate port structures */ if ((audioixp_alloc_port(statep, IXP_PLAY) != DDI_SUCCESS) || (audioixp_alloc_port(statep, IXP_REC) != DDI_SUCCESS)) { goto error; } statep->ac97 = ac97_alloc(dip, audioixp_rd97, audioixp_wr97, statep); if (statep->ac97 == NULL) { audio_dev_warn(adev, "failed to allocate ac97 handle"); goto error; } /* set PCI command register */ cmdeg = pci_config_get16(statep->pcih, PCI_CONF_COMM); pci_config_put16(statep->pcih, PCI_CONF_COMM, cmdeg | PCI_COMM_IO | PCI_COMM_MAE); /* 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; } /* initialize the AC'97 part */ if (ac97_init(statep->ac97, adev) != DDI_SUCCESS) { audio_dev_warn(adev, "ac'97 initialization failed"); 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; } ddi_report_dev(dip); return (DDI_SUCCESS); error: audioixp_destroy(statep); return (DDI_FAILURE); }
/* * audio1575_attach() * * Description: * Attach an instance of the audio1575 driver. This routine does the * device dependent attach tasks. When it is completed, it registers * with the audio framework. * * Arguments: * dev_info_t *dip Pointer to the device's dev_info struct * * Returns: * DDI_SUCCESS The driver was initialized properly * DDI_FAILURE The driver couldn't be initialized properly */ static int audio1575_attach(dev_info_t *dip) { audio1575_state_t *statep; audio_dev_t *adev; uint32_t devid; const char *name; const char *rev; int maxch; /* allocate the soft state structure */ statep = kmem_zalloc(sizeof (*statep), KM_SLEEP); ddi_set_driver_private(dip, statep); statep->dip = dip; /* * We want the micboost enabled by default as well. */ (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, AC97_PROP_MICBOOST, 1); /* allocate common audio dev structure */ adev = audio_dev_alloc(dip, 0); if (adev == NULL) { audio_dev_warn(NULL, "unable to allocate audio dev"); goto error; } statep->adev = adev; /* map in the audio registers */ if (audio1575_map_regs(statep) != DDI_SUCCESS) { audio_dev_warn(adev, "couldn't map registers"); goto error; } /* Enable PCI I/O and Memory Spaces */ audio1575_pci_enable(statep); devid = (pci_config_get16(statep->pcih, PCI_CONF_VENID) << 16) | pci_config_get16(statep->pcih, PCI_CONF_DEVID); switch (devid) { case 0x10b95455: name = "Uli M1575 AC'97"; rev = "M5455"; break; default: name = "Uli AC'97"; rev = "Unknown"; break; } /* set device information -- this should check PCI config space */ audio_dev_set_description(adev, name); audio_dev_set_version(adev, rev); statep->ac97 = ac97_alloc(dip, audio1575_read_ac97, audio1575_write_ac97, statep); ASSERT(statep->ac97 != NULL); /* * Override "max-channels" property to prevent configuration * of 4 or 6 (or possibly even 8!) channel audio. The default * is to support as many channels as the hardware can do. */ maxch = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "max-channels", ac97_num_channels(statep->ac97)); if (maxch < 2) { maxch = 2; } statep->maxch = min(maxch, 6) & ~1; /* allocate port structures */ if ((audio1575_alloc_port(statep, M1575_PLAY, statep->maxch) != DDI_SUCCESS) || (audio1575_alloc_port(statep, M1575_REC, 2) != DDI_SUCCESS)) { goto error; } if (audio1575_chip_init(statep) != DDI_SUCCESS) { audio_dev_warn(adev, "failed to init chip"); goto error; } if (ac97_init(statep->ac97, adev) != DDI_SUCCESS) { audio_dev_warn(adev, "ac'97 initialization failed"); goto error; } /* register with the framework */ if (audio_dev_register(adev) != DDI_SUCCESS) { audio_dev_warn(adev, "unable to register with framework"); goto error; } /* everything worked out, so report the device */ ddi_report_dev(dip); return (DDI_SUCCESS); error: audio1575_destroy(statep); return (DDI_FAILURE); }
/* * audio1575_alloc_port() * * Description: * This routine allocates the DMA handles and the memory for the * DMA engines to use. It also configures the BDL lists properly * for use. * * Arguments: * dev_info_t *dip Pointer to the device's devinfo * int num M1575_PLAY or M1575_REC * uint8_t nchan Number of channels (2 = stereo, 6 = 5.1, etc.) * * Returns: * DDI_SUCCESS Registers successfully mapped * DDI_FAILURE Registers not successfully mapped */ static int audio1575_alloc_port(audio1575_state_t *statep, int num, uint8_t nchan) { ddi_dma_cookie_t cookie; uint_t count; int dir; unsigned caps; audio_dev_t *adev; audio1575_port_t *port; uint32_t *kaddr; int rc; dev_info_t *dip; adev = statep->adev; dip = statep->dip; port = kmem_zalloc(sizeof (*port), KM_SLEEP); statep->ports[num] = port; port->num = num; port->statep = statep; port->nchan = nchan; if (num == M1575_REC) { dir = DDI_DMA_READ; caps = ENGINE_INPUT_CAP; port->sync_dir = DDI_DMA_SYNC_FORKERNEL; } else { dir = DDI_DMA_WRITE; caps = ENGINE_OUTPUT_CAP; port->sync_dir = DDI_DMA_SYNC_FORDEV; } /* * 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->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, NULL, &port->samp_dmah); if (rc != DDI_SUCCESS) { audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc); return (DDI_FAILURE); } /* allocate DMA buffer */ rc = ddi_dma_mem_alloc(port->samp_dmah, port->samp_size, &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->samp_kaddr, &port->samp_size, &port->samp_acch); if (rc == DDI_FAILURE) { audio_dev_warn(adev, "dma_mem_alloc failed"); return (DDI_FAILURE); } /* bind DMA buffer */ rc = ddi_dma_addr_bind_handle(port->samp_dmah, NULL, port->samp_kaddr, port->samp_size, dir|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &cookie, &count); if ((rc != DDI_DMA_MAPPED) || (count != 1)) { audio_dev_warn(adev, "ddi_dma_addr_bind_handle failed: %d", rc); return (DDI_FAILURE); } port->samp_paddr = cookie.dmac_address; /* * now, from here we allocate DMA memory for buffer descriptor list. * we allocate adjacent DMA memory for all DMA engines. */ rc = ddi_dma_alloc_handle(dip, &bdlist_dma_attr, DDI_DMA_SLEEP, NULL, &port->bdl_dmah); if (rc != DDI_SUCCESS) { audio_dev_warn(adev, "ddi_dma_alloc_handle(bdlist) failed"); return (DDI_FAILURE); } /* * we allocate all buffer descriptors lists in continuous dma memory. */ port->bdl_size = sizeof (m1575_bd_entry_t) * M1575_BD_NUMS; rc = ddi_dma_mem_alloc(port->bdl_dmah, port->bdl_size, &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->bdl_kaddr, &port->bdl_size, &port->bdl_acch); if (rc != DDI_SUCCESS) { audio_dev_warn(adev, "ddi_dma_mem_alloc(bdlist) failed"); return (DDI_FAILURE); } /* * 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. */ 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, port->samp_paddr); kaddr++; /* set size in frames, and enable IOC interrupt */ ddi_put32(port->bdl_acch, kaddr, ((port->samp_size / sizeof (int16_t)) | (1U << 31))); kaddr++; } rc = ddi_dma_addr_bind_handle(port->bdl_dmah, NULL, port->bdl_kaddr, port->bdl_size, DDI_DMA_WRITE|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &cookie, &count); if ((rc != DDI_DMA_MAPPED) || (count != 1)) { audio_dev_warn(adev, "addr_bind_handle failed"); return (DDI_FAILURE); } port->bdl_paddr = cookie.dmac_address; port->engine = audio_engine_alloc(&audio1575_engine_ops, caps); if (port->engine == NULL) { audio_dev_warn(adev, "audio_engine_alloc failed"); return (DDI_FAILURE); } audio_engine_set_private(port->engine, port); audio_dev_add_engine(adev, port->engine); return (DDI_SUCCESS); }
/* * audio1575_chip_init() * * Description: * This routine initializes the M1575 AC97 audio controller and the AC97 * codec. The AC97 codec registers are programmed from codec_shadow[]. * If we are not doing a restore, we initialize codec_shadow[], otherwise * we use the current values of shadow. This routine expects that the * PCI IO and Memory spaces have been mapped and enabled already. * Arguments: * audio1575_state_t *state The device's state structure * restore from codec_shadow[] * Returns: * DDI_SUCCESS The hardware was initialized properly * DDI_FAILURE The hardware couldn't be initialized properly */ static int audio1575_chip_init(audio1575_state_t *statep) { uint32_t ssr; uint32_t rtsr; uint32_t intrsr; int i; int j; #ifdef __sparc uint8_t clk_detect; ddi_acc_handle_t pcih; #endif clock_t ticks; /* * clear the interrupt control and status register * READ/WRITE/READ workaround required * for buggy hardware */ PUT32(M1575_INTRCR_REG, 0); (void) GET32(M1575_INTRCR_REG); intrsr = GET32(M1575_INTRSR_REG); PUT32(M1575_INTRSR_REG, (intrsr & M1575_INTR_MASK)); (void) GET32(M1575_INTRSR_REG); ticks = drv_usectohz(M1575_LOOP_CTR); /* * SADA only supports stereo, so we set the channel bits * to "00" to select 2 channels. * will also set the following: * * Disable double rate enable * no SPDIF output selected * 16 bit audio record mode * 16 bit pcm out mode * PCM Out 6 chan mode FL FR CEN BL BR LFE * PCM Out 2 channel mode (00) */ for (i = 0; i < M1575_LOOP_CTR; i++) { /* Reset the AC97 Codec and default to 2 channel 16 bit mode */ PUT32(M1575_SCR_REG, M1575_SCR_COLDRST); delay(ticks<<1); /* Read the System Status Reg */ ssr = GET32(M1575_SSR_REG); /* make sure and release the blocked reset bit */ if (ssr & M1575_SSR_RSTBLK) { SET32(M1575_INTFCR_REG, M1575_INTFCR_RSTREL); delay(ticks); /* Read the System Status Reg */ ssr = GET32(M1575_SSR_REG); /* make sure and release the blocked reset bit */ if (ssr & M1575_SSR_RSTBLK) { return (DDI_FAILURE); } /* Reset the controller */ PUT32(M1575_SCR_REG, M1575_SCR_COLDRST); delay(ticks); } /* according AC'97 spec, wait for codec reset */ for (j = 0; j < M1575_LOOP_CTR; j++) { if ((GET32(M1575_SCR_REG) & M1575_SCR_COLDRST) == 0) { break; } delay(ticks); } /* codec reset failed */ if (j >= M1575_LOOP_CTR) { audio_dev_warn(statep->adev, "failure to reset codec"); return (DDI_FAILURE); } /* * Wait for FACRDY First codec ready. The hardware can * provide the state of * codec ready bit on SDATA_IN[0] and as reflected in * the Recv Tag Slot Reg. */ rtsr = GET32(M1575_RTSR_REG); if (rtsr & M1575_RTSR_FACRDY) { break; } else { /* reset the status and wait for new status to set */ rtsr |= M1575_RTSR_FACRDY; PUT32(M1575_RTSR_REG, rtsr); drv_usecwait(10); } } /* if we could not reset the AC97 codec then report failure */ if (i >= M1575_LOOP_CTR) { audio_dev_warn(statep->adev, "no codec ready signal received"); return (DDI_FAILURE); } #ifdef __sparc /* Magic code from ULi to Turn on the AC_LINK clock */ pcih = statep->pcih; pci_config_put8(pcih, M1575_PCIACD_REG, 0); pci_config_put8(pcih, M1575_PCIACD_REG, 4); pci_config_put8(pcih, M1575_PCIACD_REG, 0); (void) pci_config_get8(pcih, M1575_PCIACD_REG); pci_config_put8(pcih, M1575_PCIACD_REG, 2); pci_config_put8(pcih, M1575_PCIACD_REG, 0); clk_detect = pci_config_get8(pcih, M1575_PCIACD_REG); if (clk_detect != 1) { audio_dev_warn(statep->adev, "No AC97 Clock Detected"); return (DDI_FAILURE); } #endif /* Magic code from Uli to Init FIFO1 and FIFO2 */ PUT32(M1575_FIFOCR1_REG, 0x81818181); PUT32(M1575_FIFOCR2_REG, 0x81818181); PUT32(M1575_FIFOCR3_REG, 0x81818181); /* Make sure that PCM in and PCM out are enabled */ SET32(M1575_INTFCR_REG, (M1575_INTFCR_PCMIENB | M1575_INTFCR_PCMOENB)); audio1575_dma_stop(statep, B_FALSE); return (DDI_SUCCESS); }