[alsa-devel] X-Fi suspend/resume support patch

Takashi Iwai tiwai at suse.de
Tue Jun 9 18:41:18 CEST 2009


Hi,

can anyone test the patch below for supporting suspend/resume with
X-Fi driver?

Note that it's just compile tested but not really checked with any
real hardware, so highly likely incomplete.  Keep backup before trying
this :)


thanks,

Takashi

---
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 80fb2ba..9861418 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -1119,18 +1119,67 @@ static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state)
 	return err;
 }
 
-static int ct_atc_destroy(struct ct_atc *atc)
+static void ct_daio_clear_connections(struct ct_atc *atc)
 {
 	struct daio_mgr *daio_mgr;
 	struct dao *dao;
 	struct dai *dai;
 	struct daio *daio;
+	int i;
+
+	daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
+	for (i = 0; i < atc->n_daio; i++) {
+		daio = atc->daios[i];
+		if (daio->type < LINEIM) {
+			dao = container_of(daio, struct dao, daio);
+			dao->ops->clear_left_input(dao);
+			dao->ops->clear_right_input(dao);
+		} else {
+			dai = container_of(daio, struct dai, daio);
+			/* some thing to do for dai ... */
+		}
+		daio_mgr->put_daio(daio_mgr, daio);
+	}
+}
+
+static void ct_sum_clear_connections(struct ct_atc *atc)
+{
 	struct sum_mgr *sum_mgr;
+	int i;
+
+	sum_mgr = atc->rsc_mgrs[SUM];
+	for (i = 0; i < atc->n_pcm; i++)
+		sum_mgr->put_sum(sum_mgr, atc->pcm[i]);
+}
+
+static void ct_src_clear_connections(struct ct_atc *atc)
+{
 	struct src_mgr *src_mgr;
+	int i;
+
+	src_mgr = atc->rsc_mgrs[SRC];
+	for (i = 0; i < atc->n_src; i++)
+		src_mgr->put_src(src_mgr, atc->srcs[i]);
+}
+
+static void ct_srcimp_clear_connections(struct ct_atc *atc)
+{
 	struct srcimp_mgr *srcimp_mgr;
 	struct srcimp *srcimp;
+	int i;
+
+	srcimp_mgr = atc->rsc_mgrs[SRCIMP];
+	for (i = 0; i < atc->n_srcimp; i++) {
+		srcimp = atc->srcimps[i];
+		srcimp->ops->unmap(srcimp);
+		srcimp_mgr->put_srcimp(srcimp_mgr, atc->srcimps[i]);
+	}
+}
+
+static int ct_atc_destroy(struct ct_atc *atc)
+{
 	struct ct_mixer *mixer;
-	int i = 0;
+	int i;
 
 	if (NULL == atc)
 		return 0;
@@ -1157,45 +1206,22 @@ static int ct_atc_destroy(struct ct_atc *atc)
 	}
 
 	if (NULL != atc->daios) {
-		daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
-		for (i = 0; i < atc->n_daio; i++) {
-			daio = atc->daios[i];
-			if (daio->type < LINEIM) {
-				dao = container_of(daio, struct dao, daio);
-				dao->ops->clear_left_input(dao);
-				dao->ops->clear_right_input(dao);
-			} else {
-				dai = container_of(daio, struct dai, daio);
-				/* some thing to do for dai ... */
-			}
-			daio_mgr->put_daio(daio_mgr, daio);
-		}
+		ct_daio_clear_connections(atc);
 		kfree(atc->daios);
 	}
 
 	if (NULL != atc->pcm) {
-		sum_mgr = atc->rsc_mgrs[SUM];
-		for (i = 0; i < atc->n_pcm; i++)
-			sum_mgr->put_sum(sum_mgr, atc->pcm[i]);
-
+		ct_sum_clear_connections(atc);
 		kfree(atc->pcm);
 	}
 
 	if (NULL != atc->srcs) {
-		src_mgr = atc->rsc_mgrs[SRC];
-		for (i = 0; i < atc->n_src; i++)
-			src_mgr->put_src(src_mgr, atc->srcs[i]);
-
+		ct_src_clear_connections(atc);
 		kfree(atc->srcs);
 	}
 
 	if (NULL != atc->srcimps) {
-		srcimp_mgr = atc->rsc_mgrs[SRCIMP];
-		for (i = 0; i < atc->n_srcimp; i++) {
-			srcimp = atc->srcimps[i];
-			srcimp->ops->unmap(srcimp);
-			srcimp_mgr->put_srcimp(srcimp_mgr, atc->srcimps[i]);
-		}
+		ct_srcimp_clear_connections(atc);
 		kfree(atc->srcimps);
 	}
 
@@ -1314,37 +1340,15 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc)
 	return 0;
 }
 
-static int __devinit atc_get_resources(struct ct_atc *atc)
+static int ct_daio_init_connections(struct ct_atc *atc)
 {
 	struct daio_desc da_desc = {0};
 	struct daio_mgr *daio_mgr;
-	struct src_desc src_dsc = {0};
-	struct src_mgr *src_mgr;
-	struct srcimp_desc srcimp_dsc = {0};
-	struct srcimp_mgr *srcimp_mgr;
-	struct sum_desc sum_dsc = {0};
-	struct sum_mgr *sum_mgr;
-	int err, i;
-
-	atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
-	if (NULL == atc->daios)
-		return -ENOMEM;
-
-	atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
-	if (NULL == atc->srcs)
-		return -ENOMEM;
-
-	atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
-	if (NULL == atc->srcimps)
-		return -ENOMEM;
-
-	atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL);
-	if (NULL == atc->pcm)
-		return -ENOMEM;
+	int i, err;
 
 	daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
 	da_desc.msr = atc->msr;
-	for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) {
+	for (i = 0, atc->n_daio = 0; i < DAIONUM - 1; i++) {
 		da_desc.type = i;
 		err = daio_mgr->get_daio(daio_mgr, &da_desc,
 					(struct daio **)&atc->daios[i]);
@@ -1366,12 +1370,20 @@ static int __devinit atc_get_resources(struct ct_atc *atc)
 		return err;
 	}
 	atc->n_daio++;
+	return 0;
+}
+
+static int ct_src_init_connections(struct ct_atc *atc)
+{
+	struct src_desc src_dsc = {0};
+	struct src_mgr *src_mgr;
+	int i, err;
 
 	src_mgr = atc->rsc_mgrs[SRC];
 	src_dsc.multi = 1;
 	src_dsc.msr = atc->msr;
 	src_dsc.mode = ARCRW;
-	for (i = 0, atc->n_src = 0; i < (2*2); i++) {
+	for (i = 0, atc->n_src = 0; i < (2 * 2); i++) {
 		err = src_mgr->get_src(src_mgr, &src_dsc,
 					(struct src **)&atc->srcs[i]);
 		if (err)
@@ -1379,10 +1391,18 @@ static int __devinit atc_get_resources(struct ct_atc *atc)
 
 		atc->n_src++;
 	}
+	return 0;
+}
+
+static int ct_srcimp_init_connections(struct ct_atc *atc)
+{
+	struct srcimp_desc srcimp_dsc = {0};
+	struct srcimp_mgr *srcimp_mgr;
+	int i, err;
 
 	srcimp_mgr = atc->rsc_mgrs[SRCIMP];
 	srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */
-	for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) {
+	for (i = 0, atc->n_srcimp = 0; i < (2 * 1); i++) {
 		err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
 					(struct srcimp **)&atc->srcimps[i]);
 		if (err)
@@ -1391,7 +1411,7 @@ static int __devinit atc_get_resources(struct ct_atc *atc)
 		atc->n_srcimp++;
 	}
 	srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */
-	for (i = 0; i < (2*1); i++) {
+	for (i = 0; i < (2 * 1); i++) {
 		err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
 				(struct srcimp **)&atc->srcimps[2*1+i]);
 		if (err)
@@ -1399,10 +1419,18 @@ static int __devinit atc_get_resources(struct ct_atc *atc)
 
 		atc->n_srcimp++;
 	}
+	return 0;
+}
+
+static int ct_sum_init_connections(struct ct_atc *atc)
+{
+	struct sum_desc sum_dsc = {0};
+	struct sum_mgr *sum_mgr;
+	int i, err;
 
 	sum_mgr = atc->rsc_mgrs[SUM];
 	sum_dsc.msr = atc->msr;
-	for (i = 0, atc->n_pcm = 0; i < (2*4); i++) {
+	for (i = 0, atc->n_pcm = 0; i < (2 * 4); i++) {
 		err = sum_mgr->get_sum(sum_mgr, &sum_dsc,
 					(struct sum **)&atc->pcm[i]);
 		if (err)
@@ -1410,6 +1438,41 @@ static int __devinit atc_get_resources(struct ct_atc *atc)
 
 		atc->n_pcm++;
 	}
+	return 0;
+}
+
+static int __devinit atc_get_resources(struct ct_atc *atc)
+{
+	int err;
+
+	atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
+	if (NULL == atc->daios)
+		return -ENOMEM;
+
+	atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+	if (NULL == atc->srcs)
+		return -ENOMEM;
+
+	atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+	if (NULL == atc->srcimps)
+		return -ENOMEM;
+
+	atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL);
+	if (NULL == atc->pcm)
+		return -ENOMEM;
+
+	err = ct_daio_init_connections(atc);
+	if (err < 0)
+		return err;
+	err = ct_src_init_connections(atc);
+	if (err < 0)
+		return err;
+	err = ct_srcimp_init_connections(atc);
+	if (err < 0)
+		return err;
+	err = ct_sum_init_connections(atc);
+	if (err < 0)
+		return err;
 
 	err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer);
 	if (err) {
@@ -1420,7 +1483,7 @@ static int __devinit atc_get_resources(struct ct_atc *atc)
 	return 0;
 }
 
-static void __devinit
+static void
 atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai,
 		struct src **srcs, struct srcimp **srcimps)
 {
@@ -1459,7 +1522,7 @@ atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai,
 	src_mgr->commit_write(src_mgr); /* Synchronously enable SRCs */
 }
 
-static void __devinit atc_connect_resources(struct ct_atc *atc)
+static void atc_connect_resources(struct ct_atc *atc)
 {
 	struct dai *dai;
 	struct dao *dao;
@@ -1617,3 +1680,51 @@ error1:
 	printk(KERN_ERR "ctxfi: Something wrong!!!\n");
 	return err;
 }
+
+#ifdef CONFIG_PM
+int ct_atc_suspend(struct ct_atc *atc)
+{
+	struct hw *hw = atc->hw;
+	int i;
+
+	snd_power_change_state(atc->card, SNDRV_CTL_POWER_D3hot);
+	for (i = FRONT; i <= IEC958; i++)
+		ct_pcm_suspend(atc->pcm_devs[i]);
+	ct_mixer_suspend(atc->mixer);
+
+	ct_daio_clear_connections(atc);
+	ct_sum_clear_connections(atc);
+	ct_src_clear_connections(atc);
+	ct_srcimp_clear_connections(atc);
+
+	memset(atc->daios, 0, sizeof(void *) * DAIONUM);
+	memset(atc->pcm, 0, sizeof(void *) * (2 * 4));
+	memset(atc->srcs, 0, sizeof(void *) * (2 *2));
+	memset(atc->srcimps, 0, sizeof(void *) * (2*2));
+
+	hw->card_suspend(hw);
+	return 0;
+}
+
+int ct_atc_resume(struct ct_atc *atc)
+{
+	struct hw *hw = atc->hw;
+	int err;
+
+	err = hw->card_resume(hw);
+	if (err < 0) {
+		snd_card_disconnect(atc->card);
+		return -EIO;
+	}
+
+	ct_daio_init_connections(atc);
+	ct_src_init_connections(atc);
+	ct_srcimp_init_connections(atc);
+	ct_sum_init_connections(atc);
+
+	atc_connect_resources(atc);
+	ct_mixer_resume(atc->mixer);
+	snd_power_change_state(atc->card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+#endif
diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h
index a033472..e8251ff 100644
--- a/sound/pci/ctxfi/ctatc.h
+++ b/sound/pci/ctxfi/ctatc.h
@@ -79,6 +79,7 @@ struct ct_atc {
 	unsigned int rsr; /* reference sample rate in Hz */
 	unsigned int msr; /* master sample rate in rsr */
 	unsigned int pll_rate; /* current rate of Phase Lock Loop */
+	struct snd_pcm *pcm_devs[NUM_CTALSADEVS];
 
 	int chip_type;
 	int model;
@@ -144,4 +145,9 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
 			    struct ct_atc **ratc);
 int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc);
 
+#ifdef CONFIG_PM
+int ct_atc_suspend(struct ct_atc *atc);
+int ct_atc_resume(struct ct_atc *atc);
+#endif
+
 #endif /* CTATC_H */
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h
index 4a8e04f..6a3de6a 100644
--- a/sound/pci/ctxfi/cthardware.h
+++ b/sound/pci/ctxfi/cthardware.h
@@ -60,6 +60,10 @@ struct card_conf {
 struct hw {
 	int (*card_init)(struct hw *hw, struct card_conf *info);
 	int (*card_stop)(struct hw *hw);
+#ifdef CONFIG_PM
+	int (*card_suspend)(struct hw *hw);
+	int (*card_resume)(struct hw *hw);
+#endif
 	int (*pll_init)(struct hw *hw, unsigned int rsr);
 	int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source);
 	int (*select_adc_source)(struct hw *hw, enum ADCSRC source);
@@ -164,6 +168,8 @@ struct hw {
 	void (*irq_callback)(void *data, unsigned int bit);
 	void *irq_callback_data;
 
+	struct card_conf info;
+
 	struct pci_dev *pci;	/* the pci kernel structure of this card */
 	int irq;
 	unsigned long io_base;
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index cb69d9d..e1b6d8d 100644
--- a/sound/pci/ctxfi/cthw20k1.c
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -1976,7 +1976,7 @@ static int hw_card_shutdown(struct hw *hw)
 	return 0;
 }
 
-static int hw_card_init(struct hw *hw, struct card_conf *info)
+static int hw_init(struct hw *hw, struct card_conf *info)
 {
 	int err;
 	unsigned int gctl;
@@ -2119,11 +2119,54 @@ static void hw_write_pci(struct hw *hw, u32 reg, u32 data)
 		&container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
 }
 
+static int hw_card_init(struct hw *hw, struct card_conf *info)
+{
+	hw->info = *info;
+	return hw_init(hw, info);
+}
+
+#ifdef CONFIG_PM
+static int hw_card_suspend(struct hw *hw)
+{
+	hw_card_stop(hw);
+	pci_disable_device(hw->pci);
+	pci_save_state(hw->pci);
+	return 0;
+}
+
+static int hw_card_resume(struct hw *hw)
+{
+	int err;
+	struct pci_dev *pci = hw->pci;
+
+	pci_set_power_state(pci, PCI_D0);
+	pci_restore_state(pci);
+	if (pci_enable_device(pci) < 0) {
+		printk(KERN_ERR "ctxfi: pci_enable_device failed, "
+		       "disabling device\n");
+		return -EIO;
+	}
+	if (hw->model == CTUAA) {
+		err = uaa_to_xfi(pci);
+		if (err)
+			return err;
+	}
+	pci_set_master(pci);
+
+	hw_init(hw, &hw->info);
+	return 0;
+}
+#endif
+
 static struct hw ct20k1_preset __devinitdata = {
 	.irq = -1,
 
 	.card_init = hw_card_init,
 	.card_stop = hw_card_stop,
+#ifdef CONFIG_PM
+	.card_suspend = hw_card_suspend,
+	.card_resume = hw_card_resume,
+#endif
 	.pll_init = hw_pll_init,
 	.is_adc_source_selected = hw_is_adc_input_selected,
 	.select_adc_source = hw_adc_input_select,
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
index 4493a51..5db75ea 100644
--- a/sound/pci/ctxfi/cthw20k2.c
+++ b/sound/pci/ctxfi/cthw20k2.c
@@ -1845,7 +1845,6 @@ static int hw_card_start(struct hw *hw)
 {
 	int err = 0;
 	struct pci_dev *pci = hw->pci;
-	unsigned int gctl;
 
 	err = pci_enable_device(pci);
 	if (err < 0)
@@ -1872,11 +1871,6 @@ static int hw_card_start(struct hw *hw)
 		goto error2;
 	}
 
-	/* Switch to 20k2 mode from UAA mode. */
-	gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
-	set_field(&gctl, GCTL_UAA, 0);
-	hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
-
 	/*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED,
 				atc->chip_details->nm_card, hw))) {
 		goto error3;
@@ -1927,7 +1921,7 @@ static int hw_card_shutdown(struct hw *hw)
 	return 0;
 }
 
-static int hw_card_init(struct hw *hw, struct card_conf *info)
+static int hw_init(struct hw *hw, struct card_conf *info)
 {
 	int err;
 	unsigned int gctl;
@@ -1945,6 +1939,11 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
 			return err;
 	}
 
+	/* Switch to 20k2 mode from UAA mode. */
+	gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
+	set_field(&gctl, GCTL_UAA, 0);
+	hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
+
 	/* PLL init */
 	err = hw_pll_init(hw, info->rsr);
 	if (err < 0)
@@ -2006,6 +2005,39 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
 	return 0;
 }
 
+static int hw_card_init(struct hw *hw, struct card_conf *info)
+{
+	hw->info = *info;
+	return hw_init(hw, info);
+}
+
+#ifdef CONFIG_PM
+static int hw_card_suspend(struct hw *hw)
+{
+	hw_card_stop(hw);
+	pci_disable_device(hw->pci);
+	pci_save_state(hw->pci);
+	return 0;
+}
+
+static int hw_card_resume(struct hw *hw)
+{
+	struct pci_dev *pci = hw->pci;
+
+	pci_set_power_state(pci, PCI_D0);
+	pci_restore_state(pci);
+	if (pci_enable_device(pci) < 0) {
+		printk(KERN_ERR "ctxfi: pci_enable_device failed, "
+		       "disabling device\n");
+		return -EIO;
+	}
+	pci_set_master(pci);
+
+	hw_init(hw, &hw->info);
+	return 0;
+}
+#endif
+
 static u32 hw_read_20kx(struct hw *hw, u32 reg)
 {
 	return readl((void *)(hw->mem_base + reg));
@@ -2021,6 +2053,10 @@ static struct hw ct20k2_preset __devinitdata = {
 
 	.card_init = hw_card_init,
 	.card_stop = hw_card_stop,
+#ifdef CONFIG_PM
+	.card_suspend = hw_card_suspend,
+	.card_resume = hw_card_resume,
+#endif
 	.pll_init = hw_pll_init,
 	.is_adc_source_selected = hw_is_adc_input_selected,
 	.select_adc_source = hw_adc_input_select,
diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c
index 666722d..4e5547a 100644
--- a/sound/pci/ctxfi/ctmixer.c
+++ b/sound/pci/ctxfi/ctmixer.c
@@ -485,6 +485,57 @@ static int ct_alsa_mix_switch_get(struct snd_kcontrol *kcontrol,
 	return 0;
 }
 
+static void ct_mixer_switch_update(struct ct_atc *atc,
+				   struct ct_mixer *mixer, 
+				   enum CTALSA_MIXER_CTL type,
+				   int state)
+{
+	/* Do changes in mixer. */
+	if (SWH_CAPTURE_START <= type && SWH_CAPTURE_END >= type) {
+		enum CT_AMIXER_CTL idx = get_amixer_index(type);
+		if (state)
+			ct_mixer_recording_select(mixer, idx);
+		else
+			ct_mixer_recording_unselect(mixer, idx);
+		return;
+	}
+
+	/* Do changes out of mixer. */
+	switch (type) {
+	case MIXER_LINEIN_C_S:
+	case MIXER_MIC_C_S:
+		if (state)
+			do_line_mic_switch(atc, type);
+		break;
+	case MIXER_WAVEF_P_S:
+		atc->line_front_unmute(atc, state);
+		break;
+	case MIXER_WAVES_P_S:
+		atc->line_surround_unmute(atc, state);
+		break;
+	case MIXER_WAVEC_P_S:
+		atc->line_clfe_unmute(atc, state);
+		break;
+	case MIXER_WAVER_P_S:
+		atc->line_rear_unmute(atc, state);
+		break;
+	case MIXER_LINEIN_P_S:
+		atc->line_in_unmute(atc, state);
+		break;
+	case MIXER_SPDIFO_P_S:
+		atc->spdif_out_unmute(atc, state);
+		break;
+	case MIXER_SPDIFI_P_S:
+		atc->spdif_in_unmute(atc, state);
+		break;
+	case MIXER_DIGITAL_IO_S:
+		do_digit_io_switch(atc, state);
+		break;
+	default:
+		break;
+	}
+}
+
 static int ct_alsa_mix_switch_put(struct snd_kcontrol *kcontrol,
 				  struct snd_ctl_elem_value *ucontrol)
 {
@@ -498,35 +549,7 @@ static int ct_alsa_mix_switch_put(struct snd_kcontrol *kcontrol,
 		return 0;
 
 	set_switch_state(mixer, type, state);
-	/* Do changes in mixer. */
-	if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) {
-		if (state) {
-			ct_mixer_recording_select(mixer,
-						  get_amixer_index(type));
-		} else {
-			ct_mixer_recording_unselect(mixer,
-						    get_amixer_index(type));
-		}
-	}
-	/* Do changes out of mixer. */
-	if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type))
-		do_line_mic_switch(atc, type);
-	else if (MIXER_WAVEF_P_S == type)
-		atc->line_front_unmute(atc, state);
-	else if (MIXER_WAVES_P_S == type)
-		atc->line_surround_unmute(atc, state);
-	else if (MIXER_WAVEC_P_S == type)
-		atc->line_clfe_unmute(atc, state);
-	else if (MIXER_WAVER_P_S == type)
-		atc->line_rear_unmute(atc, state);
-	else if (MIXER_LINEIN_P_S == type)
-		atc->line_in_unmute(atc, state);
-	else if (MIXER_SPDIFO_P_S == type)
-		atc->spdif_out_unmute(atc, state);
-	else if (MIXER_SPDIFI_P_S == type)
-		atc->spdif_in_unmute(atc, state);
-	else if (MIXER_DIGITAL_IO_S == type)
-		do_digit_io_switch(atc, state);
+	ct_mixer_switch_update(atc, mixer, type, state);
 
 	return 1;
 }
@@ -823,7 +846,6 @@ error1:
 static int ct_mixer_get_mem(struct ct_mixer **rmixer)
 {
 	struct ct_mixer *mixer;
-	int err;
 
 	*rmixer = NULL;
 	/* Allocate mem for mixer obj */
@@ -833,24 +855,27 @@ static int ct_mixer_get_mem(struct ct_mixer **rmixer)
 
 	mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM),
 				 GFP_KERNEL);
-	if (NULL == mixer->amixers) {
-		err = -ENOMEM;
-		goto error1;
-	}
+	if (!mixer->amixers)
+		goto error;
 	mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL);
-	if (NULL == mixer->sums) {
-		err = -ENOMEM;
-		goto error2;
-	}
+	if (!mixer->sums)
+		goto error;
+
+#ifdef CONFIG_PM
+	mixer->saved_vol = kmalloc(sizeof(u32) * VOL_MIXER_NUM * CHN_NUM,
+				   GFP_KERNEL);
+	if (!mixer->saved_vol)
+		goto error;
+#endif
 
 	*rmixer = mixer;
 	return 0;
 
-error2:
+error:
 	kfree(mixer->amixers);
-error1:
+	kfree(mixer->sums);
 	kfree(mixer);
-	return err;
+	return -ENOMEM;
 }
 
 static int ct_mixer_topology_build(struct ct_mixer *mixer)
@@ -1039,13 +1064,13 @@ mixer_set_input_right(struct ct_mixer *mixer,
 	return 0;
 }
 
-int ct_mixer_destroy(struct ct_mixer *mixer)
+static void ct_mixer_release_resources(struct ct_mixer *mixer)
 {
 	struct sum_mgr *sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM];
 	struct amixer_mgr *amixer_mgr =
 			(struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER];
 	struct amixer *amixer;
-	int i = 0;
+	int i;
 
 	/* Release amixer resources */
 	for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
@@ -1060,8 +1085,15 @@ int ct_mixer_destroy(struct ct_mixer *mixer)
 		if (NULL != mixer->sums[i])
 			sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]);
 	}
+}
 
+int ct_mixer_destroy(struct ct_mixer *mixer)
+{
+	ct_mixer_release_resources(mixer);
 	/* Release mem assigned to mixer object */
+#ifdef CONFIG_PM
+	kfree(mixer->saved_vol);
+#endif
 	kfree(mixer->sums);
 	kfree(mixer->amixers);
 	kfree(mixer);
@@ -1121,3 +1153,49 @@ int ct_alsa_mix_create(struct ct_atc *atc,
 
 	return 0;
 }
+
+#ifdef CONFIG_PM
+void ct_mixer_suspend(struct ct_mixer *mixer)
+{
+	struct ct_atc *atc = mixer->atc;
+	int i, c, idx;
+
+	idx = 0;
+	for (i = VOL_MIXER_START; i <= VOL_MIXER_END; i++) {
+		for (c = 0; c < CHN_NUM; c++) {
+			struct amixer *amixer = mixer->amixers[idx];
+			mixer->saved_vol[idx] = amixer->ops->get_scale(amixer);
+			idx++;
+		}
+	}
+
+	atc->spdif_out_get_status(atc, &mixer->saved_spdif_status);
+	ct_mixer_release_resources(mixer);
+}
+
+void ct_mixer_resume(struct ct_mixer *mixer)
+{
+	struct ct_atc *atc = mixer->atc;
+	int i, c, idx;
+
+	ct_mixer_topology_build(mixer);
+
+	idx = 0;
+	for (i = VOL_MIXER_START; i <= VOL_MIXER_END; i++) {
+		for (c = 0; c < CHN_NUM; c++) {
+			struct amixer *amixer = mixer->amixers[idx];
+			amixer->ops->set_scale(amixer, mixer->saved_vol[idx]);
+			idx++;
+		}
+	}
+
+	for (i = SWH_MIXER_START; i <= SWH_MIXER_END; i++) {
+		if (i == MIXER_DIGITAL_IO_S &&
+		    !atc->have_digit_io_switch(atc))
+			continue;
+		ct_mixer_switch_update(atc, mixer, i,
+				       get_switch_state(mixer, i));
+	}
+	atc->spdif_out_set_status(atc, mixer->saved_spdif_status);
+}
+#endif
diff --git a/sound/pci/ctxfi/ctmixer.h b/sound/pci/ctxfi/ctmixer.h
index e2d96eb..597a0c9 100644
--- a/sound/pci/ctxfi/ctmixer.h
+++ b/sound/pci/ctxfi/ctmixer.h
@@ -48,6 +48,10 @@ struct ct_mixer {
 	void **amixers;		/* amixer resources for volume control */
 	void **sums;		/* sum resources for signal collection */
 	unsigned int switch_state; /* A bit-map to indicate state of switches */
+#ifdef CONFIG_PM
+	u32 *saved_vol;
+	unsigned int saved_spdif_status;
+#endif
 
 	int (*get_output_ports)(struct ct_mixer *mixer, enum MIXER_PORT_T type,
 				  struct rsc **rleft, struct rsc **rright);
@@ -64,4 +68,9 @@ int ct_alsa_mix_create(struct ct_atc *atc,
 int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer);
 int ct_mixer_destroy(struct ct_mixer *mixer);
 
+#ifdef CONFIG_PM
+void ct_mixer_suspend(struct ct_mixer *mixer);
+void ct_mixer_resume(struct ct_mixer *mixer);
+#endif
+
 #endif /* CTMIXER_H */
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c
index 9e5c0c4..6f872ba 100644
--- a/sound/pci/ctxfi/ctpcm.c
+++ b/sound/pci/ctxfi/ctpcm.c
@@ -422,5 +422,31 @@ int ct_alsa_pcm_create(struct ct_atc *atc,
 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
 			snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
 
+	atc->pcm_devs[device] = pcm;
+
 	return 0;
 }
+
+#ifdef CONFIG_PM
+void ct_pcm_suspend(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *s;
+	int i;
+
+	if (!pcm)
+		return;
+	snd_pcm_suspend_all(pcm);
+	/* clear all PCM resources once */
+	for (i = 0; i < 2; i++) {
+		for (s = pcm->streams[i].substream; s; s = s->next) {
+			struct ct_atc *atc;
+			struct ct_atc_pcm *apcm;
+			if (!s->runtime)
+				continue;
+			apcm = s->runtime->private_data;
+			atc = snd_pcm_substream_chip(s);
+			atc->pcm_release_resources(atc, apcm);
+		}
+	}
+}
+#endif
diff --git a/sound/pci/ctxfi/ctpcm.h b/sound/pci/ctxfi/ctpcm.h
index 178da0d..867139b 100644
--- a/sound/pci/ctxfi/ctpcm.h
+++ b/sound/pci/ctxfi/ctpcm.h
@@ -24,4 +24,8 @@ int ct_alsa_pcm_create(struct ct_atc *atc,
 		       enum CTALSADEVS device,
 		       const char *device_name);
 
+#ifdef CONFIG_PM
+void ct_pcm_suspend(struct snd_pcm *pcm);
+#endif
+
 #endif /* CTPCM_H */
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c
index 2d3dd89..59a0876 100644
--- a/sound/pci/ctxfi/xfi.c
+++ b/sound/pci/ctxfi/xfi.c
@@ -121,11 +121,31 @@ static void __devexit ct_card_remove(struct pci_dev *pci)
 	pci_set_drvdata(pci, NULL);
 }
 
+#ifdef CONFIG_PM
+static int ct_card_suspend(struct pci_dev *pci, pm_message_t state)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct ct_atc *atc = card->private_data;
+	return ct_atc_suspend(atc);
+}
+
+static int ct_card_resume(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct ct_atc *atc = card->private_data;
+	return ct_atc_resume(atc);
+}
+#endif
+
 static struct pci_driver ct_driver = {
 	.name = "SB-XFi",
 	.id_table = ct_pci_dev_ids,
 	.probe = ct_card_probe,
 	.remove = __devexit_p(ct_card_remove),
+#ifdef CONFIG_PM
+	.suspend = ct_card_suspend,
+	.resume = ct_card_resume,
+#endif
 };
 
 static int __init ct_card_init(void)


More information about the Alsa-devel mailing list