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);
}
Exemple #4
0
/*
 * 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);
}
Exemple #9
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);
}
Exemple #15
0
/*
 * 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);
}
Exemple #17
0
/*
 * 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);
}
Exemple #18
0
/*
 * 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);
}
Exemple #19
0
/*
 * 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);
}