ALSA: cs46xx - Fix suspend/resume with new DSP
Fix the basic suspend/resume of snd-cs46xx drivers with new DSP. References: https://bugzilla.redhat.com/show_bug.cgi?id=498287 https://bugzilla.redhat.com/show_bug.cgi?id=160751 Tested-by: Florian Zumbiehl <florz@florz.de> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
8374e24c23
commit
41116e926c
@ -118,9 +118,11 @@ struct dsp_scb_descriptor {
|
|||||||
|
|
||||||
struct snd_info_entry *proc_info;
|
struct snd_info_entry *proc_info;
|
||||||
int ref_count;
|
int ref_count;
|
||||||
spinlock_t lock;
|
|
||||||
|
|
||||||
int deleted;
|
u16 volume[2];
|
||||||
|
unsigned int deleted :1;
|
||||||
|
unsigned int updated :1;
|
||||||
|
unsigned int volume_set :1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dsp_task_descriptor {
|
struct dsp_task_descriptor {
|
||||||
|
@ -3597,7 +3597,7 @@ static struct cs_card_type __devinitdata cards[] = {
|
|||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static unsigned int saved_regs[] = {
|
static unsigned int saved_regs[] = {
|
||||||
BA0_ACOSV,
|
BA0_ACOSV,
|
||||||
BA0_ASER_FADDR,
|
/*BA0_ASER_FADDR,*/
|
||||||
BA0_ASER_MASTER,
|
BA0_ASER_MASTER,
|
||||||
BA1_PVOL,
|
BA1_PVOL,
|
||||||
BA1_CVOL,
|
BA1_CVOL,
|
||||||
|
@ -298,6 +298,9 @@ void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip)
|
|||||||
if (ins->scbs[i].deleted) continue;
|
if (ins->scbs[i].deleted) continue;
|
||||||
|
|
||||||
cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
|
cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
kfree(ins->scbs[i].data);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(ins->code.data);
|
kfree(ins->code.data);
|
||||||
@ -974,13 +977,11 @@ static struct dsp_scb_descriptor * _map_scb (struct snd_cs46xx *chip, char * nam
|
|||||||
|
|
||||||
index = find_free_scb_index (ins);
|
index = find_free_scb_index (ins);
|
||||||
|
|
||||||
|
memset(&ins->scbs[index], 0, sizeof(ins->scbs[index]));
|
||||||
strcpy(ins->scbs[index].scb_name, name);
|
strcpy(ins->scbs[index].scb_name, name);
|
||||||
ins->scbs[index].address = dest;
|
ins->scbs[index].address = dest;
|
||||||
ins->scbs[index].index = index;
|
ins->scbs[index].index = index;
|
||||||
ins->scbs[index].proc_info = NULL;
|
|
||||||
ins->scbs[index].ref_count = 1;
|
ins->scbs[index].ref_count = 1;
|
||||||
ins->scbs[index].deleted = 0;
|
|
||||||
spin_lock_init(&ins->scbs[index].lock);
|
|
||||||
|
|
||||||
desc = (ins->scbs + index);
|
desc = (ins->scbs + index);
|
||||||
ins->scbs[index].scb_symbol = add_symbol (chip, name, dest, SYMBOL_PARAMETER);
|
ins->scbs[index].scb_symbol = add_symbol (chip, name, dest, SYMBOL_PARAMETER);
|
||||||
@ -1022,17 +1023,29 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size)
|
|||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SCB_BYTES (0x10 * 4)
|
||||||
|
|
||||||
struct dsp_scb_descriptor *
|
struct dsp_scb_descriptor *
|
||||||
cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32 dest)
|
cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32 dest)
|
||||||
{
|
{
|
||||||
struct dsp_scb_descriptor * desc;
|
struct dsp_scb_descriptor * desc;
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
/* copy the data for resume */
|
||||||
|
scb_data = kmemdup(scb_data, SCB_BYTES, GFP_KERNEL);
|
||||||
|
if (!scb_data)
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
desc = _map_scb (chip,name,dest);
|
desc = _map_scb (chip,name,dest);
|
||||||
if (desc) {
|
if (desc) {
|
||||||
desc->data = scb_data;
|
desc->data = scb_data;
|
||||||
_dsp_create_scb(chip,scb_data,dest);
|
_dsp_create_scb(chip,scb_data,dest);
|
||||||
} else {
|
} else {
|
||||||
snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n");
|
snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n");
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
kfree(scb_data);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return desc;
|
return desc;
|
||||||
@ -1988,7 +2001,28 @@ int cs46xx_dsp_resume(struct snd_cs46xx * chip)
|
|||||||
continue;
|
continue;
|
||||||
_dsp_create_scb(chip, s->data, s->address);
|
_dsp_create_scb(chip, s->data, s->address);
|
||||||
}
|
}
|
||||||
|
for (i = 0; i < ins->nscb; i++) {
|
||||||
|
struct dsp_scb_descriptor *s = &ins->scbs[i];
|
||||||
|
if (s->deleted)
|
||||||
|
continue;
|
||||||
|
if (s->updated)
|
||||||
|
cs46xx_dsp_spos_update_scb(chip, s);
|
||||||
|
if (s->volume_set)
|
||||||
|
cs46xx_dsp_scb_set_volume(chip, s,
|
||||||
|
s->volume[0], s->volume[1]);
|
||||||
|
}
|
||||||
|
if (ins->spdif_status_out & DSP_SPDIF_STATUS_HW_ENABLED) {
|
||||||
|
cs46xx_dsp_enable_spdif_hw(chip);
|
||||||
|
snd_cs46xx_poke(chip, (ins->ref_snoop_scb->address + 2) << 2,
|
||||||
|
(OUTPUT_SNOOP_BUFFER + 0x10) << 0x10);
|
||||||
|
if (ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN)
|
||||||
|
cs46xx_poke_via_dsp(chip, SP_SPDOUT_CSUV,
|
||||||
|
ins->spdif_csuv_stream);
|
||||||
|
}
|
||||||
|
if (chip->dsp_spos_instance->spdif_status_in) {
|
||||||
|
cs46xx_poke_via_dsp(chip, SP_ASER_COUNTDOWN, 0x80000005);
|
||||||
|
cs46xx_poke_via_dsp(chip, SP_SPDIN_CONTROL, 0x800003ff);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -212,6 +212,7 @@ static inline void cs46xx_dsp_spos_update_scb (struct snd_cs46xx * chip,
|
|||||||
(scb->address + SCBsubListPtr) << 2,
|
(scb->address + SCBsubListPtr) << 2,
|
||||||
(scb->sub_list_ptr->address << 0x10) |
|
(scb->sub_list_ptr->address << 0x10) |
|
||||||
(scb->next_scb_ptr->address));
|
(scb->next_scb_ptr->address));
|
||||||
|
scb->updated = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cs46xx_dsp_scb_set_volume (struct snd_cs46xx * chip,
|
static inline void cs46xx_dsp_scb_set_volume (struct snd_cs46xx * chip,
|
||||||
@ -222,6 +223,9 @@ static inline void cs46xx_dsp_scb_set_volume (struct snd_cs46xx * chip,
|
|||||||
|
|
||||||
snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl) << 2, val);
|
snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl) << 2, val);
|
||||||
snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl + 1) << 2, val);
|
snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl + 1) << 2, val);
|
||||||
|
scb->volume_set = 1;
|
||||||
|
scb->volume[0] = left;
|
||||||
|
scb->volume[1] = right;
|
||||||
}
|
}
|
||||||
#endif /* __DSP_SPOS_H__ */
|
#endif /* __DSP_SPOS_H__ */
|
||||||
#endif /* CONFIG_SND_CS46XX_NEW_DSP */
|
#endif /* CONFIG_SND_CS46XX_NEW_DSP */
|
||||||
|
@ -115,7 +115,6 @@ static void cs46xx_dsp_proc_scb_info_read (struct snd_info_entry *entry,
|
|||||||
static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * scb)
|
static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * scb)
|
||||||
{
|
{
|
||||||
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
|
struct dsp_spos_instance * ins = chip->dsp_spos_instance;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
if ( scb->parent_scb_ptr ) {
|
if ( scb->parent_scb_ptr ) {
|
||||||
/* unlink parent SCB */
|
/* unlink parent SCB */
|
||||||
@ -153,8 +152,6 @@ static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor
|
|||||||
scb->next_scb_ptr = ins->the_null_scb;
|
scb->next_scb_ptr = ins->the_null_scb;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&chip->reg_lock, flags);
|
|
||||||
|
|
||||||
/* update parent first entry in DSP RAM */
|
/* update parent first entry in DSP RAM */
|
||||||
cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr);
|
cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr);
|
||||||
|
|
||||||
@ -162,7 +159,6 @@ static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor
|
|||||||
cs46xx_dsp_spos_update_scb(chip,scb);
|
cs46xx_dsp_spos_update_scb(chip,scb);
|
||||||
|
|
||||||
scb->parent_scb_ptr = NULL;
|
scb->parent_scb_ptr = NULL;
|
||||||
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,9 +193,9 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
|
|||||||
goto _end;
|
goto _end;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
spin_lock_irqsave(&scb->lock, flags);
|
spin_lock_irqsave(&chip->reg_lock, flags);
|
||||||
_dsp_unlink_scb (chip,scb);
|
_dsp_unlink_scb (chip,scb);
|
||||||
spin_unlock_irqrestore(&scb->lock, flags);
|
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
||||||
|
|
||||||
cs46xx_dsp_proc_free_scb_desc(scb);
|
cs46xx_dsp_proc_free_scb_desc(scb);
|
||||||
if (snd_BUG_ON(!scb->scb_symbol))
|
if (snd_BUG_ON(!scb->scb_symbol))
|
||||||
@ -207,6 +203,10 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
|
|||||||
remove_symbol (chip,scb->scb_symbol);
|
remove_symbol (chip,scb->scb_symbol);
|
||||||
|
|
||||||
ins->scbs[scb->index].deleted = 1;
|
ins->scbs[scb->index].deleted = 1;
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
kfree(ins->scbs[scb->index].data);
|
||||||
|
ins->scbs[scb->index].data = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (scb->index < ins->scb_highest_frag_index)
|
if (scb->index < ins->scb_highest_frag_index)
|
||||||
ins->scb_highest_frag_index = scb->index;
|
ins->scb_highest_frag_index = scb->index;
|
||||||
@ -1508,20 +1508,17 @@ int cs46xx_dsp_pcm_unlink (struct snd_cs46xx * chip,
|
|||||||
chip->dsp_spos_instance->npcm_channels <= 0))
|
chip->dsp_spos_instance->npcm_channels <= 0))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
spin_lock(&pcm_channel->src_scb->lock);
|
spin_lock_irqsave(&chip->reg_lock, flags);
|
||||||
|
|
||||||
if (pcm_channel->unlinked) {
|
if (pcm_channel->unlinked) {
|
||||||
spin_unlock(&pcm_channel->src_scb->lock);
|
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&chip->reg_lock, flags);
|
|
||||||
pcm_channel->unlinked = 1;
|
pcm_channel->unlinked = 1;
|
||||||
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
|
||||||
|
|
||||||
_dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb);
|
_dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb);
|
||||||
|
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
||||||
|
|
||||||
spin_unlock(&pcm_channel->src_scb->lock);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1533,10 +1530,10 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
|
|||||||
struct dsp_scb_descriptor * src_scb = pcm_channel->src_scb;
|
struct dsp_scb_descriptor * src_scb = pcm_channel->src_scb;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock(&pcm_channel->src_scb->lock);
|
spin_lock_irqsave(&chip->reg_lock, flags);
|
||||||
|
|
||||||
if (pcm_channel->unlinked == 0) {
|
if (pcm_channel->unlinked == 0) {
|
||||||
spin_unlock(&pcm_channel->src_scb->lock);
|
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1552,8 +1549,6 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
|
|||||||
snd_BUG_ON(pcm_channel->pcm_reader_scb->parent_scb_ptr);
|
snd_BUG_ON(pcm_channel->pcm_reader_scb->parent_scb_ptr);
|
||||||
pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb;
|
pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb;
|
||||||
|
|
||||||
spin_lock_irqsave(&chip->reg_lock, flags);
|
|
||||||
|
|
||||||
/* update SCB entry in DSP RAM */
|
/* update SCB entry in DSP RAM */
|
||||||
cs46xx_dsp_spos_update_scb(chip,pcm_channel->pcm_reader_scb);
|
cs46xx_dsp_spos_update_scb(chip,pcm_channel->pcm_reader_scb);
|
||||||
|
|
||||||
@ -1562,8 +1557,6 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
|
|||||||
|
|
||||||
pcm_channel->unlinked = 0;
|
pcm_channel->unlinked = 0;
|
||||||
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
||||||
|
|
||||||
spin_unlock(&pcm_channel->src_scb->lock);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1596,13 +1589,17 @@ cs46xx_add_record_source (struct snd_cs46xx *chip, struct dsp_scb_descriptor * s
|
|||||||
|
|
||||||
int cs46xx_src_unlink(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)
|
int cs46xx_src_unlink(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)
|
||||||
{
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
if (snd_BUG_ON(!src->parent_scb_ptr))
|
if (snd_BUG_ON(!src->parent_scb_ptr))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* mute SCB */
|
/* mute SCB */
|
||||||
cs46xx_dsp_scb_set_volume (chip,src,0,0);
|
cs46xx_dsp_scb_set_volume (chip,src,0,0);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&chip->reg_lock, flags);
|
||||||
_dsp_unlink_scb (chip,src);
|
_dsp_unlink_scb (chip,src);
|
||||||
|
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user