xref: /freebsd/sys/dev/sound/pci/csapcm.c (revision 516a9c0212b003e1da0c6f4476dbe4f3f431606c)
1098ca2bdSWarner Losh /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4fe1a5d1cSSeigo Tanimura  * Copyright (c) 1999 Seigo Tanimura
5fe1a5d1cSSeigo Tanimura  * All rights reserved.
6fe1a5d1cSSeigo Tanimura  *
77012990aSSeigo Tanimura  * Portions of this source are based on cwcealdr.cpp and dhwiface.cpp in
87012990aSSeigo Tanimura  * cwcealdr1.zip, the sample sources by Crystal Semiconductor.
97012990aSSeigo Tanimura  * Copyright (c) 1996-1998 Crystal Semiconductor Corp.
107012990aSSeigo Tanimura  *
11fe1a5d1cSSeigo Tanimura  * Redistribution and use in source and binary forms, with or without
12fe1a5d1cSSeigo Tanimura  * modification, are permitted provided that the following conditions
13fe1a5d1cSSeigo Tanimura  * are met:
14fe1a5d1cSSeigo Tanimura  * 1. Redistributions of source code must retain the above copyright
15fe1a5d1cSSeigo Tanimura  *    notice, this list of conditions and the following disclaimer.
16fe1a5d1cSSeigo Tanimura  * 2. Redistributions in binary form must reproduce the above copyright
17fe1a5d1cSSeigo Tanimura  *    notice, this list of conditions and the following disclaimer in the
18fe1a5d1cSSeigo Tanimura  *    documentation and/or other materials provided with the distribution.
19fe1a5d1cSSeigo Tanimura  *
20fe1a5d1cSSeigo Tanimura  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21fe1a5d1cSSeigo Tanimura  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22fe1a5d1cSSeigo Tanimura  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23fe1a5d1cSSeigo Tanimura  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24fe1a5d1cSSeigo Tanimura  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25fe1a5d1cSSeigo Tanimura  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26fe1a5d1cSSeigo Tanimura  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27fe1a5d1cSSeigo Tanimura  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28fe1a5d1cSSeigo Tanimura  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29fe1a5d1cSSeigo Tanimura  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30fe1a5d1cSSeigo Tanimura  * SUCH DAMAGE.
31fe1a5d1cSSeigo Tanimura  */
32fe1a5d1cSSeigo Tanimura 
3390da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS
3490da2b28SAriff Abdullah #include "opt_snd.h"
3590da2b28SAriff Abdullah #endif
3690da2b28SAriff Abdullah 
37fe1a5d1cSSeigo Tanimura #include <dev/sound/pcm/sound.h>
38fe1a5d1cSSeigo Tanimura #include <dev/sound/pcm/ac97.h>
39fe1a5d1cSSeigo Tanimura #include <dev/sound/pci/csareg.h>
40fe1a5d1cSSeigo Tanimura #include <dev/sound/pci/csavar.h>
41fe1a5d1cSSeigo Tanimura 
4290cf0136SWarner Losh #include <dev/pci/pcireg.h>
4390cf0136SWarner Losh #include <dev/pci/pcivar.h>
44fe1a5d1cSSeigo Tanimura 
4520ac1df7SCameron Grant /* Buffer size on dma transfer. Fixed for CS416x. */
4620ac1df7SCameron Grant #define CS461x_BUFFSIZE   (4 * 1024)
4720ac1df7SCameron Grant 
4820ac1df7SCameron Grant #define GOF_PER_SEC 200
4920ac1df7SCameron Grant 
50fe1a5d1cSSeigo Tanimura /* device private data */
51fe1a5d1cSSeigo Tanimura struct csa_info;
52fe1a5d1cSSeigo Tanimura 
53fe1a5d1cSSeigo Tanimura struct csa_chinfo {
54fe1a5d1cSSeigo Tanimura 	struct csa_info *parent;
5566ef8af5SCameron Grant 	struct pcm_channel *channel;
5666ef8af5SCameron Grant 	struct snd_dbuf *buffer;
57fe1a5d1cSSeigo Tanimura 	int dir;
5820ac1df7SCameron Grant 	u_int32_t fmt, spd;
59769309aaSSeigo Tanimura 	int dma;
60fe1a5d1cSSeigo Tanimura };
61fe1a5d1cSSeigo Tanimura 
62fe1a5d1cSSeigo Tanimura struct csa_info {
63fe1a5d1cSSeigo Tanimura 	csa_res		res; /* resource */
64fe1a5d1cSSeigo Tanimura 	void		*ih; /* Interrupt cookie */
65fe1a5d1cSSeigo Tanimura 	bus_dma_tag_t	parent_dmat; /* DMA tag */
66f259d7eeSSeigo Tanimura 	struct csa_bridgeinfo *binfo; /* The state of the parent. */
6720ac1df7SCameron Grant 	struct csa_card *card;
68fe1a5d1cSSeigo Tanimura 
6920ac1df7SCameron Grant 	int active;
70fe1a5d1cSSeigo Tanimura 	/* Contents of board's registers */
71fe1a5d1cSSeigo Tanimura 	u_long		pfie;
72fe1a5d1cSSeigo Tanimura 	u_long		pctl;
73fe1a5d1cSSeigo Tanimura 	u_long		cctl;
74fe1a5d1cSSeigo Tanimura 	struct csa_chinfo pch, rch;
75961478afSGleb Smirnoff 	u_int32_t	ac97[CS461x_AC97_NUMBER_RESTORE_REGS];
76961478afSGleb Smirnoff 	u_int32_t	ac97_powerdown;
77961478afSGleb Smirnoff 	u_int32_t	ac97_general_purpose;
78fe1a5d1cSSeigo Tanimura };
79fe1a5d1cSSeigo Tanimura 
80fe1a5d1cSSeigo Tanimura /* -------------------------------------------------------------------- */
81fe1a5d1cSSeigo Tanimura 
82fe1a5d1cSSeigo Tanimura /* prototypes */
83fe1a5d1cSSeigo Tanimura static int      csa_init(struct csa_info *);
84fe1a5d1cSSeigo Tanimura static void     csa_intr(void *);
85fe1a5d1cSSeigo Tanimura static void	csa_setplaysamplerate(csa_res *resp, u_long ulInRate);
86fe1a5d1cSSeigo Tanimura static void	csa_setcapturesamplerate(csa_res *resp, u_long ulOutRate);
87fe1a5d1cSSeigo Tanimura static void	csa_startplaydma(struct csa_info *csa);
88fe1a5d1cSSeigo Tanimura static void	csa_startcapturedma(struct csa_info *csa);
89fe1a5d1cSSeigo Tanimura static void	csa_stopplaydma(struct csa_info *csa);
90fe1a5d1cSSeigo Tanimura static void	csa_stopcapturedma(struct csa_info *csa);
91fe1a5d1cSSeigo Tanimura static int	csa_startdsp(csa_res *resp);
92961478afSGleb Smirnoff static int	csa_stopdsp(csa_res *resp);
93fe1a5d1cSSeigo Tanimura static int	csa_allocres(struct csa_info *scp, device_t dev);
94fe1a5d1cSSeigo Tanimura static void	csa_releaseres(struct csa_info *scp, device_t dev);
95961478afSGleb Smirnoff static void	csa_ac97_suspend(struct csa_info *csa);
96961478afSGleb Smirnoff static void	csa_ac97_resume(struct csa_info *csa);
97fe1a5d1cSSeigo Tanimura 
98513693beSCameron Grant static u_int32_t csa_playfmt[] = {
9990da2b28SAriff Abdullah 	SND_FORMAT(AFMT_U8, 1, 0),
10090da2b28SAriff Abdullah 	SND_FORMAT(AFMT_U8, 2, 0),
10190da2b28SAriff Abdullah 	SND_FORMAT(AFMT_S8, 1, 0),
10290da2b28SAriff Abdullah 	SND_FORMAT(AFMT_S8, 2, 0),
10390da2b28SAriff Abdullah 	SND_FORMAT(AFMT_S16_LE, 1, 0),
10490da2b28SAriff Abdullah 	SND_FORMAT(AFMT_S16_LE, 2, 0),
10590da2b28SAriff Abdullah 	SND_FORMAT(AFMT_S16_BE, 1, 0),
10690da2b28SAriff Abdullah 	SND_FORMAT(AFMT_S16_BE, 2, 0),
107513693beSCameron Grant 	0
108fe1a5d1cSSeigo Tanimura };
10966ef8af5SCameron Grant static struct pcmchan_caps csa_playcaps = {8000, 48000, csa_playfmt, 0};
110513693beSCameron Grant 
111513693beSCameron Grant static u_int32_t csa_recfmt[] = {
11290da2b28SAriff Abdullah 	SND_FORMAT(AFMT_S16_LE, 1, 0),
11390da2b28SAriff Abdullah 	SND_FORMAT(AFMT_S16_LE, 2, 0),
114513693beSCameron Grant 	0
115513693beSCameron Grant };
11666ef8af5SCameron Grant static struct pcmchan_caps csa_reccaps = {11025, 48000, csa_recfmt, 0};
117fe1a5d1cSSeigo Tanimura 
118fe1a5d1cSSeigo Tanimura /* -------------------------------------------------------------------- */
11920ac1df7SCameron Grant 
12020ac1df7SCameron Grant static int
csa_active(struct csa_info * csa,int run)12120ac1df7SCameron Grant csa_active(struct csa_info *csa, int run)
12220ac1df7SCameron Grant {
123961478afSGleb Smirnoff 	int old;
12420ac1df7SCameron Grant 
12520ac1df7SCameron Grant 	old = csa->active;
12620ac1df7SCameron Grant 	csa->active += run;
12720ac1df7SCameron Grant 
128961478afSGleb Smirnoff 	if ((csa->active > 1) || (csa->active < -1))
129961478afSGleb Smirnoff 		csa->active = 0;
13020ac1df7SCameron Grant 	if (csa->card->active)
131961478afSGleb Smirnoff 		return (csa->card->active(!(csa->active && old)));
132961478afSGleb Smirnoff 
13320ac1df7SCameron Grant 	return 0;
13420ac1df7SCameron Grant }
13520ac1df7SCameron Grant 
13620ac1df7SCameron Grant /* -------------------------------------------------------------------- */
1370f55ac6cSCameron Grant /* ac97 codec */
138fe1a5d1cSSeigo Tanimura 
1390f55ac6cSCameron Grant static int
csa_rdcd(kobj_t obj,void * devinfo,int regno)1400f55ac6cSCameron Grant csa_rdcd(kobj_t obj, void *devinfo, int regno)
141fe1a5d1cSSeigo Tanimura {
1420f55ac6cSCameron Grant 	u_int32_t data;
1430f55ac6cSCameron Grant 	struct csa_info *csa = (struct csa_info *)devinfo;
144fe1a5d1cSSeigo Tanimura 
14520ac1df7SCameron Grant 	csa_active(csa, 1);
1460f55ac6cSCameron Grant 	if (csa_readcodec(&csa->res, regno + BA0_AC97_RESET, &data))
1470f55ac6cSCameron Grant 		data = 0;
14820ac1df7SCameron Grant 	csa_active(csa, -1);
1490f55ac6cSCameron Grant 
1500f55ac6cSCameron Grant 	return data;
151fe1a5d1cSSeigo Tanimura }
152fe1a5d1cSSeigo Tanimura 
153fe1a5d1cSSeigo Tanimura static int
csa_wrcd(kobj_t obj,void * devinfo,int regno,u_int32_t data)1540f55ac6cSCameron Grant csa_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data)
155fe1a5d1cSSeigo Tanimura {
1560f55ac6cSCameron Grant 	struct csa_info *csa = (struct csa_info *)devinfo;
157fe1a5d1cSSeigo Tanimura 
15820ac1df7SCameron Grant 	csa_active(csa, 1);
1590f55ac6cSCameron Grant 	csa_writecodec(&csa->res, regno + BA0_AC97_RESET, data);
16020ac1df7SCameron Grant 	csa_active(csa, -1);
161fe1a5d1cSSeigo Tanimura 
162fe1a5d1cSSeigo Tanimura 	return 0;
163fe1a5d1cSSeigo Tanimura }
164fe1a5d1cSSeigo Tanimura 
1650f55ac6cSCameron Grant static kobj_method_t csa_ac97_methods[] = {
1660f55ac6cSCameron Grant     	KOBJMETHOD(ac97_read,		csa_rdcd),
1670f55ac6cSCameron Grant     	KOBJMETHOD(ac97_write,		csa_wrcd),
16890da2b28SAriff Abdullah 	KOBJMETHOD_END
1690f55ac6cSCameron Grant };
1700f55ac6cSCameron Grant AC97_DECLARE(csa_ac97);
171fe1a5d1cSSeigo Tanimura 
172fe1a5d1cSSeigo Tanimura static void
csa_setplaysamplerate(csa_res * resp,u_long ulInRate)173fe1a5d1cSSeigo Tanimura csa_setplaysamplerate(csa_res *resp, u_long ulInRate)
174fe1a5d1cSSeigo Tanimura {
175fe1a5d1cSSeigo Tanimura 	u_long ulTemp1, ulTemp2;
176fe1a5d1cSSeigo Tanimura 	u_long ulPhiIncr;
177fe1a5d1cSSeigo Tanimura 	u_long ulCorrectionPerGOF, ulCorrectionPerSec;
178fe1a5d1cSSeigo Tanimura 	u_long ulOutRate;
179fe1a5d1cSSeigo Tanimura 
180fe1a5d1cSSeigo Tanimura 	ulOutRate = 48000;
181fe1a5d1cSSeigo Tanimura 
182fe1a5d1cSSeigo Tanimura 	/*
183fe1a5d1cSSeigo Tanimura 	 * Compute the values used to drive the actual sample rate conversion.
184fe1a5d1cSSeigo Tanimura 	 * The following formulas are being computed, using inline assembly
185fe1a5d1cSSeigo Tanimura 	 * since we need to use 64 bit arithmetic to compute the values:
186fe1a5d1cSSeigo Tanimura 	 *
187fe1a5d1cSSeigo Tanimura 	 *     ulPhiIncr = floor((Fs,in * 2^26) / Fs,out)
188fe1a5d1cSSeigo Tanimura 	 *     ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) /
189fe1a5d1cSSeigo Tanimura 	 *                                GOF_PER_SEC)
190fe1a5d1cSSeigo Tanimura 	 *     ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
191fe1a5d1cSSeigo Tanimura 	 *                          GOF_PER_SEC * ulCorrectionPerGOF
192fe1a5d1cSSeigo Tanimura 	 *
193fe1a5d1cSSeigo Tanimura 	 * i.e.
194fe1a5d1cSSeigo Tanimura 	 *
195fe1a5d1cSSeigo Tanimura 	 *     ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
196fe1a5d1cSSeigo Tanimura 	 *     ulCorrectionPerGOF:ulCorrectionPerSec =
197fe1a5d1cSSeigo Tanimura 	 *         dividend:remainder(ulOther / GOF_PER_SEC)
198fe1a5d1cSSeigo Tanimura 	 */
199fe1a5d1cSSeigo Tanimura 	ulTemp1 = ulInRate << 16;
200fe1a5d1cSSeigo Tanimura 	ulPhiIncr = ulTemp1 / ulOutRate;
201fe1a5d1cSSeigo Tanimura 	ulTemp1 -= ulPhiIncr * ulOutRate;
202fe1a5d1cSSeigo Tanimura 	ulTemp1 <<= 10;
203fe1a5d1cSSeigo Tanimura 	ulPhiIncr <<= 10;
204fe1a5d1cSSeigo Tanimura 	ulTemp2 = ulTemp1 / ulOutRate;
205fe1a5d1cSSeigo Tanimura 	ulPhiIncr += ulTemp2;
206fe1a5d1cSSeigo Tanimura 	ulTemp1 -= ulTemp2 * ulOutRate;
207fe1a5d1cSSeigo Tanimura 	ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC;
208fe1a5d1cSSeigo Tanimura 	ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC;
209fe1a5d1cSSeigo Tanimura 	ulCorrectionPerSec = ulTemp1;
210fe1a5d1cSSeigo Tanimura 
211fe1a5d1cSSeigo Tanimura 	/*
212fe1a5d1cSSeigo Tanimura 	 * Fill in the SampleRateConverter control block.
213fe1a5d1cSSeigo Tanimura 	 */
214fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_PSRC, ((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF));
215fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_PPI, ulPhiIncr);
216fe1a5d1cSSeigo Tanimura }
217fe1a5d1cSSeigo Tanimura 
218fe1a5d1cSSeigo Tanimura static void
csa_setcapturesamplerate(csa_res * resp,u_long ulOutRate)219fe1a5d1cSSeigo Tanimura csa_setcapturesamplerate(csa_res *resp, u_long ulOutRate)
220fe1a5d1cSSeigo Tanimura {
221fe1a5d1cSSeigo Tanimura 	u_long ulPhiIncr, ulCoeffIncr, ulTemp1, ulTemp2;
222fe1a5d1cSSeigo Tanimura 	u_long ulCorrectionPerGOF, ulCorrectionPerSec, ulInitialDelay;
223fe1a5d1cSSeigo Tanimura 	u_long dwFrameGroupLength, dwCnt;
224fe1a5d1cSSeigo Tanimura 	u_long ulInRate;
225fe1a5d1cSSeigo Tanimura 
226fe1a5d1cSSeigo Tanimura 	ulInRate = 48000;
227fe1a5d1cSSeigo Tanimura 
228fe1a5d1cSSeigo Tanimura 	/*
229fe1a5d1cSSeigo Tanimura 	 * We can only decimate by up to a factor of 1/9th the hardware rate.
230fe1a5d1cSSeigo Tanimura 	 * Return an error if an attempt is made to stray outside that limit.
231fe1a5d1cSSeigo Tanimura 	 */
232fe1a5d1cSSeigo Tanimura 	if((ulOutRate * 9) < ulInRate)
233fe1a5d1cSSeigo Tanimura 		return;
234fe1a5d1cSSeigo Tanimura 
235fe1a5d1cSSeigo Tanimura 	/*
236fe1a5d1cSSeigo Tanimura 	 * We can not capture at at rate greater than the Input Rate (48000).
237fe1a5d1cSSeigo Tanimura 	 * Return an error if an attempt is made to stray outside that limit.
238fe1a5d1cSSeigo Tanimura 	 */
239fe1a5d1cSSeigo Tanimura 	if(ulOutRate > ulInRate)
240fe1a5d1cSSeigo Tanimura 		return;
241fe1a5d1cSSeigo Tanimura 
242fe1a5d1cSSeigo Tanimura 	/*
243fe1a5d1cSSeigo Tanimura 	 * Compute the values used to drive the actual sample rate conversion.
244fe1a5d1cSSeigo Tanimura 	 * The following formulas are being computed, using inline assembly
245fe1a5d1cSSeigo Tanimura 	 * since we need to use 64 bit arithmetic to compute the values:
246fe1a5d1cSSeigo Tanimura 	 *
247fe1a5d1cSSeigo Tanimura 	 *     ulCoeffIncr = -floor((Fs,out * 2^23) / Fs,in)
248fe1a5d1cSSeigo Tanimura 	 *     ulPhiIncr = floor((Fs,in * 2^26) / Fs,out)
249fe1a5d1cSSeigo Tanimura 	 *     ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) /
250fe1a5d1cSSeigo Tanimura 	 *                                GOF_PER_SEC)
251fe1a5d1cSSeigo Tanimura 	 *     ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
252fe1a5d1cSSeigo Tanimura 	 *                          GOF_PER_SEC * ulCorrectionPerGOF
253fe1a5d1cSSeigo Tanimura 	 *     ulInitialDelay = ceil((24 * Fs,in) / Fs,out)
254fe1a5d1cSSeigo Tanimura 	 *
255fe1a5d1cSSeigo Tanimura 	 * i.e.
256fe1a5d1cSSeigo Tanimura 	 *
257fe1a5d1cSSeigo Tanimura 	 *     ulCoeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in))
258fe1a5d1cSSeigo Tanimura 	 *     ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
259fe1a5d1cSSeigo Tanimura 	 *     ulCorrectionPerGOF:ulCorrectionPerSec =
260fe1a5d1cSSeigo Tanimura 	 *         dividend:remainder(ulOther / GOF_PER_SEC)
261fe1a5d1cSSeigo Tanimura 	 *     ulInitialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out)
262fe1a5d1cSSeigo Tanimura 	 */
263fe1a5d1cSSeigo Tanimura 	ulTemp1 = ulOutRate << 16;
264fe1a5d1cSSeigo Tanimura 	ulCoeffIncr = ulTemp1 / ulInRate;
265fe1a5d1cSSeigo Tanimura 	ulTemp1 -= ulCoeffIncr * ulInRate;
266fe1a5d1cSSeigo Tanimura 	ulTemp1 <<= 7;
267fe1a5d1cSSeigo Tanimura 	ulCoeffIncr <<= 7;
268fe1a5d1cSSeigo Tanimura 	ulCoeffIncr += ulTemp1 / ulInRate;
269fe1a5d1cSSeigo Tanimura 	ulCoeffIncr ^= 0xFFFFFFFF;
270fe1a5d1cSSeigo Tanimura 	ulCoeffIncr++;
271fe1a5d1cSSeigo Tanimura 	ulTemp1 = ulInRate << 16;
272fe1a5d1cSSeigo Tanimura 	ulPhiIncr = ulTemp1 / ulOutRate;
273fe1a5d1cSSeigo Tanimura 	ulTemp1 -= ulPhiIncr * ulOutRate;
274fe1a5d1cSSeigo Tanimura 	ulTemp1 <<= 10;
275fe1a5d1cSSeigo Tanimura 	ulPhiIncr <<= 10;
276fe1a5d1cSSeigo Tanimura 	ulTemp2 = ulTemp1 / ulOutRate;
277fe1a5d1cSSeigo Tanimura 	ulPhiIncr += ulTemp2;
278fe1a5d1cSSeigo Tanimura 	ulTemp1 -= ulTemp2 * ulOutRate;
279fe1a5d1cSSeigo Tanimura 	ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC;
280fe1a5d1cSSeigo Tanimura 	ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC;
281fe1a5d1cSSeigo Tanimura 	ulCorrectionPerSec = ulTemp1;
282fe1a5d1cSSeigo Tanimura 	ulInitialDelay = ((ulInRate * 24) + ulOutRate - 1) / ulOutRate;
283fe1a5d1cSSeigo Tanimura 
284fe1a5d1cSSeigo Tanimura 	/*
285fe1a5d1cSSeigo Tanimura 	 * Fill in the VariDecimate control block.
286fe1a5d1cSSeigo Tanimura 	 */
287fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_CSRC,
288fe1a5d1cSSeigo Tanimura 		     ((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF));
289fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_CCI, ulCoeffIncr);
290fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_CD,
291fe1a5d1cSSeigo Tanimura 	     (((BA1_VARIDEC_BUF_1 + (ulInitialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
292fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_CPI, ulPhiIncr);
293fe1a5d1cSSeigo Tanimura 
294fe1a5d1cSSeigo Tanimura 	/*
295fe1a5d1cSSeigo Tanimura 	 * Figure out the frame group length for the write back task.  Basically,
296fe1a5d1cSSeigo Tanimura 	 * this is just the factors of 24000 (2^6*3*5^3) that are not present in
297fe1a5d1cSSeigo Tanimura 	 * the output sample rate.
298fe1a5d1cSSeigo Tanimura 	 */
299fe1a5d1cSSeigo Tanimura 	dwFrameGroupLength = 1;
300fe1a5d1cSSeigo Tanimura 	for(dwCnt = 2; dwCnt <= 64; dwCnt *= 2)
301fe1a5d1cSSeigo Tanimura 	{
302fe1a5d1cSSeigo Tanimura 		if(((ulOutRate / dwCnt) * dwCnt) !=
303fe1a5d1cSSeigo Tanimura 		   ulOutRate)
304fe1a5d1cSSeigo Tanimura 		{
305fe1a5d1cSSeigo Tanimura 			dwFrameGroupLength *= 2;
306fe1a5d1cSSeigo Tanimura 		}
307fe1a5d1cSSeigo Tanimura 	}
308fe1a5d1cSSeigo Tanimura 	if(((ulOutRate / 3) * 3) !=
309fe1a5d1cSSeigo Tanimura 	   ulOutRate)
310fe1a5d1cSSeigo Tanimura 	{
311fe1a5d1cSSeigo Tanimura 		dwFrameGroupLength *= 3;
312fe1a5d1cSSeigo Tanimura 	}
313fe1a5d1cSSeigo Tanimura 	for(dwCnt = 5; dwCnt <= 125; dwCnt *= 5)
314fe1a5d1cSSeigo Tanimura 	{
315fe1a5d1cSSeigo Tanimura 		if(((ulOutRate / dwCnt) * dwCnt) !=
316fe1a5d1cSSeigo Tanimura 		   ulOutRate)
317fe1a5d1cSSeigo Tanimura 		{
318fe1a5d1cSSeigo Tanimura 			dwFrameGroupLength *= 5;
319fe1a5d1cSSeigo Tanimura 		}
320fe1a5d1cSSeigo Tanimura 	}
321fe1a5d1cSSeigo Tanimura 
322fe1a5d1cSSeigo Tanimura 	/*
323fe1a5d1cSSeigo Tanimura 	 * Fill in the WriteBack control block.
324fe1a5d1cSSeigo Tanimura 	 */
325fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_CFG1, dwFrameGroupLength);
326fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_CFG2, (0x00800000 | dwFrameGroupLength));
327fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_CCST, 0x0000FFFF);
328fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_CSPB, ((65536 * ulOutRate) / 24000));
329fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, (BA1_CSPB + 4), 0x0000FFFF);
330fe1a5d1cSSeigo Tanimura }
331fe1a5d1cSSeigo Tanimura 
332fe1a5d1cSSeigo Tanimura static void
csa_startplaydma(struct csa_info * csa)333fe1a5d1cSSeigo Tanimura csa_startplaydma(struct csa_info *csa)
334fe1a5d1cSSeigo Tanimura {
335fe1a5d1cSSeigo Tanimura 	csa_res *resp;
336fe1a5d1cSSeigo Tanimura 	u_long ul;
337fe1a5d1cSSeigo Tanimura 
338769309aaSSeigo Tanimura 	if (!csa->pch.dma) {
339fe1a5d1cSSeigo Tanimura 		resp = &csa->res;
340fe1a5d1cSSeigo Tanimura 		ul = csa_readmem(resp, BA1_PCTL);
341fe1a5d1cSSeigo Tanimura 		ul &= 0x0000ffff;
342fe1a5d1cSSeigo Tanimura 		csa_writemem(resp, BA1_PCTL, ul | csa->pctl);
343fe1a5d1cSSeigo Tanimura 		csa_writemem(resp, BA1_PVOL, 0x80008000);
344769309aaSSeigo Tanimura 		csa->pch.dma = 1;
345769309aaSSeigo Tanimura 	}
346fe1a5d1cSSeigo Tanimura }
347fe1a5d1cSSeigo Tanimura 
348fe1a5d1cSSeigo Tanimura static void
csa_startcapturedma(struct csa_info * csa)349fe1a5d1cSSeigo Tanimura csa_startcapturedma(struct csa_info *csa)
350fe1a5d1cSSeigo Tanimura {
351fe1a5d1cSSeigo Tanimura 	csa_res *resp;
352fe1a5d1cSSeigo Tanimura 	u_long ul;
353fe1a5d1cSSeigo Tanimura 
354769309aaSSeigo Tanimura 	if (!csa->rch.dma) {
355fe1a5d1cSSeigo Tanimura 		resp = &csa->res;
356fe1a5d1cSSeigo Tanimura 		ul = csa_readmem(resp, BA1_CCTL);
357fe1a5d1cSSeigo Tanimura 		ul &= 0xffff0000;
358fe1a5d1cSSeigo Tanimura 		csa_writemem(resp, BA1_CCTL, ul | csa->cctl);
359fe1a5d1cSSeigo Tanimura 		csa_writemem(resp, BA1_CVOL, 0x80008000);
360769309aaSSeigo Tanimura 		csa->rch.dma = 1;
361769309aaSSeigo Tanimura 	}
362fe1a5d1cSSeigo Tanimura }
363fe1a5d1cSSeigo Tanimura 
364fe1a5d1cSSeigo Tanimura static void
csa_stopplaydma(struct csa_info * csa)365fe1a5d1cSSeigo Tanimura csa_stopplaydma(struct csa_info *csa)
366fe1a5d1cSSeigo Tanimura {
367fe1a5d1cSSeigo Tanimura 	csa_res *resp;
368fe1a5d1cSSeigo Tanimura 	u_long ul;
369fe1a5d1cSSeigo Tanimura 
370769309aaSSeigo Tanimura 	if (csa->pch.dma) {
371fe1a5d1cSSeigo Tanimura 		resp = &csa->res;
372fe1a5d1cSSeigo Tanimura 		ul = csa_readmem(resp, BA1_PCTL);
373fe1a5d1cSSeigo Tanimura 		csa->pctl = ul & 0xffff0000;
374fe1a5d1cSSeigo Tanimura 		csa_writemem(resp, BA1_PCTL, ul & 0x0000ffff);
375fe1a5d1cSSeigo Tanimura 		csa_writemem(resp, BA1_PVOL, 0xffffffff);
376769309aaSSeigo Tanimura 		csa->pch.dma = 0;
3778f7076b8SSeigo Tanimura 
3788f7076b8SSeigo Tanimura 		/*
3798f7076b8SSeigo Tanimura 		 * The bitwise pointer of the serial FIFO in the DSP
3808f7076b8SSeigo Tanimura 		 * seems to make an error upon starting or stopping the
3818f7076b8SSeigo Tanimura 		 * DSP. Clear the FIFO and correct the pointer if we
3828f7076b8SSeigo Tanimura 		 * are not capturing.
3838f7076b8SSeigo Tanimura 		 */
3848f7076b8SSeigo Tanimura 		if (!csa->rch.dma) {
3858f7076b8SSeigo Tanimura 			csa_clearserialfifos(resp);
3868f7076b8SSeigo Tanimura 			csa_writeio(resp, BA0_SERBSP, 0);
3878f7076b8SSeigo Tanimura 		}
388769309aaSSeigo Tanimura 	}
389fe1a5d1cSSeigo Tanimura }
390fe1a5d1cSSeigo Tanimura 
391fe1a5d1cSSeigo Tanimura static void
csa_stopcapturedma(struct csa_info * csa)392fe1a5d1cSSeigo Tanimura csa_stopcapturedma(struct csa_info *csa)
393fe1a5d1cSSeigo Tanimura {
394fe1a5d1cSSeigo Tanimura 	csa_res *resp;
395fe1a5d1cSSeigo Tanimura 	u_long ul;
396fe1a5d1cSSeigo Tanimura 
397769309aaSSeigo Tanimura 	if (csa->rch.dma) {
398fe1a5d1cSSeigo Tanimura 		resp = &csa->res;
399fe1a5d1cSSeigo Tanimura 		ul = csa_readmem(resp, BA1_CCTL);
400fe1a5d1cSSeigo Tanimura 		csa->cctl = ul & 0x0000ffff;
401fe1a5d1cSSeigo Tanimura 		csa_writemem(resp, BA1_CCTL, ul & 0xffff0000);
402fe1a5d1cSSeigo Tanimura 		csa_writemem(resp, BA1_CVOL, 0xffffffff);
403769309aaSSeigo Tanimura 		csa->rch.dma = 0;
4048f7076b8SSeigo Tanimura 
4058f7076b8SSeigo Tanimura 		/*
4068f7076b8SSeigo Tanimura 		 * The bitwise pointer of the serial FIFO in the DSP
4078f7076b8SSeigo Tanimura 		 * seems to make an error upon starting or stopping the
4088f7076b8SSeigo Tanimura 		 * DSP. Clear the FIFO and correct the pointer if we
4098f7076b8SSeigo Tanimura 		 * are not playing.
4108f7076b8SSeigo Tanimura 		 */
4118f7076b8SSeigo Tanimura 		if (!csa->pch.dma) {
4128f7076b8SSeigo Tanimura 			csa_clearserialfifos(resp);
4138f7076b8SSeigo Tanimura 			csa_writeio(resp, BA0_SERBSP, 0);
4148f7076b8SSeigo Tanimura 		}
415769309aaSSeigo Tanimura 	}
416fe1a5d1cSSeigo Tanimura }
417fe1a5d1cSSeigo Tanimura 
418fe1a5d1cSSeigo Tanimura static int
csa_startdsp(csa_res * resp)419fe1a5d1cSSeigo Tanimura csa_startdsp(csa_res *resp)
420fe1a5d1cSSeigo Tanimura {
421fe1a5d1cSSeigo Tanimura 	int i;
422fe1a5d1cSSeigo Tanimura 	u_long ul;
423fe1a5d1cSSeigo Tanimura 
424fe1a5d1cSSeigo Tanimura 	/*
425fe1a5d1cSSeigo Tanimura 	 * Set the frame timer to reflect the number of cycles per frame.
426fe1a5d1cSSeigo Tanimura 	 */
427fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_FRMT, 0xadf);
428fe1a5d1cSSeigo Tanimura 
429fe1a5d1cSSeigo Tanimura 	/*
430fe1a5d1cSSeigo Tanimura 	 * Turn on the run, run at frame, and DMA enable bits in the local copy of
431fe1a5d1cSSeigo Tanimura 	 * the SP control register.
432fe1a5d1cSSeigo Tanimura 	 */
433fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN);
434fe1a5d1cSSeigo Tanimura 
435fe1a5d1cSSeigo Tanimura 	/*
436fe1a5d1cSSeigo Tanimura 	 * Wait until the run at frame bit resets itself in the SP control
437fe1a5d1cSSeigo Tanimura 	 * register.
438fe1a5d1cSSeigo Tanimura 	 */
439fe1a5d1cSSeigo Tanimura 	ul = 0;
440fe1a5d1cSSeigo Tanimura 	for (i = 0 ; i < 25 ; i++) {
441fe1a5d1cSSeigo Tanimura 		/*
442fe1a5d1cSSeigo Tanimura 		 * Wait a little bit, so we don't issue PCI reads too frequently.
443fe1a5d1cSSeigo Tanimura 		 */
44420ac1df7SCameron Grant 		DELAY(50);
445fe1a5d1cSSeigo Tanimura 		/*
446fe1a5d1cSSeigo Tanimura 		 * Fetch the current value of the SP status register.
447fe1a5d1cSSeigo Tanimura 		 */
448fe1a5d1cSSeigo Tanimura 		ul = csa_readmem(resp, BA1_SPCR);
449fe1a5d1cSSeigo Tanimura 
450fe1a5d1cSSeigo Tanimura 		/*
451fe1a5d1cSSeigo Tanimura 		 * If the run at frame bit has reset, then stop waiting.
452fe1a5d1cSSeigo Tanimura 		 */
453fe1a5d1cSSeigo Tanimura 		if((ul & SPCR_RUNFR) == 0)
454fe1a5d1cSSeigo Tanimura 			break;
455fe1a5d1cSSeigo Tanimura 	}
456fe1a5d1cSSeigo Tanimura 	/*
457fe1a5d1cSSeigo Tanimura 	 * If the run at frame bit never reset, then return an error.
458fe1a5d1cSSeigo Tanimura 	 */
459fe1a5d1cSSeigo Tanimura 	if((ul & SPCR_RUNFR) != 0)
460fe1a5d1cSSeigo Tanimura 		return (EAGAIN);
461fe1a5d1cSSeigo Tanimura 
462fe1a5d1cSSeigo Tanimura 	return (0);
463fe1a5d1cSSeigo Tanimura }
464fe1a5d1cSSeigo Tanimura 
46520ac1df7SCameron Grant static int
csa_stopdsp(csa_res * resp)466961478afSGleb Smirnoff csa_stopdsp(csa_res *resp)
467961478afSGleb Smirnoff {
468961478afSGleb Smirnoff 	/*
469961478afSGleb Smirnoff 	 * Turn off the run, run at frame, and DMA enable bits in
470961478afSGleb Smirnoff 	 * the local copy of the SP control register.
471961478afSGleb Smirnoff 	 */
472961478afSGleb Smirnoff 	csa_writemem(resp, BA1_SPCR, 0);
473961478afSGleb Smirnoff 
474961478afSGleb Smirnoff 	return (0);
475961478afSGleb Smirnoff }
476961478afSGleb Smirnoff 
477961478afSGleb Smirnoff static int
csa_setupchan(struct csa_chinfo * ch)47820ac1df7SCameron Grant csa_setupchan(struct csa_chinfo *ch)
47920ac1df7SCameron Grant {
48020ac1df7SCameron Grant 	struct csa_info *csa = ch->parent;
48120ac1df7SCameron Grant 	csa_res *resp = &csa->res;
48220ac1df7SCameron Grant 	u_long pdtc, tmp;
48320ac1df7SCameron Grant 
48420ac1df7SCameron Grant 	if (ch->dir == PCMDIR_PLAY) {
48520ac1df7SCameron Grant 		/* direction */
48638cc9942SOlivier Houchard 		csa_writemem(resp, BA1_PBA, sndbuf_getbufaddr(ch->buffer));
48720ac1df7SCameron Grant 
48820ac1df7SCameron Grant 		/* format */
48920ac1df7SCameron Grant 		csa->pfie = csa_readmem(resp, BA1_PFIE) & ~0x0000f03f;
49020ac1df7SCameron Grant 		if (!(ch->fmt & AFMT_SIGNED))
49120ac1df7SCameron Grant 			csa->pfie |= 0x8000;
49220ac1df7SCameron Grant 		if (ch->fmt & AFMT_BIGENDIAN)
49320ac1df7SCameron Grant 			csa->pfie |= 0x4000;
49490da2b28SAriff Abdullah 		if (AFMT_CHANNEL(ch->fmt) < 2)
49520ac1df7SCameron Grant 			csa->pfie |= 0x2000;
49620ac1df7SCameron Grant 		if (ch->fmt & AFMT_8BIT)
49720ac1df7SCameron Grant 			csa->pfie |= 0x1000;
49820ac1df7SCameron Grant 		csa_writemem(resp, BA1_PFIE, csa->pfie);
49920ac1df7SCameron Grant 
50020ac1df7SCameron Grant 		tmp = 4;
50120ac1df7SCameron Grant 		if (ch->fmt & AFMT_16BIT)
50220ac1df7SCameron Grant 			tmp <<= 1;
50390da2b28SAriff Abdullah 		if (AFMT_CHANNEL(ch->fmt) > 1)
50420ac1df7SCameron Grant 			tmp <<= 1;
50520ac1df7SCameron Grant 		tmp--;
50620ac1df7SCameron Grant 
50720ac1df7SCameron Grant 		pdtc = csa_readmem(resp, BA1_PDTC) & ~0x000001ff;
50820ac1df7SCameron Grant 		pdtc |= tmp;
50920ac1df7SCameron Grant 		csa_writemem(resp, BA1_PDTC, pdtc);
51020ac1df7SCameron Grant 
51120ac1df7SCameron Grant 		/* rate */
51220ac1df7SCameron Grant 		csa_setplaysamplerate(resp, ch->spd);
51320ac1df7SCameron Grant 	} else if (ch->dir == PCMDIR_REC) {
51420ac1df7SCameron Grant 		/* direction */
51538cc9942SOlivier Houchard 		csa_writemem(resp, BA1_CBA, sndbuf_getbufaddr(ch->buffer));
51620ac1df7SCameron Grant 
51720ac1df7SCameron Grant 		/* format */
51820ac1df7SCameron Grant 		csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001);
51920ac1df7SCameron Grant 
52020ac1df7SCameron Grant 		/* rate */
52120ac1df7SCameron Grant 		csa_setcapturesamplerate(resp, ch->spd);
52220ac1df7SCameron Grant 	}
52320ac1df7SCameron Grant 	return 0;
52420ac1df7SCameron Grant }
52520ac1df7SCameron Grant 
5260f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
5270f55ac6cSCameron Grant /* channel interface */
5280f55ac6cSCameron Grant 
5290f55ac6cSCameron Grant static void *
csachan_init(kobj_t obj,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir)53066ef8af5SCameron Grant csachan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
5310f55ac6cSCameron Grant {
5320f55ac6cSCameron Grant 	struct csa_info *csa = devinfo;
5330f55ac6cSCameron Grant 	struct csa_chinfo *ch = (dir == PCMDIR_PLAY)? &csa->pch : &csa->rch;
5340f55ac6cSCameron Grant 
5350f55ac6cSCameron Grant 	ch->parent = csa;
5360f55ac6cSCameron Grant 	ch->channel = c;
5370f55ac6cSCameron Grant 	ch->buffer = b;
53820ac1df7SCameron Grant 	ch->dir = dir;
5392e334adfSAriff Abdullah 	if (sndbuf_alloc(ch->buffer, csa->parent_dmat, 0, CS461x_BUFFSIZE) != 0)
540eba1cb6eSPyun YongHyeon 		return NULL;
5410f55ac6cSCameron Grant 	return ch;
5420f55ac6cSCameron Grant }
5430f55ac6cSCameron Grant 
544fe1a5d1cSSeigo Tanimura static int
csachan_setformat(kobj_t obj,void * data,u_int32_t format)5450f55ac6cSCameron Grant csachan_setformat(kobj_t obj, void *data, u_int32_t format)
5460f55ac6cSCameron Grant {
5470f55ac6cSCameron Grant 	struct csa_chinfo *ch = data;
5480f55ac6cSCameron Grant 
5490f55ac6cSCameron Grant 	ch->fmt = format;
5500f55ac6cSCameron Grant 	return 0;
5510f55ac6cSCameron Grant }
5520f55ac6cSCameron Grant 
55390da2b28SAriff Abdullah static u_int32_t
csachan_setspeed(kobj_t obj,void * data,u_int32_t speed)5540f55ac6cSCameron Grant csachan_setspeed(kobj_t obj, void *data, u_int32_t speed)
5550f55ac6cSCameron Grant {
5560f55ac6cSCameron Grant 	struct csa_chinfo *ch = data;
5570f55ac6cSCameron Grant 
55820ac1df7SCameron Grant 	ch->spd = speed;
55920ac1df7SCameron Grant 	return ch->spd; /* XXX calc real speed */
5600f55ac6cSCameron Grant }
5610f55ac6cSCameron Grant 
56290da2b28SAriff Abdullah static u_int32_t
csachan_setblocksize(kobj_t obj,void * data,u_int32_t blocksize)5630f55ac6cSCameron Grant csachan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
5640f55ac6cSCameron Grant {
565350a5fafSCameron Grant 	return CS461x_BUFFSIZE / 2;
5660f55ac6cSCameron Grant }
5670f55ac6cSCameron Grant 
5680f55ac6cSCameron Grant static int
csachan_trigger(kobj_t obj,void * data,int go)5690f55ac6cSCameron Grant csachan_trigger(kobj_t obj, void *data, int go)
5700f55ac6cSCameron Grant {
5710f55ac6cSCameron Grant 	struct csa_chinfo *ch = data;
5720f55ac6cSCameron Grant 	struct csa_info *csa = ch->parent;
5730f55ac6cSCameron Grant 
574bdfbdcecSAriff Abdullah 	if (!PCMTRIG_COMMON(go))
5750f55ac6cSCameron Grant 		return 0;
5760f55ac6cSCameron Grant 
57720ac1df7SCameron Grant 	if (go == PCMTRIG_START) {
57820ac1df7SCameron Grant 		csa_active(csa, 1);
57920ac1df7SCameron Grant 		csa_setupchan(ch);
58020ac1df7SCameron Grant 		if (ch->dir == PCMDIR_PLAY)
5810f55ac6cSCameron Grant 			csa_startplaydma(csa);
5820f55ac6cSCameron Grant 		else
5830f55ac6cSCameron Grant 			csa_startcapturedma(csa);
58420ac1df7SCameron Grant 	} else {
58520ac1df7SCameron Grant 		if (ch->dir == PCMDIR_PLAY)
58620ac1df7SCameron Grant 			csa_stopplaydma(csa);
5870f55ac6cSCameron Grant 		else
5880f55ac6cSCameron Grant 			csa_stopcapturedma(csa);
58920ac1df7SCameron Grant 		csa_active(csa, -1);
5900f55ac6cSCameron Grant 	}
5910f55ac6cSCameron Grant 	return 0;
5920f55ac6cSCameron Grant }
5930f55ac6cSCameron Grant 
59490da2b28SAriff Abdullah static u_int32_t
csachan_getptr(kobj_t obj,void * data)5950f55ac6cSCameron Grant csachan_getptr(kobj_t obj, void *data)
596fe1a5d1cSSeigo Tanimura {
597fe1a5d1cSSeigo Tanimura 	struct csa_chinfo *ch = data;
598fe1a5d1cSSeigo Tanimura 	struct csa_info *csa = ch->parent;
599fe1a5d1cSSeigo Tanimura 	csa_res *resp;
60090da2b28SAriff Abdullah 	u_int32_t ptr;
601fe1a5d1cSSeigo Tanimura 
602fe1a5d1cSSeigo Tanimura 	resp = &csa->res;
603fe1a5d1cSSeigo Tanimura 
604fe1a5d1cSSeigo Tanimura 	if (ch->dir == PCMDIR_PLAY) {
60538cc9942SOlivier Houchard 		ptr = csa_readmem(resp, BA1_PBA) - sndbuf_getbufaddr(ch->buffer);
606fe1a5d1cSSeigo Tanimura 		if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0)
607fe1a5d1cSSeigo Tanimura 			ptr >>= 1;
608fe1a5d1cSSeigo Tanimura 	} else {
60938cc9942SOlivier Houchard 		ptr = csa_readmem(resp, BA1_CBA) - sndbuf_getbufaddr(ch->buffer);
610fe1a5d1cSSeigo Tanimura 		if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0)
611fe1a5d1cSSeigo Tanimura 			ptr >>= 1;
612fe1a5d1cSSeigo Tanimura 	}
613fe1a5d1cSSeigo Tanimura 
614fe1a5d1cSSeigo Tanimura 	return (ptr);
615fe1a5d1cSSeigo Tanimura }
616fe1a5d1cSSeigo Tanimura 
61766ef8af5SCameron Grant static struct pcmchan_caps *
csachan_getcaps(kobj_t obj,void * data)6180f55ac6cSCameron Grant csachan_getcaps(kobj_t obj, void *data)
619fe1a5d1cSSeigo Tanimura {
620fe1a5d1cSSeigo Tanimura 	struct csa_chinfo *ch = data;
621fe1a5d1cSSeigo Tanimura 	return (ch->dir == PCMDIR_PLAY)? &csa_playcaps : &csa_reccaps;
622fe1a5d1cSSeigo Tanimura }
623fe1a5d1cSSeigo Tanimura 
6240f55ac6cSCameron Grant static kobj_method_t csachan_methods[] = {
6250f55ac6cSCameron Grant     	KOBJMETHOD(channel_init,		csachan_init),
6260f55ac6cSCameron Grant     	KOBJMETHOD(channel_setformat,		csachan_setformat),
6270f55ac6cSCameron Grant     	KOBJMETHOD(channel_setspeed,		csachan_setspeed),
6280f55ac6cSCameron Grant     	KOBJMETHOD(channel_setblocksize,	csachan_setblocksize),
6290f55ac6cSCameron Grant     	KOBJMETHOD(channel_trigger,		csachan_trigger),
6300f55ac6cSCameron Grant     	KOBJMETHOD(channel_getptr,		csachan_getptr),
6310f55ac6cSCameron Grant     	KOBJMETHOD(channel_getcaps,		csachan_getcaps),
63290da2b28SAriff Abdullah 	KOBJMETHOD_END
6330f55ac6cSCameron Grant };
6340f55ac6cSCameron Grant CHANNEL_DECLARE(csachan);
6350f55ac6cSCameron Grant 
6360f55ac6cSCameron Grant /* -------------------------------------------------------------------- */
637fe1a5d1cSSeigo Tanimura /* The interrupt handler */
638fe1a5d1cSSeigo Tanimura static void
csa_intr(void * p)639fe1a5d1cSSeigo Tanimura csa_intr(void *p)
640fe1a5d1cSSeigo Tanimura {
641fe1a5d1cSSeigo Tanimura 	struct csa_info *csa = p;
642fe1a5d1cSSeigo Tanimura 
643f259d7eeSSeigo Tanimura 	if ((csa->binfo->hisr & HISR_VC0) != 0)
644fe1a5d1cSSeigo Tanimura 		chn_intr(csa->pch.channel);
645f259d7eeSSeigo Tanimura 	if ((csa->binfo->hisr & HISR_VC1) != 0)
646fe1a5d1cSSeigo Tanimura 		chn_intr(csa->rch.channel);
647fe1a5d1cSSeigo Tanimura }
648fe1a5d1cSSeigo Tanimura 
649fe1a5d1cSSeigo Tanimura /* -------------------------------------------------------------------- */
650fe1a5d1cSSeigo Tanimura 
651fe1a5d1cSSeigo Tanimura /*
652fe1a5d1cSSeigo Tanimura  * Probe and attach the card
653fe1a5d1cSSeigo Tanimura  */
654fe1a5d1cSSeigo Tanimura 
655fe1a5d1cSSeigo Tanimura static int
csa_init(struct csa_info * csa)656fe1a5d1cSSeigo Tanimura csa_init(struct csa_info *csa)
657fe1a5d1cSSeigo Tanimura {
658fe1a5d1cSSeigo Tanimura 	csa_res *resp;
659fe1a5d1cSSeigo Tanimura 
660fe1a5d1cSSeigo Tanimura 	resp = &csa->res;
661fe1a5d1cSSeigo Tanimura 
662fe1a5d1cSSeigo Tanimura 	csa->pfie = 0;
663fe1a5d1cSSeigo Tanimura 	csa_stopplaydma(csa);
664fe1a5d1cSSeigo Tanimura 	csa_stopcapturedma(csa);
665fe1a5d1cSSeigo Tanimura 
666fe1a5d1cSSeigo Tanimura 	if (csa_startdsp(resp))
667fe1a5d1cSSeigo Tanimura 		return (1);
668fe1a5d1cSSeigo Tanimura 
66920ac1df7SCameron Grant 	/* Crank up the power on the DAC and ADC. */
67020ac1df7SCameron Grant 	csa_setplaysamplerate(resp, 8000);
67120ac1df7SCameron Grant 	csa_setcapturesamplerate(resp, 8000);
6725ad14b75SAlexander Leidinger 	/* Set defaults */
6735ad14b75SAlexander Leidinger 	csa_writeio(resp, BA0_EGPIODR, EGPIODR_GPOE0);
6745ad14b75SAlexander Leidinger 	csa_writeio(resp, BA0_EGPIOPTR, EGPIOPTR_GPPT0);
6755ad14b75SAlexander Leidinger 	/* Power up amplifier */
6765ad14b75SAlexander Leidinger 	csa_writeio(resp, BA0_EGPIODR, csa_readio(resp, BA0_EGPIODR) |
6775ad14b75SAlexander Leidinger 		EGPIODR_GPOE2);
6785ad14b75SAlexander Leidinger 	csa_writeio(resp, BA0_EGPIOPTR, csa_readio(resp, BA0_EGPIOPTR) |
6795ad14b75SAlexander Leidinger 		EGPIOPTR_GPPT2);
68020ac1df7SCameron Grant 
681fe1a5d1cSSeigo Tanimura 	return 0;
682fe1a5d1cSSeigo Tanimura }
683fe1a5d1cSSeigo Tanimura 
684fe1a5d1cSSeigo Tanimura /* Allocates resources. */
685fe1a5d1cSSeigo Tanimura static int
csa_allocres(struct csa_info * csa,device_t dev)686fe1a5d1cSSeigo Tanimura csa_allocres(struct csa_info *csa, device_t dev)
687fe1a5d1cSSeigo Tanimura {
688fe1a5d1cSSeigo Tanimura 	csa_res *resp;
689fe1a5d1cSSeigo Tanimura 
690fe1a5d1cSSeigo Tanimura 	resp = &csa->res;
691fe1a5d1cSSeigo Tanimura 	if (resp->io == NULL) {
6925f96beb9SNate Lawson 		resp->io = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
6935f96beb9SNate Lawson 			&resp->io_rid, RF_ACTIVE);
694fe1a5d1cSSeigo Tanimura 		if (resp->io == NULL)
695fe1a5d1cSSeigo Tanimura 			return (1);
696fe1a5d1cSSeigo Tanimura 	}
697fe1a5d1cSSeigo Tanimura 	if (resp->mem == NULL) {
6985f96beb9SNate Lawson 		resp->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
6995f96beb9SNate Lawson 			&resp->mem_rid, RF_ACTIVE);
700fe1a5d1cSSeigo Tanimura 		if (resp->mem == NULL)
701fe1a5d1cSSeigo Tanimura 			return (1);
702fe1a5d1cSSeigo Tanimura 	}
703fe1a5d1cSSeigo Tanimura 	if (resp->irq == NULL) {
7045f96beb9SNate Lawson 		resp->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
7055f96beb9SNate Lawson 			&resp->irq_rid, RF_ACTIVE | RF_SHAREABLE);
706fe1a5d1cSSeigo Tanimura 		if (resp->irq == NULL)
707fe1a5d1cSSeigo Tanimura 			return (1);
708fe1a5d1cSSeigo Tanimura 	}
7090b989078SAlexander Leidinger 	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev),
7100b989078SAlexander Leidinger 			       /*alignment*/CS461x_BUFFSIZE,
7110b989078SAlexander Leidinger 			       /*boundary*/CS461x_BUFFSIZE,
712fe1a5d1cSSeigo Tanimura 			       /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
713fe1a5d1cSSeigo Tanimura 			       /*highaddr*/BUS_SPACE_MAXADDR,
714fe1a5d1cSSeigo Tanimura 			       /*filter*/NULL, /*filterarg*/NULL,
715fe1a5d1cSSeigo Tanimura 			       /*maxsize*/CS461x_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff,
7161f7a6325SAlexander Motin 			       /*flags*/0, /*lockfunc*/NULL, /*lockarg*/NULL,
7171f7a6325SAlexander Motin 			       &csa->parent_dmat) != 0)
718fe1a5d1cSSeigo Tanimura 		return (1);
719fe1a5d1cSSeigo Tanimura 
720fe1a5d1cSSeigo Tanimura 	return (0);
721fe1a5d1cSSeigo Tanimura }
722fe1a5d1cSSeigo Tanimura 
723fe1a5d1cSSeigo Tanimura /* Releases resources. */
724fe1a5d1cSSeigo Tanimura static void
csa_releaseres(struct csa_info * csa,device_t dev)725fe1a5d1cSSeigo Tanimura csa_releaseres(struct csa_info *csa, device_t dev)
726fe1a5d1cSSeigo Tanimura {
727fe1a5d1cSSeigo Tanimura 	csa_res *resp;
728fe1a5d1cSSeigo Tanimura 
729cd9de7eeSAlexander Leidinger 	KASSERT(csa != NULL, ("called with bogus resource structure"));
730cd9de7eeSAlexander Leidinger 
731fe1a5d1cSSeigo Tanimura 	resp = &csa->res;
732fe1a5d1cSSeigo Tanimura 	if (resp->irq != NULL) {
733306f91b6SCameron Grant 		if (csa->ih)
734306f91b6SCameron Grant 			bus_teardown_intr(dev, resp->irq, csa->ih);
735fe1a5d1cSSeigo Tanimura 		bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq);
736fe1a5d1cSSeigo Tanimura 		resp->irq = NULL;
737fe1a5d1cSSeigo Tanimura 	}
738fe1a5d1cSSeigo Tanimura 	if (resp->io != NULL) {
739fe1a5d1cSSeigo Tanimura 		bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io);
740fe1a5d1cSSeigo Tanimura 		resp->io = NULL;
741fe1a5d1cSSeigo Tanimura 	}
742fe1a5d1cSSeigo Tanimura 	if (resp->mem != NULL) {
743fe1a5d1cSSeigo Tanimura 		bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem);
744fe1a5d1cSSeigo Tanimura 		resp->mem = NULL;
745fe1a5d1cSSeigo Tanimura 	}
746306f91b6SCameron Grant 	if (csa->parent_dmat != NULL) {
747306f91b6SCameron Grant 		bus_dma_tag_destroy(csa->parent_dmat);
748306f91b6SCameron Grant 		csa->parent_dmat = NULL;
749306f91b6SCameron Grant 	}
750cd9de7eeSAlexander Leidinger 
751306f91b6SCameron Grant 	free(csa, M_DEVBUF);
752fe1a5d1cSSeigo Tanimura }
753fe1a5d1cSSeigo Tanimura 
754fe1a5d1cSSeigo Tanimura static int
pcmcsa_probe(device_t dev)755fe1a5d1cSSeigo Tanimura pcmcsa_probe(device_t dev)
756fe1a5d1cSSeigo Tanimura {
757fe1a5d1cSSeigo Tanimura 	char *s;
758fe1a5d1cSSeigo Tanimura 	struct sndcard_func *func;
759fe1a5d1cSSeigo Tanimura 
760fe1a5d1cSSeigo Tanimura 	/* The parent device has already been probed. */
761fe1a5d1cSSeigo Tanimura 
762fe1a5d1cSSeigo Tanimura 	func = device_get_ivars(dev);
763fe1a5d1cSSeigo Tanimura 	if (func == NULL || func->func != SCF_PCM)
764fe1a5d1cSSeigo Tanimura 		return (ENXIO);
765fe1a5d1cSSeigo Tanimura 
766fe1a5d1cSSeigo Tanimura 	s = "CS461x PCM Audio";
767fe1a5d1cSSeigo Tanimura 
768fe1a5d1cSSeigo Tanimura 	device_set_desc(dev, s);
769fe1a5d1cSSeigo Tanimura 	return (0);
770fe1a5d1cSSeigo Tanimura }
771fe1a5d1cSSeigo Tanimura 
772fe1a5d1cSSeigo Tanimura static int
pcmcsa_attach(device_t dev)773fe1a5d1cSSeigo Tanimura pcmcsa_attach(device_t dev)
774fe1a5d1cSSeigo Tanimura {
775fe1a5d1cSSeigo Tanimura 	struct csa_info *csa;
776fe1a5d1cSSeigo Tanimura 	csa_res *resp;
777fe1a5d1cSSeigo Tanimura 	char status[SND_STATUSLEN];
778fe1a5d1cSSeigo Tanimura 	struct ac97_info *codec;
779f259d7eeSSeigo Tanimura 	struct sndcard_func *func;
780fe1a5d1cSSeigo Tanimura 
781082f6383SAriff Abdullah 	csa = malloc(sizeof(*csa), M_DEVBUF, M_WAITOK | M_ZERO);
782f259d7eeSSeigo Tanimura 	func = device_get_ivars(dev);
783f259d7eeSSeigo Tanimura 	csa->binfo = func->varinfo;
784769309aaSSeigo Tanimura 	/*
785769309aaSSeigo Tanimura 	 * Fake the status of DMA so that the initial value of
786769309aaSSeigo Tanimura 	 * PCTL and CCTL can be stored into csa->pctl and csa->cctl,
787769309aaSSeigo Tanimura 	 * respectively.
788769309aaSSeigo Tanimura 	 */
789769309aaSSeigo Tanimura 	csa->pch.dma = csa->rch.dma = 1;
79020ac1df7SCameron Grant 	csa->active = 0;
79120ac1df7SCameron Grant 	csa->card = csa->binfo->card;
792fe1a5d1cSSeigo Tanimura 
793fe1a5d1cSSeigo Tanimura 	/* Allocate the resources. */
794fe1a5d1cSSeigo Tanimura 	resp = &csa->res;
795e27951b2SJohn Baldwin 	resp->io_rid = PCIR_BAR(0);
796e27951b2SJohn Baldwin 	resp->mem_rid = PCIR_BAR(1);
797fe1a5d1cSSeigo Tanimura 	resp->irq_rid = 0;
798fe1a5d1cSSeigo Tanimura 	if (csa_allocres(csa, dev)) {
799fe1a5d1cSSeigo Tanimura 		csa_releaseres(csa, dev);
800fe1a5d1cSSeigo Tanimura 		return (ENXIO);
801fe1a5d1cSSeigo Tanimura 	}
802fe1a5d1cSSeigo Tanimura 
80320ac1df7SCameron Grant 	csa_active(csa, 1);
804fe1a5d1cSSeigo Tanimura 	if (csa_init(csa)) {
805fe1a5d1cSSeigo Tanimura 		csa_releaseres(csa, dev);
806fe1a5d1cSSeigo Tanimura 		return (ENXIO);
807fe1a5d1cSSeigo Tanimura 	}
8080f55ac6cSCameron Grant 	codec = AC97_CREATE(dev, csa, csa_ac97);
809306f91b6SCameron Grant 	if (codec == NULL) {
810306f91b6SCameron Grant 		csa_releaseres(csa, dev);
811fe1a5d1cSSeigo Tanimura 		return (ENXIO);
812306f91b6SCameron Grant 	}
8138e81760bSCameron Grant 	if (csa->card->inv_eapd)
8148e81760bSCameron Grant 		ac97_setflags(codec, AC97_F_EAPD_INV);
8150f55ac6cSCameron Grant 	if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) {
816306f91b6SCameron Grant 		ac97_destroy(codec);
817306f91b6SCameron Grant 		csa_releaseres(csa, dev);
818e620d959SCameron Grant 		return (ENXIO);
819306f91b6SCameron Grant 	}
820fe1a5d1cSSeigo Tanimura 
821837cd192SChristos Margiolis 	snprintf(status, SND_STATUSLEN, "irq %jd on %s",
822837cd192SChristos Margiolis 			rman_get_start(resp->irq),
823837cd192SChristos Margiolis 			device_get_nameunit(device_get_parent(dev)));
824fe1a5d1cSSeigo Tanimura 
825fe1a5d1cSSeigo Tanimura 	/* Enable interrupt. */
8268fb9a995SBrian Feldman 	if (snd_setup_intr(dev, resp->irq, 0, csa_intr, csa, &csa->ih)) {
827306f91b6SCameron Grant 		ac97_destroy(codec);
828fe1a5d1cSSeigo Tanimura 		csa_releaseres(csa, dev);
829fe1a5d1cSSeigo Tanimura 		return (ENXIO);
830fe1a5d1cSSeigo Tanimura 	}
831fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_PFIE, csa_readmem(resp, BA1_PFIE) & ~0x0000f03f);
832fe1a5d1cSSeigo Tanimura 	csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001);
83320ac1df7SCameron Grant 	csa_active(csa, -1);
834fe1a5d1cSSeigo Tanimura 
835*516a9c02SChristos Margiolis 	pcm_init(dev, csa);
836*516a9c02SChristos Margiolis 	pcm_addchan(dev, PCMDIR_REC, &csachan_class, csa);
837*516a9c02SChristos Margiolis 	pcm_addchan(dev, PCMDIR_PLAY, &csachan_class, csa);
838*516a9c02SChristos Margiolis 	if (pcm_register(dev, status)) {
839306f91b6SCameron Grant 		ac97_destroy(codec);
840fe1a5d1cSSeigo Tanimura 		csa_releaseres(csa, dev);
841fe1a5d1cSSeigo Tanimura 		return (ENXIO);
842fe1a5d1cSSeigo Tanimura 	}
843fe1a5d1cSSeigo Tanimura 
844fe1a5d1cSSeigo Tanimura 	return (0);
845fe1a5d1cSSeigo Tanimura }
846fe1a5d1cSSeigo Tanimura 
847306f91b6SCameron Grant static int
pcmcsa_detach(device_t dev)848306f91b6SCameron Grant pcmcsa_detach(device_t dev)
849306f91b6SCameron Grant {
850306f91b6SCameron Grant 	int r;
851306f91b6SCameron Grant 	struct csa_info *csa;
852306f91b6SCameron Grant 
853306f91b6SCameron Grant 	r = pcm_unregister(dev);
854306f91b6SCameron Grant 	if (r)
855306f91b6SCameron Grant 		return r;
856306f91b6SCameron Grant 
857306f91b6SCameron Grant 	csa = pcm_getdevinfo(dev);
858306f91b6SCameron Grant 	csa_releaseres(csa, dev);
859306f91b6SCameron Grant 
860306f91b6SCameron Grant 	return 0;
861306f91b6SCameron Grant }
862306f91b6SCameron Grant 
863961478afSGleb Smirnoff static void
csa_ac97_suspend(struct csa_info * csa)864961478afSGleb Smirnoff csa_ac97_suspend(struct csa_info *csa)
865961478afSGleb Smirnoff {
866961478afSGleb Smirnoff 	int count, i;
867961478afSGleb Smirnoff 	uint32_t tmp;
868961478afSGleb Smirnoff 
869961478afSGleb Smirnoff 	for (count = 0x2, i=0;
870961478afSGleb Smirnoff 	    (count <= CS461x_AC97_HIGHESTREGTORESTORE) &&
871961478afSGleb Smirnoff 	    (i < CS461x_AC97_NUMBER_RESTORE_REGS);
872961478afSGleb Smirnoff 	    count += 2, i++)
873961478afSGleb Smirnoff 		csa_readcodec(&csa->res, BA0_AC97_RESET + count, &csa->ac97[i]);
874961478afSGleb Smirnoff 
875961478afSGleb Smirnoff 	/* mute the outputs */
876961478afSGleb Smirnoff 	csa_writecodec(&csa->res, BA0_AC97_MASTER_VOLUME, 0x8000);
877961478afSGleb Smirnoff 	csa_writecodec(&csa->res, BA0_AC97_HEADPHONE_VOLUME, 0x8000);
878961478afSGleb Smirnoff 	csa_writecodec(&csa->res, BA0_AC97_MASTER_VOLUME_MONO, 0x8000);
879961478afSGleb Smirnoff 	csa_writecodec(&csa->res, BA0_AC97_PCM_OUT_VOLUME, 0x8000);
880961478afSGleb Smirnoff 	/* save the registers that cause pops */
881961478afSGleb Smirnoff 	csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &csa->ac97_powerdown);
882961478afSGleb Smirnoff 	csa_readcodec(&csa->res, BA0_AC97_GENERAL_PURPOSE,
883961478afSGleb Smirnoff 	    &csa->ac97_general_purpose);
884961478afSGleb Smirnoff 
885961478afSGleb Smirnoff 	/*
886961478afSGleb Smirnoff 	 * And power down everything on the AC97 codec. Well, for now,
887961478afSGleb Smirnoff 	 * only power down the DAC/ADC and MIXER VREFON components.
888961478afSGleb Smirnoff 	 * trouble with removing VREF.
889961478afSGleb Smirnoff 	 */
890961478afSGleb Smirnoff 
891961478afSGleb Smirnoff 	/* MIXVON */
892961478afSGleb Smirnoff 	csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp);
893961478afSGleb Smirnoff 	csa_writecodec(&csa->res, BA0_AC97_POWERDOWN,
894961478afSGleb Smirnoff 	    tmp | CS_AC97_POWER_CONTROL_MIXVON);
895961478afSGleb Smirnoff 	/* ADC */
896961478afSGleb Smirnoff 	csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp);
897961478afSGleb Smirnoff 	csa_writecodec(&csa->res, BA0_AC97_POWERDOWN,
898961478afSGleb Smirnoff 	    tmp | CS_AC97_POWER_CONTROL_ADC);
899961478afSGleb Smirnoff 	/* DAC */
900961478afSGleb Smirnoff 	csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp);
901961478afSGleb Smirnoff 	csa_writecodec(&csa->res, BA0_AC97_POWERDOWN,
902961478afSGleb Smirnoff 	    tmp | CS_AC97_POWER_CONTROL_DAC);
903961478afSGleb Smirnoff }
904961478afSGleb Smirnoff 
905961478afSGleb Smirnoff static void
csa_ac97_resume(struct csa_info * csa)906961478afSGleb Smirnoff csa_ac97_resume(struct csa_info *csa)
907961478afSGleb Smirnoff {
908961478afSGleb Smirnoff 	int count, i;
909961478afSGleb Smirnoff 
910961478afSGleb Smirnoff 	/*
911961478afSGleb Smirnoff 	 * First, we restore the state of the general purpose register.  This
912961478afSGleb Smirnoff 	 * contains the mic select (mic1 or mic2) and if we restore this after
913961478afSGleb Smirnoff 	 * we restore the mic volume/boost state and mic2 was selected at
914961478afSGleb Smirnoff 	 * suspend time, we will end up with a brief period of time where mic1
915961478afSGleb Smirnoff 	 * is selected with the volume/boost settings for mic2, causing
916961478afSGleb Smirnoff 	 * acoustic feedback.  So we restore the general purpose register
917961478afSGleb Smirnoff 	 * first, thereby getting the correct mic selected before we restore
918961478afSGleb Smirnoff 	 * the mic volume/boost.
919961478afSGleb Smirnoff 	 */
920961478afSGleb Smirnoff 	csa_writecodec(&csa->res, BA0_AC97_GENERAL_PURPOSE,
921961478afSGleb Smirnoff 	    csa->ac97_general_purpose);
922961478afSGleb Smirnoff 	/*
923961478afSGleb Smirnoff 	 * Now, while the outputs are still muted, restore the state of power
924961478afSGleb Smirnoff 	 * on the AC97 part.
925961478afSGleb Smirnoff 	 */
926961478afSGleb Smirnoff 	csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, csa->ac97_powerdown);
927961478afSGleb Smirnoff 	/*
928961478afSGleb Smirnoff 	 * Restore just the first set of registers, from register number
929961478afSGleb Smirnoff 	 * 0x02 to the register number that ulHighestRegToRestore specifies.
930961478afSGleb Smirnoff 	 */
931961478afSGleb Smirnoff 	for (count = 0x2, i=0;
932961478afSGleb Smirnoff 	    (count <= CS461x_AC97_HIGHESTREGTORESTORE) &&
933961478afSGleb Smirnoff 	    (i < CS461x_AC97_NUMBER_RESTORE_REGS);
934961478afSGleb Smirnoff 	    count += 2, i++)
935961478afSGleb Smirnoff 		csa_writecodec(&csa->res, BA0_AC97_RESET + count, csa->ac97[i]);
936961478afSGleb Smirnoff }
937961478afSGleb Smirnoff 
938961478afSGleb Smirnoff static int
pcmcsa_suspend(device_t dev)939961478afSGleb Smirnoff pcmcsa_suspend(device_t dev)
940961478afSGleb Smirnoff {
941961478afSGleb Smirnoff 	struct csa_info *csa;
942961478afSGleb Smirnoff 	csa_res *resp;
943961478afSGleb Smirnoff 
944961478afSGleb Smirnoff 	csa = pcm_getdevinfo(dev);
945961478afSGleb Smirnoff 	resp = &csa->res;
946961478afSGleb Smirnoff 
947961478afSGleb Smirnoff 	csa_active(csa, 1);
948961478afSGleb Smirnoff 
949961478afSGleb Smirnoff 	/* playback interrupt disable */
950961478afSGleb Smirnoff 	csa_writemem(resp, BA1_PFIE,
951961478afSGleb Smirnoff 	    (csa_readmem(resp, BA1_PFIE) & ~0x0000f03f) | 0x00000010);
952961478afSGleb Smirnoff 	/* capture interrupt disable */
953961478afSGleb Smirnoff 	csa_writemem(resp, BA1_CIE,
954961478afSGleb Smirnoff 	    (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000011);
955961478afSGleb Smirnoff 	csa_stopplaydma(csa);
956961478afSGleb Smirnoff 	csa_stopcapturedma(csa);
957961478afSGleb Smirnoff 
958961478afSGleb Smirnoff 	csa_ac97_suspend(csa);
959961478afSGleb Smirnoff 
960961478afSGleb Smirnoff 	csa_resetdsp(resp);
961961478afSGleb Smirnoff 
962961478afSGleb Smirnoff 	csa_stopdsp(resp);
963961478afSGleb Smirnoff 	/*
964961478afSGleb Smirnoff 	 *  Power down the DAC and ADC.  For now leave the other areas on.
965961478afSGleb Smirnoff 	 */
966961478afSGleb Smirnoff 	csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, 0x300);
967961478afSGleb Smirnoff 	/*
968961478afSGleb Smirnoff 	 *  Power down the PLL.
969961478afSGleb Smirnoff 	 */
970961478afSGleb Smirnoff 	csa_writemem(resp, BA0_CLKCR1, 0);
971961478afSGleb Smirnoff 	/*
972961478afSGleb Smirnoff 	 * Turn off the Processor by turning off the software clock
973961478afSGleb Smirnoff 	 * enable flag in the clock control register.
974961478afSGleb Smirnoff 	 */
975961478afSGleb Smirnoff 	csa_writemem(resp, BA0_CLKCR1,
976961478afSGleb Smirnoff 	    csa_readmem(resp, BA0_CLKCR1) & ~CLKCR1_SWCE);
977961478afSGleb Smirnoff 
978961478afSGleb Smirnoff 	csa_active(csa, -1);
979961478afSGleb Smirnoff 
980961478afSGleb Smirnoff 	return 0;
981961478afSGleb Smirnoff }
982961478afSGleb Smirnoff 
983961478afSGleb Smirnoff static int
pcmcsa_resume(device_t dev)984961478afSGleb Smirnoff pcmcsa_resume(device_t dev)
985961478afSGleb Smirnoff {
986961478afSGleb Smirnoff 	struct csa_info *csa;
987961478afSGleb Smirnoff 	csa_res *resp;
988961478afSGleb Smirnoff 
989961478afSGleb Smirnoff 	csa = pcm_getdevinfo(dev);
990961478afSGleb Smirnoff 	resp = &csa->res;
991961478afSGleb Smirnoff 
992961478afSGleb Smirnoff 	csa_active(csa, 1);
993961478afSGleb Smirnoff 
994961478afSGleb Smirnoff 	/* cs_hardware_init */
995961478afSGleb Smirnoff 	csa_stopplaydma(csa);
996961478afSGleb Smirnoff 	csa_stopcapturedma(csa);
997961478afSGleb Smirnoff 	csa_ac97_resume(csa);
998961478afSGleb Smirnoff 	if (csa_startdsp(resp))
999961478afSGleb Smirnoff 		return (ENXIO);
1000961478afSGleb Smirnoff 	/* Enable interrupts on the part. */
1001961478afSGleb Smirnoff 	if ((csa_readio(resp, BA0_HISR) & HISR_INTENA) == 0)
1002961478afSGleb Smirnoff 		csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM);
1003961478afSGleb Smirnoff 	/* playback interrupt enable */
1004961478afSGleb Smirnoff 	csa_writemem(resp, BA1_PFIE, csa_readmem(resp, BA1_PFIE) & ~0x0000f03f);
1005961478afSGleb Smirnoff 	/* capture interrupt enable */
1006961478afSGleb Smirnoff 	csa_writemem(resp, BA1_CIE,
1007961478afSGleb Smirnoff 	    (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001);
1008961478afSGleb Smirnoff 	/* cs_restart_part */
1009961478afSGleb Smirnoff 	csa_setupchan(&csa->pch);
1010961478afSGleb Smirnoff 	csa_startplaydma(csa);
1011961478afSGleb Smirnoff 	csa_setupchan(&csa->rch);
1012961478afSGleb Smirnoff 	csa_startcapturedma(csa);
1013961478afSGleb Smirnoff 
1014961478afSGleb Smirnoff 	csa_active(csa, -1);
1015961478afSGleb Smirnoff 
1016961478afSGleb Smirnoff 	return 0;
1017961478afSGleb Smirnoff }
1018961478afSGleb Smirnoff 
1019fe1a5d1cSSeigo Tanimura static device_method_t pcmcsa_methods[] = {
1020fe1a5d1cSSeigo Tanimura 	/* Device interface */
1021fe1a5d1cSSeigo Tanimura 	DEVMETHOD(device_probe , pcmcsa_probe ),
1022fe1a5d1cSSeigo Tanimura 	DEVMETHOD(device_attach, pcmcsa_attach),
1023306f91b6SCameron Grant 	DEVMETHOD(device_detach, pcmcsa_detach),
1024961478afSGleb Smirnoff 	DEVMETHOD(device_suspend, pcmcsa_suspend),
1025961478afSGleb Smirnoff 	DEVMETHOD(device_resume, pcmcsa_resume),
1026fe1a5d1cSSeigo Tanimura 
1027fe1a5d1cSSeigo Tanimura 	{ 0, 0 },
1028fe1a5d1cSSeigo Tanimura };
1029fe1a5d1cSSeigo Tanimura 
1030fe1a5d1cSSeigo Tanimura static driver_t pcmcsa_driver = {
1031fe1a5d1cSSeigo Tanimura 	"pcm",
1032fe1a5d1cSSeigo Tanimura 	pcmcsa_methods,
103367b1dce3SCameron Grant 	PCM_SOFTC_SIZE,
1034fe1a5d1cSSeigo Tanimura };
1035fe1a5d1cSSeigo Tanimura 
10362287364eSJohn Baldwin DRIVER_MODULE(snd_csapcm, csa, pcmcsa_driver, 0, 0);
10370739ea1dSSeigo Tanimura MODULE_DEPEND(snd_csapcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
1038f314f3daSCameron Grant MODULE_DEPEND(snd_csapcm, snd_csa, 1, 1, 1);
1039f314f3daSCameron Grant MODULE_VERSION(snd_csapcm, 1);
1040