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)