1139749Simp/*- 253553Stanimura * Copyright (c) 1999 Seigo Tanimura 353553Stanimura * All rights reserved. 453553Stanimura * 554377Stanimura * Portions of this source are based on cwcealdr.cpp and dhwiface.cpp in 654377Stanimura * cwcealdr1.zip, the sample sources by Crystal Semiconductor. 754377Stanimura * Copyright (c) 1996-1998 Crystal Semiconductor Corp. 854377Stanimura * 953553Stanimura * Redistribution and use in source and binary forms, with or without 1053553Stanimura * modification, are permitted provided that the following conditions 1153553Stanimura * are met: 1253553Stanimura * 1. Redistributions of source code must retain the above copyright 1353553Stanimura * notice, this list of conditions and the following disclaimer. 1453553Stanimura * 2. Redistributions in binary form must reproduce the above copyright 1553553Stanimura * notice, this list of conditions and the following disclaimer in the 1653553Stanimura * documentation and/or other materials provided with the distribution. 1753553Stanimura * 1853553Stanimura * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1953553Stanimura * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2053553Stanimura * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2153553Stanimura * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2253553Stanimura * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2353553Stanimura * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2453553Stanimura * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2553553Stanimura * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2653553Stanimura * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2753553Stanimura * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2853553Stanimura * SUCH DAMAGE. 2953553Stanimura */ 3053553Stanimura 31193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 32193640Sariff#include "opt_snd.h" 33193640Sariff#endif 34193640Sariff 3553553Stanimura#include <dev/sound/pcm/sound.h> 3653553Stanimura#include <dev/sound/pcm/ac97.h> 3753553Stanimura#include <dev/sound/chip.h> 3853553Stanimura#include <dev/sound/pci/csareg.h> 3953553Stanimura#include <dev/sound/pci/csavar.h> 4053553Stanimura 41119287Simp#include <dev/pci/pcireg.h> 42119287Simp#include <dev/pci/pcivar.h> 4353553Stanimura 4482180ScgSND_DECLARE_FILE("$FreeBSD$"); 4582180Scg 4677504Scg/* Buffer size on dma transfer. Fixed for CS416x. */ 4777504Scg#define CS461x_BUFFSIZE (4 * 1024) 4877504Scg 4977504Scg#define GOF_PER_SEC 200 5077504Scg 5153553Stanimura/* device private data */ 5253553Stanimurastruct csa_info; 5353553Stanimura 5453553Stanimurastruct csa_chinfo { 5553553Stanimura struct csa_info *parent; 5674763Scg struct pcm_channel *channel; 5774763Scg struct snd_dbuf *buffer; 5853553Stanimura int dir; 5977504Scg u_int32_t fmt, spd; 6055321Stanimura int dma; 6153553Stanimura}; 6253553Stanimura 6353553Stanimurastruct csa_info { 6453553Stanimura csa_res res; /* resource */ 6553553Stanimura void *ih; /* Interrupt cookie */ 6653553Stanimura bus_dma_tag_t parent_dmat; /* DMA tag */ 6755320Stanimura struct csa_bridgeinfo *binfo; /* The state of the parent. */ 6877504Scg struct csa_card *card; 6953553Stanimura 7077504Scg int active; 7153553Stanimura /* Contents of board's registers */ 7253553Stanimura u_long pfie; 7353553Stanimura u_long pctl; 7453553Stanimura u_long cctl; 7553553Stanimura struct csa_chinfo pch, rch; 76147626Sglebius u_int32_t ac97[CS461x_AC97_NUMBER_RESTORE_REGS]; 77147626Sglebius u_int32_t ac97_powerdown; 78147626Sglebius u_int32_t ac97_general_purpose; 7953553Stanimura}; 8053553Stanimura 8153553Stanimura/* -------------------------------------------------------------------- */ 8253553Stanimura 8353553Stanimura/* prototypes */ 8453553Stanimurastatic int csa_init(struct csa_info *); 8553553Stanimurastatic void csa_intr(void *); 8653553Stanimurastatic void csa_setplaysamplerate(csa_res *resp, u_long ulInRate); 8753553Stanimurastatic void csa_setcapturesamplerate(csa_res *resp, u_long ulOutRate); 8853553Stanimurastatic void csa_startplaydma(struct csa_info *csa); 8953553Stanimurastatic void csa_startcapturedma(struct csa_info *csa); 9053553Stanimurastatic void csa_stopplaydma(struct csa_info *csa); 9153553Stanimurastatic void csa_stopcapturedma(struct csa_info *csa); 9253553Stanimurastatic int csa_startdsp(csa_res *resp); 93147626Sglebiusstatic int csa_stopdsp(csa_res *resp); 9453553Stanimurastatic int csa_allocres(struct csa_info *scp, device_t dev); 9553553Stanimurastatic void csa_releaseres(struct csa_info *scp, device_t dev); 96147626Sglebiusstatic void csa_ac97_suspend(struct csa_info *csa); 97147626Sglebiusstatic void csa_ac97_resume(struct csa_info *csa); 9853553Stanimura 9964881Scgstatic u_int32_t csa_playfmt[] = { 100193640Sariff SND_FORMAT(AFMT_U8, 1, 0), 101193640Sariff SND_FORMAT(AFMT_U8, 2, 0), 102193640Sariff SND_FORMAT(AFMT_S8, 1, 0), 103193640Sariff SND_FORMAT(AFMT_S8, 2, 0), 104193640Sariff SND_FORMAT(AFMT_S16_LE, 1, 0), 105193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 106193640Sariff SND_FORMAT(AFMT_S16_BE, 1, 0), 107193640Sariff SND_FORMAT(AFMT_S16_BE, 2, 0), 10864881Scg 0 10953553Stanimura}; 11074763Scgstatic struct pcmchan_caps csa_playcaps = {8000, 48000, csa_playfmt, 0}; 11153553Stanimura 11264881Scgstatic u_int32_t csa_recfmt[] = { 113193640Sariff SND_FORMAT(AFMT_S16_LE, 1, 0), 114193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 11564881Scg 0 11653553Stanimura}; 11774763Scgstatic struct pcmchan_caps csa_reccaps = {11025, 48000, csa_recfmt, 0}; 11853553Stanimura 11953553Stanimura/* -------------------------------------------------------------------- */ 12077504Scg 12177504Scgstatic int 12277504Scgcsa_active(struct csa_info *csa, int run) 12377504Scg{ 124147626Sglebius int old; 12577504Scg 12677504Scg old = csa->active; 12777504Scg csa->active += run; 12877504Scg 129147626Sglebius if ((csa->active > 1) || (csa->active < -1)) 130147626Sglebius csa->active = 0; 131147626Sglebius if (csa->card->active) 132147626Sglebius return (csa->card->active(!(csa->active && old))); 133147626Sglebius 13477504Scg return 0; 13577504Scg} 13677504Scg 13777504Scg/* -------------------------------------------------------------------- */ 13870134Scg/* ac97 codec */ 13953553Stanimura 14053553Stanimurastatic int 14170134Scgcsa_rdcd(kobj_t obj, void *devinfo, int regno) 14253553Stanimura{ 14370134Scg u_int32_t data; 14470134Scg struct csa_info *csa = (struct csa_info *)devinfo; 14553553Stanimura 14677504Scg csa_active(csa, 1); 14770134Scg if (csa_readcodec(&csa->res, regno + BA0_AC97_RESET, &data)) 14870134Scg data = 0; 14977504Scg csa_active(csa, -1); 15053553Stanimura 15170134Scg return data; 15253553Stanimura} 15353553Stanimura 15453553Stanimurastatic int 15570134Scgcsa_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) 15653553Stanimura{ 15770134Scg struct csa_info *csa = (struct csa_info *)devinfo; 15853553Stanimura 15977504Scg csa_active(csa, 1); 16070134Scg csa_writecodec(&csa->res, regno + BA0_AC97_RESET, data); 16177504Scg csa_active(csa, -1); 16253553Stanimura 16353553Stanimura return 0; 16453553Stanimura} 16553553Stanimura 16670134Scgstatic kobj_method_t csa_ac97_methods[] = { 16770134Scg KOBJMETHOD(ac97_read, csa_rdcd), 16870134Scg KOBJMETHOD(ac97_write, csa_wrcd), 169193640Sariff KOBJMETHOD_END 17070134Scg}; 17170134ScgAC97_DECLARE(csa_ac97); 17253553Stanimura 17353553Stanimurastatic void 17453553Stanimuracsa_setplaysamplerate(csa_res *resp, u_long ulInRate) 17553553Stanimura{ 17653553Stanimura u_long ulTemp1, ulTemp2; 17753553Stanimura u_long ulPhiIncr; 17853553Stanimura u_long ulCorrectionPerGOF, ulCorrectionPerSec; 17953553Stanimura u_long ulOutRate; 18053553Stanimura 18153553Stanimura ulOutRate = 48000; 18253553Stanimura 18353553Stanimura /* 18453553Stanimura * Compute the values used to drive the actual sample rate conversion. 18553553Stanimura * The following formulas are being computed, using inline assembly 18653553Stanimura * since we need to use 64 bit arithmetic to compute the values: 18753553Stanimura * 18853553Stanimura * ulPhiIncr = floor((Fs,in * 2^26) / Fs,out) 18953553Stanimura * ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) / 19053553Stanimura * GOF_PER_SEC) 19153553Stanimura * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr - 19253553Stanimura * GOF_PER_SEC * ulCorrectionPerGOF 19353553Stanimura * 19453553Stanimura * i.e. 19553553Stanimura * 19653553Stanimura * ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out) 19753553Stanimura * ulCorrectionPerGOF:ulCorrectionPerSec = 19853553Stanimura * dividend:remainder(ulOther / GOF_PER_SEC) 19953553Stanimura */ 20053553Stanimura ulTemp1 = ulInRate << 16; 20153553Stanimura ulPhiIncr = ulTemp1 / ulOutRate; 20253553Stanimura ulTemp1 -= ulPhiIncr * ulOutRate; 20353553Stanimura ulTemp1 <<= 10; 20453553Stanimura ulPhiIncr <<= 10; 20553553Stanimura ulTemp2 = ulTemp1 / ulOutRate; 20653553Stanimura ulPhiIncr += ulTemp2; 20753553Stanimura ulTemp1 -= ulTemp2 * ulOutRate; 20853553Stanimura ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC; 20953553Stanimura ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC; 21053553Stanimura ulCorrectionPerSec = ulTemp1; 21153553Stanimura 21253553Stanimura /* 21353553Stanimura * Fill in the SampleRateConverter control block. 21453553Stanimura */ 21553553Stanimura csa_writemem(resp, BA1_PSRC, ((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF)); 21653553Stanimura csa_writemem(resp, BA1_PPI, ulPhiIncr); 21753553Stanimura} 21853553Stanimura 21953553Stanimurastatic void 22053553Stanimuracsa_setcapturesamplerate(csa_res *resp, u_long ulOutRate) 22153553Stanimura{ 22253553Stanimura u_long ulPhiIncr, ulCoeffIncr, ulTemp1, ulTemp2; 22353553Stanimura u_long ulCorrectionPerGOF, ulCorrectionPerSec, ulInitialDelay; 22453553Stanimura u_long dwFrameGroupLength, dwCnt; 22553553Stanimura u_long ulInRate; 22653553Stanimura 22753553Stanimura ulInRate = 48000; 22853553Stanimura 22953553Stanimura /* 23053553Stanimura * We can only decimate by up to a factor of 1/9th the hardware rate. 23153553Stanimura * Return an error if an attempt is made to stray outside that limit. 23253553Stanimura */ 23353553Stanimura if((ulOutRate * 9) < ulInRate) 23453553Stanimura return; 23553553Stanimura 23653553Stanimura /* 23753553Stanimura * We can not capture at at rate greater than the Input Rate (48000). 23853553Stanimura * Return an error if an attempt is made to stray outside that limit. 23953553Stanimura */ 24053553Stanimura if(ulOutRate > ulInRate) 24153553Stanimura return; 24253553Stanimura 24353553Stanimura /* 24453553Stanimura * Compute the values used to drive the actual sample rate conversion. 24553553Stanimura * The following formulas are being computed, using inline assembly 24653553Stanimura * since we need to use 64 bit arithmetic to compute the values: 24753553Stanimura * 24853553Stanimura * ulCoeffIncr = -floor((Fs,out * 2^23) / Fs,in) 24953553Stanimura * ulPhiIncr = floor((Fs,in * 2^26) / Fs,out) 25053553Stanimura * ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) / 25153553Stanimura * GOF_PER_SEC) 25253553Stanimura * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr - 25353553Stanimura * GOF_PER_SEC * ulCorrectionPerGOF 25453553Stanimura * ulInitialDelay = ceil((24 * Fs,in) / Fs,out) 25553553Stanimura * 25653553Stanimura * i.e. 25753553Stanimura * 25853553Stanimura * ulCoeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in)) 25953553Stanimura * ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out) 26053553Stanimura * ulCorrectionPerGOF:ulCorrectionPerSec = 26153553Stanimura * dividend:remainder(ulOther / GOF_PER_SEC) 26253553Stanimura * ulInitialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out) 26353553Stanimura */ 26453553Stanimura ulTemp1 = ulOutRate << 16; 26553553Stanimura ulCoeffIncr = ulTemp1 / ulInRate; 26653553Stanimura ulTemp1 -= ulCoeffIncr * ulInRate; 26753553Stanimura ulTemp1 <<= 7; 26853553Stanimura ulCoeffIncr <<= 7; 26953553Stanimura ulCoeffIncr += ulTemp1 / ulInRate; 27053553Stanimura ulCoeffIncr ^= 0xFFFFFFFF; 27153553Stanimura ulCoeffIncr++; 27253553Stanimura ulTemp1 = ulInRate << 16; 27353553Stanimura ulPhiIncr = ulTemp1 / ulOutRate; 27453553Stanimura ulTemp1 -= ulPhiIncr * ulOutRate; 27553553Stanimura ulTemp1 <<= 10; 27653553Stanimura ulPhiIncr <<= 10; 27753553Stanimura ulTemp2 = ulTemp1 / ulOutRate; 27853553Stanimura ulPhiIncr += ulTemp2; 27953553Stanimura ulTemp1 -= ulTemp2 * ulOutRate; 28053553Stanimura ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC; 28153553Stanimura ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC; 28253553Stanimura ulCorrectionPerSec = ulTemp1; 28353553Stanimura ulInitialDelay = ((ulInRate * 24) + ulOutRate - 1) / ulOutRate; 28453553Stanimura 28553553Stanimura /* 28653553Stanimura * Fill in the VariDecimate control block. 28753553Stanimura */ 28856249Scg csa_writemem(resp, BA1_CSRC, 28953553Stanimura ((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF)); 29053553Stanimura csa_writemem(resp, BA1_CCI, ulCoeffIncr); 29156249Scg csa_writemem(resp, BA1_CD, 29253553Stanimura (((BA1_VARIDEC_BUF_1 + (ulInitialDelay << 2)) << 16) & 0xFFFF0000) | 0x80); 29353553Stanimura csa_writemem(resp, BA1_CPI, ulPhiIncr); 29453553Stanimura 29553553Stanimura /* 29653553Stanimura * Figure out the frame group length for the write back task. Basically, 29753553Stanimura * this is just the factors of 24000 (2^6*3*5^3) that are not present in 29853553Stanimura * the output sample rate. 29953553Stanimura */ 30053553Stanimura dwFrameGroupLength = 1; 30153553Stanimura for(dwCnt = 2; dwCnt <= 64; dwCnt *= 2) 30253553Stanimura { 30353553Stanimura if(((ulOutRate / dwCnt) * dwCnt) != 30453553Stanimura ulOutRate) 30553553Stanimura { 30653553Stanimura dwFrameGroupLength *= 2; 30753553Stanimura } 30853553Stanimura } 30953553Stanimura if(((ulOutRate / 3) * 3) != 31053553Stanimura ulOutRate) 31153553Stanimura { 31253553Stanimura dwFrameGroupLength *= 3; 31353553Stanimura } 31453553Stanimura for(dwCnt = 5; dwCnt <= 125; dwCnt *= 5) 31553553Stanimura { 31653553Stanimura if(((ulOutRate / dwCnt) * dwCnt) != 31753553Stanimura ulOutRate) 31853553Stanimura { 31953553Stanimura dwFrameGroupLength *= 5; 32053553Stanimura } 32153553Stanimura } 32253553Stanimura 32353553Stanimura /* 32453553Stanimura * Fill in the WriteBack control block. 32553553Stanimura */ 32653553Stanimura csa_writemem(resp, BA1_CFG1, dwFrameGroupLength); 32753553Stanimura csa_writemem(resp, BA1_CFG2, (0x00800000 | dwFrameGroupLength)); 32853553Stanimura csa_writemem(resp, BA1_CCST, 0x0000FFFF); 32953553Stanimura csa_writemem(resp, BA1_CSPB, ((65536 * ulOutRate) / 24000)); 33053553Stanimura csa_writemem(resp, (BA1_CSPB + 4), 0x0000FFFF); 33153553Stanimura} 33253553Stanimura 33353553Stanimurastatic void 33453553Stanimuracsa_startplaydma(struct csa_info *csa) 33553553Stanimura{ 33653553Stanimura csa_res *resp; 33753553Stanimura u_long ul; 33853553Stanimura 33955321Stanimura if (!csa->pch.dma) { 34055321Stanimura resp = &csa->res; 34155321Stanimura ul = csa_readmem(resp, BA1_PCTL); 34255321Stanimura ul &= 0x0000ffff; 34355321Stanimura csa_writemem(resp, BA1_PCTL, ul | csa->pctl); 34455321Stanimura csa_writemem(resp, BA1_PVOL, 0x80008000); 34555321Stanimura csa->pch.dma = 1; 34655321Stanimura } 34753553Stanimura} 34853553Stanimura 34953553Stanimurastatic void 35053553Stanimuracsa_startcapturedma(struct csa_info *csa) 35153553Stanimura{ 35253553Stanimura csa_res *resp; 35353553Stanimura u_long ul; 35453553Stanimura 35555321Stanimura if (!csa->rch.dma) { 35655321Stanimura resp = &csa->res; 35755321Stanimura ul = csa_readmem(resp, BA1_CCTL); 35855321Stanimura ul &= 0xffff0000; 35955321Stanimura csa_writemem(resp, BA1_CCTL, ul | csa->cctl); 36055321Stanimura csa_writemem(resp, BA1_CVOL, 0x80008000); 36155321Stanimura csa->rch.dma = 1; 36255321Stanimura } 36353553Stanimura} 36453553Stanimura 36553553Stanimurastatic void 36653553Stanimuracsa_stopplaydma(struct csa_info *csa) 36753553Stanimura{ 36853553Stanimura csa_res *resp; 36953553Stanimura u_long ul; 37053553Stanimura 37155321Stanimura if (csa->pch.dma) { 37255321Stanimura resp = &csa->res; 37355321Stanimura ul = csa_readmem(resp, BA1_PCTL); 37455321Stanimura csa->pctl = ul & 0xffff0000; 37555321Stanimura csa_writemem(resp, BA1_PCTL, ul & 0x0000ffff); 37655321Stanimura csa_writemem(resp, BA1_PVOL, 0xffffffff); 37756427Stanimura csa->pch.dma = 0; 37855320Stanimura 37956427Stanimura /* 38056427Stanimura * The bitwise pointer of the serial FIFO in the DSP 38156427Stanimura * seems to make an error upon starting or stopping the 38256427Stanimura * DSP. Clear the FIFO and correct the pointer if we 38356427Stanimura * are not capturing. 38456427Stanimura */ 38556427Stanimura if (!csa->rch.dma) { 38656427Stanimura csa_clearserialfifos(resp); 38756427Stanimura csa_writeio(resp, BA0_SERBSP, 0); 38856427Stanimura } 38955321Stanimura } 39053553Stanimura} 39153553Stanimura 39253553Stanimurastatic void 39353553Stanimuracsa_stopcapturedma(struct csa_info *csa) 39453553Stanimura{ 39553553Stanimura csa_res *resp; 39653553Stanimura u_long ul; 39753553Stanimura 39855321Stanimura if (csa->rch.dma) { 39955321Stanimura resp = &csa->res; 40055321Stanimura ul = csa_readmem(resp, BA1_CCTL); 40155321Stanimura csa->cctl = ul & 0x0000ffff; 40255321Stanimura csa_writemem(resp, BA1_CCTL, ul & 0xffff0000); 40355321Stanimura csa_writemem(resp, BA1_CVOL, 0xffffffff); 40455321Stanimura csa->rch.dma = 0; 40556427Stanimura 40656427Stanimura /* 40756427Stanimura * The bitwise pointer of the serial FIFO in the DSP 40856427Stanimura * seems to make an error upon starting or stopping the 40956427Stanimura * DSP. Clear the FIFO and correct the pointer if we 41056427Stanimura * are not playing. 41156427Stanimura */ 41256427Stanimura if (!csa->pch.dma) { 41356427Stanimura csa_clearserialfifos(resp); 41456427Stanimura csa_writeio(resp, BA0_SERBSP, 0); 41556427Stanimura } 41655321Stanimura } 41753553Stanimura} 41853553Stanimura 41953553Stanimurastatic int 42053553Stanimuracsa_startdsp(csa_res *resp) 42153553Stanimura{ 42253553Stanimura int i; 42353553Stanimura u_long ul; 42453553Stanimura 42553553Stanimura /* 42653553Stanimura * Set the frame timer to reflect the number of cycles per frame. 42753553Stanimura */ 42853553Stanimura csa_writemem(resp, BA1_FRMT, 0xadf); 42953553Stanimura 43053553Stanimura /* 43153553Stanimura * Turn on the run, run at frame, and DMA enable bits in the local copy of 43253553Stanimura * the SP control register. 43353553Stanimura */ 43453553Stanimura csa_writemem(resp, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN); 43553553Stanimura 43653553Stanimura /* 43753553Stanimura * Wait until the run at frame bit resets itself in the SP control 43853553Stanimura * register. 43953553Stanimura */ 44053553Stanimura ul = 0; 44153553Stanimura for (i = 0 ; i < 25 ; i++) { 44253553Stanimura /* 44353553Stanimura * Wait a little bit, so we don't issue PCI reads too frequently. 44453553Stanimura */ 44577504Scg DELAY(50); 44653553Stanimura /* 44753553Stanimura * Fetch the current value of the SP status register. 44853553Stanimura */ 44953553Stanimura ul = csa_readmem(resp, BA1_SPCR); 45053553Stanimura 45153553Stanimura /* 45253553Stanimura * If the run at frame bit has reset, then stop waiting. 45353553Stanimura */ 45453553Stanimura if((ul & SPCR_RUNFR) == 0) 45553553Stanimura break; 45653553Stanimura } 45753553Stanimura /* 45853553Stanimura * If the run at frame bit never reset, then return an error. 45953553Stanimura */ 46053553Stanimura if((ul & SPCR_RUNFR) != 0) 46153553Stanimura return (EAGAIN); 46253553Stanimura 46353553Stanimura return (0); 46453553Stanimura} 46553553Stanimura 46677504Scgstatic int 467147626Sglebiuscsa_stopdsp(csa_res *resp) 468147626Sglebius{ 469147626Sglebius /* 470147626Sglebius * Turn off the run, run at frame, and DMA enable bits in 471147626Sglebius * the local copy of the SP control register. 472147626Sglebius */ 473147626Sglebius csa_writemem(resp, BA1_SPCR, 0); 474147626Sglebius 475147626Sglebius return (0); 476147626Sglebius} 477147626Sglebius 478147626Sglebiusstatic int 47977504Scgcsa_setupchan(struct csa_chinfo *ch) 48077504Scg{ 48177504Scg struct csa_info *csa = ch->parent; 48277504Scg csa_res *resp = &csa->res; 48377504Scg u_long pdtc, tmp; 48477504Scg 48577504Scg if (ch->dir == PCMDIR_PLAY) { 48677504Scg /* direction */ 487111183Scognet csa_writemem(resp, BA1_PBA, sndbuf_getbufaddr(ch->buffer)); 48877504Scg 48977504Scg /* format */ 49077504Scg csa->pfie = csa_readmem(resp, BA1_PFIE) & ~0x0000f03f; 49177504Scg if (!(ch->fmt & AFMT_SIGNED)) 49277504Scg csa->pfie |= 0x8000; 49377504Scg if (ch->fmt & AFMT_BIGENDIAN) 49477504Scg csa->pfie |= 0x4000; 495193640Sariff if (AFMT_CHANNEL(ch->fmt) < 2) 49677504Scg csa->pfie |= 0x2000; 49777504Scg if (ch->fmt & AFMT_8BIT) 49877504Scg csa->pfie |= 0x1000; 49977504Scg csa_writemem(resp, BA1_PFIE, csa->pfie); 50077504Scg 50177504Scg tmp = 4; 50277504Scg if (ch->fmt & AFMT_16BIT) 50377504Scg tmp <<= 1; 504193640Sariff if (AFMT_CHANNEL(ch->fmt) > 1) 50577504Scg tmp <<= 1; 50677504Scg tmp--; 50777504Scg 50877504Scg pdtc = csa_readmem(resp, BA1_PDTC) & ~0x000001ff; 50977504Scg pdtc |= tmp; 51077504Scg csa_writemem(resp, BA1_PDTC, pdtc); 51177504Scg 51277504Scg /* rate */ 51377504Scg csa_setplaysamplerate(resp, ch->spd); 51477504Scg } else if (ch->dir == PCMDIR_REC) { 51577504Scg /* direction */ 516111183Scognet csa_writemem(resp, BA1_CBA, sndbuf_getbufaddr(ch->buffer)); 51777504Scg 51877504Scg /* format */ 51977504Scg csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001); 52077504Scg 52177504Scg /* rate */ 52277504Scg csa_setcapturesamplerate(resp, ch->spd); 52377504Scg } 52477504Scg return 0; 52577504Scg} 52677504Scg 52770134Scg/* -------------------------------------------------------------------- */ 52870134Scg/* channel interface */ 52970134Scg 53070134Scgstatic void * 53174763Scgcsachan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 53270134Scg{ 53370134Scg struct csa_info *csa = devinfo; 53470134Scg struct csa_chinfo *ch = (dir == PCMDIR_PLAY)? &csa->pch : &csa->rch; 53570134Scg 53670134Scg ch->parent = csa; 53770134Scg ch->channel = c; 53870134Scg ch->buffer = b; 53977504Scg ch->dir = dir; 540168847Sariff if (sndbuf_alloc(ch->buffer, csa->parent_dmat, 0, CS461x_BUFFSIZE) != 0) 541136469Syongari return NULL; 54270134Scg return ch; 54370134Scg} 54470134Scg 54553553Stanimurastatic int 54670134Scgcsachan_setformat(kobj_t obj, void *data, u_int32_t format) 54770134Scg{ 54870134Scg struct csa_chinfo *ch = data; 54970134Scg 55070134Scg ch->fmt = format; 55170134Scg return 0; 55270134Scg} 55370134Scg 554193640Sariffstatic u_int32_t 55570134Scgcsachan_setspeed(kobj_t obj, void *data, u_int32_t speed) 55670134Scg{ 55770134Scg struct csa_chinfo *ch = data; 55870134Scg 55977504Scg ch->spd = speed; 56077504Scg return ch->spd; /* XXX calc real speed */ 56170134Scg} 56270134Scg 563193640Sariffstatic u_int32_t 56470134Scgcsachan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 56570134Scg{ 56670291Scg return CS461x_BUFFSIZE / 2; 56770134Scg} 56870134Scg 56970134Scgstatic int 57070134Scgcsachan_trigger(kobj_t obj, void *data, int go) 57170134Scg{ 57270134Scg struct csa_chinfo *ch = data; 57370134Scg struct csa_info *csa = ch->parent; 57470134Scg 575170521Sariff if (!PCMTRIG_COMMON(go)) 57670134Scg return 0; 57770134Scg 57877504Scg if (go == PCMTRIG_START) { 57977504Scg csa_active(csa, 1); 58077504Scg csa_setupchan(ch); 58177504Scg if (ch->dir == PCMDIR_PLAY) 58270134Scg csa_startplaydma(csa); 58370134Scg else 58477504Scg csa_startcapturedma(csa); 58577504Scg } else { 58677504Scg if (ch->dir == PCMDIR_PLAY) 58770134Scg csa_stopplaydma(csa); 58870134Scg else 58970134Scg csa_stopcapturedma(csa); 59077504Scg csa_active(csa, -1); 59170134Scg } 59270134Scg return 0; 59370134Scg} 59470134Scg 595193640Sariffstatic u_int32_t 59670134Scgcsachan_getptr(kobj_t obj, void *data) 59770134Scg{ 59870134Scg struct csa_chinfo *ch = data; 59970134Scg struct csa_info *csa = ch->parent; 60070134Scg csa_res *resp; 601193640Sariff u_int32_t ptr; 60253553Stanimura 60353553Stanimura resp = &csa->res; 60453553Stanimura 60553553Stanimura if (ch->dir == PCMDIR_PLAY) { 606111183Scognet ptr = csa_readmem(resp, BA1_PBA) - sndbuf_getbufaddr(ch->buffer); 60753553Stanimura if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0) 60853553Stanimura ptr >>= 1; 60953553Stanimura } else { 610111183Scognet ptr = csa_readmem(resp, BA1_CBA) - sndbuf_getbufaddr(ch->buffer); 61153553Stanimura if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0) 61253553Stanimura ptr >>= 1; 61353553Stanimura } 61453553Stanimura 61553553Stanimura return (ptr); 61653553Stanimura} 61753553Stanimura 61874763Scgstatic struct pcmchan_caps * 61970134Scgcsachan_getcaps(kobj_t obj, void *data) 62053553Stanimura{ 62153553Stanimura struct csa_chinfo *ch = data; 62253553Stanimura return (ch->dir == PCMDIR_PLAY)? &csa_playcaps : &csa_reccaps; 62353553Stanimura} 62453553Stanimura 62570134Scgstatic kobj_method_t csachan_methods[] = { 62670134Scg KOBJMETHOD(channel_init, csachan_init), 62770134Scg KOBJMETHOD(channel_setformat, csachan_setformat), 62870134Scg KOBJMETHOD(channel_setspeed, csachan_setspeed), 62970134Scg KOBJMETHOD(channel_setblocksize, csachan_setblocksize), 63070134Scg KOBJMETHOD(channel_trigger, csachan_trigger), 63170134Scg KOBJMETHOD(channel_getptr, csachan_getptr), 63270134Scg KOBJMETHOD(channel_getcaps, csachan_getcaps), 633193640Sariff KOBJMETHOD_END 63470134Scg}; 63570134ScgCHANNEL_DECLARE(csachan); 63670134Scg 63770134Scg/* -------------------------------------------------------------------- */ 63853553Stanimura/* The interrupt handler */ 63953553Stanimurastatic void 64077504Scgcsa_intr(void *p) 64153553Stanimura{ 64253553Stanimura struct csa_info *csa = p; 64353553Stanimura 64455320Stanimura if ((csa->binfo->hisr & HISR_VC0) != 0) 64553553Stanimura chn_intr(csa->pch.channel); 64655320Stanimura if ((csa->binfo->hisr & HISR_VC1) != 0) 64753553Stanimura chn_intr(csa->rch.channel); 64853553Stanimura} 64953553Stanimura 65053553Stanimura/* -------------------------------------------------------------------- */ 65153553Stanimura 65253553Stanimura/* 65353553Stanimura * Probe and attach the card 65453553Stanimura */ 65553553Stanimura 65653553Stanimurastatic int 65753553Stanimuracsa_init(struct csa_info *csa) 65853553Stanimura{ 65953553Stanimura csa_res *resp; 66053553Stanimura 66153553Stanimura resp = &csa->res; 66253553Stanimura 66353553Stanimura csa->pfie = 0; 66453553Stanimura csa_stopplaydma(csa); 66553553Stanimura csa_stopcapturedma(csa); 66653553Stanimura 66777504Scg if (csa_startdsp(resp)) 66877504Scg return (1); 66977504Scg 67053553Stanimura /* Crank up the power on the DAC and ADC. */ 67153553Stanimura csa_setplaysamplerate(resp, 8000); 67253553Stanimura csa_setcapturesamplerate(resp, 8000); 673149988Snetchild /* Set defaults */ 674149988Snetchild csa_writeio(resp, BA0_EGPIODR, EGPIODR_GPOE0); 675149988Snetchild csa_writeio(resp, BA0_EGPIOPTR, EGPIOPTR_GPPT0); 676149988Snetchild /* Power up amplifier */ 677149988Snetchild csa_writeio(resp, BA0_EGPIODR, csa_readio(resp, BA0_EGPIODR) | 678149988Snetchild EGPIODR_GPOE2); 679149988Snetchild csa_writeio(resp, BA0_EGPIOPTR, csa_readio(resp, BA0_EGPIOPTR) | 680149988Snetchild EGPIOPTR_GPPT2); 68153553Stanimura 68253553Stanimura return 0; 68353553Stanimura} 68453553Stanimura 68553553Stanimura/* Allocates resources. */ 68653553Stanimurastatic int 68753553Stanimuracsa_allocres(struct csa_info *csa, device_t dev) 68853553Stanimura{ 68953553Stanimura csa_res *resp; 69053553Stanimura 69153553Stanimura resp = &csa->res; 69253553Stanimura if (resp->io == NULL) { 693127135Snjl resp->io = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 694127135Snjl &resp->io_rid, RF_ACTIVE); 69553553Stanimura if (resp->io == NULL) 69653553Stanimura return (1); 69753553Stanimura } 69853553Stanimura if (resp->mem == NULL) { 699127135Snjl resp->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 700127135Snjl &resp->mem_rid, RF_ACTIVE); 70153553Stanimura if (resp->mem == NULL) 70253553Stanimura return (1); 70353553Stanimura } 70453553Stanimura if (resp->irq == NULL) { 705127135Snjl resp->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 706127135Snjl &resp->irq_rid, RF_ACTIVE | RF_SHAREABLE); 70753553Stanimura if (resp->irq == NULL) 70853553Stanimura return (1); 70953553Stanimura } 710166904Snetchild if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), 711166904Snetchild /*alignment*/CS461x_BUFFSIZE, 712166904Snetchild /*boundary*/CS461x_BUFFSIZE, 71353553Stanimura /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 71453553Stanimura /*highaddr*/BUS_SPACE_MAXADDR, 71553553Stanimura /*filter*/NULL, /*filterarg*/NULL, 71653553Stanimura /*maxsize*/CS461x_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, 717117126Sscottl /*flags*/0, /*lockfunc*/busdma_lock_mutex, 718117126Sscottl /*lockarg*/&Giant, &csa->parent_dmat) != 0) 71953553Stanimura return (1); 72053553Stanimura 72153553Stanimura return (0); 72253553Stanimura} 72353553Stanimura 72453553Stanimura/* Releases resources. */ 72553553Stanimurastatic void 72653553Stanimuracsa_releaseres(struct csa_info *csa, device_t dev) 72753553Stanimura{ 72853553Stanimura csa_res *resp; 72953553Stanimura 730155338Snetchild KASSERT(csa != NULL, ("called with bogus resource structure")); 731155338Snetchild 73253553Stanimura resp = &csa->res; 73353553Stanimura if (resp->irq != NULL) { 73465644Scg if (csa->ih) 73565644Scg bus_teardown_intr(dev, resp->irq, csa->ih); 73653553Stanimura bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq); 73753553Stanimura resp->irq = NULL; 73853553Stanimura } 73953553Stanimura if (resp->io != NULL) { 74053553Stanimura bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); 74153553Stanimura resp->io = NULL; 74253553Stanimura } 74353553Stanimura if (resp->mem != NULL) { 74453553Stanimura bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem); 74553553Stanimura resp->mem = NULL; 74653553Stanimura } 74765644Scg if (csa->parent_dmat != NULL) { 74865644Scg bus_dma_tag_destroy(csa->parent_dmat); 74965644Scg csa->parent_dmat = NULL; 75065644Scg } 751155338Snetchild 752155338Snetchild free(csa, M_DEVBUF); 75353553Stanimura} 75453553Stanimura 75553553Stanimurastatic int 75653553Stanimurapcmcsa_probe(device_t dev) 75753553Stanimura{ 75853553Stanimura char *s; 75953553Stanimura struct sndcard_func *func; 76053553Stanimura 76153553Stanimura /* The parent device has already been probed. */ 76253553Stanimura 76353553Stanimura func = device_get_ivars(dev); 76453553Stanimura if (func == NULL || func->func != SCF_PCM) 76553553Stanimura return (ENXIO); 76653553Stanimura 76753553Stanimura s = "CS461x PCM Audio"; 76853553Stanimura 76953553Stanimura device_set_desc(dev, s); 77053553Stanimura return (0); 77153553Stanimura} 77253553Stanimura 77353553Stanimurastatic int 77453553Stanimurapcmcsa_attach(device_t dev) 77553553Stanimura{ 77653553Stanimura struct csa_info *csa; 77753553Stanimura csa_res *resp; 77853553Stanimura int unit; 77953553Stanimura char status[SND_STATUSLEN]; 78053553Stanimura struct ac97_info *codec; 78155320Stanimura struct sndcard_func *func; 78253553Stanimura 783170873Sariff csa = malloc(sizeof(*csa), M_DEVBUF, M_WAITOK | M_ZERO); 78453553Stanimura unit = device_get_unit(dev); 78555320Stanimura func = device_get_ivars(dev); 78655320Stanimura csa->binfo = func->varinfo; 78755321Stanimura /* 78855321Stanimura * Fake the status of DMA so that the initial value of 78955321Stanimura * PCTL and CCTL can be stored into csa->pctl and csa->cctl, 79055321Stanimura * respectively. 79155321Stanimura */ 79255321Stanimura csa->pch.dma = csa->rch.dma = 1; 79377504Scg csa->active = 0; 79477504Scg csa->card = csa->binfo->card; 79553553Stanimura 79653553Stanimura /* Allocate the resources. */ 79753553Stanimura resp = &csa->res; 798119690Sjhb resp->io_rid = PCIR_BAR(0); 799119690Sjhb resp->mem_rid = PCIR_BAR(1); 80053553Stanimura resp->irq_rid = 0; 80153553Stanimura if (csa_allocres(csa, dev)) { 80253553Stanimura csa_releaseres(csa, dev); 80353553Stanimura return (ENXIO); 80453553Stanimura } 80553553Stanimura 80677504Scg csa_active(csa, 1); 80753553Stanimura if (csa_init(csa)) { 80853553Stanimura csa_releaseres(csa, dev); 80953553Stanimura return (ENXIO); 81053553Stanimura } 81170134Scg codec = AC97_CREATE(dev, csa, csa_ac97); 81265644Scg if (codec == NULL) { 81365644Scg csa_releaseres(csa, dev); 81453553Stanimura return (ENXIO); 81565644Scg } 81678673Scg if (csa->card->inv_eapd) 81778673Scg ac97_setflags(codec, AC97_F_EAPD_INV); 81870134Scg if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) { 81965644Scg ac97_destroy(codec); 82065644Scg csa_releaseres(csa, dev); 82158905Scg return (ENXIO); 82265644Scg } 82353553Stanimura 824126695Smatk snprintf(status, SND_STATUSLEN, "at irq %ld %s", 825126695Smatk rman_get_start(resp->irq),PCM_KLDSTRING(snd_csa)); 82653553Stanimura 82753553Stanimura /* Enable interrupt. */ 828128232Sgreen if (snd_setup_intr(dev, resp->irq, 0, csa_intr, csa, &csa->ih)) { 82965644Scg ac97_destroy(codec); 83053553Stanimura csa_releaseres(csa, dev); 83153553Stanimura return (ENXIO); 83253553Stanimura } 83353553Stanimura csa_writemem(resp, BA1_PFIE, csa_readmem(resp, BA1_PFIE) & ~0x0000f03f); 83453553Stanimura csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001); 83577504Scg csa_active(csa, -1); 83653553Stanimura 83753553Stanimura if (pcm_register(dev, csa, 1, 1)) { 83865644Scg ac97_destroy(codec); 83953553Stanimura csa_releaseres(csa, dev); 84053553Stanimura return (ENXIO); 84153553Stanimura } 84270134Scg pcm_addchan(dev, PCMDIR_REC, &csachan_class, csa); 84370134Scg pcm_addchan(dev, PCMDIR_PLAY, &csachan_class, csa); 84453553Stanimura pcm_setstatus(dev, status); 84553553Stanimura 84653553Stanimura return (0); 84753553Stanimura} 84853553Stanimura 84965644Scgstatic int 85065644Scgpcmcsa_detach(device_t dev) 85165644Scg{ 85265644Scg int r; 85365644Scg struct csa_info *csa; 85465644Scg 85565644Scg r = pcm_unregister(dev); 85665644Scg if (r) 85765644Scg return r; 85865644Scg 85965644Scg csa = pcm_getdevinfo(dev); 86065644Scg csa_releaseres(csa, dev); 86165644Scg 86265644Scg return 0; 86365644Scg} 86465644Scg 865147626Sglebiusstatic void 866147626Sglebiuscsa_ac97_suspend(struct csa_info *csa) 867147626Sglebius{ 868147626Sglebius int count, i; 869147626Sglebius uint32_t tmp; 870147626Sglebius 871147626Sglebius for (count = 0x2, i=0; 872147626Sglebius (count <= CS461x_AC97_HIGHESTREGTORESTORE) && 873147626Sglebius (i < CS461x_AC97_NUMBER_RESTORE_REGS); 874147626Sglebius count += 2, i++) 875147626Sglebius csa_readcodec(&csa->res, BA0_AC97_RESET + count, &csa->ac97[i]); 876147626Sglebius 877147626Sglebius /* mute the outputs */ 878147626Sglebius csa_writecodec(&csa->res, BA0_AC97_MASTER_VOLUME, 0x8000); 879147626Sglebius csa_writecodec(&csa->res, BA0_AC97_HEADPHONE_VOLUME, 0x8000); 880147626Sglebius csa_writecodec(&csa->res, BA0_AC97_MASTER_VOLUME_MONO, 0x8000); 881147626Sglebius csa_writecodec(&csa->res, BA0_AC97_PCM_OUT_VOLUME, 0x8000); 882147626Sglebius /* save the registers that cause pops */ 883147626Sglebius csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &csa->ac97_powerdown); 884147626Sglebius csa_readcodec(&csa->res, BA0_AC97_GENERAL_PURPOSE, 885147626Sglebius &csa->ac97_general_purpose); 886147626Sglebius 887147626Sglebius /* 888147626Sglebius * And power down everything on the AC97 codec. Well, for now, 889147626Sglebius * only power down the DAC/ADC and MIXER VREFON components. 890147626Sglebius * trouble with removing VREF. 891147626Sglebius */ 892147626Sglebius 893147626Sglebius /* MIXVON */ 894147626Sglebius csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp); 895147626Sglebius csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, 896147626Sglebius tmp | CS_AC97_POWER_CONTROL_MIXVON); 897147626Sglebius /* ADC */ 898147626Sglebius csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp); 899147626Sglebius csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, 900147626Sglebius tmp | CS_AC97_POWER_CONTROL_ADC); 901147626Sglebius /* DAC */ 902147626Sglebius csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp); 903147626Sglebius csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, 904147626Sglebius tmp | CS_AC97_POWER_CONTROL_DAC); 905147626Sglebius} 906147626Sglebius 907147626Sglebiusstatic void 908147626Sglebiuscsa_ac97_resume(struct csa_info *csa) 909147626Sglebius{ 910147626Sglebius int count, i; 911147626Sglebius 912147626Sglebius /* 913147626Sglebius * First, we restore the state of the general purpose register. This 914147626Sglebius * contains the mic select (mic1 or mic2) and if we restore this after 915147626Sglebius * we restore the mic volume/boost state and mic2 was selected at 916147626Sglebius * suspend time, we will end up with a brief period of time where mic1 917147626Sglebius * is selected with the volume/boost settings for mic2, causing 918147626Sglebius * acoustic feedback. So we restore the general purpose register 919147626Sglebius * first, thereby getting the correct mic selected before we restore 920147626Sglebius * the mic volume/boost. 921147626Sglebius */ 922147626Sglebius csa_writecodec(&csa->res, BA0_AC97_GENERAL_PURPOSE, 923147626Sglebius csa->ac97_general_purpose); 924147626Sglebius /* 925147626Sglebius * Now, while the outputs are still muted, restore the state of power 926147626Sglebius * on the AC97 part. 927147626Sglebius */ 928147626Sglebius csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, csa->ac97_powerdown); 929147626Sglebius /* 930147626Sglebius * Restore just the first set of registers, from register number 931147626Sglebius * 0x02 to the register number that ulHighestRegToRestore specifies. 932147626Sglebius */ 933147626Sglebius for (count = 0x2, i=0; 934147626Sglebius (count <= CS461x_AC97_HIGHESTREGTORESTORE) && 935147626Sglebius (i < CS461x_AC97_NUMBER_RESTORE_REGS); 936147626Sglebius count += 2, i++) 937147626Sglebius csa_writecodec(&csa->res, BA0_AC97_RESET + count, csa->ac97[i]); 938147626Sglebius} 939147626Sglebius 940147626Sglebiusstatic int 941147626Sglebiuspcmcsa_suspend(device_t dev) 942147626Sglebius{ 943147626Sglebius struct csa_info *csa; 944147626Sglebius csa_res *resp; 945147626Sglebius 946147626Sglebius csa = pcm_getdevinfo(dev); 947147626Sglebius resp = &csa->res; 948147626Sglebius 949147626Sglebius csa_active(csa, 1); 950147626Sglebius 951147626Sglebius /* playback interrupt disable */ 952147626Sglebius csa_writemem(resp, BA1_PFIE, 953147626Sglebius (csa_readmem(resp, BA1_PFIE) & ~0x0000f03f) | 0x00000010); 954147626Sglebius /* capture interrupt disable */ 955147626Sglebius csa_writemem(resp, BA1_CIE, 956147626Sglebius (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000011); 957147626Sglebius csa_stopplaydma(csa); 958147626Sglebius csa_stopcapturedma(csa); 959147626Sglebius 960147626Sglebius csa_ac97_suspend(csa); 961147626Sglebius 962147626Sglebius csa_resetdsp(resp); 963147626Sglebius 964147626Sglebius csa_stopdsp(resp); 965147626Sglebius /* 966147626Sglebius * Power down the DAC and ADC. For now leave the other areas on. 967147626Sglebius */ 968147626Sglebius csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, 0x300); 969147626Sglebius /* 970147626Sglebius * Power down the PLL. 971147626Sglebius */ 972147626Sglebius csa_writemem(resp, BA0_CLKCR1, 0); 973147626Sglebius /* 974147626Sglebius * Turn off the Processor by turning off the software clock 975147626Sglebius * enable flag in the clock control register. 976147626Sglebius */ 977147626Sglebius csa_writemem(resp, BA0_CLKCR1, 978147626Sglebius csa_readmem(resp, BA0_CLKCR1) & ~CLKCR1_SWCE); 979147626Sglebius 980147626Sglebius csa_active(csa, -1); 981147626Sglebius 982147626Sglebius return 0; 983147626Sglebius} 984147626Sglebius 985147626Sglebiusstatic int 986147626Sglebiuspcmcsa_resume(device_t dev) 987147626Sglebius{ 988147626Sglebius struct csa_info *csa; 989147626Sglebius csa_res *resp; 990147626Sglebius 991147626Sglebius csa = pcm_getdevinfo(dev); 992147626Sglebius resp = &csa->res; 993147626Sglebius 994147626Sglebius csa_active(csa, 1); 995147626Sglebius 996147626Sglebius /* cs_hardware_init */ 997147626Sglebius csa_stopplaydma(csa); 998147626Sglebius csa_stopcapturedma(csa); 999147626Sglebius csa_ac97_resume(csa); 1000147626Sglebius if (csa_startdsp(resp)) 1001147626Sglebius return (ENXIO); 1002147626Sglebius /* Enable interrupts on the part. */ 1003147626Sglebius if ((csa_readio(resp, BA0_HISR) & HISR_INTENA) == 0) 1004147626Sglebius csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); 1005147626Sglebius /* playback interrupt enable */ 1006147626Sglebius csa_writemem(resp, BA1_PFIE, csa_readmem(resp, BA1_PFIE) & ~0x0000f03f); 1007147626Sglebius /* capture interrupt enable */ 1008147626Sglebius csa_writemem(resp, BA1_CIE, 1009147626Sglebius (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001); 1010147626Sglebius /* cs_restart_part */ 1011147626Sglebius csa_setupchan(&csa->pch); 1012147626Sglebius csa_startplaydma(csa); 1013147626Sglebius csa_setupchan(&csa->rch); 1014147626Sglebius csa_startcapturedma(csa); 1015147626Sglebius 1016147626Sglebius csa_active(csa, -1); 1017147626Sglebius 1018147626Sglebius return 0; 1019147626Sglebius} 1020147626Sglebius 102153553Stanimurastatic device_method_t pcmcsa_methods[] = { 102253553Stanimura /* Device interface */ 102353553Stanimura DEVMETHOD(device_probe , pcmcsa_probe ), 102453553Stanimura DEVMETHOD(device_attach, pcmcsa_attach), 102565644Scg DEVMETHOD(device_detach, pcmcsa_detach), 1026147626Sglebius DEVMETHOD(device_suspend, pcmcsa_suspend), 1027147626Sglebius DEVMETHOD(device_resume, pcmcsa_resume), 102853553Stanimura 102953553Stanimura { 0, 0 }, 103053553Stanimura}; 103153553Stanimura 103253553Stanimurastatic driver_t pcmcsa_driver = { 103353553Stanimura "pcm", 103453553Stanimura pcmcsa_methods, 103582180Scg PCM_SOFTC_SIZE, 103653553Stanimura}; 103753553Stanimura 103862483ScgDRIVER_MODULE(snd_csapcm, csa, pcmcsa_driver, pcm_devclass, 0, 0); 1039132236StanimuraMODULE_DEPEND(snd_csapcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 104062483ScgMODULE_DEPEND(snd_csapcm, snd_csa, 1, 1, 1); 104162483ScgMODULE_VERSION(snd_csapcm, 1); 1042