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