Index: csa.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pci/csa.c,v retrieving revision 1.32 diff -u -r1.32 csa.c --- csa.c 1 Mar 2005 08:58:05 -0000 1.32 +++ csa.c 23 Jun 2005 19:42:44 -0000 @@ -87,7 +87,6 @@ struct resource *irq, void *cookie); static driver_intr_t csa_intr; static int csa_initialize(sc_p scp); -static void csa_resetdsp(csa_res *resp); static int csa_downloadimage(csa_res *resp); static devclass_t csa_devclass; @@ -366,15 +365,24 @@ static int csa_resume(device_t dev) { -#if 0 - /* - * XXX: this cannot possibly work - * needs to be properly implemented - */ - csa_detach(dev); - csa_attach(dev); -#endif - return 0; + csa_res *resp; + sc_p scp; + + scp = device_get_softc(dev); + resp = &scp->res; + + /* Initialize the chip. */ + if (csa_initialize(scp)) + return (ENXIO); + + /* Reset the Processor. */ + csa_resetdsp(resp); + + /* Download the Processor Image to the processor. */ + if (csa_downloadimage(resp)) + return (ENXIO); + + return (bus_generic_resume(dev)); } static struct resource * @@ -794,7 +802,7 @@ csa_writeio(resp, BA0_CLKCR1, clkcr1); } -static void +void csa_resetdsp(csa_res *resp) { int i; Index: csapcm.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pci/csapcm.c,v retrieving revision 1.33 diff -u -r1.33 csapcm.c --- csapcm.c 6 Jan 2005 01:43:19 -0000 1.33 +++ csapcm.c 23 Jun 2005 21:18:39 -0000 @@ -45,6 +45,23 @@ #define GOF_PER_SEC 200 +#define CS461x_AC97_HIGHESTREGTORESTORE 0x26 +#define CS461x_AC97_NUMBER_RESTORE_REGS (CS461x_AC97_HIGHESTREGTORESTORE/2-1) + +#define CS_POWER_DAC 0x0001 +#define CS_POWER_ADC 0x0002 +#define CS_POWER_MIXVON 0x0004 +#define CS_POWER_MIXVOFF 0x0008 +#define CS_AC97_POWER_CONTROL_ON 0xf000 /* always on bits (inverted) */ +#define CS_AC97_POWER_CONTROL_ADC 0x0100 +#define CS_AC97_POWER_CONTROL_DAC 0x0200 +#define CS_AC97_POWER_CONTROL_MIXVON 0x0400 +#define CS_AC97_POWER_CONTROL_MIXVOFF 0x0800 +#define CS_AC97_POWER_CONTROL_ADC_ON 0x0001 +#define CS_AC97_POWER_CONTROL_DAC_ON 0x0002 +#define CS_AC97_POWER_CONTROL_MIXVON_ON 0x0004 +#define CS_AC97_POWER_CONTROL_MIXVOFF_ON 0x0008 + /* device private data */ struct csa_info; @@ -70,6 +87,9 @@ u_long pctl; u_long cctl; struct csa_chinfo pch, rch; + u_int32_t ac97[CS461x_AC97_NUMBER_RESTORE_REGS]; + u_int32_t ac97_powerdown; + u_int32_t ac97_general_purpose; }; /* -------------------------------------------------------------------- */ @@ -84,8 +104,11 @@ static void csa_stopplaydma(struct csa_info *csa); static void csa_stopcapturedma(struct csa_info *csa); static int csa_startdsp(csa_res *resp); +static int csa_stopdsp(csa_res *resp); static int csa_allocres(struct csa_info *scp, device_t dev); static void csa_releaseres(struct csa_info *scp, device_t dev); +static void csa_ac97_suspend(struct csa_info *csa); +static void csa_ac97_resume(struct csa_info *csa); static u_int32_t csa_playfmt[] = { AFMT_U8, @@ -112,16 +135,16 @@ static int csa_active(struct csa_info *csa, int run) { - int old, go; + int old; old = csa->active; csa->active += run; - if ((csa->active == 0 && old == 1) || (csa->active == 1 && old == 0)) { - go = csa->active; - if (csa->card->active) - return csa->card->active(go); - } + if ((csa->active > 1) || (csa->active < -1)) + csa->active = 0; + if (csa->card->active) + return (csa->card->active(!(csa->active && old))); + return 0; } @@ -455,6 +478,18 @@ } static int +csa_stopdsp(csa_res *resp) +{ + /* + * Turn off the run, run at frame, and DMA enable bits in the local copy of + * the SP control register. + */ + csa_writemem(resp, BA1_SPCR, 0); + + return (0); +} + +static int csa_setupchan(struct csa_chinfo *ch) { struct csa_info *csa = ch->parent; @@ -833,11 +868,156 @@ return 0; } +static void +csa_ac97_suspend(struct csa_info *csa) +{ + int Count, i; + u_int32_t tmp; + + for(Count = 0x2, i=0; (Count <= CS461x_AC97_HIGHESTREGTORESTORE) && (i < CS461x_AC97_NUMBER_RESTORE_REGS); Count += 2, i++) + { + csa_readcodec(&csa->res, BA0_AC97_RESET + Count, &csa->ac97[i]); + } + /* mute the outputs */ + csa_writecodec(&csa->res, BA0_AC97_MASTER_VOLUME, 0x8000); + csa_writecodec(&csa->res, BA0_AC97_HEADPHONE_VOLUME, 0x8000); + csa_writecodec(&csa->res, BA0_AC97_MASTER_VOLUME_MONO, 0x8000); + csa_writecodec(&csa->res, BA0_AC97_PCM_OUT_VOLUME, 0x8000); + /* save the registers that cause pops */ + csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &csa->ac97_powerdown); + csa_readcodec(&csa->res, BA0_AC97_GENERAL_PURPOSE, &csa->ac97_general_purpose); + /* + * And power down everything on the AC97 codec. + * well, for now, only power down the DAC/ADC and MIXER VREFON components. + * trouble with removing VREF. + */ + /* MIXVON*/ + csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp); + csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, tmp | CS_AC97_POWER_CONTROL_MIXVON); + /* ADC */ + csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp); + csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, tmp | CS_AC97_POWER_CONTROL_ADC); + /* DAC */ + csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp); + csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, tmp | CS_AC97_POWER_CONTROL_DAC); +} + +static void +csa_ac97_resume(struct csa_info *csa) +{ + int Count, i; + + /* + * First, we restore the state of the general purpose register. This + * contains the mic select (mic1 or mic2) and if we restore this after + * we restore the mic volume/boost state and mic2 was selected at + * suspend time, we will end up with a brief period of time where mic1 + * is selected with the volume/boost settings for mic2, causing + * acoustic feedback. So we restore the general purpose register + * first, thereby getting the correct mic selected before we restore + * the mic volume/boost. + */ + csa_writecodec(&csa->res, BA0_AC97_GENERAL_PURPOSE, csa->ac97_general_purpose); + /* + * Now, while the outputs are still muted, restore the state of power + * on the AC97 part. + */ + csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, csa->ac97_powerdown); + /* + * Restore just the first set of registers, from register number + * 0x02 to the register number that ulHighestRegToRestore specifies. + */ + for(Count = 0x2, i=0; (Count <= CS461x_AC97_HIGHESTREGTORESTORE) && (i < CS461x_AC97_NUMBER_RESTORE_REGS); Count += 2, i++) + { + csa_writecodec(&csa->res, BA0_AC97_RESET + Count, csa->ac97[i]); + } + +} + +static int +pcmcsa_suspend(device_t dev) +{ + struct csa_info *csa; + csa_res *resp; + + csa = pcm_getdevinfo(dev); + resp = &csa->res; + + csa_active(csa, 1); + + /* playback interrupt disable */ + csa_writemem(resp, BA1_PFIE, (csa_readmem(resp, BA1_PFIE) & ~0x0000f03f) | 0x00000010); + /* capture interrupt disable */ + csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000011); + csa_stopplaydma(csa); + csa_stopcapturedma(csa); + + csa_ac97_suspend(csa); + + csa_resetdsp(resp); + + csa_stopdsp(resp); + /* + * Power down the DAC and ADC. For now leave the other areas on. + */ + csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, 0x300); + /* + * Power down the PLL. + */ + csa_writemem(resp, BA0_CLKCR1, 0); + /* + * Turn off the Processor by turning off the software clock enable flag in + * the clock control register. + */ + csa_writemem(resp, BA0_CLKCR1, csa_readmem(resp, BA0_CLKCR1) & ~CLKCR1_SWCE); + + csa_active(csa, -1); + + return 0; +} + +static int +pcmcsa_resume(device_t dev) +{ + struct csa_info *csa; + csa_res *resp; + + csa = pcm_getdevinfo(dev); + resp = &csa->res; + + csa_active(csa, 1); + + /* cs_hardware_init */ + csa_stopplaydma(csa); + csa_stopcapturedma(csa); + csa_ac97_resume(csa); + if (csa_startdsp(resp)) + return (ENXIO); + /* Enable interrupts on the part. */ + if ((csa_readio(resp, BA0_HISR) & HISR_INTENA) == 0) + csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); + /* playback interrupt enable */ + csa_writemem(resp, BA1_PFIE, csa_readmem(resp, BA1_PFIE) & ~0x0000f03f); + /* capture interrupt enable */ + csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001); + /* cs_restart_part */ + csa_setupchan(&csa->pch); + csa_startplaydma(csa); + csa_setupchan(&csa->rch); + csa_startcapturedma(csa); + + csa_active(csa, -1); + + return 0; +} + static device_method_t pcmcsa_methods[] = { /* Device interface */ DEVMETHOD(device_probe , pcmcsa_probe ), DEVMETHOD(device_attach, pcmcsa_attach), DEVMETHOD(device_detach, pcmcsa_detach), + DEVMETHOD(device_suspend, pcmcsa_suspend), + DEVMETHOD(device_resume, pcmcsa_resume), { 0, 0 }, }; Index: csavar.h =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pci/csavar.h,v retrieving revision 1.4 diff -u -r1.4 csavar.h --- csavar.h 23 Jun 2001 18:00:06 -0000 1.4 +++ csavar.h 23 Jun 2005 19:42:44 -0000 @@ -66,4 +66,5 @@ u_int32_t csa_readmem(csa_res *resp, u_long offset); void csa_writemem(csa_res *resp, u_long offset, u_int32_t data); +void csa_resetdsp(csa_res *resp); #endif /* _CSA_VAR_H */