Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29959a09 authored by Wai Yew CHAY's avatar Wai Yew CHAY Committed by Takashi Iwai
Browse files

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: default avatarWai Yew CHAY <wychay@ctl.creative.com>
Singed-off-by: default avatarRyan RICHARDS <ryan_richards@creativelabs.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent a8f4310b
Loading
Loading
Loading
Loading
+133 −48
Original line number Diff line number Diff line
@@ -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)
+7 −0
Original line number Diff line number Diff line
@@ -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
};


+4 −0
Original line number Diff line number Diff line
@@ -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);
+66 −17
Original line number Diff line number Diff line
@@ -1911,21 +1911,27 @@ static int hw_card_start(struct hw *hw)
		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) {
		err = uaa_to_xfi(pci);
		if (err)
			goto error2;

		hw->io_base = pci_resource_start(pci, 5);
	} else {
		hw->io_base = pci_resource_start(pci, 0);
	}

	if (hw->irq < 0) {
		err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED,
				  "ctxfi", hw);
		if (err < 0) {
@@ -1933,6 +1939,7 @@ static int hw_card_start(struct hw *hw)
			goto error2;
		}
		hw->irq = pci->irq;
	}

	pci_set_master(pci);

@@ -1948,6 +1955,15 @@ static int hw_card_start(struct hw *hw)

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;
	}

	/* 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,
+52 −13
Original line number Diff line number Diff line
@@ -1860,6 +1860,7 @@ static int hw_card_start(struct hw *hw)
		goto error1;
	}

	if (!hw->io_base) {
		err = pci_request_regions(pci, "XFi");
		if (err < 0)
			goto error1;
@@ -1871,6 +1872,7 @@ static int hw_card_start(struct hw *hw)
			err = -ENOENT;
			goto error2;
		}
	}

	/* Switch to 20k2 mode from UAA mode. */
	gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
@@ -1901,6 +1903,15 @@ static int hw_card_start(struct hw *hw)

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;
	}

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