From 89f157d9e6bf08b65f93dcb1dca1de037079885f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 19 Jul 2007 11:23:21 +0200 Subject: [PATCH] [ALSA] cs46xx - Fix PM resume Fixed PM resume of cs46xx devices. It now restores properly the DSP image and kick-off the DSP. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/cs46xx.h | 4 + include/sound/cs46xx_dsp_spos.h | 2 + sound/pci/cs46xx/cs46xx_lib.c | 77 ++++++++++++--- sound/pci/cs46xx/cs46xx_lib.h | 3 + sound/pci/cs46xx/dsp_spos.c | 170 +++++++++++++++++++++++--------- 5 files changed, 197 insertions(+), 59 deletions(-) diff --git a/include/sound/cs46xx.h b/include/sound/cs46xx.h index 685928e6f65..353910ce975 100644 --- a/include/sound/cs46xx.h +++ b/include/sound/cs46xx.h @@ -1723,6 +1723,10 @@ struct snd_cs46xx { struct snd_cs46xx_pcm *playback_pcm; unsigned int play_ctl; #endif + +#ifdef CONFIG_PM + u32 *saved_regs; +#endif }; int snd_cs46xx_create(struct snd_card *card, diff --git a/include/sound/cs46xx_dsp_spos.h b/include/sound/cs46xx_dsp_spos.h index da934def31e..d9da9e59cf3 100644 --- a/include/sound/cs46xx_dsp_spos.h +++ b/include/sound/cs46xx_dsp_spos.h @@ -107,6 +107,7 @@ struct dsp_scb_descriptor { char scb_name[DSP_MAX_SCB_NAME]; u32 address; int index; + u32 *data; struct dsp_scb_descriptor * sub_list_ptr; struct dsp_scb_descriptor * next_scb_ptr; @@ -127,6 +128,7 @@ struct dsp_task_descriptor { int size; u32 address; int index; + u32 *data; }; struct dsp_pcm_channel_descriptor { diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index bef1f6d1859..71d7aab9d86 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2897,6 +2897,10 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip) } #endif +#ifdef CONFIG_PM + kfree(chip->saved_regs); +#endif + pci_disable_device(chip->pci); kfree(chip); return 0; @@ -3140,6 +3144,23 @@ static int snd_cs46xx_chip_init(struct snd_cs46xx *chip) /* * start and load DSP */ + +static void cs46xx_enable_stream_irqs(struct snd_cs46xx *chip) +{ + unsigned int tmp; + + snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM); + + tmp = snd_cs46xx_peek(chip, BA1_PFIE); + tmp &= ~0x0000f03f; + snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */ + + tmp = snd_cs46xx_peek(chip, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000001; + snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */ +} + int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip) { unsigned int tmp; @@ -3214,19 +3235,7 @@ int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip) snd_cs46xx_proc_start(chip); - /* - * Enable interrupts on the part. - */ - snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM); - - tmp = snd_cs46xx_peek(chip, BA1_PFIE); - tmp &= ~0x0000f03f; - snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */ - - tmp = snd_cs46xx_peek(chip, BA1_CIE); - tmp &= ~0x0000003f; - tmp |= 0x00000001; - snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */ + cs46xx_enable_stream_irqs(chip); #ifndef CONFIG_SND_CS46XX_NEW_DSP /* set the attenuation to 0dB */ @@ -3665,11 +3674,19 @@ static struct cs_card_type __devinitdata cards[] = { * APM support */ #ifdef CONFIG_PM +static unsigned int saved_regs[] = { + BA0_ACOSV, + BA0_ASER_FADDR, + BA0_ASER_MASTER, + BA1_PVOL, + BA1_CVOL, +}; + int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state) { struct snd_card *card = pci_get_drvdata(pci); struct snd_cs46xx *chip = card->private_data; - int amp_saved; + int i, amp_saved; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); chip->in_suspend = 1; @@ -3680,6 +3697,10 @@ int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state) snd_ac97_suspend(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]); snd_ac97_suspend(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]); + /* save some registers */ + for (i = 0; i < ARRAY_SIZE(saved_regs); i++) + chip->saved_regs[i] = snd_cs46xx_peekBA0(chip, saved_regs[i]); + amp_saved = chip->amplifier; /* turn off amp */ chip->amplifier_ctrl(chip, -chip->amplifier); @@ -3698,7 +3719,7 @@ int snd_cs46xx_resume(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); struct snd_cs46xx *chip = card->private_data; - int amp_saved; + int i, amp_saved; pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); @@ -3716,6 +3737,16 @@ int snd_cs46xx_resume(struct pci_dev *pci) snd_cs46xx_chip_init(chip); + snd_cs46xx_reset(chip); +#ifdef CONFIG_SND_CS46XX_NEW_DSP + cs46xx_dsp_resume(chip); + /* restore some registers */ + for (i = 0; i < ARRAY_SIZE(saved_regs); i++) + snd_cs46xx_pokeBA0(chip, saved_regs[i], chip->saved_regs[i]); +#else + snd_cs46xx_download_image(chip); +#endif + #if 0 snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE, chip->ac97_general_purpose); @@ -3730,6 +3761,13 @@ int snd_cs46xx_resume(struct pci_dev *pci) snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]); snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]); + /* reset playback/capture */ + snd_cs46xx_set_play_sample_rate(chip, 8000); + snd_cs46xx_set_capture_sample_rate(chip, 8000); + snd_cs46xx_proc_start(chip); + + cs46xx_enable_stream_irqs(chip); + if (amp_saved) chip->amplifier_ctrl(chip, 1); /* turn amp on */ else @@ -3896,6 +3934,15 @@ int __devinit snd_cs46xx_create(struct snd_card *card, snd_cs46xx_proc_init(card, chip); +#ifdef CONFIG_PM + chip->saved_regs = kmalloc(sizeof(*chip->saved_regs) * + ARRAY_SIZE(saved_regs), GFP_KERNEL); + if (!chip->saved_regs) { + snd_cs46xx_free(chip); + return -ENOMEM; + } +#endif + chip->active_ctrl(chip, -1); /* disable CLKRUN */ snd_card_set_dev(card, &pci->dev); diff --git a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h index f75750c2bd2..20dcd72f06c 100644 --- a/sound/pci/cs46xx/cs46xx_lib.h +++ b/sound/pci/cs46xx/cs46xx_lib.h @@ -86,6 +86,9 @@ static inline unsigned int snd_cs46xx_peekBA0(struct snd_cs46xx *chip, unsigned struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip); void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip); int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module); +#ifdef CONFIG_PM +int cs46xx_dsp_resume(struct snd_cs46xx * chip); +#endif struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name, int symbol_type); #ifdef CONFIG_PROC_FS diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index 336e77e2600..590b35d91df 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -306,13 +306,59 @@ void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip) mutex_unlock(&chip->spos_mutex); } +static int dsp_load_parameter(struct snd_cs46xx *chip, + struct dsp_segment_desc *parameter) +{ + u32 doffset, dsize; + + if (!parameter) { + snd_printdd("dsp_spos: module got no parameter segment\n"); + return 0; + } + + doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET); + dsize = parameter->size * 4; + + snd_printdd("dsp_spos: " + "downloading parameter data to chip (%08x-%08x)\n", + doffset,doffset + dsize); + if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) { + snd_printk(KERN_ERR "dsp_spos: " + "failed to download parameter data to DSP\n"); + return -EINVAL; + } + return 0; +} + +static int dsp_load_sample(struct snd_cs46xx *chip, + struct dsp_segment_desc *sample) +{ + u32 doffset, dsize; + + if (!sample) { + snd_printdd("dsp_spos: module got no sample segment\n"); + return 0; + } + + doffset = (sample->offset * 4 + DSP_SAMPLE_BYTE_OFFSET); + dsize = sample->size * 4; + + snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n", + doffset,doffset + dsize); + + if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) { + snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n"); + return -EINVAL; + } + return 0; +} + int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module) { struct dsp_spos_instance * ins = chip->dsp_spos_instance; struct dsp_segment_desc * code = get_segment_desc (module,SEGTYPE_SP_PROGRAM); - struct dsp_segment_desc * parameter = get_segment_desc (module,SEGTYPE_SP_PARAMETER); - struct dsp_segment_desc * sample = get_segment_desc (module,SEGTYPE_SP_SAMPLE); u32 doffset, dsize; + int err; if (ins->nmodules == DSP_MAX_MODULES - 1) { snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n"); @@ -326,49 +372,20 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE); } - if (parameter == NULL) { - snd_printdd("dsp_spos: module got no parameter segment\n"); - } else { - if (ins->nmodules > 0) { - snd_printk(KERN_WARNING "dsp_spos: WARNING current parameter data may be overwriten!\n"); - } - - doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET); - dsize = parameter->size * 4; - - snd_printdd("dsp_spos: downloading parameter data to chip (%08x-%08x)\n", - doffset,doffset + dsize); - - if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) { - snd_printk(KERN_ERR "dsp_spos: failed to download parameter data to DSP\n"); - return -EINVAL; - } - } + err = dsp_load_parameter(chip, get_segment_desc(module, + SEGTYPE_SP_PARAMETER)); + if (err < 0) + return err; if (ins->nmodules == 0) { snd_printdd("dsp_spos: clearing sample area\n"); snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE); } - if (sample == NULL) { - snd_printdd("dsp_spos: module got no sample segment\n"); - } else { - if (ins->nmodules > 0) { - snd_printk(KERN_WARNING "dsp_spos: WARNING current sample data may be overwriten\n"); - } - - doffset = (sample->offset * 4 + DSP_SAMPLE_BYTE_OFFSET); - dsize = sample->size * 4; - - snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n", - doffset,doffset + dsize); - - if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) { - snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n"); - return -EINVAL; - } - } - + err = dsp_load_sample(chip, get_segment_desc(module, + SEGTYPE_SP_SAMPLE)); + if (err < 0) + return err; if (ins->nmodules == 0) { snd_printdd("dsp_spos: clearing code area\n"); @@ -986,7 +1003,10 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size) return NULL; } - strcpy(ins->tasks[ins->ntask].task_name,name); + if (name) + strcpy(ins->tasks[ins->ntask].task_name, name); + else + strcpy(ins->tasks[ins->ntask].task_name, "(NULL)"); ins->tasks[ins->ntask].address = dest; ins->tasks[ins->ntask].size = size; @@ -995,7 +1015,8 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size) desc = (ins->tasks + ins->ntask); ins->ntask++; - add_symbol (chip,name,dest,SYMBOL_PARAMETER); + if (name) + add_symbol (chip,name,dest,SYMBOL_PARAMETER); return desc; } @@ -1006,6 +1027,7 @@ cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32 desc = _map_scb (chip,name,dest); if (desc) { + desc->data = scb_data; _dsp_create_scb(chip,scb_data,dest); } else { snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n"); @@ -1023,6 +1045,7 @@ cs46xx_dsp_create_task_tree (struct snd_cs46xx *chip, char * name, u32 * task_da desc = _map_task_tree (chip,name,dest,size); if (desc) { + desc->data = task_data; _dsp_create_task_tree(chip,task_data,dest,size); } else { snd_printk(KERN_ERR "dsp_spos: failed to map TASK\n"); @@ -1320,8 +1343,10 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip) 0x0000ffff }; - /* dirty hack ... */ - _dsp_create_task_tree (chip,(u32 *)&mix2_ostream_spb,WRITE_BACK_SPB,2); + if (!cs46xx_dsp_create_task_tree(chip, NULL, + (u32 *)&mix2_ostream_spb, + WRITE_BACK_SPB, 2)) + goto _fail_end; } /* input sample converter */ @@ -1622,7 +1647,6 @@ static int cs46xx_dsp_async_init (struct snd_cs46xx *chip, return 0; } - static void cs46xx_dsp_disable_spdif_hw (struct snd_cs46xx *chip) { struct dsp_spos_instance * ins = chip->dsp_spos_instance; @@ -1894,3 +1918,61 @@ int cs46xx_dsp_set_iec958_volume (struct snd_cs46xx * chip, u16 left, u16 right) return 0; } + +#ifdef CONFIG_PM +int cs46xx_dsp_resume(struct snd_cs46xx * chip) +{ + struct dsp_spos_instance * ins = chip->dsp_spos_instance; + int i, err; + + /* clear parameter, sample and code areas */ + snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, + DSP_PARAMETER_BYTE_SIZE); + snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, + DSP_SAMPLE_BYTE_SIZE); + snd_cs46xx_clear_BA1(chip, DSP_CODE_BYTE_OFFSET, DSP_CODE_BYTE_SIZE); + + for (i = 0; i < ins->nmodules; i++) { + struct dsp_module_desc *module = &ins->modules[i]; + struct dsp_segment_desc *seg; + u32 doffset, dsize; + + seg = get_segment_desc(module, SEGTYPE_SP_PARAMETER); + err = dsp_load_parameter(chip, seg); + if (err < 0) + return err; + + seg = get_segment_desc(module, SEGTYPE_SP_SAMPLE); + err = dsp_load_sample(chip, seg); + if (err < 0) + return err; + + seg = get_segment_desc(module, SEGTYPE_SP_PROGRAM); + if (!seg) + continue; + + doffset = seg->offset * 4 + module->load_address * 4 + + DSP_CODE_BYTE_OFFSET; + dsize = seg->size * 4; + err = snd_cs46xx_download(chip, + ins->code.data + module->load_address, + doffset, dsize); + if (err < 0) + return err; + } + + for (i = 0; i < ins->ntask; i++) { + struct dsp_task_descriptor *t = &ins->tasks[i]; + _dsp_create_task_tree(chip, t->data, t->address, t->size); + } + + for (i = 0; i < ins->nscb; i++) { + struct dsp_scb_descriptor *s = &ins->scbs[i]; + if (s->deleted) + continue; + _dsp_create_scb(chip, s->data, s->address); + } + + return 0; +} +#endif -- 2.20.1