Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 24 Jun 2005 11:34:00 +0400
From:      Gleb Smirnoff <glebius@FreeBSD.org>
To:        current@FreeBSD.org
Subject:   who can review&commit kern/82243?
Message-ID:  <20050624073400.GA70323@cell.sick.ru>

next in thread | raw e-mail | index | archive | help

--AqsLC8rIMeq19msA
Content-Type: text/plain; charset=koi8-r
Content-Disposition: inline

  Colleagues,

recently a patch making csa(4) suspend/resume working. I have tested
it on my notebook and it works perfectly.

The patch is attached (with small style editing from me) to this message.

-- 
Totus tuus, Glebius.
GLEBIUS-RIPN GLEB-RIPE

--AqsLC8rIMeq19msA
Content-Type: text/plain; charset=koi8-r
Content-Disposition: attachment; filename="csa.diff"

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 */

--AqsLC8rIMeq19msA--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20050624073400.GA70323>