csapcm.c revision 78673
155320Stanimura/*
253553Stanimura * Copyright (c) 1999 Seigo Tanimura
353553Stanimura * All rights reserved.
453553Stanimura *
554377Stanimura * Portions of this source are based on cwcealdr.cpp and dhwiface.cpp in
654377Stanimura * cwcealdr1.zip, the sample sources by Crystal Semiconductor.
754377Stanimura * Copyright (c) 1996-1998 Crystal Semiconductor Corp.
854377Stanimura *
953553Stanimura * Redistribution and use in source and binary forms, with or without
1053553Stanimura * modification, are permitted provided that the following conditions
1153553Stanimura * are met:
1253553Stanimura * 1. Redistributions of source code must retain the above copyright
1353553Stanimura *    notice, this list of conditions and the following disclaimer.
1453553Stanimura * 2. Redistributions in binary form must reproduce the above copyright
1553553Stanimura *    notice, this list of conditions and the following disclaimer in the
1653553Stanimura *    documentation and/or other materials provided with the distribution.
1753553Stanimura *
1853553Stanimura * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1953553Stanimura * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2053553Stanimura * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2153553Stanimura * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2253553Stanimura * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2353553Stanimura * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2453553Stanimura * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2553553Stanimura * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2653553Stanimura * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2753553Stanimura * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2853553Stanimura * SUCH DAMAGE.
2953553Stanimura *
3053553Stanimura * $FreeBSD: head/sys/dev/sound/pci/csapcm.c 78673 2001-06-23 18:00:06Z cg $
3153553Stanimura */
3253553Stanimura
3353553Stanimura#include <sys/soundcard.h>
3453553Stanimura#include <dev/sound/pcm/sound.h>
3553553Stanimura#include <dev/sound/pcm/ac97.h>
3653553Stanimura#include <dev/sound/chip.h>
3753553Stanimura#include <dev/sound/pci/csareg.h>
3853553Stanimura#include <dev/sound/pci/csavar.h>
3953553Stanimura
4053553Stanimura#include <pci/pcireg.h>
4153553Stanimura#include <pci/pcivar.h>
4253553Stanimura
4377504Scg/* Buffer size on dma transfer. Fixed for CS416x. */
4477504Scg#define CS461x_BUFFSIZE   (4 * 1024)
4577504Scg
4677504Scg#define GOF_PER_SEC 200
4777504Scg
4853553Stanimura/* device private data */
4953553Stanimurastruct csa_info;
5053553Stanimura
5153553Stanimurastruct csa_chinfo {
5253553Stanimura	struct csa_info *parent;
5374763Scg	struct pcm_channel *channel;
5474763Scg	struct snd_dbuf *buffer;
5553553Stanimura	int dir;
5677504Scg	u_int32_t fmt, spd;
5755321Stanimura	int dma;
5853553Stanimura};
5953553Stanimura
6053553Stanimurastruct csa_info {
6153553Stanimura	csa_res		res; /* resource */
6253553Stanimura	void		*ih; /* Interrupt cookie */
6353553Stanimura	bus_dma_tag_t	parent_dmat; /* DMA tag */
6455320Stanimura	struct csa_bridgeinfo *binfo; /* The state of the parent. */
6577504Scg	struct csa_card *card;
6653553Stanimura
6777504Scg	int active;
6853553Stanimura	/* Contents of board's registers */
6953553Stanimura	u_long		pfie;
7053553Stanimura	u_long		pctl;
7153553Stanimura	u_long		cctl;
7253553Stanimura	struct csa_chinfo pch, rch;
7353553Stanimura};
7453553Stanimura
7553553Stanimura/* -------------------------------------------------------------------- */
7653553Stanimura
7753553Stanimura/* prototypes */
7853553Stanimurastatic int      csa_init(struct csa_info *);
7953553Stanimurastatic void     csa_intr(void *);
8053553Stanimurastatic void	csa_setplaysamplerate(csa_res *resp, u_long ulInRate);
8153553Stanimurastatic void	csa_setcapturesamplerate(csa_res *resp, u_long ulOutRate);
8253553Stanimurastatic void	csa_startplaydma(struct csa_info *csa);
8353553Stanimurastatic void	csa_startcapturedma(struct csa_info *csa);
8453553Stanimurastatic void	csa_stopplaydma(struct csa_info *csa);
8553553Stanimurastatic void	csa_stopcapturedma(struct csa_info *csa);
8653553Stanimurastatic int	csa_startdsp(csa_res *resp);
8753553Stanimurastatic int	csa_allocres(struct csa_info *scp, device_t dev);
8853553Stanimurastatic void	csa_releaseres(struct csa_info *scp, device_t dev);
8953553Stanimura
9064881Scgstatic u_int32_t csa_playfmt[] = {
9164881Scg	AFMT_U8,
9264881Scg	AFMT_STEREO | AFMT_U8,
9364881Scg	AFMT_S8,
9464881Scg	AFMT_STEREO | AFMT_S8,
9564881Scg	AFMT_S16_LE,
9664881Scg	AFMT_STEREO | AFMT_S16_LE,
9764881Scg	AFMT_S16_BE,
9864881Scg	AFMT_STEREO | AFMT_S16_BE,
9964881Scg	0
10053553Stanimura};
10174763Scgstatic struct pcmchan_caps csa_playcaps = {8000, 48000, csa_playfmt, 0};
10253553Stanimura
10364881Scgstatic u_int32_t csa_recfmt[] = {
10464881Scg	AFMT_S16_LE,
10553553Stanimura	AFMT_STEREO | AFMT_S16_LE,
10664881Scg	0
10753553Stanimura};
10874763Scgstatic struct pcmchan_caps csa_reccaps = {11025, 48000, csa_recfmt, 0};
10953553Stanimura
11053553Stanimura/* -------------------------------------------------------------------- */
11177504Scg
11277504Scgstatic int
11377504Scgcsa_active(struct csa_info *csa, int run)
11477504Scg{
11577504Scg	int old, go;
11677504Scg
11777504Scg	old = csa->active;
11877504Scg	csa->active += run;
11977504Scg
12077504Scg	if ((csa->active == 0 && old == 1) || (csa->active == 1 && old == 0)) {
12177504Scg		go = csa->active;
12277504Scg		if (csa->card->active)
12377504Scg			return csa->card->active(go);
12477504Scg	}
12577504Scg	return 0;
12677504Scg}
12777504Scg
12877504Scg/* -------------------------------------------------------------------- */
12970134Scg/* ac97 codec */
13053553Stanimura
13153553Stanimurastatic int
13270134Scgcsa_rdcd(kobj_t obj, void *devinfo, int regno)
13353553Stanimura{
13470134Scg	u_int32_t data;
13570134Scg	struct csa_info *csa = (struct csa_info *)devinfo;
13653553Stanimura
13777504Scg	csa_active(csa, 1);
13870134Scg	if (csa_readcodec(&csa->res, regno + BA0_AC97_RESET, &data))
13970134Scg		data = 0;
14077504Scg	csa_active(csa, -1);
14153553Stanimura
14270134Scg	return data;
14353553Stanimura}
14453553Stanimura
14553553Stanimurastatic int
14670134Scgcsa_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
14753553Stanimura{
14870134Scg	struct csa_info *csa = (struct csa_info *)devinfo;
14953553Stanimura
15077504Scg	csa_active(csa, 1);
15170134Scg	csa_writecodec(&csa->res, regno + BA0_AC97_RESET, data);
15277504Scg	csa_active(csa, -1);
15353553Stanimura
15453553Stanimura	return 0;
15553553Stanimura}
15653553Stanimura
15770134Scgstatic kobj_method_t csa_ac97_methods[] = {
15870134Scg    	KOBJMETHOD(ac97_read,		csa_rdcd),
15970134Scg    	KOBJMETHOD(ac97_write,		csa_wrcd),
16070134Scg	{ 0, 0 }
16170134Scg};
16270134ScgAC97_DECLARE(csa_ac97);
16353553Stanimura
16453553Stanimurastatic void
16553553Stanimuracsa_setplaysamplerate(csa_res *resp, u_long ulInRate)
16653553Stanimura{
16753553Stanimura	u_long ulTemp1, ulTemp2;
16853553Stanimura	u_long ulPhiIncr;
16953553Stanimura	u_long ulCorrectionPerGOF, ulCorrectionPerSec;
17053553Stanimura	u_long ulOutRate;
17153553Stanimura
17253553Stanimura	ulOutRate = 48000;
17353553Stanimura
17453553Stanimura	/*
17553553Stanimura	 * Compute the values used to drive the actual sample rate conversion.
17653553Stanimura	 * The following formulas are being computed, using inline assembly
17753553Stanimura	 * since we need to use 64 bit arithmetic to compute the values:
17853553Stanimura	 *
17953553Stanimura	 *     ulPhiIncr = floor((Fs,in * 2^26) / Fs,out)
18053553Stanimura	 *     ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) /
18153553Stanimura	 *                                GOF_PER_SEC)
18253553Stanimura	 *     ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
18353553Stanimura	 *                          GOF_PER_SEC * ulCorrectionPerGOF
18453553Stanimura	 *
18553553Stanimura	 * i.e.
18653553Stanimura	 *
18753553Stanimura	 *     ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
18853553Stanimura	 *     ulCorrectionPerGOF:ulCorrectionPerSec =
18953553Stanimura	 *         dividend:remainder(ulOther / GOF_PER_SEC)
19053553Stanimura	 */
19153553Stanimura	ulTemp1 = ulInRate << 16;
19253553Stanimura	ulPhiIncr = ulTemp1 / ulOutRate;
19353553Stanimura	ulTemp1 -= ulPhiIncr * ulOutRate;
19453553Stanimura	ulTemp1 <<= 10;
19553553Stanimura	ulPhiIncr <<= 10;
19653553Stanimura	ulTemp2 = ulTemp1 / ulOutRate;
19753553Stanimura	ulPhiIncr += ulTemp2;
19853553Stanimura	ulTemp1 -= ulTemp2 * ulOutRate;
19953553Stanimura	ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC;
20053553Stanimura	ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC;
20153553Stanimura	ulCorrectionPerSec = ulTemp1;
20253553Stanimura
20353553Stanimura	/*
20453553Stanimura	 * Fill in the SampleRateConverter control block.
20553553Stanimura	 */
20653553Stanimura	csa_writemem(resp, BA1_PSRC, ((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF));
20753553Stanimura	csa_writemem(resp, BA1_PPI, ulPhiIncr);
20853553Stanimura}
20953553Stanimura
21053553Stanimurastatic void
21153553Stanimuracsa_setcapturesamplerate(csa_res *resp, u_long ulOutRate)
21253553Stanimura{
21353553Stanimura	u_long ulPhiIncr, ulCoeffIncr, ulTemp1, ulTemp2;
21453553Stanimura	u_long ulCorrectionPerGOF, ulCorrectionPerSec, ulInitialDelay;
21553553Stanimura	u_long dwFrameGroupLength, dwCnt;
21653553Stanimura	u_long ulInRate;
21753553Stanimura
21853553Stanimura	ulInRate = 48000;
21953553Stanimura
22053553Stanimura	/*
22153553Stanimura	 * We can only decimate by up to a factor of 1/9th the hardware rate.
22253553Stanimura	 * Return an error if an attempt is made to stray outside that limit.
22353553Stanimura	 */
22453553Stanimura	if((ulOutRate * 9) < ulInRate)
22553553Stanimura		return;
22653553Stanimura
22753553Stanimura	/*
22853553Stanimura	 * We can not capture at at rate greater than the Input Rate (48000).
22953553Stanimura	 * Return an error if an attempt is made to stray outside that limit.
23053553Stanimura	 */
23153553Stanimura	if(ulOutRate > ulInRate)
23253553Stanimura		return;
23353553Stanimura
23453553Stanimura	/*
23553553Stanimura	 * Compute the values used to drive the actual sample rate conversion.
23653553Stanimura	 * The following formulas are being computed, using inline assembly
23753553Stanimura	 * since we need to use 64 bit arithmetic to compute the values:
23853553Stanimura	 *
23953553Stanimura	 *     ulCoeffIncr = -floor((Fs,out * 2^23) / Fs,in)
24053553Stanimura	 *     ulPhiIncr = floor((Fs,in * 2^26) / Fs,out)
24153553Stanimura	 *     ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) /
24253553Stanimura	 *                                GOF_PER_SEC)
24353553Stanimura	 *     ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
24453553Stanimura	 *                          GOF_PER_SEC * ulCorrectionPerGOF
24553553Stanimura	 *     ulInitialDelay = ceil((24 * Fs,in) / Fs,out)
24653553Stanimura	 *
24753553Stanimura	 * i.e.
24853553Stanimura	 *
24953553Stanimura	 *     ulCoeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in))
25053553Stanimura	 *     ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
25153553Stanimura	 *     ulCorrectionPerGOF:ulCorrectionPerSec =
25253553Stanimura	 *         dividend:remainder(ulOther / GOF_PER_SEC)
25353553Stanimura	 *     ulInitialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out)
25453553Stanimura	 */
25553553Stanimura	ulTemp1 = ulOutRate << 16;
25653553Stanimura	ulCoeffIncr = ulTemp1 / ulInRate;
25753553Stanimura	ulTemp1 -= ulCoeffIncr * ulInRate;
25853553Stanimura	ulTemp1 <<= 7;
25953553Stanimura	ulCoeffIncr <<= 7;
26053553Stanimura	ulCoeffIncr += ulTemp1 / ulInRate;
26153553Stanimura	ulCoeffIncr ^= 0xFFFFFFFF;
26253553Stanimura	ulCoeffIncr++;
26353553Stanimura	ulTemp1 = ulInRate << 16;
26453553Stanimura	ulPhiIncr = ulTemp1 / ulOutRate;
26553553Stanimura	ulTemp1 -= ulPhiIncr * ulOutRate;
26653553Stanimura	ulTemp1 <<= 10;
26753553Stanimura	ulPhiIncr <<= 10;
26853553Stanimura	ulTemp2 = ulTemp1 / ulOutRate;
26953553Stanimura	ulPhiIncr += ulTemp2;
27053553Stanimura	ulTemp1 -= ulTemp2 * ulOutRate;
27153553Stanimura	ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC;
27253553Stanimura	ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC;
27353553Stanimura	ulCorrectionPerSec = ulTemp1;
27453553Stanimura	ulInitialDelay = ((ulInRate * 24) + ulOutRate - 1) / ulOutRate;
27553553Stanimura
27653553Stanimura	/*
27753553Stanimura	 * Fill in the VariDecimate control block.
27853553Stanimura	 */
27956249Scg	csa_writemem(resp, BA1_CSRC,
28053553Stanimura		     ((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF));
28153553Stanimura	csa_writemem(resp, BA1_CCI, ulCoeffIncr);
28256249Scg	csa_writemem(resp, BA1_CD,
28353553Stanimura	     (((BA1_VARIDEC_BUF_1 + (ulInitialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
28453553Stanimura	csa_writemem(resp, BA1_CPI, ulPhiIncr);
28553553Stanimura
28653553Stanimura	/*
28753553Stanimura	 * Figure out the frame group length for the write back task.  Basically,
28853553Stanimura	 * this is just the factors of 24000 (2^6*3*5^3) that are not present in
28953553Stanimura	 * the output sample rate.
29053553Stanimura	 */
29153553Stanimura	dwFrameGroupLength = 1;
29253553Stanimura	for(dwCnt = 2; dwCnt <= 64; dwCnt *= 2)
29353553Stanimura	{
29453553Stanimura		if(((ulOutRate / dwCnt) * dwCnt) !=
29553553Stanimura		   ulOutRate)
29653553Stanimura		{
29753553Stanimura			dwFrameGroupLength *= 2;
29853553Stanimura		}
29953553Stanimura	}
30053553Stanimura	if(((ulOutRate / 3) * 3) !=
30153553Stanimura	   ulOutRate)
30253553Stanimura	{
30353553Stanimura		dwFrameGroupLength *= 3;
30453553Stanimura	}
30553553Stanimura	for(dwCnt = 5; dwCnt <= 125; dwCnt *= 5)
30653553Stanimura	{
30753553Stanimura		if(((ulOutRate / dwCnt) * dwCnt) !=
30853553Stanimura		   ulOutRate)
30953553Stanimura		{
31053553Stanimura			dwFrameGroupLength *= 5;
31153553Stanimura		}
31253553Stanimura	}
31353553Stanimura
31453553Stanimura	/*
31553553Stanimura	 * Fill in the WriteBack control block.
31653553Stanimura	 */
31753553Stanimura	csa_writemem(resp, BA1_CFG1, dwFrameGroupLength);
31853553Stanimura	csa_writemem(resp, BA1_CFG2, (0x00800000 | dwFrameGroupLength));
31953553Stanimura	csa_writemem(resp, BA1_CCST, 0x0000FFFF);
32053553Stanimura	csa_writemem(resp, BA1_CSPB, ((65536 * ulOutRate) / 24000));
32153553Stanimura	csa_writemem(resp, (BA1_CSPB + 4), 0x0000FFFF);
32253553Stanimura}
32353553Stanimura
32453553Stanimurastatic void
32553553Stanimuracsa_startplaydma(struct csa_info *csa)
32653553Stanimura{
32753553Stanimura	csa_res *resp;
32853553Stanimura	u_long ul;
32953553Stanimura
33055321Stanimura	if (!csa->pch.dma) {
33155321Stanimura		resp = &csa->res;
33255321Stanimura		ul = csa_readmem(resp, BA1_PCTL);
33355321Stanimura		ul &= 0x0000ffff;
33455321Stanimura		csa_writemem(resp, BA1_PCTL, ul | csa->pctl);
33555321Stanimura		csa_writemem(resp, BA1_PVOL, 0x80008000);
33655321Stanimura		csa->pch.dma = 1;
33755321Stanimura	}
33853553Stanimura}
33953553Stanimura
34053553Stanimurastatic void
34153553Stanimuracsa_startcapturedma(struct csa_info *csa)
34253553Stanimura{
34353553Stanimura	csa_res *resp;
34453553Stanimura	u_long ul;
34553553Stanimura
34655321Stanimura	if (!csa->rch.dma) {
34755321Stanimura		resp = &csa->res;
34855321Stanimura		ul = csa_readmem(resp, BA1_CCTL);
34955321Stanimura		ul &= 0xffff0000;
35055321Stanimura		csa_writemem(resp, BA1_CCTL, ul | csa->cctl);
35155321Stanimura		csa_writemem(resp, BA1_CVOL, 0x80008000);
35255321Stanimura		csa->rch.dma = 1;
35355321Stanimura	}
35453553Stanimura}
35553553Stanimura
35653553Stanimurastatic void
35753553Stanimuracsa_stopplaydma(struct csa_info *csa)
35853553Stanimura{
35953553Stanimura	csa_res *resp;
36053553Stanimura	u_long ul;
36153553Stanimura
36255321Stanimura	if (csa->pch.dma) {
36355321Stanimura		resp = &csa->res;
36455321Stanimura		ul = csa_readmem(resp, BA1_PCTL);
36555321Stanimura		csa->pctl = ul & 0xffff0000;
36655321Stanimura		csa_writemem(resp, BA1_PCTL, ul & 0x0000ffff);
36755321Stanimura		csa_writemem(resp, BA1_PVOL, 0xffffffff);
36856427Stanimura		csa->pch.dma = 0;
36955320Stanimura
37056427Stanimura		/*
37156427Stanimura		 * The bitwise pointer of the serial FIFO in the DSP
37256427Stanimura		 * seems to make an error upon starting or stopping the
37356427Stanimura		 * DSP. Clear the FIFO and correct the pointer if we
37456427Stanimura		 * are not capturing.
37556427Stanimura		 */
37656427Stanimura		if (!csa->rch.dma) {
37756427Stanimura			csa_clearserialfifos(resp);
37856427Stanimura			csa_writeio(resp, BA0_SERBSP, 0);
37956427Stanimura		}
38055321Stanimura	}
38153553Stanimura}
38253553Stanimura
38353553Stanimurastatic void
38453553Stanimuracsa_stopcapturedma(struct csa_info *csa)
38553553Stanimura{
38653553Stanimura	csa_res *resp;
38753553Stanimura	u_long ul;
38853553Stanimura
38955321Stanimura	if (csa->rch.dma) {
39055321Stanimura		resp = &csa->res;
39155321Stanimura		ul = csa_readmem(resp, BA1_CCTL);
39255321Stanimura		csa->cctl = ul & 0x0000ffff;
39355321Stanimura		csa_writemem(resp, BA1_CCTL, ul & 0xffff0000);
39455321Stanimura		csa_writemem(resp, BA1_CVOL, 0xffffffff);
39555321Stanimura		csa->rch.dma = 0;
39656427Stanimura
39756427Stanimura		/*
39856427Stanimura		 * The bitwise pointer of the serial FIFO in the DSP
39956427Stanimura		 * seems to make an error upon starting or stopping the
40056427Stanimura		 * DSP. Clear the FIFO and correct the pointer if we
40156427Stanimura		 * are not playing.
40256427Stanimura		 */
40356427Stanimura		if (!csa->pch.dma) {
40456427Stanimura			csa_clearserialfifos(resp);
40556427Stanimura			csa_writeio(resp, BA0_SERBSP, 0);
40656427Stanimura		}
40755321Stanimura	}
40853553Stanimura}
40953553Stanimura
41053553Stanimurastatic int
41153553Stanimuracsa_startdsp(csa_res *resp)
41253553Stanimura{
41353553Stanimura	int i;
41453553Stanimura	u_long ul;
41553553Stanimura
41653553Stanimura	/*
41753553Stanimura	 * Set the frame timer to reflect the number of cycles per frame.
41853553Stanimura	 */
41953553Stanimura	csa_writemem(resp, BA1_FRMT, 0xadf);
42053553Stanimura
42153553Stanimura	/*
42253553Stanimura	 * Turn on the run, run at frame, and DMA enable bits in the local copy of
42353553Stanimura	 * the SP control register.
42453553Stanimura	 */
42553553Stanimura	csa_writemem(resp, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN);
42653553Stanimura
42753553Stanimura	/*
42853553Stanimura	 * Wait until the run at frame bit resets itself in the SP control
42953553Stanimura	 * register.
43053553Stanimura	 */
43153553Stanimura	ul = 0;
43253553Stanimura	for (i = 0 ; i < 25 ; i++) {
43353553Stanimura		/*
43453553Stanimura		 * Wait a little bit, so we don't issue PCI reads too frequently.
43553553Stanimura		 */
43677504Scg		DELAY(50);
43753553Stanimura		/*
43853553Stanimura		 * Fetch the current value of the SP status register.
43953553Stanimura		 */
44053553Stanimura		ul = csa_readmem(resp, BA1_SPCR);
44153553Stanimura
44253553Stanimura		/*
44353553Stanimura		 * If the run at frame bit has reset, then stop waiting.
44453553Stanimura		 */
44553553Stanimura		if((ul & SPCR_RUNFR) == 0)
44653553Stanimura			break;
44753553Stanimura	}
44853553Stanimura	/*
44953553Stanimura	 * If the run at frame bit never reset, then return an error.
45053553Stanimura	 */
45153553Stanimura	if((ul & SPCR_RUNFR) != 0)
45253553Stanimura		return (EAGAIN);
45353553Stanimura
45453553Stanimura	return (0);
45553553Stanimura}
45653553Stanimura
45777504Scgstatic int
45877504Scgcsa_setupchan(struct csa_chinfo *ch)
45977504Scg{
46077504Scg	struct csa_info *csa = ch->parent;
46177504Scg	csa_res *resp = &csa->res;
46277504Scg	u_long pdtc, tmp;
46377504Scg
46477504Scg	if (ch->dir == PCMDIR_PLAY) {
46577504Scg		/* direction */
46677504Scg		csa_writemem(resp, BA1_PBA, vtophys(sndbuf_getbuf(ch->buffer)));
46777504Scg
46877504Scg		/* format */
46977504Scg		csa->pfie = csa_readmem(resp, BA1_PFIE) & ~0x0000f03f;
47077504Scg		if (!(ch->fmt & AFMT_SIGNED))
47177504Scg			csa->pfie |= 0x8000;
47277504Scg		if (ch->fmt & AFMT_BIGENDIAN)
47377504Scg			csa->pfie |= 0x4000;
47477504Scg		if (!(ch->fmt & AFMT_STEREO))
47577504Scg			csa->pfie |= 0x2000;
47677504Scg		if (ch->fmt & AFMT_8BIT)
47777504Scg			csa->pfie |= 0x1000;
47877504Scg		csa_writemem(resp, BA1_PFIE, csa->pfie);
47977504Scg
48077504Scg		tmp = 4;
48177504Scg		if (ch->fmt & AFMT_16BIT)
48277504Scg			tmp <<= 1;
48377504Scg		if (ch->fmt & AFMT_STEREO)
48477504Scg			tmp <<= 1;
48577504Scg		tmp--;
48677504Scg
48777504Scg		pdtc = csa_readmem(resp, BA1_PDTC) & ~0x000001ff;
48877504Scg		pdtc |= tmp;
48977504Scg		csa_writemem(resp, BA1_PDTC, pdtc);
49077504Scg
49177504Scg		/* rate */
49277504Scg		csa_setplaysamplerate(resp, ch->spd);
49377504Scg	} else if (ch->dir == PCMDIR_REC) {
49477504Scg		/* direction */
49577504Scg		csa_writemem(resp, BA1_CBA, vtophys(sndbuf_getbuf(ch->buffer)));
49677504Scg
49777504Scg		/* format */
49877504Scg		csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001);
49977504Scg
50077504Scg		/* rate */
50177504Scg		csa_setcapturesamplerate(resp, ch->spd);
50277504Scg	}
50377504Scg	return 0;
50477504Scg}
50577504Scg
50670134Scg/* -------------------------------------------------------------------- */
50770134Scg/* channel interface */
50870134Scg
50970134Scgstatic void *
51074763Scgcsachan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
51170134Scg{
51270134Scg	struct csa_info *csa = devinfo;
51370134Scg	struct csa_chinfo *ch = (dir == PCMDIR_PLAY)? &csa->pch : &csa->rch;
51470134Scg
51570134Scg	ch->parent = csa;
51670134Scg	ch->channel = c;
51770134Scg	ch->buffer = b;
51877504Scg	ch->dir = dir;
51970291Scg	if (sndbuf_alloc(ch->buffer, csa->parent_dmat, CS461x_BUFFSIZE) == -1) return NULL;
52070134Scg	return ch;
52170134Scg}
52270134Scg
52353553Stanimurastatic int
52470134Scgcsachan_setformat(kobj_t obj, void *data, u_int32_t format)
52570134Scg{
52670134Scg	struct csa_chinfo *ch = data;
52770134Scg
52870134Scg	ch->fmt = format;
52970134Scg	return 0;
53070134Scg}
53170134Scg
53270134Scgstatic int
53370134Scgcsachan_setspeed(kobj_t obj, void *data, u_int32_t speed)
53470134Scg{
53570134Scg	struct csa_chinfo *ch = data;
53670134Scg
53777504Scg	ch->spd = speed;
53877504Scg	return ch->spd; /* XXX calc real speed */
53970134Scg}
54070134Scg
54170134Scgstatic int
54270134Scgcsachan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
54370134Scg{
54470291Scg	return CS461x_BUFFSIZE / 2;
54570134Scg}
54670134Scg
54770134Scgstatic int
54870134Scgcsachan_trigger(kobj_t obj, void *data, int go)
54970134Scg{
55070134Scg	struct csa_chinfo *ch = data;
55170134Scg	struct csa_info *csa = ch->parent;
55270134Scg
55370134Scg	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
55470134Scg		return 0;
55570134Scg
55677504Scg	if (go == PCMTRIG_START) {
55777504Scg		csa_active(csa, 1);
55877504Scg		csa_setupchan(ch);
55977504Scg		if (ch->dir == PCMDIR_PLAY)
56070134Scg			csa_startplaydma(csa);
56170134Scg		else
56277504Scg			csa_startcapturedma(csa);
56377504Scg	} else {
56477504Scg		if (ch->dir == PCMDIR_PLAY)
56570134Scg			csa_stopplaydma(csa);
56670134Scg		else
56770134Scg			csa_stopcapturedma(csa);
56877504Scg		csa_active(csa, -1);
56970134Scg	}
57070134Scg	return 0;
57170134Scg}
57270134Scg
57370134Scgstatic int
57470134Scgcsachan_getptr(kobj_t obj, void *data)
57570134Scg{
57670134Scg	struct csa_chinfo *ch = data;
57770134Scg	struct csa_info *csa = ch->parent;
57870134Scg	csa_res *resp;
57953553Stanimura	int ptr;
58053553Stanimura
58153553Stanimura	resp = &csa->res;
58253553Stanimura
58353553Stanimura	if (ch->dir == PCMDIR_PLAY) {
58470291Scg		ptr = csa_readmem(resp, BA1_PBA) - vtophys(sndbuf_getbuf(ch->buffer));
58553553Stanimura		if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0)
58653553Stanimura			ptr >>= 1;
58753553Stanimura	} else {
58870291Scg		ptr = csa_readmem(resp, BA1_CBA) - vtophys(sndbuf_getbuf(ch->buffer));
58953553Stanimura		if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0)
59053553Stanimura			ptr >>= 1;
59153553Stanimura	}
59253553Stanimura
59353553Stanimura	return (ptr);
59453553Stanimura}
59553553Stanimura
59674763Scgstatic struct pcmchan_caps *
59770134Scgcsachan_getcaps(kobj_t obj, void *data)
59853553Stanimura{
59953553Stanimura	struct csa_chinfo *ch = data;
60053553Stanimura	return (ch->dir == PCMDIR_PLAY)? &csa_playcaps : &csa_reccaps;
60153553Stanimura}
60253553Stanimura
60370134Scgstatic kobj_method_t csachan_methods[] = {
60470134Scg    	KOBJMETHOD(channel_init,		csachan_init),
60570134Scg    	KOBJMETHOD(channel_setformat,		csachan_setformat),
60670134Scg    	KOBJMETHOD(channel_setspeed,		csachan_setspeed),
60770134Scg    	KOBJMETHOD(channel_setblocksize,	csachan_setblocksize),
60870134Scg    	KOBJMETHOD(channel_trigger,		csachan_trigger),
60970134Scg    	KOBJMETHOD(channel_getptr,		csachan_getptr),
61070134Scg    	KOBJMETHOD(channel_getcaps,		csachan_getcaps),
61170134Scg	{ 0, 0 }
61270134Scg};
61370134ScgCHANNEL_DECLARE(csachan);
61470134Scg
61570134Scg/* -------------------------------------------------------------------- */
61653553Stanimura/* The interrupt handler */
61753553Stanimurastatic void
61877504Scgcsa_intr(void *p)
61953553Stanimura{
62053553Stanimura	struct csa_info *csa = p;
62153553Stanimura
62255320Stanimura	if ((csa->binfo->hisr & HISR_VC0) != 0)
62353553Stanimura		chn_intr(csa->pch.channel);
62455320Stanimura	if ((csa->binfo->hisr & HISR_VC1) != 0)
62553553Stanimura		chn_intr(csa->rch.channel);
62653553Stanimura}
62753553Stanimura
62853553Stanimura/* -------------------------------------------------------------------- */
62953553Stanimura
63053553Stanimura/*
63153553Stanimura * Probe and attach the card
63253553Stanimura */
63353553Stanimura
63453553Stanimurastatic int
63553553Stanimuracsa_init(struct csa_info *csa)
63653553Stanimura{
63753553Stanimura	csa_res *resp;
63853553Stanimura
63953553Stanimura	resp = &csa->res;
64053553Stanimura
64153553Stanimura	csa->pfie = 0;
64253553Stanimura	csa_stopplaydma(csa);
64353553Stanimura	csa_stopcapturedma(csa);
64453553Stanimura
64577504Scg	if (csa_startdsp(resp))
64677504Scg		return (1);
64777504Scg
64853553Stanimura	/* Crank up the power on the DAC and ADC. */
64953553Stanimura	csa_setplaysamplerate(resp, 8000);
65053553Stanimura	csa_setcapturesamplerate(resp, 8000);
65153553Stanimura
65253553Stanimura	return 0;
65353553Stanimura}
65453553Stanimura
65553553Stanimura/* Allocates resources. */
65653553Stanimurastatic int
65753553Stanimuracsa_allocres(struct csa_info *csa, device_t dev)
65853553Stanimura{
65953553Stanimura	csa_res *resp;
66053553Stanimura
66153553Stanimura	resp = &csa->res;
66253553Stanimura	if (resp->io == NULL) {
66377504Scg		resp->io = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->io_rid, 0, ~0, 1, RF_ACTIVE);
66453553Stanimura		if (resp->io == NULL)
66553553Stanimura			return (1);
66653553Stanimura	}
66753553Stanimura	if (resp->mem == NULL) {
66877504Scg		resp->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->mem_rid, 0, ~0, 1, RF_ACTIVE);
66953553Stanimura		if (resp->mem == NULL)
67053553Stanimura			return (1);
67153553Stanimura	}
67253553Stanimura	if (resp->irq == NULL) {
67353553Stanimura		resp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &resp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
67453553Stanimura		if (resp->irq == NULL)
67553553Stanimura			return (1);
67653553Stanimura	}
67753553Stanimura	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/CS461x_BUFFSIZE, /*boundary*/CS461x_BUFFSIZE,
67853553Stanimura			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
67953553Stanimura			       /*highaddr*/BUS_SPACE_MAXADDR,
68053553Stanimura			       /*filter*/NULL, /*filterarg*/NULL,
68153553Stanimura			       /*maxsize*/CS461x_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
68253553Stanimura			       /*flags*/0, &csa->parent_dmat) != 0)
68353553Stanimura		return (1);
68453553Stanimura
68553553Stanimura	return (0);
68653553Stanimura}
68753553Stanimura
68853553Stanimura/* Releases resources. */
68953553Stanimurastatic void
69053553Stanimuracsa_releaseres(struct csa_info *csa, device_t dev)
69153553Stanimura{
69253553Stanimura	csa_res *resp;
69353553Stanimura
69453553Stanimura	resp = &csa->res;
69553553Stanimura	if (resp->irq != NULL) {
69665644Scg		if (csa->ih)
69765644Scg			bus_teardown_intr(dev, resp->irq, csa->ih);
69853553Stanimura		bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq);
69953553Stanimura		resp->irq = NULL;
70053553Stanimura	}
70153553Stanimura	if (resp->io != NULL) {
70253553Stanimura		bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io);
70353553Stanimura		resp->io = NULL;
70453553Stanimura	}
70553553Stanimura	if (resp->mem != NULL) {
70653553Stanimura		bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem);
70753553Stanimura		resp->mem = NULL;
70853553Stanimura	}
70965644Scg	if (csa->parent_dmat != NULL) {
71065644Scg		bus_dma_tag_destroy(csa->parent_dmat);
71165644Scg		csa->parent_dmat = NULL;
71265644Scg	}
71365644Scg	if (csa != NULL) {
71465644Scg		free(csa, M_DEVBUF);
71565644Scg		csa = NULL;
71665644Scg	}
71753553Stanimura}
71853553Stanimura
71953553Stanimurastatic int
72053553Stanimurapcmcsa_probe(device_t dev)
72153553Stanimura{
72253553Stanimura	char *s;
72353553Stanimura	struct sndcard_func *func;
72453553Stanimura
72553553Stanimura	/* The parent device has already been probed. */
72653553Stanimura
72753553Stanimura	func = device_get_ivars(dev);
72853553Stanimura	if (func == NULL || func->func != SCF_PCM)
72953553Stanimura		return (ENXIO);
73053553Stanimura
73153553Stanimura	s = "CS461x PCM Audio";
73253553Stanimura
73353553Stanimura	device_set_desc(dev, s);
73453553Stanimura	return (0);
73553553Stanimura}
73653553Stanimura
73753553Stanimurastatic int
73853553Stanimurapcmcsa_attach(device_t dev)
73953553Stanimura{
74053553Stanimura	struct csa_info *csa;
74153553Stanimura	csa_res *resp;
74253553Stanimura	int unit;
74353553Stanimura	char status[SND_STATUSLEN];
74453553Stanimura	struct ac97_info *codec;
74555320Stanimura	struct sndcard_func *func;
74653553Stanimura
74778564Sgreid	csa = malloc(sizeof(*csa), M_DEVBUF, M_NOWAIT | M_ZERO);
74853553Stanimura	if (csa == NULL)
74953553Stanimura		return (ENOMEM);
75053553Stanimura	unit = device_get_unit(dev);
75155320Stanimura	func = device_get_ivars(dev);
75255320Stanimura	csa->binfo = func->varinfo;
75355321Stanimura	/*
75455321Stanimura	 * Fake the status of DMA so that the initial value of
75555321Stanimura	 * PCTL and CCTL can be stored into csa->pctl and csa->cctl,
75655321Stanimura	 * respectively.
75755321Stanimura	 */
75855321Stanimura	csa->pch.dma = csa->rch.dma = 1;
75977504Scg	csa->active = 0;
76077504Scg	csa->card = csa->binfo->card;
76153553Stanimura
76253553Stanimura	/* Allocate the resources. */
76353553Stanimura	resp = &csa->res;
76477504Scg	resp->io_rid = PCIR_MAPS;
76577504Scg	resp->mem_rid = PCIR_MAPS + 4;
76653553Stanimura	resp->irq_rid = 0;
76753553Stanimura	if (csa_allocres(csa, dev)) {
76853553Stanimura		csa_releaseres(csa, dev);
76953553Stanimura		return (ENXIO);
77053553Stanimura	}
77153553Stanimura
77277504Scg	csa_active(csa, 1);
77353553Stanimura	if (csa_init(csa)) {
77453553Stanimura		csa_releaseres(csa, dev);
77553553Stanimura		return (ENXIO);
77653553Stanimura	}
77770134Scg	codec = AC97_CREATE(dev, csa, csa_ac97);
77865644Scg	if (codec == NULL) {
77965644Scg		csa_releaseres(csa, dev);
78053553Stanimura		return (ENXIO);
78165644Scg	}
78278673Scg	if (csa->card->inv_eapd)
78378673Scg		ac97_setflags(codec, AC97_F_EAPD_INV);
78470134Scg	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) {
78565644Scg		ac97_destroy(codec);
78665644Scg		csa_releaseres(csa, dev);
78758905Scg		return (ENXIO);
78865644Scg	}
78953553Stanimura
79053553Stanimura	snprintf(status, SND_STATUSLEN, "at irq %ld", rman_get_start(resp->irq));
79153553Stanimura
79253553Stanimura	/* Enable interrupt. */
79377504Scg	if (snd_setup_intr(dev, resp->irq, INTR_MPSAFE, csa_intr, csa, &csa->ih)) {
79465644Scg		ac97_destroy(codec);
79553553Stanimura		csa_releaseres(csa, dev);
79653553Stanimura		return (ENXIO);
79753553Stanimura	}
79853553Stanimura	csa_writemem(resp, BA1_PFIE, csa_readmem(resp, BA1_PFIE) & ~0x0000f03f);
79953553Stanimura	csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001);
80077504Scg	csa_active(csa, -1);
80153553Stanimura
80253553Stanimura	if (pcm_register(dev, csa, 1, 1)) {
80365644Scg		ac97_destroy(codec);
80453553Stanimura		csa_releaseres(csa, dev);
80553553Stanimura		return (ENXIO);
80653553Stanimura	}
80770134Scg	pcm_addchan(dev, PCMDIR_REC, &csachan_class, csa);
80870134Scg	pcm_addchan(dev, PCMDIR_PLAY, &csachan_class, csa);
80953553Stanimura	pcm_setstatus(dev, status);
81053553Stanimura
81153553Stanimura	return (0);
81253553Stanimura}
81353553Stanimura
81465644Scgstatic int
81565644Scgpcmcsa_detach(device_t dev)
81665644Scg{
81765644Scg	int r;
81865644Scg	struct csa_info *csa;
81965644Scg
82065644Scg	r = pcm_unregister(dev);
82165644Scg	if (r)
82265644Scg		return r;
82365644Scg
82465644Scg	csa = pcm_getdevinfo(dev);
82565644Scg	csa_releaseres(csa, dev);
82665644Scg
82765644Scg	return 0;
82865644Scg}
82965644Scg
83053553Stanimurastatic device_method_t pcmcsa_methods[] = {
83153553Stanimura	/* Device interface */
83253553Stanimura	DEVMETHOD(device_probe , pcmcsa_probe ),
83353553Stanimura	DEVMETHOD(device_attach, pcmcsa_attach),
83465644Scg	DEVMETHOD(device_detach, pcmcsa_detach),
83553553Stanimura
83653553Stanimura	{ 0, 0 },
83753553Stanimura};
83853553Stanimura
83953553Stanimurastatic driver_t pcmcsa_driver = {
84053553Stanimura	"pcm",
84153553Stanimura	pcmcsa_methods,
84274763Scg	sizeof(struct snddev_info),
84353553Stanimura};
84453553Stanimura
84562483ScgDRIVER_MODULE(snd_csapcm, csa, pcmcsa_driver, pcm_devclass, 0, 0);
84662483ScgMODULE_DEPEND(snd_csapcm, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
84762483ScgMODULE_DEPEND(snd_csapcm, snd_csa, 1, 1, 1);
84862483ScgMODULE_VERSION(snd_csapcm, 1);
849