From 29959a09cc1aabd2d5f4f03afc0305de6bd29248 Mon Sep 17 00:00:00 2001
From: Wai Yew CHAY <wychay@ctl.creative.com>
Date: Mon, 22 Jun 2009 14:52:34 +0200
Subject: ALSA: ctxfi - Add PM support

Added the suspend/resume support to ctxfi driver.

The team tested on the following seems ok:
  AMD Athlon 64 3500+ / ASUS A8N-E / 512MB DDR ATI / Radeon X1300
  20k1 & 20k2 cards

Signed-off-by: Wai Yew CHAY <wychay@ctl.creative.com>
Singed-off-by: Ryan RICHARDS <ryan_richards@creativelabs.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/pci/ctxfi/ctatc.c      | 181 +++++++++++++++++++++++++++++++------------
 sound/pci/ctxfi/ctatc.h      |   7 ++
 sound/pci/ctxfi/cthardware.h |   4 +
 sound/pci/ctxfi/cthw20k1.c   |  83 ++++++++++++++++----
 sound/pci/ctxfi/cthw20k2.c   |  65 ++++++++++++----
 sound/pci/ctxfi/ctmixer.c    |  92 +++++++++++++++-------
 sound/pci/ctxfi/ctmixer.h    |   3 +
 sound/pci/ctxfi/ctpcm.c      |   4 +
 sound/pci/ctxfi/xfi.c        |  22 ++++++
 9 files changed, 354 insertions(+), 107 deletions(-)

(limited to 'sound')

diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 32e3c26e969e..a49c76647307 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -261,13 +261,8 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 	int device = apcm->substream->pcm->device;
 	unsigned int pitch;
 
-	if (NULL != apcm->src) {
-		/* Prepared pcm playback */
-		return 0;
-	}
-
 	/* first release old resources */
-	atc->pcm_release_resources(atc, apcm);
+	atc_pcm_release_resources(atc, apcm);
 
 	/* Get SRC resource */
 	desc.multi = apcm->substream->runtime->channels;
@@ -661,10 +656,7 @@ static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 	unsigned int pitch;
 	int mix_base = 0, imp_base = 0;
 
-	if (NULL != apcm->src) {
-		/* Prepared pcm capture */
-		return 0;
-	}
+	atc_pcm_release_resources(atc, apcm);
 
 	/* Get needed resources. */
 	err = atc_pcm_capture_get_resources(atc, apcm);
@@ -867,7 +859,7 @@ spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 	struct dao *dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
 	unsigned int rate = apcm->substream->runtime->rate;
 	unsigned int status;
-	int err;
+	int err = 0;
 	unsigned char iec958_con_fs;
 
 	switch (rate) {
@@ -908,8 +900,7 @@ spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 	int err;
 	int i;
 
-	if (NULL != apcm->src)
-		return 0;
+	atc_pcm_release_resources(atc, apcm);
 
 	/* Configure SPDIFOO and PLL to passthrough mode;
 	 * determine pll_rate. */
@@ -1116,32 +1107,20 @@ 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 int atc_release_resources(struct ct_atc *atc)
 {
-	struct daio_mgr *daio_mgr;
-	struct dao *dao;
-	struct dai *dai;
-	struct daio *daio;
-	struct sum_mgr *sum_mgr;
-	struct src_mgr *src_mgr;
-	struct srcimp_mgr *srcimp_mgr;
-	struct srcimp *srcimp;
-	struct ct_mixer *mixer;
-	int i = 0;
-
-	if (NULL == atc)
-		return 0;
-
-	if (atc->timer) {
-		ct_timer_free(atc->timer);
-		atc->timer = NULL;
-	}
-
-	/* Stop hardware and disable all interrupts */
-	if (NULL != atc->hw)
-		((struct hw *)atc->hw)->card_stop(atc->hw);
-
-	/* Destroy internal mixer objects */
+	int i;
+	struct daio_mgr *daio_mgr = NULL;
+	struct dao *dao = NULL;
+	struct dai *dai = NULL;
+	struct daio *daio = NULL;
+	struct sum_mgr *sum_mgr = NULL;
+	struct src_mgr *src_mgr = NULL;
+	struct srcimp_mgr *srcimp_mgr = NULL;
+	struct srcimp *srcimp = NULL;
+	struct ct_mixer *mixer = NULL;
+
+	/* disconnect internal mixer objects */
 	if (NULL != atc->mixer) {
 		mixer = atc->mixer;
 		mixer->set_input_left(mixer, MIX_LINE_IN, NULL);
@@ -1150,7 +1129,6 @@ static int ct_atc_destroy(struct ct_atc *atc)
 		mixer->set_input_right(mixer, MIX_MIC_IN, NULL);
 		mixer->set_input_left(mixer, MIX_SPDIF_IN, NULL);
 		mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL);
-		ct_mixer_destroy(atc->mixer);
 	}
 
 	if (NULL != atc->daios) {
@@ -1168,6 +1146,7 @@ static int ct_atc_destroy(struct ct_atc *atc)
 			daio_mgr->put_daio(daio_mgr, daio);
 		}
 		kfree(atc->daios);
+		atc->daios = NULL;
 	}
 
 	if (NULL != atc->pcm) {
@@ -1176,6 +1155,7 @@ static int ct_atc_destroy(struct ct_atc *atc)
 			sum_mgr->put_sum(sum_mgr, atc->pcm[i]);
 
 		kfree(atc->pcm);
+		atc->pcm = NULL;
 	}
 
 	if (NULL != atc->srcs) {
@@ -1184,6 +1164,7 @@ static int ct_atc_destroy(struct ct_atc *atc)
 			src_mgr->put_src(src_mgr, atc->srcs[i]);
 
 		kfree(atc->srcs);
+		atc->srcs = NULL;
 	}
 
 	if (NULL != atc->srcimps) {
@@ -1194,8 +1175,30 @@ static int ct_atc_destroy(struct ct_atc *atc)
 			srcimp_mgr->put_srcimp(srcimp_mgr, atc->srcimps[i]);
 		}
 		kfree(atc->srcimps);
+		atc->srcimps = NULL;
+	}
+
+	return 0;
+}
+
+static int ct_atc_destroy(struct ct_atc *atc)
+{
+	int i = 0;
+
+	if (NULL == atc)
+		return 0;
+
+	if (atc->timer) {
+		ct_timer_free(atc->timer);
+		atc->timer = NULL;
 	}
 
+	atc_release_resources(atc);
+
+	/* Destroy internal mixer objects */
+	if (NULL != atc->mixer)
+		ct_mixer_destroy(atc->mixer);
+
 	for (i = 0; i < NUM_RSCTYP; i++) {
 		if ((NULL != rsc_mgr_funcs[i].destroy) &&
 		    (NULL != atc->rsc_mgrs[i]))
@@ -1323,7 +1326,7 @@ 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 atc_get_resources(struct ct_atc *atc)
 {
 	struct daio_desc da_desc = {0};
 	struct daio_mgr *daio_mgr;
@@ -1420,16 +1423,10 @@ static int __devinit atc_get_resources(struct ct_atc *atc)
 		atc->n_pcm++;
 	}
 
-	err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer);
-	if (err) {
-		printk(KERN_ERR "ctxfi: Failed to create mixer obj!!!\n");
-		return err;
-	}
-
 	return 0;
 }
 
-static void __devinit
+static void
 atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai,
 		struct src **srcs, struct srcimp **srcimps)
 {
@@ -1468,7 +1465,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;
@@ -1514,6 +1511,84 @@ static void __devinit atc_connect_resources(struct ct_atc *atc)
 	}
 }
 
+#ifdef CONFIG_PM
+static int atc_suspend(struct ct_atc *atc, pm_message_t state)
+{
+	int i;
+	struct hw *hw = atc->hw;
+
+	snd_power_change_state(atc->card, SNDRV_CTL_POWER_D3hot);
+
+	for (i = FRONT; i < NUM_PCMS; i++) {
+		if (!atc->pcms[i])
+			continue;
+
+		snd_pcm_suspend_all(atc->pcms[i]);
+	}
+
+	atc_release_resources(atc);
+
+	hw->suspend(hw, state);
+
+	return 0;
+}
+
+static int atc_hw_resume(struct ct_atc *atc)
+{
+	struct hw *hw = atc->hw;
+	struct card_conf info = {0};
+
+	/* Re-initialize card hardware. */
+	info.rsr = atc->rsr;
+	info.msr = atc->msr;
+	info.vm_pgt_phys = atc_get_ptp_phys(atc, 0);
+	return hw->resume(hw, &info);
+}
+
+static int atc_resources_resume(struct ct_atc *atc)
+{
+	struct ct_mixer *mixer;
+	int err = 0;
+
+	/* Get resources */
+	err = atc_get_resources(atc);
+	if (err < 0) {
+		atc_release_resources(atc);
+		return err;
+	}
+
+	/* Build topology */
+	atc_connect_resources(atc);
+
+	mixer = atc->mixer;
+	mixer->resume(mixer);
+
+	return 0;
+}
+
+static int atc_resume(struct ct_atc *atc)
+{
+	int err = 0;
+
+	/* Do hardware resume. */
+	err = atc_hw_resume(atc);
+	if (err < 0) {
+		printk(KERN_ERR "ctxfi: pci_enable_device failed, "
+		       "disabling device\n");
+		snd_card_disconnect(atc->card);
+		return err;
+	}
+
+	err = atc_resources_resume(atc);
+	if (err < 0)
+		return err;
+
+	snd_power_change_state(atc->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+#endif
+
 static struct ct_atc atc_preset __devinitdata = {
 	.map_audio_buffer = ct_map_audio_buffer,
 	.unmap_audio_buffer = ct_unmap_audio_buffer,
@@ -1542,6 +1617,10 @@ static struct ct_atc atc_preset __devinitdata = {
 	.spdif_out_set_status = atc_spdif_out_set_status,
 	.spdif_out_passthru = atc_spdif_out_passthru,
 	.have_digit_io_switch = atc_have_digit_io_switch,
+#ifdef CONFIG_PM
+	.suspend = atc_suspend,
+	.resume = atc_resume,
+#endif
 };
 
 /**
@@ -1600,6 +1679,12 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
 	if (err < 0)
 		goto error1;
 
+	err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer);
+	if (err) {
+		printk(KERN_ERR "ctxfi: Failed to create mixer obj!!!\n");
+		goto error1;
+	}
+
 	/* Get resources */
 	err = atc_get_resources(atc);
 	if (err < 0)
diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h
index 9fe620ea5f3f..9fd8a5708943 100644
--- a/sound/pci/ctxfi/ctatc.h
+++ b/sound/pci/ctxfi/ctatc.h
@@ -136,6 +136,13 @@ struct ct_atc {
 	unsigned char n_pcm;
 
 	struct ct_timer *timer;
+
+#ifdef CONFIG_PM
+	int (*suspend)(struct ct_atc *atc, pm_message_t state);
+	int (*resume)(struct ct_atc *atc);
+#define NUM_PCMS (NUM_CTALSADEVS - 1)
+	struct snd_pcm *pcms[NUM_PCMS];
+#endif
 };
 
 
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h
index a4c2cad80f3a..af55405f5dec 100644
--- a/sound/pci/ctxfi/cthardware.h
+++ b/sound/pci/ctxfi/cthardware.h
@@ -64,6 +64,10 @@ struct hw {
 	int (*card_init)(struct hw *hw, struct card_conf *info);
 	int (*card_stop)(struct hw *hw);
 	int (*pll_init)(struct hw *hw, unsigned int rsr);
+#ifdef CONFIG_PM
+	int (*suspend)(struct hw *hw, pm_message_t state);
+	int (*resume)(struct hw *hw, struct card_conf *info);
+#endif
 	int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source);
 	int (*select_adc_source)(struct hw *hw, enum ADCSRC source);
 	int (*have_digit_io_switch)(struct hw *hw);
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index cb69d9ddfbe3..ad3e1d144464 100644
--- a/sound/pci/ctxfi/cthw20k1.c
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -1911,9 +1911,17 @@ static int hw_card_start(struct hw *hw)
 		goto error1;
 	}
 
-	err = pci_request_regions(pci, "XFi");
-	if (err < 0)
-		goto error1;
+	if (!hw->io_base) {
+		err = pci_request_regions(pci, "XFi");
+		if (err < 0)
+			goto error1;
+
+		if (hw->model == CTUAA)
+			hw->io_base = pci_resource_start(pci, 5);
+		else
+			hw->io_base = pci_resource_start(pci, 0);
+
+	}
 
 	/* Switch to X-Fi mode from UAA mode if neeeded */
 	if (hw->model == CTUAA) {
@@ -1921,18 +1929,17 @@ static int hw_card_start(struct hw *hw)
 		if (err)
 			goto error2;
 
-		hw->io_base = pci_resource_start(pci, 5);
-	} else {
-		hw->io_base = pci_resource_start(pci, 0);
 	}
 
-	err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED,
-			  "ctxfi", hw);
-	if (err < 0) {
-		printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
-		goto error2;
+	if (hw->irq < 0) {
+		err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED,
+				  "ctxfi", hw);
+		if (err < 0) {
+			printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
+			goto error2;
+		}
+		hw->irq = pci->irq;
 	}
-	hw->irq = pci->irq;
 
 	pci_set_master(pci);
 
@@ -1948,6 +1955,15 @@ error1:
 
 static int hw_card_stop(struct hw *hw)
 {
+	unsigned int data;
+
+	/* disable transport bus master and queueing of request */
+	hw_write_20kx(hw, TRNCTL, 0x00);
+
+	/* disable pll */
+	data = hw_read_20kx(hw, PLLCTL);
+	hw_write_20kx(hw, PLLCTL, (data & (~(0x0F<<12))));
+
 	/* TODO: Disable interrupt and so on... */
 	if (hw->irq >= 0)
 		synchronize_irq(hw->irq);
@@ -1987,11 +2003,9 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
 	struct trn_conf trn_info = {0};
 
 	/* Get PCI io port base address and do Hendrix switch if needed. */
-	if (!hw->io_base) {
-		err = hw_card_start(hw);
-		if (err)
-			return err;
-	}
+	err = hw_card_start(hw);
+	if (err)
+		return err;
 
 	/* PLL init */
 	err = hw_pll_init(hw, info->rsr);
@@ -2064,6 +2078,37 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int hw_suspend(struct hw *hw, pm_message_t state)
+{
+	struct pci_dev *pci = hw->pci;
+
+	hw_card_stop(hw);
+
+	if (hw->model == CTUAA) {
+		/* Switch to UAA config space. */
+		pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x0);
+	}
+
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	pci_set_power_state(pci, pci_choose_state(pci, state));
+
+	return 0;
+}
+
+static int hw_resume(struct hw *hw, struct card_conf *info)
+{
+	struct pci_dev *pci = hw->pci;
+
+	pci_set_power_state(pci, PCI_D0);
+	pci_restore_state(pci);
+
+	/* Re-initialize card hardware. */
+	return hw_card_init(hw, info);
+}
+#endif
+
 static u32 hw_read_20kx(struct hw *hw, u32 reg)
 {
 	u32 value;
@@ -2128,6 +2173,10 @@ static struct hw ct20k1_preset __devinitdata = {
 	.is_adc_source_selected = hw_is_adc_input_selected,
 	.select_adc_source = hw_adc_input_select,
 	.have_digit_io_switch = hw_have_digit_io_switch,
+#ifdef CONFIG_PM
+	.suspend = hw_suspend,
+	.resume = hw_resume,
+#endif
 
 	.src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk,
 	.src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk,
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
index 4493a51c6b01..dec46d04b041 100644
--- a/sound/pci/ctxfi/cthw20k2.c
+++ b/sound/pci/ctxfi/cthw20k2.c
@@ -1860,16 +1860,18 @@ static int hw_card_start(struct hw *hw)
 		goto error1;
 	}
 
-	err = pci_request_regions(pci, "XFi");
-	if (err < 0)
-		goto error1;
+	if (!hw->io_base) {
+		err = pci_request_regions(pci, "XFi");
+		if (err < 0)
+			goto error1;
 
-	hw->io_base = pci_resource_start(hw->pci, 2);
-	hw->mem_base = (unsigned long)ioremap(hw->io_base,
+		hw->io_base = pci_resource_start(hw->pci, 2);
+		hw->mem_base = (unsigned long)ioremap(hw->io_base,
 					pci_resource_len(hw->pci, 2));
-	if (NULL == (void *)hw->mem_base) {
-		err = -ENOENT;
-		goto error2;
+		if (NULL == (void *)hw->mem_base) {
+			err = -ENOENT;
+			goto error2;
+		}
 	}
 
 	/* Switch to 20k2 mode from UAA mode. */
@@ -1901,6 +1903,15 @@ error1:
 
 static int hw_card_stop(struct hw *hw)
 {
+	unsigned int data;
+
+	/* disable transport bus master and queueing of request */
+	hw_write_20kx(hw, TRANSPORT_CTL, 0x00);
+
+	/* disable pll */
+	data = hw_read_20kx(hw, PLL_ENB);
+	hw_write_20kx(hw, PLL_ENB, (data & (~0x07)));
+
 	/* TODO: Disable interrupt and so on... */
 	return 0;
 }
@@ -1939,11 +1950,9 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
 
 	/* Get PCI io port/memory base address and
 	 * do 20kx core switch if needed. */
-	if (!hw->io_base) {
-		err = hw_card_start(hw);
-		if (err)
-			return err;
-	}
+	err = hw_card_start(hw);
+	if (err)
+		return err;
 
 	/* PLL init */
 	err = hw_pll_init(hw, info->rsr);
@@ -2006,6 +2015,32 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int hw_suspend(struct hw *hw, pm_message_t state)
+{
+	struct pci_dev *pci = hw->pci;
+
+	hw_card_stop(hw);
+
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	pci_set_power_state(pci, pci_choose_state(pci, state));
+
+	return 0;
+}
+
+static int hw_resume(struct hw *hw, struct card_conf *info)
+{
+	struct pci_dev *pci = hw->pci;
+
+	pci_set_power_state(pci, PCI_D0);
+	pci_restore_state(pci);
+
+	/* Re-initialize card hardware. */
+	return hw_card_init(hw, info);
+}
+#endif
+
 static u32 hw_read_20kx(struct hw *hw, u32 reg)
 {
 	return readl((void *)(hw->mem_base + reg));
@@ -2025,6 +2060,10 @@ static struct hw ct20k2_preset __devinitdata = {
 	.is_adc_source_selected = hw_is_adc_input_selected,
 	.select_adc_source = hw_adc_input_select,
 	.have_digit_io_switch = hw_have_digit_io_switch,
+#ifdef CONFIG_PM
+	.suspend = hw_suspend,
+	.resume = hw_resume,
+#endif
 
 	.src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk,
 	.src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk,
diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c
index 666722d9de41..f26d7cd9db9f 100644
--- a/sound/pci/ctxfi/ctmixer.c
+++ b/sound/pci/ctxfi/ctmixer.c
@@ -462,6 +462,43 @@ do_digit_io_switch(struct ct_atc *atc, int state)
 	return;
 }
 
+static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
+{
+	struct ct_mixer *mixer = atc->mixer;
+
+	/* 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);
+
+	return;
+}
+
 static int ct_alsa_mix_switch_info(struct snd_kcontrol *kcontrol,
 				   struct snd_ctl_elem_info *uinfo)
 {
@@ -498,35 +535,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);
+	do_switch(atc, type, state);
 
 	return 1;
 }
@@ -1039,6 +1048,28 @@ mixer_set_input_right(struct ct_mixer *mixer,
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int mixer_resume(struct ct_mixer *mixer)
+{
+	int i, state;
+	struct amixer *amixer;
+
+	/* resume topology and volume gain. */
+	for (i = 0; i < NUM_CT_AMIXERS*CHN_NUM; i++) {
+		amixer = mixer->amixers[i];
+		amixer->ops->commit_write(amixer);
+	}
+
+	/* resume switch state. */
+	for (i = SWH_MIXER_START; i <= SWH_MIXER_END; i++) {
+		state = get_switch_state(mixer, i);
+		do_switch(mixer->atc, i, state);
+	}
+
+	return 0;
+}
+#endif
+
 int ct_mixer_destroy(struct ct_mixer *mixer)
 {
 	struct sum_mgr *sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM];
@@ -1087,6 +1118,9 @@ int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer)
 	mixer->get_output_ports = mixer_get_output_ports;
 	mixer->set_input_left = mixer_set_input_left;
 	mixer->set_input_right = mixer_set_input_right;
+#ifdef CONFIG_PM
+	mixer->resume = mixer_resume;
+#endif
 
 	/* Allocate chip resources for mixer obj */
 	err = ct_mixer_get_resources(mixer);
diff --git a/sound/pci/ctxfi/ctmixer.h b/sound/pci/ctxfi/ctmixer.h
index e2d96ebde746..b009e989e77d 100644
--- a/sound/pci/ctxfi/ctmixer.h
+++ b/sound/pci/ctxfi/ctmixer.h
@@ -56,6 +56,9 @@ struct ct_mixer {
 			      enum MIXER_PORT_T type, struct rsc *rsc);
 	int (*set_input_right)(struct ct_mixer *mixer,
 			       enum MIXER_PORT_T type, struct rsc *rsc);
+#ifdef CONFIG_PM
+	int (*resume)(struct ct_mixer *mixer);
+#endif
 };
 
 int ct_alsa_mix_create(struct ct_atc *atc,
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c
index 9e5c0c4da726..60ea23180acb 100644
--- a/sound/pci/ctxfi/ctpcm.c
+++ b/sound/pci/ctxfi/ctpcm.c
@@ -422,5 +422,9 @@ 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);
 
+#ifdef CONFIG_PM
+	atc->pcms[device] = pcm;
+#endif
+
 	return 0;
 }
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c
index 2d3dd89af151..76541748e7bc 100644
--- a/sound/pci/ctxfi/xfi.c
+++ b/sound/pci/ctxfi/xfi.c
@@ -121,11 +121,33 @@ 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 atc->suspend(atc, state);
+}
+
+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 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)
-- 
cgit v1.2.3