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)