csapcm.c revision 55320
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 55320 2000-01-03 02:51:16Z tanimura $
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
4353553Stanimura/* device private data */
4453553Stanimurastruct csa_info;
4553553Stanimura
4653553Stanimurastruct csa_chinfo {
4753553Stanimura	struct csa_info *parent;
4853553Stanimura	pcm_channel *channel;
4953553Stanimura	snd_dbuf *buffer;
5053553Stanimura	int dir;
5153553Stanimura	u_int32_t fmt;
5253553Stanimura};
5353553Stanimura
5453553Stanimurastruct csa_info {
5553553Stanimura	csa_res		res; /* resource */
5653553Stanimura	void		*ih; /* Interrupt cookie */
5753553Stanimura	bus_dma_tag_t	parent_dmat; /* DMA tag */
5855320Stanimura	struct csa_bridgeinfo *binfo; /* The state of the parent. */
5953553Stanimura
6053553Stanimura	/* Contents of board's registers */
6153553Stanimura	u_long		pfie;
6253553Stanimura	u_long		pctl;
6353553Stanimura	u_long		cctl;
6453553Stanimura	struct csa_chinfo pch, rch;
6553553Stanimura};
6653553Stanimura
6753553Stanimura/* -------------------------------------------------------------------- */
6853553Stanimura
6953553Stanimura/* prototypes */
7053553Stanimurastatic int      csa_init(struct csa_info *);
7153553Stanimurastatic void     csa_intr(void *);
7253553Stanimurastatic void	csa_setplaysamplerate(csa_res *resp, u_long ulInRate);
7353553Stanimurastatic void	csa_setcapturesamplerate(csa_res *resp, u_long ulOutRate);
7453553Stanimurastatic void	csa_startplaydma(struct csa_info *csa);
7553553Stanimurastatic void	csa_startcapturedma(struct csa_info *csa);
7653553Stanimurastatic void	csa_stopplaydma(struct csa_info *csa);
7753553Stanimurastatic void	csa_stopcapturedma(struct csa_info *csa);
7853553Stanimurastatic void	csa_powerupadc(csa_res *resp);
7953553Stanimurastatic void	csa_powerupdac(csa_res *resp);
8053553Stanimurastatic int	csa_startdsp(csa_res *resp);
8153553Stanimurastatic int	csa_allocres(struct csa_info *scp, device_t dev);
8253553Stanimurastatic void	csa_releaseres(struct csa_info *scp, device_t dev);
8353553Stanimura
8453553Stanimura/* talk to the codec - called from ac97.c */
8553553Stanimurastatic u_int32_t csa_rdcd(void *, int);
8653553Stanimurastatic void  	 csa_wrcd(void *, int, u_int32_t);
8753553Stanimura
8853553Stanimura/* channel interface */
8953553Stanimurastatic void *csachan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir);
9053553Stanimurastatic int csachan_setdir(void *data, int dir);
9153553Stanimurastatic int csachan_setformat(void *data, u_int32_t format);
9253553Stanimurastatic int csachan_setspeed(void *data, u_int32_t speed);
9353553Stanimurastatic int csachan_setblocksize(void *data, u_int32_t blocksize);
9453553Stanimurastatic int csachan_trigger(void *data, int go);
9553553Stanimurastatic int csachan_getptr(void *data);
9653553Stanimurastatic pcmchan_caps *csachan_getcaps(void *data);
9753553Stanimura
9853553Stanimurastatic pcmchan_caps csa_playcaps = {
9953553Stanimura	8000, 48000,
10053553Stanimura	AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE,
10153553Stanimura	AFMT_STEREO | AFMT_S16_LE
10253553Stanimura};
10353553Stanimura
10453553Stanimurastatic pcmchan_caps csa_reccaps = {
10553553Stanimura	11025, 48000,
10653553Stanimura	AFMT_STEREO | AFMT_S16_LE,
10753553Stanimura	AFMT_STEREO | AFMT_S16_LE
10853553Stanimura};
10953553Stanimura
11053553Stanimurastatic pcm_channel csa_chantemplate = {
11153553Stanimura	csachan_init,
11253553Stanimura	csachan_setdir,
11353553Stanimura	csachan_setformat,
11453553Stanimura	csachan_setspeed,
11553553Stanimura	csachan_setblocksize,
11653553Stanimura	csachan_trigger,
11753553Stanimura	csachan_getptr,
11853553Stanimura	csachan_getcaps,
11953553Stanimura};
12053553Stanimura
12153553Stanimura/* -------------------------------------------------------------------- */
12253553Stanimura
12353553Stanimura/* channel interface */
12453553Stanimurastatic void *
12553553Stanimuracsachan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir)
12653553Stanimura{
12753553Stanimura	struct csa_info *csa = devinfo;
12853553Stanimura	struct csa_chinfo *ch = (dir == PCMDIR_PLAY)? &csa->pch : &csa->rch;
12953553Stanimura
13053553Stanimura	ch->parent = csa;
13153553Stanimura	ch->channel = c;
13253553Stanimura	ch->buffer = b;
13353553Stanimura	ch->buffer->bufsize = CS461x_BUFFSIZE;
13453553Stanimura	if (chn_allocbuf(ch->buffer, csa->parent_dmat) == -1) return NULL;
13553553Stanimura	return ch;
13653553Stanimura}
13753553Stanimura
13853553Stanimurastatic int
13953553Stanimuracsachan_setdir(void *data, int dir)
14053553Stanimura{
14153553Stanimura	struct csa_chinfo *ch = data;
14253553Stanimura	struct csa_info *csa = ch->parent;
14353553Stanimura	csa_res *resp;
14453553Stanimura
14553553Stanimura	resp = &csa->res;
14653553Stanimura
14753553Stanimura	if (dir == PCMDIR_PLAY)
14853553Stanimura		csa_writemem(resp, BA1_PBA, vtophys(ch->buffer->buf));
14953553Stanimura	else
15053553Stanimura		csa_writemem(resp, BA1_CBA, vtophys(ch->buffer->buf));
15153553Stanimura	ch->dir = dir;
15253553Stanimura	return 0;
15353553Stanimura}
15453553Stanimura
15553553Stanimurastatic int
15653553Stanimuracsachan_setformat(void *data, u_int32_t format)
15753553Stanimura{
15853553Stanimura	struct csa_chinfo *ch = data;
15953553Stanimura	struct csa_info *csa = ch->parent;
16053553Stanimura	u_long pdtc;
16153553Stanimura	csa_res *resp;
16253553Stanimura
16353553Stanimura	resp = &csa->res;
16453553Stanimura
16553553Stanimura	if (ch->dir == PCMDIR_REC)
16653553Stanimura		csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001);
16753553Stanimura	else {
16853553Stanimura		csa->pfie = csa_readmem(resp, BA1_PFIE) & ~0x0000f03f;
16953553Stanimura		if (format & AFMT_U8 || format & AFMT_U16_LE || format & AFMT_U16_BE)
17053553Stanimura			csa->pfie |= 0x8000;
17153553Stanimura		if (format & AFMT_S16_BE || format & AFMT_U16_BE)
17253553Stanimura			csa->pfie |= 0x4000;
17353553Stanimura		if (!(format & AFMT_STEREO))
17453553Stanimura			csa->pfie |= 0x2000;
17553553Stanimura		if (format & AFMT_U8 || format & AFMT_S8)
17653553Stanimura			csa->pfie |= 0x1000;
17753553Stanimura		csa_writemem(resp, BA1_PFIE, csa->pfie);
17853553Stanimura		pdtc = csa_readmem(resp, BA1_PDTC) & ~0x000003ff;
17953553Stanimura		if ((format & AFMT_S16_BE || format & AFMT_U16_BE || format & AFMT_S16_LE || format & AFMT_U16_LE) && (format & AFMT_STEREO))
18053553Stanimura			pdtc |= 0x00f;
18153553Stanimura		else if ((format & AFMT_S16_BE || format & AFMT_U16_BE || format & AFMT_S16_LE || format & AFMT_U16_LE) || (format & AFMT_STEREO))
18253553Stanimura			pdtc |= 0x007;
18353553Stanimura		else
18453553Stanimura			pdtc |= 0x003;
18553553Stanimura		csa_writemem(resp, BA1_PDTC, pdtc);
18653553Stanimura	}
18753553Stanimura	ch->fmt = format;
18853553Stanimura	return 0;
18953553Stanimura}
19053553Stanimura
19153553Stanimurastatic int
19253553Stanimuracsachan_setspeed(void *data, u_int32_t speed)
19353553Stanimura{
19453553Stanimura	struct csa_chinfo *ch = data;
19553553Stanimura	struct csa_info *csa = ch->parent;
19653553Stanimura	csa_res *resp;
19753553Stanimura
19853553Stanimura	resp = &csa->res;
19953553Stanimura
20053553Stanimura	if (ch->dir == PCMDIR_PLAY)
20153553Stanimura		csa_setplaysamplerate(resp, speed);
20253553Stanimura	else if (ch->dir == PCMDIR_REC)
20353553Stanimura		csa_setcapturesamplerate(resp, speed);
20453553Stanimura
20553553Stanimura	/* rec/play speeds locked together - should indicate in flags */
20653553Stanimura#if 0
20753553Stanimura	if (ch->direction == PCMDIR_PLAY) d->rec[0].speed = speed;
20853553Stanimura	else d->play[0].speed = speed;
20953553Stanimura#endif
21053553Stanimura	return speed; /* XXX calc real speed */
21153553Stanimura}
21253553Stanimura
21353553Stanimurastatic void
21453553Stanimuracsa_setplaysamplerate(csa_res *resp, u_long ulInRate)
21553553Stanimura{
21653553Stanimura	u_long ulTemp1, ulTemp2;
21753553Stanimura	u_long ulPhiIncr;
21853553Stanimura	u_long ulCorrectionPerGOF, ulCorrectionPerSec;
21953553Stanimura	u_long ulOutRate;
22053553Stanimura
22153553Stanimura	ulOutRate = 48000;
22253553Stanimura
22353553Stanimura	/*
22453553Stanimura	 * Compute the values used to drive the actual sample rate conversion.
22553553Stanimura	 * The following formulas are being computed, using inline assembly
22653553Stanimura	 * since we need to use 64 bit arithmetic to compute the values:
22753553Stanimura	 *
22853553Stanimura	 *     ulPhiIncr = floor((Fs,in * 2^26) / Fs,out)
22953553Stanimura	 *     ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) /
23053553Stanimura	 *                                GOF_PER_SEC)
23153553Stanimura	 *     ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
23253553Stanimura	 *                          GOF_PER_SEC * ulCorrectionPerGOF
23353553Stanimura	 *
23453553Stanimura	 * i.e.
23553553Stanimura	 *
23653553Stanimura	 *     ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
23753553Stanimura	 *     ulCorrectionPerGOF:ulCorrectionPerSec =
23853553Stanimura	 *         dividend:remainder(ulOther / GOF_PER_SEC)
23953553Stanimura	 */
24053553Stanimura	ulTemp1 = ulInRate << 16;
24153553Stanimura	ulPhiIncr = ulTemp1 / ulOutRate;
24253553Stanimura	ulTemp1 -= ulPhiIncr * ulOutRate;
24353553Stanimura	ulTemp1 <<= 10;
24453553Stanimura	ulPhiIncr <<= 10;
24553553Stanimura	ulTemp2 = ulTemp1 / ulOutRate;
24653553Stanimura	ulPhiIncr += ulTemp2;
24753553Stanimura	ulTemp1 -= ulTemp2 * ulOutRate;
24853553Stanimura	ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC;
24953553Stanimura	ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC;
25053553Stanimura	ulCorrectionPerSec = ulTemp1;
25153553Stanimura
25253553Stanimura	/*
25353553Stanimura	 * Fill in the SampleRateConverter control block.
25453553Stanimura	 */
25553553Stanimura	csa_writemem(resp, BA1_PSRC, ((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF));
25653553Stanimura	csa_writemem(resp, BA1_PPI, ulPhiIncr);
25753553Stanimura}
25853553Stanimura
25953553Stanimurastatic void
26053553Stanimuracsa_setcapturesamplerate(csa_res *resp, u_long ulOutRate)
26153553Stanimura{
26253553Stanimura	u_long ulPhiIncr, ulCoeffIncr, ulTemp1, ulTemp2;
26353553Stanimura	u_long ulCorrectionPerGOF, ulCorrectionPerSec, ulInitialDelay;
26453553Stanimura	u_long dwFrameGroupLength, dwCnt;
26553553Stanimura	u_long ulInRate;
26653553Stanimura
26753553Stanimura	ulInRate = 48000;
26853553Stanimura
26953553Stanimura	/*
27053553Stanimura	 * We can only decimate by up to a factor of 1/9th the hardware rate.
27153553Stanimura	 * Return an error if an attempt is made to stray outside that limit.
27253553Stanimura	 */
27353553Stanimura	if((ulOutRate * 9) < ulInRate)
27453553Stanimura		return;
27553553Stanimura
27653553Stanimura	/*
27753553Stanimura	 * We can not capture at at rate greater than the Input Rate (48000).
27853553Stanimura	 * Return an error if an attempt is made to stray outside that limit.
27953553Stanimura	 */
28053553Stanimura	if(ulOutRate > ulInRate)
28153553Stanimura		return;
28253553Stanimura
28353553Stanimura	/*
28453553Stanimura	 * Compute the values used to drive the actual sample rate conversion.
28553553Stanimura	 * The following formulas are being computed, using inline assembly
28653553Stanimura	 * since we need to use 64 bit arithmetic to compute the values:
28753553Stanimura	 *
28853553Stanimura	 *     ulCoeffIncr = -floor((Fs,out * 2^23) / Fs,in)
28953553Stanimura	 *     ulPhiIncr = floor((Fs,in * 2^26) / Fs,out)
29053553Stanimura	 *     ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) /
29153553Stanimura	 *                                GOF_PER_SEC)
29253553Stanimura	 *     ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
29353553Stanimura	 *                          GOF_PER_SEC * ulCorrectionPerGOF
29453553Stanimura	 *     ulInitialDelay = ceil((24 * Fs,in) / Fs,out)
29553553Stanimura	 *
29653553Stanimura	 * i.e.
29753553Stanimura	 *
29853553Stanimura	 *     ulCoeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in))
29953553Stanimura	 *     ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
30053553Stanimura	 *     ulCorrectionPerGOF:ulCorrectionPerSec =
30153553Stanimura	 *         dividend:remainder(ulOther / GOF_PER_SEC)
30253553Stanimura	 *     ulInitialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out)
30353553Stanimura	 */
30453553Stanimura	ulTemp1 = ulOutRate << 16;
30553553Stanimura	ulCoeffIncr = ulTemp1 / ulInRate;
30653553Stanimura	ulTemp1 -= ulCoeffIncr * ulInRate;
30753553Stanimura	ulTemp1 <<= 7;
30853553Stanimura	ulCoeffIncr <<= 7;
30953553Stanimura	ulCoeffIncr += ulTemp1 / ulInRate;
31053553Stanimura	ulCoeffIncr ^= 0xFFFFFFFF;
31153553Stanimura	ulCoeffIncr++;
31253553Stanimura	ulTemp1 = ulInRate << 16;
31353553Stanimura	ulPhiIncr = ulTemp1 / ulOutRate;
31453553Stanimura	ulTemp1 -= ulPhiIncr * ulOutRate;
31553553Stanimura	ulTemp1 <<= 10;
31653553Stanimura	ulPhiIncr <<= 10;
31753553Stanimura	ulTemp2 = ulTemp1 / ulOutRate;
31853553Stanimura	ulPhiIncr += ulTemp2;
31953553Stanimura	ulTemp1 -= ulTemp2 * ulOutRate;
32053553Stanimura	ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC;
32153553Stanimura	ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC;
32253553Stanimura	ulCorrectionPerSec = ulTemp1;
32353553Stanimura	ulInitialDelay = ((ulInRate * 24) + ulOutRate - 1) / ulOutRate;
32453553Stanimura
32553553Stanimura	/*
32653553Stanimura	 * Fill in the VariDecimate control block.
32753553Stanimura	 */
32853553Stanimura	csa_writemem(resp, BA1_CSRC,
32953553Stanimura		     ((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF));
33053553Stanimura	csa_writemem(resp, BA1_CCI, ulCoeffIncr);
33153553Stanimura	csa_writemem(resp, BA1_CD,
33253553Stanimura	     (((BA1_VARIDEC_BUF_1 + (ulInitialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
33353553Stanimura	csa_writemem(resp, BA1_CPI, ulPhiIncr);
33453553Stanimura
33553553Stanimura	/*
33653553Stanimura	 * Figure out the frame group length for the write back task.  Basically,
33753553Stanimura	 * this is just the factors of 24000 (2^6*3*5^3) that are not present in
33853553Stanimura	 * the output sample rate.
33953553Stanimura	 */
34053553Stanimura	dwFrameGroupLength = 1;
34153553Stanimura	for(dwCnt = 2; dwCnt <= 64; dwCnt *= 2)
34253553Stanimura	{
34353553Stanimura		if(((ulOutRate / dwCnt) * dwCnt) !=
34453553Stanimura		   ulOutRate)
34553553Stanimura		{
34653553Stanimura			dwFrameGroupLength *= 2;
34753553Stanimura		}
34853553Stanimura	}
34953553Stanimura	if(((ulOutRate / 3) * 3) !=
35053553Stanimura	   ulOutRate)
35153553Stanimura	{
35253553Stanimura		dwFrameGroupLength *= 3;
35353553Stanimura	}
35453553Stanimura	for(dwCnt = 5; dwCnt <= 125; dwCnt *= 5)
35553553Stanimura	{
35653553Stanimura		if(((ulOutRate / dwCnt) * dwCnt) !=
35753553Stanimura		   ulOutRate)
35853553Stanimura		{
35953553Stanimura			dwFrameGroupLength *= 5;
36053553Stanimura		}
36153553Stanimura	}
36253553Stanimura
36353553Stanimura	/*
36453553Stanimura	 * Fill in the WriteBack control block.
36553553Stanimura	 */
36653553Stanimura	csa_writemem(resp, BA1_CFG1, dwFrameGroupLength);
36753553Stanimura	csa_writemem(resp, BA1_CFG2, (0x00800000 | dwFrameGroupLength));
36853553Stanimura	csa_writemem(resp, BA1_CCST, 0x0000FFFF);
36953553Stanimura	csa_writemem(resp, BA1_CSPB, ((65536 * ulOutRate) / 24000));
37053553Stanimura	csa_writemem(resp, (BA1_CSPB + 4), 0x0000FFFF);
37153553Stanimura}
37253553Stanimura
37353553Stanimurastatic int
37453553Stanimuracsachan_setblocksize(void *data, u_int32_t blocksize)
37553553Stanimura{
37653553Stanimura#if notdef
37753553Stanimura	return blocksize;
37853553Stanimura#else
37953553Stanimura	struct csa_chinfo *ch = data;
38053553Stanimura	return ch->buffer->bufsize / 2;
38153553Stanimura#endif /* notdef */
38253553Stanimura}
38353553Stanimura
38453553Stanimurastatic int
38553553Stanimuracsachan_trigger(void *data, int go)
38653553Stanimura{
38753553Stanimura	struct csa_chinfo *ch = data;
38853553Stanimura	struct csa_info *csa = ch->parent;
38953553Stanimura
39055204Scg	if (go == PCMTRIG_EMLDMAWR) return 0;
39153553Stanimura	if (ch->dir == PCMDIR_PLAY) {
39253553Stanimura		if (go == PCMTRIG_START)
39353553Stanimura			csa_startplaydma(csa);
39453553Stanimura		else
39553553Stanimura			csa_stopplaydma(csa);
39653553Stanimura	} else {
39753553Stanimura		if (go == PCMTRIG_START)
39853553Stanimura			csa_startcapturedma(csa);
39953553Stanimura		else
40053553Stanimura			csa_stopcapturedma(csa);
40153553Stanimura	}
40253553Stanimura	return 0;
40353553Stanimura}
40453553Stanimura
40553553Stanimurastatic void
40653553Stanimuracsa_startplaydma(struct csa_info *csa)
40753553Stanimura{
40853553Stanimura	csa_res *resp;
40953553Stanimura	u_long ul;
41053553Stanimura
41153553Stanimura	resp = &csa->res;
41253553Stanimura	ul = csa_readmem(resp, BA1_PCTL);
41353553Stanimura	ul &= 0x0000ffff;
41453553Stanimura	csa_writemem(resp, BA1_PCTL, ul | csa->pctl);
41553553Stanimura	csa_writemem(resp, BA1_PVOL, 0x80008000);
41653553Stanimura}
41753553Stanimura
41853553Stanimurastatic void
41953553Stanimuracsa_startcapturedma(struct csa_info *csa)
42053553Stanimura{
42153553Stanimura	csa_res *resp;
42253553Stanimura	u_long ul;
42353553Stanimura
42453553Stanimura	resp = &csa->res;
42553553Stanimura	ul = csa_readmem(resp, BA1_CCTL);
42653553Stanimura	ul &= 0xffff0000;
42753553Stanimura	csa_writemem(resp, BA1_CCTL, ul | csa->cctl);
42853553Stanimura	csa_writemem(resp, BA1_CVOL, 0x80008000);
42953553Stanimura}
43053553Stanimura
43153553Stanimurastatic void
43253553Stanimuracsa_stopplaydma(struct csa_info *csa)
43353553Stanimura{
43453553Stanimura	csa_res *resp;
43553553Stanimura	u_long ul;
43653553Stanimura
43753553Stanimura	resp = &csa->res;
43853553Stanimura	ul = csa_readmem(resp, BA1_PCTL);
43953553Stanimura	csa->pctl = ul & 0xffff0000;
44053553Stanimura	csa_writemem(resp, BA1_PCTL, ul & 0x0000ffff);
44153553Stanimura	csa_writemem(resp, BA1_PVOL, 0xffffffff);
44255320Stanimura
44355320Stanimura	/* Clear the serial fifos. */
44455320Stanimura	csa_clearserialfifos(resp);
44553553Stanimura}
44653553Stanimura
44753553Stanimurastatic void
44853553Stanimuracsa_stopcapturedma(struct csa_info *csa)
44953553Stanimura{
45053553Stanimura	csa_res *resp;
45153553Stanimura	u_long ul;
45253553Stanimura
45353553Stanimura	resp = &csa->res;
45453553Stanimura	ul = csa_readmem(resp, BA1_CCTL);
45553553Stanimura	csa->cctl = ul & 0x0000ffff;
45653553Stanimura	csa_writemem(resp, BA1_CCTL, ul & 0xffff0000);
45753553Stanimura	csa_writemem(resp, BA1_CVOL, 0xffffffff);
45853553Stanimura}
45953553Stanimura
46053553Stanimurastatic void
46153553Stanimuracsa_powerupdac(csa_res *resp)
46253553Stanimura{
46353553Stanimura	int i;
46453553Stanimura	u_long ul;
46553553Stanimura
46653553Stanimura	/*
46753553Stanimura	 * Power on the DACs on the AC97 codec.  We turn off the DAC
46853553Stanimura	 * powerdown bit and write the new value of the power control
46953553Stanimura	 * register.
47053553Stanimura	 */
47153553Stanimura	ul = csa_readio(resp, BA0_AC97_POWERDOWN);
47253553Stanimura	ul &= 0xfdff;
47353553Stanimura	csa_writeio(resp, BA0_AC97_POWERDOWN, ul);
47453553Stanimura
47553553Stanimura	/*
47653553Stanimura	 * Now, we wait until we sample a DAC ready state.
47753553Stanimura	 */
47853553Stanimura	for (i = 0 ; i < 32 ; i++) {
47953553Stanimura		/*
48053553Stanimura		 * First, lets wait a short while to let things settle out a
48153553Stanimura		 * bit, and to prevent retrying the read too quickly.
48253553Stanimura		 */
48353553Stanimura		DELAY(125);
48453553Stanimura
48553553Stanimura		/*
48653553Stanimura		 * Read the current state of the power control register.
48753553Stanimura		 */
48853553Stanimura		ul = csa_readio(resp, BA0_AC97_POWERDOWN);
48953553Stanimura
49053553Stanimura		/*
49153553Stanimura		 * If the DAC ready state bit is set, then stop waiting.
49253553Stanimura		 */
49353553Stanimura		if ((ul & 0x2) != 0)
49453553Stanimura			break;
49553553Stanimura	}
49653553Stanimura	/*
49753553Stanimura	 * The DACs are now calibrated, so we can unmute the DAC output.
49853553Stanimura	 */
49953553Stanimura	csa_writeio(resp, BA0_AC97_PCM_OUT_VOLUME, 0x0808);
50053553Stanimura}
50153553Stanimura
50253553Stanimurastatic void
50353553Stanimuracsa_powerupadc(csa_res *resp)
50453553Stanimura{
50553553Stanimura	int i;
50653553Stanimura	u_long ul;
50753553Stanimura
50853553Stanimura	/*
50953553Stanimura	 * Power on the ADCs on the AC97 codec.  We turn off the ADC
51053553Stanimura	 * powerdown bit and write the new value of the power control
51153553Stanimura	 * register.
51253553Stanimura	 */
51353553Stanimura	ul = csa_readio(resp, BA0_AC97_POWERDOWN);
51453553Stanimura	ul &= 0xfeff;
51553553Stanimura	csa_writeio(resp, BA0_AC97_POWERDOWN, ul);
51653553Stanimura
51753553Stanimura	/*
51853553Stanimura	 * Now, we wait until we sample a ADC ready state.
51953553Stanimura	 */
52053553Stanimura	for (i = 0 ; i < 32 ; i++) {
52153553Stanimura		/*
52253553Stanimura		 * First, lets wait a short while to let things settle out a
52353553Stanimura		 * bit, and to prevent retrying the read too quickly.
52453553Stanimura		 */
52553553Stanimura		DELAY(125);
52653553Stanimura
52753553Stanimura		/*
52853553Stanimura		 * Read the current state of the power control register.
52953553Stanimura		 */
53053553Stanimura		ul = csa_readio(resp, BA0_AC97_POWERDOWN);
53153553Stanimura
53253553Stanimura		/*
53353553Stanimura		 * If the ADC ready state bit is set, then stop waiting.
53453553Stanimura		 */
53553553Stanimura		if ((ul & 0x1) != 0)
53653553Stanimura			break;
53753553Stanimura	}
53853553Stanimura}
53953553Stanimura
54053553Stanimurastatic int
54153553Stanimuracsa_startdsp(csa_res *resp)
54253553Stanimura{
54353553Stanimura	int i;
54453553Stanimura	u_long ul;
54553553Stanimura
54653553Stanimura	/*
54753553Stanimura	 * Set the frame timer to reflect the number of cycles per frame.
54853553Stanimura	 */
54953553Stanimura	csa_writemem(resp, BA1_FRMT, 0xadf);
55053553Stanimura
55153553Stanimura	/*
55253553Stanimura	 * Turn on the run, run at frame, and DMA enable bits in the local copy of
55353553Stanimura	 * the SP control register.
55453553Stanimura	 */
55553553Stanimura	csa_writemem(resp, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN);
55653553Stanimura
55753553Stanimura	/*
55853553Stanimura	 * Wait until the run at frame bit resets itself in the SP control
55953553Stanimura	 * register.
56053553Stanimura	 */
56153553Stanimura	ul = 0;
56253553Stanimura	for (i = 0 ; i < 25 ; i++) {
56353553Stanimura		/*
56453553Stanimura		 * Wait a little bit, so we don't issue PCI reads too frequently.
56553553Stanimura		 */
56653553Stanimura#if notdef
56753553Stanimura		DELAY(1000);
56853553Stanimura#else
56953553Stanimura		DELAY(125);
57053553Stanimura#endif /* notdef */
57153553Stanimura		/*
57253553Stanimura		 * Fetch the current value of the SP status register.
57353553Stanimura		 */
57453553Stanimura		ul = csa_readmem(resp, BA1_SPCR);
57553553Stanimura
57653553Stanimura		/*
57753553Stanimura		 * If the run at frame bit has reset, then stop waiting.
57853553Stanimura		 */
57953553Stanimura		if((ul & SPCR_RUNFR) == 0)
58053553Stanimura			break;
58153553Stanimura	}
58253553Stanimura	/*
58353553Stanimura	 * If the run at frame bit never reset, then return an error.
58453553Stanimura	 */
58553553Stanimura	if((ul & SPCR_RUNFR) != 0)
58653553Stanimura		return (EAGAIN);
58753553Stanimura
58853553Stanimura	return (0);
58953553Stanimura}
59053553Stanimura
59153553Stanimurastatic int
59253553Stanimuracsachan_getptr(void *data)
59353553Stanimura{
59453553Stanimura	struct csa_chinfo *ch = data;
59553553Stanimura	struct csa_info *csa = ch->parent;
59653553Stanimura	csa_res *resp;
59753553Stanimura	int ptr;
59853553Stanimura
59953553Stanimura	resp = &csa->res;
60053553Stanimura
60153553Stanimura	if (ch->dir == PCMDIR_PLAY) {
60253553Stanimura		ptr = csa_readmem(resp, BA1_PBA) - vtophys(ch->buffer->buf);
60353553Stanimura		if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0)
60453553Stanimura			ptr >>= 1;
60553553Stanimura	} else {
60653553Stanimura		ptr = csa_readmem(resp, BA1_CBA) - vtophys(ch->buffer->buf);
60753553Stanimura		if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0)
60853553Stanimura			ptr >>= 1;
60953553Stanimura	}
61053553Stanimura
61153553Stanimura	return (ptr);
61253553Stanimura}
61353553Stanimura
61453553Stanimurastatic pcmchan_caps *
61553553Stanimuracsachan_getcaps(void *data)
61653553Stanimura{
61753553Stanimura	struct csa_chinfo *ch = data;
61853553Stanimura	return (ch->dir == PCMDIR_PLAY)? &csa_playcaps : &csa_reccaps;
61953553Stanimura}
62053553Stanimura
62153553Stanimura/* The interrupt handler */
62253553Stanimurastatic void
62353553Stanimuracsa_intr (void *p)
62453553Stanimura{
62553553Stanimura	struct csa_info *csa = p;
62653553Stanimura
62755320Stanimura	if ((csa->binfo->hisr & HISR_VC0) != 0)
62853553Stanimura		chn_intr(csa->pch.channel);
62955320Stanimura	if ((csa->binfo->hisr & HISR_VC1) != 0)
63053553Stanimura		chn_intr(csa->rch.channel);
63153553Stanimura}
63253553Stanimura
63353553Stanimura/* -------------------------------------------------------------------- */
63453553Stanimura
63553553Stanimura/*
63653553Stanimura * Probe and attach the card
63753553Stanimura */
63853553Stanimura
63953553Stanimurastatic int
64053553Stanimuracsa_init(struct csa_info *csa)
64153553Stanimura{
64253553Stanimura	csa_res *resp;
64353553Stanimura
64453553Stanimura	resp = &csa->res;
64553553Stanimura
64653553Stanimura	csa->pfie = 0;
64753553Stanimura	csa_stopplaydma(csa);
64853553Stanimura	csa_stopcapturedma(csa);
64953553Stanimura
65053553Stanimura	/* Crank up the power on the DAC and ADC. */
65153553Stanimura	csa_powerupadc(resp);
65253553Stanimura	csa_powerupdac(resp);
65353553Stanimura
65453553Stanimura	csa_setplaysamplerate(resp, 8000);
65553553Stanimura	csa_setcapturesamplerate(resp, 8000);
65653553Stanimura
65753553Stanimura	if (csa_startdsp(resp))
65853553Stanimura		return (1);
65953553Stanimura
66053553Stanimura	return 0;
66153553Stanimura}
66253553Stanimura
66353553Stanimura/* Allocates resources. */
66453553Stanimurastatic int
66553553Stanimuracsa_allocres(struct csa_info *csa, device_t dev)
66653553Stanimura{
66753553Stanimura	csa_res *resp;
66853553Stanimura
66953553Stanimura	resp = &csa->res;
67053553Stanimura	if (resp->io == NULL) {
67153553Stanimura		resp->io = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->io_rid, 0, ~0, CS461x_IO_SIZE, RF_ACTIVE);
67253553Stanimura		if (resp->io == NULL)
67353553Stanimura			return (1);
67453553Stanimura	}
67553553Stanimura	if (resp->mem == NULL) {
67653553Stanimura		resp->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->mem_rid, 0, ~0, CS461x_MEM_SIZE, RF_ACTIVE);
67753553Stanimura		if (resp->mem == NULL)
67853553Stanimura			return (1);
67953553Stanimura	}
68053553Stanimura	if (resp->irq == NULL) {
68153553Stanimura		resp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &resp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
68253553Stanimura		if (resp->irq == NULL)
68353553Stanimura			return (1);
68453553Stanimura	}
68553553Stanimura	if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/CS461x_BUFFSIZE, /*boundary*/CS461x_BUFFSIZE,
68653553Stanimura			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
68753553Stanimura			       /*highaddr*/BUS_SPACE_MAXADDR,
68853553Stanimura			       /*filter*/NULL, /*filterarg*/NULL,
68953553Stanimura			       /*maxsize*/CS461x_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
69053553Stanimura			       /*flags*/0, &csa->parent_dmat) != 0)
69153553Stanimura		return (1);
69253553Stanimura
69353553Stanimura	return (0);
69453553Stanimura}
69553553Stanimura
69653553Stanimura/* Releases resources. */
69753553Stanimurastatic void
69853553Stanimuracsa_releaseres(struct csa_info *csa, device_t dev)
69953553Stanimura{
70053553Stanimura	csa_res *resp;
70153553Stanimura
70253553Stanimura	resp = &csa->res;
70353553Stanimura	if (resp->irq != NULL) {
70453553Stanimura		bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq);
70553553Stanimura		resp->irq = NULL;
70653553Stanimura	}
70753553Stanimura	if (resp->io != NULL) {
70853553Stanimura		bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io);
70953553Stanimura		resp->io = NULL;
71053553Stanimura	}
71153553Stanimura	if (resp->mem != NULL) {
71253553Stanimura		bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem);
71353553Stanimura		resp->mem = NULL;
71453553Stanimura	}
71553553Stanimura}
71653553Stanimura
71753553Stanimurastatic int pcmcsa_probe(device_t dev);
71853553Stanimurastatic int pcmcsa_attach(device_t dev);
71953553Stanimura
72053553Stanimurastatic int
72153553Stanimurapcmcsa_probe(device_t dev)
72253553Stanimura{
72353553Stanimura	char *s;
72453553Stanimura	struct sndcard_func *func;
72553553Stanimura
72653553Stanimura	/* The parent device has already been probed. */
72753553Stanimura
72853553Stanimura	func = device_get_ivars(dev);
72953553Stanimura	if (func == NULL || func->func != SCF_PCM)
73053553Stanimura		return (ENXIO);
73153553Stanimura
73253553Stanimura	s = "CS461x PCM Audio";
73353553Stanimura
73453553Stanimura	device_set_desc(dev, s);
73553553Stanimura	return (0);
73653553Stanimura}
73753553Stanimura
73853553Stanimurastatic int
73953553Stanimurapcmcsa_attach(device_t dev)
74053553Stanimura{
74153553Stanimura	snddev_info *devinfo;
74253553Stanimura	struct csa_info *csa;
74353553Stanimura	csa_res *resp;
74453553Stanimura	int unit;
74553553Stanimura	char status[SND_STATUSLEN];
74653553Stanimura	struct ac97_info *codec;
74755320Stanimura	struct sndcard_func *func;
74853553Stanimura
74953553Stanimura	devinfo = device_get_softc(dev);
75053553Stanimura	csa = malloc(sizeof(*csa), M_DEVBUF, M_NOWAIT);
75153553Stanimura	if (csa == NULL)
75253553Stanimura		return (ENOMEM);
75353553Stanimura	bzero(csa, sizeof(*csa));
75453553Stanimura	unit = device_get_unit(dev);
75555320Stanimura	func = device_get_ivars(dev);
75655320Stanimura	csa->binfo = func->varinfo;
75753553Stanimura
75853553Stanimura	/* Allocate the resources. */
75953553Stanimura	resp = &csa->res;
76053553Stanimura	resp->io_rid = CS461x_IO_OFFSET;
76153553Stanimura	resp->mem_rid = CS461x_MEM_OFFSET;
76253553Stanimura	resp->irq_rid = 0;
76353553Stanimura	if (csa_allocres(csa, dev)) {
76453553Stanimura		csa_releaseres(csa, dev);
76553553Stanimura		return (ENXIO);
76653553Stanimura	}
76753553Stanimura
76853553Stanimura	if (csa_init(csa)) {
76953553Stanimura		csa_releaseres(csa, dev);
77053553Stanimura		return (ENXIO);
77153553Stanimura	}
77253553Stanimura	codec = ac97_create(csa, csa_rdcd, csa_wrcd);
77353553Stanimura	if (codec == NULL)
77453553Stanimura		return (ENXIO);
77553553Stanimura	mixer_init(devinfo, &ac97_mixer, codec);
77653553Stanimura
77753553Stanimura	snprintf(status, SND_STATUSLEN, "at irq %ld", rman_get_start(resp->irq));
77853553Stanimura
77953553Stanimura	/* Enable interrupt. */
78053553Stanimura	if (bus_setup_intr(dev, resp->irq, INTR_TYPE_TTY, csa_intr, csa, &csa->ih)) {
78153553Stanimura		csa_releaseres(csa, dev);
78253553Stanimura		return (ENXIO);
78353553Stanimura	}
78453553Stanimura	csa_writemem(resp, BA1_PFIE, csa_readmem(resp, BA1_PFIE) & ~0x0000f03f);
78553553Stanimura	csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001);
78653553Stanimura
78753553Stanimura	if (pcm_register(dev, csa, 1, 1)) {
78853553Stanimura		csa_releaseres(csa, dev);
78953553Stanimura		return (ENXIO);
79053553Stanimura	}
79153553Stanimura	pcm_addchan(dev, PCMDIR_REC, &csa_chantemplate, csa);
79253553Stanimura	pcm_addchan(dev, PCMDIR_PLAY, &csa_chantemplate, csa);
79353553Stanimura	pcm_setstatus(dev, status);
79453553Stanimura
79553553Stanimura	return (0);
79653553Stanimura}
79753553Stanimura
79853553Stanimura/* ac97 codec */
79953553Stanimura
80053553Stanimurastatic u_int32_t
80153553Stanimuracsa_rdcd(void *devinfo, int regno)
80253553Stanimura{
80353553Stanimura	u_int32_t data;
80453553Stanimura	struct csa_info *csa = (struct csa_info *)devinfo;
80553553Stanimura
80653553Stanimura	if (csa_readcodec(&csa->res, regno + BA0_AC97_RESET, &data))
80753553Stanimura		data = 0;
80853553Stanimura
80953553Stanimura	return data;
81053553Stanimura}
81153553Stanimura
81253553Stanimurastatic void
81353553Stanimuracsa_wrcd(void *devinfo, int regno, u_int32_t data)
81453553Stanimura{
81553553Stanimura	struct csa_info *csa = (struct csa_info *)devinfo;
81653553Stanimura
81753553Stanimura	csa_writecodec(&csa->res, regno + BA0_AC97_RESET, data);
81853553Stanimura}
81953553Stanimura
82053553Stanimurastatic device_method_t pcmcsa_methods[] = {
82153553Stanimura	/* Device interface */
82253553Stanimura	DEVMETHOD(device_probe , pcmcsa_probe ),
82353553Stanimura	DEVMETHOD(device_attach, pcmcsa_attach),
82453553Stanimura
82553553Stanimura	{ 0, 0 },
82653553Stanimura};
82753553Stanimura
82853553Stanimurastatic driver_t pcmcsa_driver = {
82953553Stanimura	"pcm",
83053553Stanimura	pcmcsa_methods,
83153553Stanimura	sizeof(snddev_info),
83253553Stanimura};
83353553Stanimura
83453553Stanimurastatic devclass_t pcm_devclass;
83553553Stanimura
83653553StanimuraDRIVER_MODULE(pcmcsa, csa, pcmcsa_driver, pcm_devclass, 0, 0);
837