csapcm.c revision 149988
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 3153553Stanimura#include <sys/soundcard.h> 3253553Stanimura#include <dev/sound/pcm/sound.h> 3353553Stanimura#include <dev/sound/pcm/ac97.h> 3453553Stanimura#include <dev/sound/chip.h> 3553553Stanimura#include <dev/sound/pci/csareg.h> 3653553Stanimura#include <dev/sound/pci/csavar.h> 3753553Stanimura 38119287Simp#include <dev/pci/pcireg.h> 39119287Simp#include <dev/pci/pcivar.h> 4053553Stanimura 4182180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/csapcm.c 149988 2005-09-11 14:15:05Z netchild $"); 4282180Scg 4377504Scg/* Buffer size on dma transfer. Fixed for CS416x. */ 4477504Scg#define CS461x_BUFFSIZE (4 * 1024) 4577504Scg 4677504Scg#define GOF_PER_SEC 200 4777504Scg 4853553Stanimura/* device private data */ 4953553Stanimurastruct csa_info; 5053553Stanimura 5153553Stanimurastruct csa_chinfo { 5253553Stanimura struct csa_info *parent; 5374763Scg struct pcm_channel *channel; 5474763Scg struct snd_dbuf *buffer; 5553553Stanimura int dir; 5677504Scg u_int32_t fmt, spd; 5755321Stanimura int dma; 5853553Stanimura}; 5953553Stanimura 6053553Stanimurastruct csa_info { 6153553Stanimura csa_res res; /* resource */ 6253553Stanimura void *ih; /* Interrupt cookie */ 6353553Stanimura bus_dma_tag_t parent_dmat; /* DMA tag */ 6455320Stanimura struct csa_bridgeinfo *binfo; /* The state of the parent. */ 6577504Scg struct csa_card *card; 6653553Stanimura 6777504Scg int active; 6853553Stanimura /* Contents of board's registers */ 6953553Stanimura u_long pfie; 7053553Stanimura u_long pctl; 7153553Stanimura u_long cctl; 7253553Stanimura struct csa_chinfo pch, rch; 73147626Sglebius u_int32_t ac97[CS461x_AC97_NUMBER_RESTORE_REGS]; 74147626Sglebius u_int32_t ac97_powerdown; 75147626Sglebius u_int32_t ac97_general_purpose; 7653553Stanimura}; 7753553Stanimura 7853553Stanimura/* -------------------------------------------------------------------- */ 7953553Stanimura 8053553Stanimura/* prototypes */ 8153553Stanimurastatic int csa_init(struct csa_info *); 8253553Stanimurastatic void csa_intr(void *); 8353553Stanimurastatic void csa_setplaysamplerate(csa_res *resp, u_long ulInRate); 8453553Stanimurastatic void csa_setcapturesamplerate(csa_res *resp, u_long ulOutRate); 8553553Stanimurastatic void csa_startplaydma(struct csa_info *csa); 8653553Stanimurastatic void csa_startcapturedma(struct csa_info *csa); 8753553Stanimurastatic void csa_stopplaydma(struct csa_info *csa); 8853553Stanimurastatic void csa_stopcapturedma(struct csa_info *csa); 8953553Stanimurastatic int csa_startdsp(csa_res *resp); 90147626Sglebiusstatic int csa_stopdsp(csa_res *resp); 9153553Stanimurastatic int csa_allocres(struct csa_info *scp, device_t dev); 9253553Stanimurastatic void csa_releaseres(struct csa_info *scp, device_t dev); 93147626Sglebiusstatic void csa_ac97_suspend(struct csa_info *csa); 94147626Sglebiusstatic void csa_ac97_resume(struct csa_info *csa); 9553553Stanimura 9664881Scgstatic u_int32_t csa_playfmt[] = { 9764881Scg AFMT_U8, 9864881Scg AFMT_STEREO | AFMT_U8, 9964881Scg AFMT_S8, 10064881Scg AFMT_STEREO | AFMT_S8, 10164881Scg AFMT_S16_LE, 10264881Scg AFMT_STEREO | AFMT_S16_LE, 10364881Scg AFMT_S16_BE, 10464881Scg AFMT_STEREO | AFMT_S16_BE, 10564881Scg 0 10653553Stanimura}; 10774763Scgstatic struct pcmchan_caps csa_playcaps = {8000, 48000, csa_playfmt, 0}; 10853553Stanimura 10964881Scgstatic u_int32_t csa_recfmt[] = { 11064881Scg AFMT_S16_LE, 11153553Stanimura AFMT_STEREO | AFMT_S16_LE, 11264881Scg 0 11353553Stanimura}; 11474763Scgstatic struct pcmchan_caps csa_reccaps = {11025, 48000, csa_recfmt, 0}; 11553553Stanimura 11653553Stanimura/* -------------------------------------------------------------------- */ 11777504Scg 11877504Scgstatic int 11977504Scgcsa_active(struct csa_info *csa, int run) 12077504Scg{ 121147626Sglebius int old; 12277504Scg 12377504Scg old = csa->active; 12477504Scg csa->active += run; 12577504Scg 126147626Sglebius if ((csa->active > 1) || (csa->active < -1)) 127147626Sglebius csa->active = 0; 128147626Sglebius if (csa->card->active) 129147626Sglebius return (csa->card->active(!(csa->active && old))); 130147626Sglebius 13177504Scg return 0; 13277504Scg} 13377504Scg 13477504Scg/* -------------------------------------------------------------------- */ 13570134Scg/* ac97 codec */ 13653553Stanimura 13753553Stanimurastatic int 13870134Scgcsa_rdcd(kobj_t obj, void *devinfo, int regno) 13953553Stanimura{ 14070134Scg u_int32_t data; 14170134Scg struct csa_info *csa = (struct csa_info *)devinfo; 14253553Stanimura 14377504Scg csa_active(csa, 1); 14470134Scg if (csa_readcodec(&csa->res, regno + BA0_AC97_RESET, &data)) 14570134Scg data = 0; 14677504Scg csa_active(csa, -1); 14753553Stanimura 14870134Scg return data; 14953553Stanimura} 15053553Stanimura 15153553Stanimurastatic int 15270134Scgcsa_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) 15353553Stanimura{ 15470134Scg struct csa_info *csa = (struct csa_info *)devinfo; 15553553Stanimura 15677504Scg csa_active(csa, 1); 15770134Scg csa_writecodec(&csa->res, regno + BA0_AC97_RESET, data); 15877504Scg csa_active(csa, -1); 15953553Stanimura 16053553Stanimura return 0; 16153553Stanimura} 16253553Stanimura 16370134Scgstatic kobj_method_t csa_ac97_methods[] = { 16470134Scg KOBJMETHOD(ac97_read, csa_rdcd), 16570134Scg KOBJMETHOD(ac97_write, csa_wrcd), 16670134Scg { 0, 0 } 16770134Scg}; 16870134ScgAC97_DECLARE(csa_ac97); 16953553Stanimura 17053553Stanimurastatic void 17153553Stanimuracsa_setplaysamplerate(csa_res *resp, u_long ulInRate) 17253553Stanimura{ 17353553Stanimura u_long ulTemp1, ulTemp2; 17453553Stanimura u_long ulPhiIncr; 17553553Stanimura u_long ulCorrectionPerGOF, ulCorrectionPerSec; 17653553Stanimura u_long ulOutRate; 17753553Stanimura 17853553Stanimura ulOutRate = 48000; 17953553Stanimura 18053553Stanimura /* 18153553Stanimura * Compute the values used to drive the actual sample rate conversion. 18253553Stanimura * The following formulas are being computed, using inline assembly 18353553Stanimura * since we need to use 64 bit arithmetic to compute the values: 18453553Stanimura * 18553553Stanimura * ulPhiIncr = floor((Fs,in * 2^26) / Fs,out) 18653553Stanimura * ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) / 18753553Stanimura * GOF_PER_SEC) 18853553Stanimura * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr - 18953553Stanimura * GOF_PER_SEC * ulCorrectionPerGOF 19053553Stanimura * 19153553Stanimura * i.e. 19253553Stanimura * 19353553Stanimura * ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out) 19453553Stanimura * ulCorrectionPerGOF:ulCorrectionPerSec = 19553553Stanimura * dividend:remainder(ulOther / GOF_PER_SEC) 19653553Stanimura */ 19753553Stanimura ulTemp1 = ulInRate << 16; 19853553Stanimura ulPhiIncr = ulTemp1 / ulOutRate; 19953553Stanimura ulTemp1 -= ulPhiIncr * ulOutRate; 20053553Stanimura ulTemp1 <<= 10; 20153553Stanimura ulPhiIncr <<= 10; 20253553Stanimura ulTemp2 = ulTemp1 / ulOutRate; 20353553Stanimura ulPhiIncr += ulTemp2; 20453553Stanimura ulTemp1 -= ulTemp2 * ulOutRate; 20553553Stanimura ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC; 20653553Stanimura ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC; 20753553Stanimura ulCorrectionPerSec = ulTemp1; 20853553Stanimura 20953553Stanimura /* 21053553Stanimura * Fill in the SampleRateConverter control block. 21153553Stanimura */ 21253553Stanimura csa_writemem(resp, BA1_PSRC, ((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF)); 21353553Stanimura csa_writemem(resp, BA1_PPI, ulPhiIncr); 21453553Stanimura} 21553553Stanimura 21653553Stanimurastatic void 21753553Stanimuracsa_setcapturesamplerate(csa_res *resp, u_long ulOutRate) 21853553Stanimura{ 21953553Stanimura u_long ulPhiIncr, ulCoeffIncr, ulTemp1, ulTemp2; 22053553Stanimura u_long ulCorrectionPerGOF, ulCorrectionPerSec, ulInitialDelay; 22153553Stanimura u_long dwFrameGroupLength, dwCnt; 22253553Stanimura u_long ulInRate; 22353553Stanimura 22453553Stanimura ulInRate = 48000; 22553553Stanimura 22653553Stanimura /* 22753553Stanimura * We can only decimate by up to a factor of 1/9th the hardware rate. 22853553Stanimura * Return an error if an attempt is made to stray outside that limit. 22953553Stanimura */ 23053553Stanimura if((ulOutRate * 9) < ulInRate) 23153553Stanimura return; 23253553Stanimura 23353553Stanimura /* 23453553Stanimura * We can not capture at at rate greater than the Input Rate (48000). 23553553Stanimura * Return an error if an attempt is made to stray outside that limit. 23653553Stanimura */ 23753553Stanimura if(ulOutRate > ulInRate) 23853553Stanimura return; 23953553Stanimura 24053553Stanimura /* 24153553Stanimura * Compute the values used to drive the actual sample rate conversion. 24253553Stanimura * The following formulas are being computed, using inline assembly 24353553Stanimura * since we need to use 64 bit arithmetic to compute the values: 24453553Stanimura * 24553553Stanimura * ulCoeffIncr = -floor((Fs,out * 2^23) / Fs,in) 24653553Stanimura * ulPhiIncr = floor((Fs,in * 2^26) / Fs,out) 24753553Stanimura * ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) / 24853553Stanimura * GOF_PER_SEC) 24953553Stanimura * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr - 25053553Stanimura * GOF_PER_SEC * ulCorrectionPerGOF 25153553Stanimura * ulInitialDelay = ceil((24 * Fs,in) / Fs,out) 25253553Stanimura * 25353553Stanimura * i.e. 25453553Stanimura * 25553553Stanimura * ulCoeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in)) 25653553Stanimura * ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out) 25753553Stanimura * ulCorrectionPerGOF:ulCorrectionPerSec = 25853553Stanimura * dividend:remainder(ulOther / GOF_PER_SEC) 25953553Stanimura * ulInitialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out) 26053553Stanimura */ 26153553Stanimura ulTemp1 = ulOutRate << 16; 26253553Stanimura ulCoeffIncr = ulTemp1 / ulInRate; 26353553Stanimura ulTemp1 -= ulCoeffIncr * ulInRate; 26453553Stanimura ulTemp1 <<= 7; 26553553Stanimura ulCoeffIncr <<= 7; 26653553Stanimura ulCoeffIncr += ulTemp1 / ulInRate; 26753553Stanimura ulCoeffIncr ^= 0xFFFFFFFF; 26853553Stanimura ulCoeffIncr++; 26953553Stanimura ulTemp1 = ulInRate << 16; 27053553Stanimura ulPhiIncr = ulTemp1 / ulOutRate; 27153553Stanimura ulTemp1 -= ulPhiIncr * ulOutRate; 27253553Stanimura ulTemp1 <<= 10; 27353553Stanimura ulPhiIncr <<= 10; 27453553Stanimura ulTemp2 = ulTemp1 / ulOutRate; 27553553Stanimura ulPhiIncr += ulTemp2; 27653553Stanimura ulTemp1 -= ulTemp2 * ulOutRate; 27753553Stanimura ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC; 27853553Stanimura ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC; 27953553Stanimura ulCorrectionPerSec = ulTemp1; 28053553Stanimura ulInitialDelay = ((ulInRate * 24) + ulOutRate - 1) / ulOutRate; 28153553Stanimura 28253553Stanimura /* 28353553Stanimura * Fill in the VariDecimate control block. 28453553Stanimura */ 28556249Scg csa_writemem(resp, BA1_CSRC, 28653553Stanimura ((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF)); 28753553Stanimura csa_writemem(resp, BA1_CCI, ulCoeffIncr); 28856249Scg csa_writemem(resp, BA1_CD, 28953553Stanimura (((BA1_VARIDEC_BUF_1 + (ulInitialDelay << 2)) << 16) & 0xFFFF0000) | 0x80); 29053553Stanimura csa_writemem(resp, BA1_CPI, ulPhiIncr); 29153553Stanimura 29253553Stanimura /* 29353553Stanimura * Figure out the frame group length for the write back task. Basically, 29453553Stanimura * this is just the factors of 24000 (2^6*3*5^3) that are not present in 29553553Stanimura * the output sample rate. 29653553Stanimura */ 29753553Stanimura dwFrameGroupLength = 1; 29853553Stanimura for(dwCnt = 2; dwCnt <= 64; dwCnt *= 2) 29953553Stanimura { 30053553Stanimura if(((ulOutRate / dwCnt) * dwCnt) != 30153553Stanimura ulOutRate) 30253553Stanimura { 30353553Stanimura dwFrameGroupLength *= 2; 30453553Stanimura } 30553553Stanimura } 30653553Stanimura if(((ulOutRate / 3) * 3) != 30753553Stanimura ulOutRate) 30853553Stanimura { 30953553Stanimura dwFrameGroupLength *= 3; 31053553Stanimura } 31153553Stanimura for(dwCnt = 5; dwCnt <= 125; dwCnt *= 5) 31253553Stanimura { 31353553Stanimura if(((ulOutRate / dwCnt) * dwCnt) != 31453553Stanimura ulOutRate) 31553553Stanimura { 31653553Stanimura dwFrameGroupLength *= 5; 31753553Stanimura } 31853553Stanimura } 31953553Stanimura 32053553Stanimura /* 32153553Stanimura * Fill in the WriteBack control block. 32253553Stanimura */ 32353553Stanimura csa_writemem(resp, BA1_CFG1, dwFrameGroupLength); 32453553Stanimura csa_writemem(resp, BA1_CFG2, (0x00800000 | dwFrameGroupLength)); 32553553Stanimura csa_writemem(resp, BA1_CCST, 0x0000FFFF); 32653553Stanimura csa_writemem(resp, BA1_CSPB, ((65536 * ulOutRate) / 24000)); 32753553Stanimura csa_writemem(resp, (BA1_CSPB + 4), 0x0000FFFF); 32853553Stanimura} 32953553Stanimura 33053553Stanimurastatic void 33153553Stanimuracsa_startplaydma(struct csa_info *csa) 33253553Stanimura{ 33353553Stanimura csa_res *resp; 33453553Stanimura u_long ul; 33553553Stanimura 33655321Stanimura if (!csa->pch.dma) { 33755321Stanimura resp = &csa->res; 33855321Stanimura ul = csa_readmem(resp, BA1_PCTL); 33955321Stanimura ul &= 0x0000ffff; 34055321Stanimura csa_writemem(resp, BA1_PCTL, ul | csa->pctl); 34155321Stanimura csa_writemem(resp, BA1_PVOL, 0x80008000); 34255321Stanimura csa->pch.dma = 1; 34355321Stanimura } 34453553Stanimura} 34553553Stanimura 34653553Stanimurastatic void 34753553Stanimuracsa_startcapturedma(struct csa_info *csa) 34853553Stanimura{ 34953553Stanimura csa_res *resp; 35053553Stanimura u_long ul; 35153553Stanimura 35255321Stanimura if (!csa->rch.dma) { 35355321Stanimura resp = &csa->res; 35455321Stanimura ul = csa_readmem(resp, BA1_CCTL); 35555321Stanimura ul &= 0xffff0000; 35655321Stanimura csa_writemem(resp, BA1_CCTL, ul | csa->cctl); 35755321Stanimura csa_writemem(resp, BA1_CVOL, 0x80008000); 35855321Stanimura csa->rch.dma = 1; 35955321Stanimura } 36053553Stanimura} 36153553Stanimura 36253553Stanimurastatic void 36353553Stanimuracsa_stopplaydma(struct csa_info *csa) 36453553Stanimura{ 36553553Stanimura csa_res *resp; 36653553Stanimura u_long ul; 36753553Stanimura 36855321Stanimura if (csa->pch.dma) { 36955321Stanimura resp = &csa->res; 37055321Stanimura ul = csa_readmem(resp, BA1_PCTL); 37155321Stanimura csa->pctl = ul & 0xffff0000; 37255321Stanimura csa_writemem(resp, BA1_PCTL, ul & 0x0000ffff); 37355321Stanimura csa_writemem(resp, BA1_PVOL, 0xffffffff); 37456427Stanimura csa->pch.dma = 0; 37555320Stanimura 37656427Stanimura /* 37756427Stanimura * The bitwise pointer of the serial FIFO in the DSP 37856427Stanimura * seems to make an error upon starting or stopping the 37956427Stanimura * DSP. Clear the FIFO and correct the pointer if we 38056427Stanimura * are not capturing. 38156427Stanimura */ 38256427Stanimura if (!csa->rch.dma) { 38356427Stanimura csa_clearserialfifos(resp); 38456427Stanimura csa_writeio(resp, BA0_SERBSP, 0); 38556427Stanimura } 38655321Stanimura } 38753553Stanimura} 38853553Stanimura 38953553Stanimurastatic void 39053553Stanimuracsa_stopcapturedma(struct csa_info *csa) 39153553Stanimura{ 39253553Stanimura csa_res *resp; 39353553Stanimura u_long ul; 39453553Stanimura 39555321Stanimura if (csa->rch.dma) { 39655321Stanimura resp = &csa->res; 39755321Stanimura ul = csa_readmem(resp, BA1_CCTL); 39855321Stanimura csa->cctl = ul & 0x0000ffff; 39955321Stanimura csa_writemem(resp, BA1_CCTL, ul & 0xffff0000); 40055321Stanimura csa_writemem(resp, BA1_CVOL, 0xffffffff); 40155321Stanimura csa->rch.dma = 0; 40256427Stanimura 40356427Stanimura /* 40456427Stanimura * The bitwise pointer of the serial FIFO in the DSP 40556427Stanimura * seems to make an error upon starting or stopping the 40656427Stanimura * DSP. Clear the FIFO and correct the pointer if we 40756427Stanimura * are not playing. 40856427Stanimura */ 40956427Stanimura if (!csa->pch.dma) { 41056427Stanimura csa_clearserialfifos(resp); 41156427Stanimura csa_writeio(resp, BA0_SERBSP, 0); 41256427Stanimura } 41355321Stanimura } 41453553Stanimura} 41553553Stanimura 41653553Stanimurastatic int 41753553Stanimuracsa_startdsp(csa_res *resp) 41853553Stanimura{ 41953553Stanimura int i; 42053553Stanimura u_long ul; 42153553Stanimura 42253553Stanimura /* 42353553Stanimura * Set the frame timer to reflect the number of cycles per frame. 42453553Stanimura */ 42553553Stanimura csa_writemem(resp, BA1_FRMT, 0xadf); 42653553Stanimura 42753553Stanimura /* 42853553Stanimura * Turn on the run, run at frame, and DMA enable bits in the local copy of 42953553Stanimura * the SP control register. 43053553Stanimura */ 43153553Stanimura csa_writemem(resp, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN); 43253553Stanimura 43353553Stanimura /* 43453553Stanimura * Wait until the run at frame bit resets itself in the SP control 43553553Stanimura * register. 43653553Stanimura */ 43753553Stanimura ul = 0; 43853553Stanimura for (i = 0 ; i < 25 ; i++) { 43953553Stanimura /* 44053553Stanimura * Wait a little bit, so we don't issue PCI reads too frequently. 44153553Stanimura */ 44277504Scg DELAY(50); 44353553Stanimura /* 44453553Stanimura * Fetch the current value of the SP status register. 44553553Stanimura */ 44653553Stanimura ul = csa_readmem(resp, BA1_SPCR); 44753553Stanimura 44853553Stanimura /* 44953553Stanimura * If the run at frame bit has reset, then stop waiting. 45053553Stanimura */ 45153553Stanimura if((ul & SPCR_RUNFR) == 0) 45253553Stanimura break; 45353553Stanimura } 45453553Stanimura /* 45553553Stanimura * If the run at frame bit never reset, then return an error. 45653553Stanimura */ 45753553Stanimura if((ul & SPCR_RUNFR) != 0) 45853553Stanimura return (EAGAIN); 45953553Stanimura 46053553Stanimura return (0); 46153553Stanimura} 46253553Stanimura 46377504Scgstatic int 464147626Sglebiuscsa_stopdsp(csa_res *resp) 465147626Sglebius{ 466147626Sglebius /* 467147626Sglebius * Turn off the run, run at frame, and DMA enable bits in 468147626Sglebius * the local copy of the SP control register. 469147626Sglebius */ 470147626Sglebius csa_writemem(resp, BA1_SPCR, 0); 471147626Sglebius 472147626Sglebius return (0); 473147626Sglebius} 474147626Sglebius 475147626Sglebiusstatic int 47677504Scgcsa_setupchan(struct csa_chinfo *ch) 47777504Scg{ 47877504Scg struct csa_info *csa = ch->parent; 47977504Scg csa_res *resp = &csa->res; 48077504Scg u_long pdtc, tmp; 48177504Scg 48277504Scg if (ch->dir == PCMDIR_PLAY) { 48377504Scg /* direction */ 484111183Scognet csa_writemem(resp, BA1_PBA, sndbuf_getbufaddr(ch->buffer)); 48577504Scg 48677504Scg /* format */ 48777504Scg csa->pfie = csa_readmem(resp, BA1_PFIE) & ~0x0000f03f; 48877504Scg if (!(ch->fmt & AFMT_SIGNED)) 48977504Scg csa->pfie |= 0x8000; 49077504Scg if (ch->fmt & AFMT_BIGENDIAN) 49177504Scg csa->pfie |= 0x4000; 49277504Scg if (!(ch->fmt & AFMT_STEREO)) 49377504Scg csa->pfie |= 0x2000; 49477504Scg if (ch->fmt & AFMT_8BIT) 49577504Scg csa->pfie |= 0x1000; 49677504Scg csa_writemem(resp, BA1_PFIE, csa->pfie); 49777504Scg 49877504Scg tmp = 4; 49977504Scg if (ch->fmt & AFMT_16BIT) 50077504Scg tmp <<= 1; 50177504Scg if (ch->fmt & AFMT_STEREO) 50277504Scg tmp <<= 1; 50377504Scg tmp--; 50477504Scg 50577504Scg pdtc = csa_readmem(resp, BA1_PDTC) & ~0x000001ff; 50677504Scg pdtc |= tmp; 50777504Scg csa_writemem(resp, BA1_PDTC, pdtc); 50877504Scg 50977504Scg /* rate */ 51077504Scg csa_setplaysamplerate(resp, ch->spd); 51177504Scg } else if (ch->dir == PCMDIR_REC) { 51277504Scg /* direction */ 513111183Scognet csa_writemem(resp, BA1_CBA, sndbuf_getbufaddr(ch->buffer)); 51477504Scg 51577504Scg /* format */ 51677504Scg csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001); 51777504Scg 51877504Scg /* rate */ 51977504Scg csa_setcapturesamplerate(resp, ch->spd); 52077504Scg } 52177504Scg return 0; 52277504Scg} 52377504Scg 52470134Scg/* -------------------------------------------------------------------- */ 52570134Scg/* channel interface */ 52670134Scg 52770134Scgstatic void * 52874763Scgcsachan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 52970134Scg{ 53070134Scg struct csa_info *csa = devinfo; 53170134Scg struct csa_chinfo *ch = (dir == PCMDIR_PLAY)? &csa->pch : &csa->rch; 53270134Scg 53370134Scg ch->parent = csa; 53470134Scg ch->channel = c; 53570134Scg ch->buffer = b; 53677504Scg ch->dir = dir; 537136469Syongari if (sndbuf_alloc(ch->buffer, csa->parent_dmat, CS461x_BUFFSIZE) != 0) 538136469Syongari return NULL; 53970134Scg return ch; 54070134Scg} 54170134Scg 54253553Stanimurastatic int 54370134Scgcsachan_setformat(kobj_t obj, void *data, u_int32_t format) 54470134Scg{ 54570134Scg struct csa_chinfo *ch = data; 54670134Scg 54770134Scg ch->fmt = format; 54870134Scg return 0; 54970134Scg} 55070134Scg 55170134Scgstatic int 55270134Scgcsachan_setspeed(kobj_t obj, void *data, u_int32_t speed) 55370134Scg{ 55470134Scg struct csa_chinfo *ch = data; 55570134Scg 55677504Scg ch->spd = speed; 55777504Scg return ch->spd; /* XXX calc real speed */ 55870134Scg} 55970134Scg 56070134Scgstatic int 56170134Scgcsachan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 56270134Scg{ 56370291Scg return CS461x_BUFFSIZE / 2; 56470134Scg} 56570134Scg 56670134Scgstatic int 56770134Scgcsachan_trigger(kobj_t obj, void *data, int go) 56870134Scg{ 56970134Scg struct csa_chinfo *ch = data; 57070134Scg struct csa_info *csa = ch->parent; 57170134Scg 57270134Scg if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 57370134Scg return 0; 57470134Scg 57577504Scg if (go == PCMTRIG_START) { 57677504Scg csa_active(csa, 1); 57777504Scg csa_setupchan(ch); 57877504Scg if (ch->dir == PCMDIR_PLAY) 57970134Scg csa_startplaydma(csa); 58070134Scg else 58177504Scg csa_startcapturedma(csa); 58277504Scg } else { 58377504Scg if (ch->dir == PCMDIR_PLAY) 58470134Scg csa_stopplaydma(csa); 58570134Scg else 58670134Scg csa_stopcapturedma(csa); 58777504Scg csa_active(csa, -1); 58870134Scg } 58970134Scg return 0; 59070134Scg} 59170134Scg 59270134Scgstatic int 59370134Scgcsachan_getptr(kobj_t obj, void *data) 59470134Scg{ 59570134Scg struct csa_chinfo *ch = data; 59670134Scg struct csa_info *csa = ch->parent; 59770134Scg csa_res *resp; 59853553Stanimura int ptr; 59953553Stanimura 60053553Stanimura resp = &csa->res; 60153553Stanimura 60253553Stanimura if (ch->dir == PCMDIR_PLAY) { 603111183Scognet ptr = csa_readmem(resp, BA1_PBA) - sndbuf_getbufaddr(ch->buffer); 60453553Stanimura if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0) 60553553Stanimura ptr >>= 1; 60653553Stanimura } else { 607111183Scognet ptr = csa_readmem(resp, BA1_CBA) - sndbuf_getbufaddr(ch->buffer); 60853553Stanimura if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0) 60953553Stanimura ptr >>= 1; 61053553Stanimura } 61153553Stanimura 61253553Stanimura return (ptr); 61353553Stanimura} 61453553Stanimura 61574763Scgstatic struct pcmchan_caps * 61670134Scgcsachan_getcaps(kobj_t obj, void *data) 61753553Stanimura{ 61853553Stanimura struct csa_chinfo *ch = data; 61953553Stanimura return (ch->dir == PCMDIR_PLAY)? &csa_playcaps : &csa_reccaps; 62053553Stanimura} 62153553Stanimura 62270134Scgstatic kobj_method_t csachan_methods[] = { 62370134Scg KOBJMETHOD(channel_init, csachan_init), 62470134Scg KOBJMETHOD(channel_setformat, csachan_setformat), 62570134Scg KOBJMETHOD(channel_setspeed, csachan_setspeed), 62670134Scg KOBJMETHOD(channel_setblocksize, csachan_setblocksize), 62770134Scg KOBJMETHOD(channel_trigger, csachan_trigger), 62870134Scg KOBJMETHOD(channel_getptr, csachan_getptr), 62970134Scg KOBJMETHOD(channel_getcaps, csachan_getcaps), 63070134Scg { 0, 0 } 63170134Scg}; 63270134ScgCHANNEL_DECLARE(csachan); 63370134Scg 63470134Scg/* -------------------------------------------------------------------- */ 63553553Stanimura/* The interrupt handler */ 63653553Stanimurastatic void 63777504Scgcsa_intr(void *p) 63853553Stanimura{ 63953553Stanimura struct csa_info *csa = p; 64053553Stanimura 64155320Stanimura if ((csa->binfo->hisr & HISR_VC0) != 0) 64253553Stanimura chn_intr(csa->pch.channel); 64355320Stanimura if ((csa->binfo->hisr & HISR_VC1) != 0) 64453553Stanimura chn_intr(csa->rch.channel); 64553553Stanimura} 64653553Stanimura 64753553Stanimura/* -------------------------------------------------------------------- */ 64853553Stanimura 64953553Stanimura/* 65053553Stanimura * Probe and attach the card 65153553Stanimura */ 65253553Stanimura 65353553Stanimurastatic int 65453553Stanimuracsa_init(struct csa_info *csa) 65553553Stanimura{ 65653553Stanimura csa_res *resp; 65753553Stanimura 65853553Stanimura resp = &csa->res; 65953553Stanimura 66053553Stanimura csa->pfie = 0; 66153553Stanimura csa_stopplaydma(csa); 66253553Stanimura csa_stopcapturedma(csa); 66353553Stanimura 66477504Scg if (csa_startdsp(resp)) 66577504Scg return (1); 66677504Scg 66753553Stanimura /* Crank up the power on the DAC and ADC. */ 66853553Stanimura csa_setplaysamplerate(resp, 8000); 66953553Stanimura csa_setcapturesamplerate(resp, 8000); 670149988Snetchild /* Set defaults */ 671149988Snetchild csa_writeio(resp, BA0_EGPIODR, EGPIODR_GPOE0); 672149988Snetchild csa_writeio(resp, BA0_EGPIOPTR, EGPIOPTR_GPPT0); 673149988Snetchild /* Power up amplifier */ 674149988Snetchild csa_writeio(resp, BA0_EGPIODR, csa_readio(resp, BA0_EGPIODR) | 675149988Snetchild EGPIODR_GPOE2); 676149988Snetchild csa_writeio(resp, BA0_EGPIOPTR, csa_readio(resp, BA0_EGPIOPTR) | 677149988Snetchild EGPIOPTR_GPPT2); 67853553Stanimura 67953553Stanimura return 0; 68053553Stanimura} 68153553Stanimura 68253553Stanimura/* Allocates resources. */ 68353553Stanimurastatic int 68453553Stanimuracsa_allocres(struct csa_info *csa, device_t dev) 68553553Stanimura{ 68653553Stanimura csa_res *resp; 68753553Stanimura 68853553Stanimura resp = &csa->res; 68953553Stanimura if (resp->io == NULL) { 690127135Snjl resp->io = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 691127135Snjl &resp->io_rid, RF_ACTIVE); 69253553Stanimura if (resp->io == NULL) 69353553Stanimura return (1); 69453553Stanimura } 69553553Stanimura if (resp->mem == NULL) { 696127135Snjl resp->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 697127135Snjl &resp->mem_rid, RF_ACTIVE); 69853553Stanimura if (resp->mem == NULL) 69953553Stanimura return (1); 70053553Stanimura } 70153553Stanimura if (resp->irq == NULL) { 702127135Snjl resp->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 703127135Snjl &resp->irq_rid, RF_ACTIVE | RF_SHAREABLE); 70453553Stanimura if (resp->irq == NULL) 70553553Stanimura return (1); 70653553Stanimura } 70753553Stanimura if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/CS461x_BUFFSIZE, /*boundary*/CS461x_BUFFSIZE, 70853553Stanimura /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 70953553Stanimura /*highaddr*/BUS_SPACE_MAXADDR, 71053553Stanimura /*filter*/NULL, /*filterarg*/NULL, 71153553Stanimura /*maxsize*/CS461x_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, 712117126Sscottl /*flags*/0, /*lockfunc*/busdma_lock_mutex, 713117126Sscottl /*lockarg*/&Giant, &csa->parent_dmat) != 0) 71453553Stanimura return (1); 71553553Stanimura 71653553Stanimura return (0); 71753553Stanimura} 71853553Stanimura 71953553Stanimura/* Releases resources. */ 72053553Stanimurastatic void 72153553Stanimuracsa_releaseres(struct csa_info *csa, device_t dev) 72253553Stanimura{ 72353553Stanimura csa_res *resp; 72453553Stanimura 72553553Stanimura resp = &csa->res; 72653553Stanimura if (resp->irq != NULL) { 72765644Scg if (csa->ih) 72865644Scg bus_teardown_intr(dev, resp->irq, csa->ih); 72953553Stanimura bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq); 73053553Stanimura resp->irq = NULL; 73153553Stanimura } 73253553Stanimura if (resp->io != NULL) { 73353553Stanimura bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); 73453553Stanimura resp->io = NULL; 73553553Stanimura } 73653553Stanimura if (resp->mem != NULL) { 73753553Stanimura bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem); 73853553Stanimura resp->mem = NULL; 73953553Stanimura } 74065644Scg if (csa->parent_dmat != NULL) { 74165644Scg bus_dma_tag_destroy(csa->parent_dmat); 74265644Scg csa->parent_dmat = NULL; 74365644Scg } 74465644Scg if (csa != NULL) { 74565644Scg free(csa, M_DEVBUF); 74665644Scg csa = NULL; 74765644Scg } 74853553Stanimura} 74953553Stanimura 75053553Stanimurastatic int 75153553Stanimurapcmcsa_probe(device_t dev) 75253553Stanimura{ 75353553Stanimura char *s; 75453553Stanimura struct sndcard_func *func; 75553553Stanimura 75653553Stanimura /* The parent device has already been probed. */ 75753553Stanimura 75853553Stanimura func = device_get_ivars(dev); 75953553Stanimura if (func == NULL || func->func != SCF_PCM) 76053553Stanimura return (ENXIO); 76153553Stanimura 76253553Stanimura s = "CS461x PCM Audio"; 76353553Stanimura 76453553Stanimura device_set_desc(dev, s); 76553553Stanimura return (0); 76653553Stanimura} 76753553Stanimura 76853553Stanimurastatic int 76953553Stanimurapcmcsa_attach(device_t dev) 77053553Stanimura{ 77153553Stanimura struct csa_info *csa; 77253553Stanimura csa_res *resp; 77353553Stanimura int unit; 77453553Stanimura char status[SND_STATUSLEN]; 77553553Stanimura struct ac97_info *codec; 77655320Stanimura struct sndcard_func *func; 77753553Stanimura 77878564Sgreid csa = malloc(sizeof(*csa), M_DEVBUF, M_NOWAIT | M_ZERO); 77953553Stanimura if (csa == NULL) 78053553Stanimura return (ENOMEM); 78153553Stanimura unit = device_get_unit(dev); 78255320Stanimura func = device_get_ivars(dev); 78355320Stanimura csa->binfo = func->varinfo; 78455321Stanimura /* 78555321Stanimura * Fake the status of DMA so that the initial value of 78655321Stanimura * PCTL and CCTL can be stored into csa->pctl and csa->cctl, 78755321Stanimura * respectively. 78855321Stanimura */ 78955321Stanimura csa->pch.dma = csa->rch.dma = 1; 79077504Scg csa->active = 0; 79177504Scg csa->card = csa->binfo->card; 79253553Stanimura 79353553Stanimura /* Allocate the resources. */ 79453553Stanimura resp = &csa->res; 795119690Sjhb resp->io_rid = PCIR_BAR(0); 796119690Sjhb resp->mem_rid = PCIR_BAR(1); 79753553Stanimura resp->irq_rid = 0; 79853553Stanimura if (csa_allocres(csa, dev)) { 79953553Stanimura csa_releaseres(csa, dev); 80053553Stanimura return (ENXIO); 80153553Stanimura } 80253553Stanimura 80377504Scg csa_active(csa, 1); 80453553Stanimura if (csa_init(csa)) { 80553553Stanimura csa_releaseres(csa, dev); 80653553Stanimura return (ENXIO); 80753553Stanimura } 80870134Scg codec = AC97_CREATE(dev, csa, csa_ac97); 80965644Scg if (codec == NULL) { 81065644Scg csa_releaseres(csa, dev); 81153553Stanimura return (ENXIO); 81265644Scg } 81378673Scg if (csa->card->inv_eapd) 81478673Scg ac97_setflags(codec, AC97_F_EAPD_INV); 81570134Scg if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) { 81665644Scg ac97_destroy(codec); 81765644Scg csa_releaseres(csa, dev); 81858905Scg return (ENXIO); 81965644Scg } 82053553Stanimura 821126695Smatk snprintf(status, SND_STATUSLEN, "at irq %ld %s", 822126695Smatk rman_get_start(resp->irq),PCM_KLDSTRING(snd_csa)); 82353553Stanimura 82453553Stanimura /* Enable interrupt. */ 825128232Sgreen if (snd_setup_intr(dev, resp->irq, 0, csa_intr, csa, &csa->ih)) { 82665644Scg ac97_destroy(codec); 82753553Stanimura csa_releaseres(csa, dev); 82853553Stanimura return (ENXIO); 82953553Stanimura } 83053553Stanimura csa_writemem(resp, BA1_PFIE, csa_readmem(resp, BA1_PFIE) & ~0x0000f03f); 83153553Stanimura csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001); 83277504Scg csa_active(csa, -1); 83353553Stanimura 83453553Stanimura if (pcm_register(dev, csa, 1, 1)) { 83565644Scg ac97_destroy(codec); 83653553Stanimura csa_releaseres(csa, dev); 83753553Stanimura return (ENXIO); 83853553Stanimura } 83970134Scg pcm_addchan(dev, PCMDIR_REC, &csachan_class, csa); 84070134Scg pcm_addchan(dev, PCMDIR_PLAY, &csachan_class, csa); 84153553Stanimura pcm_setstatus(dev, status); 84253553Stanimura 84353553Stanimura return (0); 84453553Stanimura} 84553553Stanimura 84665644Scgstatic int 84765644Scgpcmcsa_detach(device_t dev) 84865644Scg{ 84965644Scg int r; 85065644Scg struct csa_info *csa; 85165644Scg 85265644Scg r = pcm_unregister(dev); 85365644Scg if (r) 85465644Scg return r; 85565644Scg 85665644Scg csa = pcm_getdevinfo(dev); 85765644Scg csa_releaseres(csa, dev); 85865644Scg 85965644Scg return 0; 86065644Scg} 86165644Scg 862147626Sglebiusstatic void 863147626Sglebiuscsa_ac97_suspend(struct csa_info *csa) 864147626Sglebius{ 865147626Sglebius int count, i; 866147626Sglebius uint32_t tmp; 867147626Sglebius 868147626Sglebius for (count = 0x2, i=0; 869147626Sglebius (count <= CS461x_AC97_HIGHESTREGTORESTORE) && 870147626Sglebius (i < CS461x_AC97_NUMBER_RESTORE_REGS); 871147626Sglebius count += 2, i++) 872147626Sglebius csa_readcodec(&csa->res, BA0_AC97_RESET + count, &csa->ac97[i]); 873147626Sglebius 874147626Sglebius /* mute the outputs */ 875147626Sglebius csa_writecodec(&csa->res, BA0_AC97_MASTER_VOLUME, 0x8000); 876147626Sglebius csa_writecodec(&csa->res, BA0_AC97_HEADPHONE_VOLUME, 0x8000); 877147626Sglebius csa_writecodec(&csa->res, BA0_AC97_MASTER_VOLUME_MONO, 0x8000); 878147626Sglebius csa_writecodec(&csa->res, BA0_AC97_PCM_OUT_VOLUME, 0x8000); 879147626Sglebius /* save the registers that cause pops */ 880147626Sglebius csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &csa->ac97_powerdown); 881147626Sglebius csa_readcodec(&csa->res, BA0_AC97_GENERAL_PURPOSE, 882147626Sglebius &csa->ac97_general_purpose); 883147626Sglebius 884147626Sglebius /* 885147626Sglebius * And power down everything on the AC97 codec. Well, for now, 886147626Sglebius * only power down the DAC/ADC and MIXER VREFON components. 887147626Sglebius * trouble with removing VREF. 888147626Sglebius */ 889147626Sglebius 890147626Sglebius /* MIXVON */ 891147626Sglebius csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp); 892147626Sglebius csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, 893147626Sglebius tmp | CS_AC97_POWER_CONTROL_MIXVON); 894147626Sglebius /* ADC */ 895147626Sglebius csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp); 896147626Sglebius csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, 897147626Sglebius tmp | CS_AC97_POWER_CONTROL_ADC); 898147626Sglebius /* DAC */ 899147626Sglebius csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp); 900147626Sglebius csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, 901147626Sglebius tmp | CS_AC97_POWER_CONTROL_DAC); 902147626Sglebius} 903147626Sglebius 904147626Sglebiusstatic void 905147626Sglebiuscsa_ac97_resume(struct csa_info *csa) 906147626Sglebius{ 907147626Sglebius int count, i; 908147626Sglebius 909147626Sglebius /* 910147626Sglebius * First, we restore the state of the general purpose register. This 911147626Sglebius * contains the mic select (mic1 or mic2) and if we restore this after 912147626Sglebius * we restore the mic volume/boost state and mic2 was selected at 913147626Sglebius * suspend time, we will end up with a brief period of time where mic1 914147626Sglebius * is selected with the volume/boost settings for mic2, causing 915147626Sglebius * acoustic feedback. So we restore the general purpose register 916147626Sglebius * first, thereby getting the correct mic selected before we restore 917147626Sglebius * the mic volume/boost. 918147626Sglebius */ 919147626Sglebius csa_writecodec(&csa->res, BA0_AC97_GENERAL_PURPOSE, 920147626Sglebius csa->ac97_general_purpose); 921147626Sglebius /* 922147626Sglebius * Now, while the outputs are still muted, restore the state of power 923147626Sglebius * on the AC97 part. 924147626Sglebius */ 925147626Sglebius csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, csa->ac97_powerdown); 926147626Sglebius /* 927147626Sglebius * Restore just the first set of registers, from register number 928147626Sglebius * 0x02 to the register number that ulHighestRegToRestore specifies. 929147626Sglebius */ 930147626Sglebius for (count = 0x2, i=0; 931147626Sglebius (count <= CS461x_AC97_HIGHESTREGTORESTORE) && 932147626Sglebius (i < CS461x_AC97_NUMBER_RESTORE_REGS); 933147626Sglebius count += 2, i++) 934147626Sglebius csa_writecodec(&csa->res, BA0_AC97_RESET + count, csa->ac97[i]); 935147626Sglebius} 936147626Sglebius 937147626Sglebiusstatic int 938147626Sglebiuspcmcsa_suspend(device_t dev) 939147626Sglebius{ 940147626Sglebius struct csa_info *csa; 941147626Sglebius csa_res *resp; 942147626Sglebius 943147626Sglebius csa = pcm_getdevinfo(dev); 944147626Sglebius resp = &csa->res; 945147626Sglebius 946147626Sglebius csa_active(csa, 1); 947147626Sglebius 948147626Sglebius /* playback interrupt disable */ 949147626Sglebius csa_writemem(resp, BA1_PFIE, 950147626Sglebius (csa_readmem(resp, BA1_PFIE) & ~0x0000f03f) | 0x00000010); 951147626Sglebius /* capture interrupt disable */ 952147626Sglebius csa_writemem(resp, BA1_CIE, 953147626Sglebius (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000011); 954147626Sglebius csa_stopplaydma(csa); 955147626Sglebius csa_stopcapturedma(csa); 956147626Sglebius 957147626Sglebius csa_ac97_suspend(csa); 958147626Sglebius 959147626Sglebius csa_resetdsp(resp); 960147626Sglebius 961147626Sglebius csa_stopdsp(resp); 962147626Sglebius /* 963147626Sglebius * Power down the DAC and ADC. For now leave the other areas on. 964147626Sglebius */ 965147626Sglebius csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, 0x300); 966147626Sglebius /* 967147626Sglebius * Power down the PLL. 968147626Sglebius */ 969147626Sglebius csa_writemem(resp, BA0_CLKCR1, 0); 970147626Sglebius /* 971147626Sglebius * Turn off the Processor by turning off the software clock 972147626Sglebius * enable flag in the clock control register. 973147626Sglebius */ 974147626Sglebius csa_writemem(resp, BA0_CLKCR1, 975147626Sglebius csa_readmem(resp, BA0_CLKCR1) & ~CLKCR1_SWCE); 976147626Sglebius 977147626Sglebius csa_active(csa, -1); 978147626Sglebius 979147626Sglebius return 0; 980147626Sglebius} 981147626Sglebius 982147626Sglebiusstatic int 983147626Sglebiuspcmcsa_resume(device_t dev) 984147626Sglebius{ 985147626Sglebius struct csa_info *csa; 986147626Sglebius csa_res *resp; 987147626Sglebius 988147626Sglebius csa = pcm_getdevinfo(dev); 989147626Sglebius resp = &csa->res; 990147626Sglebius 991147626Sglebius csa_active(csa, 1); 992147626Sglebius 993147626Sglebius /* cs_hardware_init */ 994147626Sglebius csa_stopplaydma(csa); 995147626Sglebius csa_stopcapturedma(csa); 996147626Sglebius csa_ac97_resume(csa); 997147626Sglebius if (csa_startdsp(resp)) 998147626Sglebius return (ENXIO); 999147626Sglebius /* Enable interrupts on the part. */ 1000147626Sglebius if ((csa_readio(resp, BA0_HISR) & HISR_INTENA) == 0) 1001147626Sglebius csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); 1002147626Sglebius /* playback interrupt enable */ 1003147626Sglebius csa_writemem(resp, BA1_PFIE, csa_readmem(resp, BA1_PFIE) & ~0x0000f03f); 1004147626Sglebius /* capture interrupt enable */ 1005147626Sglebius csa_writemem(resp, BA1_CIE, 1006147626Sglebius (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001); 1007147626Sglebius /* cs_restart_part */ 1008147626Sglebius csa_setupchan(&csa->pch); 1009147626Sglebius csa_startplaydma(csa); 1010147626Sglebius csa_setupchan(&csa->rch); 1011147626Sglebius csa_startcapturedma(csa); 1012147626Sglebius 1013147626Sglebius csa_active(csa, -1); 1014147626Sglebius 1015147626Sglebius return 0; 1016147626Sglebius} 1017147626Sglebius 101853553Stanimurastatic device_method_t pcmcsa_methods[] = { 101953553Stanimura /* Device interface */ 102053553Stanimura DEVMETHOD(device_probe , pcmcsa_probe ), 102153553Stanimura DEVMETHOD(device_attach, pcmcsa_attach), 102265644Scg DEVMETHOD(device_detach, pcmcsa_detach), 1023147626Sglebius DEVMETHOD(device_suspend, pcmcsa_suspend), 1024147626Sglebius DEVMETHOD(device_resume, pcmcsa_resume), 102553553Stanimura 102653553Stanimura { 0, 0 }, 102753553Stanimura}; 102853553Stanimura 102953553Stanimurastatic driver_t pcmcsa_driver = { 103053553Stanimura "pcm", 103153553Stanimura pcmcsa_methods, 103282180Scg PCM_SOFTC_SIZE, 103353553Stanimura}; 103453553Stanimura 103562483ScgDRIVER_MODULE(snd_csapcm, csa, pcmcsa_driver, pcm_devclass, 0, 0); 1036132236StanimuraMODULE_DEPEND(snd_csapcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 103762483ScgMODULE_DEPEND(snd_csapcm, snd_csa, 1, 1, 1); 103862483ScgMODULE_VERSION(snd_csapcm, 1); 1039