提交 e7cc3edd 编写于 作者: L Linus Torvalds

Merge tag 'sound-fix-4.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound fixes from Takashi Iwai:
 "Here are lots of small fixes that have been collected since the
  previous pull.  This time, not only trivial ones but fixes for some
  serious bugs are included:

   - Fix for CPU lockups by snd-hrtimer accesses
   - Fix for unsafe disconnection handling in ALSA timer code
   - Fix for Oops due to race at HD-audio module removal
   - Fixes for possible memory corruption via 32bit PCM and sequencer
     compat ioctls
   - Fix for regression in HD-audio generic model handling
   - Suppress kernel warnings for invalid TLV ioctls that may flood up
   - Fix the missing SSC clock handling for at73c213
   - A pin fixup for ASUS N550JX"

* tag 'sound-fix-4.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound:
  ALSA: timer: Introduce disconnect op to snd_timer_instance
  ALSA: timer: Handle disconnection more safely
  ALSA: hda - Flush the pending probe work at remove
  ALSA: hda - Fix missing module loading with model=generic option
  ALSA: hda - Degrade i915 binding failure message
  ALSA: at73c213: manage SSC clock
  ALSA: control: Avoid kernel warnings from tlv ioctl with numid 0
  ALSA: seq: Fix snd_seq_call_port_info_ioctl in compat mode
  ALSA: pcm: Fix snd_pcm_hw_params struct copy in compat mode
  ALSA: hrtimer: Fix stall by hrtimer_cancel()
  ALSA: hda - Fix bass pin fixup for ASUS N550JX
...@@ -104,6 +104,7 @@ struct snd_timer_instance { ...@@ -104,6 +104,7 @@ struct snd_timer_instance {
int event, int event,
struct timespec * tstamp, struct timespec * tstamp,
unsigned long resolution); unsigned long resolution);
void (*disconnect)(struct snd_timer_instance *timeri);
void *callback_data; void *callback_data;
unsigned long ticks; /* auto-load ticks when expired */ unsigned long ticks; /* auto-load ticks when expired */
unsigned long cticks; /* current ticks */ unsigned long cticks; /* current ticks */
......
...@@ -1405,6 +1405,8 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, ...@@ -1405,6 +1405,8 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
return -EFAULT; return -EFAULT;
if (tlv.length < sizeof(unsigned int) * 2) if (tlv.length < sizeof(unsigned int) * 2)
return -EINVAL; return -EINVAL;
if (!tlv.numid)
return -EINVAL;
down_read(&card->controls_rwsem); down_read(&card->controls_rwsem);
kctl = snd_ctl_find_numid(card, tlv.numid); kctl = snd_ctl_find_numid(card, tlv.numid);
if (kctl == NULL) { if (kctl == NULL) {
......
...@@ -90,7 +90,7 @@ static int snd_hrtimer_start(struct snd_timer *t) ...@@ -90,7 +90,7 @@ static int snd_hrtimer_start(struct snd_timer *t)
struct snd_hrtimer *stime = t->private_data; struct snd_hrtimer *stime = t->private_data;
atomic_set(&stime->running, 0); atomic_set(&stime->running, 0);
hrtimer_cancel(&stime->hrt); hrtimer_try_to_cancel(&stime->hrt);
hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution), hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
HRTIMER_MODE_REL); HRTIMER_MODE_REL);
atomic_set(&stime->running, 1); atomic_set(&stime->running, 1);
...@@ -101,6 +101,7 @@ static int snd_hrtimer_stop(struct snd_timer *t) ...@@ -101,6 +101,7 @@ static int snd_hrtimer_stop(struct snd_timer *t)
{ {
struct snd_hrtimer *stime = t->private_data; struct snd_hrtimer *stime = t->private_data;
atomic_set(&stime->running, 0); atomic_set(&stime->running, 0);
hrtimer_try_to_cancel(&stime->hrt);
return 0; return 0;
} }
......
...@@ -255,10 +255,15 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, ...@@ -255,10 +255,15 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
if (! (runtime = substream->runtime)) if (! (runtime = substream->runtime))
return -ENOTTY; return -ENOTTY;
/* only fifo_size is different, so just copy all */ data = kmalloc(sizeof(*data), GFP_KERNEL);
data = memdup_user(data32, sizeof(*data32)); if (!data)
if (IS_ERR(data)) return -ENOMEM;
return PTR_ERR(data);
/* only fifo_size (RO from userspace) is different, so just copy all */
if (copy_from_user(data, data32, sizeof(*data32))) {
err = -EFAULT;
goto error;
}
if (refine) if (refine)
err = snd_pcm_hw_refine(substream, data); err = snd_pcm_hw_refine(substream, data);
......
...@@ -49,11 +49,12 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned ...@@ -49,11 +49,12 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned
struct snd_seq_port_info *data; struct snd_seq_port_info *data;
mm_segment_t fs; mm_segment_t fs;
data = memdup_user(data32, sizeof(*data32)); data = kmalloc(sizeof(*data), GFP_KERNEL);
if (IS_ERR(data)) if (!data)
return PTR_ERR(data); return -ENOMEM;
if (get_user(data->flags, &data32->flags) || if (copy_from_user(data, data32, sizeof(*data32)) ||
get_user(data->flags, &data32->flags) ||
get_user(data->time_queue, &data32->time_queue)) get_user(data->time_queue, &data32->time_queue))
goto error; goto error;
data->kernel = NULL; data->kernel = NULL;
......
...@@ -65,6 +65,7 @@ struct snd_timer_user { ...@@ -65,6 +65,7 @@ struct snd_timer_user {
int qtail; int qtail;
int qused; int qused;
int queue_size; int queue_size;
bool disconnected;
struct snd_timer_read *queue; struct snd_timer_read *queue;
struct snd_timer_tread *tqueue; struct snd_timer_tread *tqueue;
spinlock_t qlock; spinlock_t qlock;
...@@ -290,6 +291,9 @@ int snd_timer_open(struct snd_timer_instance **ti, ...@@ -290,6 +291,9 @@ int snd_timer_open(struct snd_timer_instance **ti,
mutex_unlock(&register_mutex); mutex_unlock(&register_mutex);
return -ENOMEM; return -ENOMEM;
} }
/* take a card refcount for safe disconnection */
if (timer->card)
get_device(&timer->card->card_dev);
timeri->slave_class = tid->dev_sclass; timeri->slave_class = tid->dev_sclass;
timeri->slave_id = slave_id; timeri->slave_id = slave_id;
if (list_empty(&timer->open_list_head) && timer->hw.open) if (list_empty(&timer->open_list_head) && timer->hw.open)
...@@ -359,6 +363,9 @@ int snd_timer_close(struct snd_timer_instance *timeri) ...@@ -359,6 +363,9 @@ int snd_timer_close(struct snd_timer_instance *timeri)
} }
spin_unlock(&timer->lock); spin_unlock(&timer->lock);
spin_unlock_irq(&slave_active_lock); spin_unlock_irq(&slave_active_lock);
/* release a card refcount for safe disconnection */
if (timer->card)
put_device(&timer->card->card_dev);
mutex_unlock(&register_mutex); mutex_unlock(&register_mutex);
} }
out: out:
...@@ -474,6 +481,8 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) ...@@ -474,6 +481,8 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
timer = timeri->timer; timer = timeri->timer;
if (timer == NULL) if (timer == NULL)
return -EINVAL; return -EINVAL;
if (timer->card && timer->card->shutdown)
return -ENODEV;
spin_lock_irqsave(&timer->lock, flags); spin_lock_irqsave(&timer->lock, flags);
timeri->ticks = timeri->cticks = ticks; timeri->ticks = timeri->cticks = ticks;
timeri->pticks = 0; timeri->pticks = 0;
...@@ -505,6 +514,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event) ...@@ -505,6 +514,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
spin_lock_irqsave(&timer->lock, flags); spin_lock_irqsave(&timer->lock, flags);
list_del_init(&timeri->ack_list); list_del_init(&timeri->ack_list);
list_del_init(&timeri->active_list); list_del_init(&timeri->active_list);
if (timer->card && timer->card->shutdown) {
spin_unlock_irqrestore(&timer->lock, flags);
return 0;
}
if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
!(--timer->running)) { !(--timer->running)) {
timer->hw.stop(timer); timer->hw.stop(timer);
...@@ -565,6 +578,8 @@ int snd_timer_continue(struct snd_timer_instance *timeri) ...@@ -565,6 +578,8 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
timer = timeri->timer; timer = timeri->timer;
if (! timer) if (! timer)
return -EINVAL; return -EINVAL;
if (timer->card && timer->card->shutdown)
return -ENODEV;
spin_lock_irqsave(&timer->lock, flags); spin_lock_irqsave(&timer->lock, flags);
if (!timeri->cticks) if (!timeri->cticks)
timeri->cticks = 1; timeri->cticks = 1;
...@@ -628,6 +643,9 @@ static void snd_timer_tasklet(unsigned long arg) ...@@ -628,6 +643,9 @@ static void snd_timer_tasklet(unsigned long arg)
unsigned long resolution, ticks; unsigned long resolution, ticks;
unsigned long flags; unsigned long flags;
if (timer->card && timer->card->shutdown)
return;
spin_lock_irqsave(&timer->lock, flags); spin_lock_irqsave(&timer->lock, flags);
/* now process all callbacks */ /* now process all callbacks */
while (!list_empty(&timer->sack_list_head)) { while (!list_empty(&timer->sack_list_head)) {
...@@ -668,6 +686,9 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) ...@@ -668,6 +686,9 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
if (timer == NULL) if (timer == NULL)
return; return;
if (timer->card && timer->card->shutdown)
return;
spin_lock_irqsave(&timer->lock, flags); spin_lock_irqsave(&timer->lock, flags);
/* remember the current resolution */ /* remember the current resolution */
...@@ -881,8 +902,15 @@ static int snd_timer_dev_register(struct snd_device *dev) ...@@ -881,8 +902,15 @@ static int snd_timer_dev_register(struct snd_device *dev)
static int snd_timer_dev_disconnect(struct snd_device *device) static int snd_timer_dev_disconnect(struct snd_device *device)
{ {
struct snd_timer *timer = device->device_data; struct snd_timer *timer = device->device_data;
struct snd_timer_instance *ti;
mutex_lock(&register_mutex); mutex_lock(&register_mutex);
list_del_init(&timer->device_list); list_del_init(&timer->device_list);
/* wake up pending sleepers */
list_for_each_entry(ti, &timer->open_list_head, open_list) {
if (ti->disconnect)
ti->disconnect(ti);
}
mutex_unlock(&register_mutex); mutex_unlock(&register_mutex);
return 0; return 0;
} }
...@@ -893,6 +921,8 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam ...@@ -893,6 +921,8 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
unsigned long resolution = 0; unsigned long resolution = 0;
struct snd_timer_instance *ti, *ts; struct snd_timer_instance *ti, *ts;
if (timer->card && timer->card->shutdown)
return;
if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
return; return;
if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART || if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
...@@ -1051,6 +1081,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry, ...@@ -1051,6 +1081,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
mutex_lock(&register_mutex); mutex_lock(&register_mutex);
list_for_each_entry(timer, &snd_timer_list, device_list) { list_for_each_entry(timer, &snd_timer_list, device_list) {
if (timer->card && timer->card->shutdown)
continue;
switch (timer->tmr_class) { switch (timer->tmr_class) {
case SNDRV_TIMER_CLASS_GLOBAL: case SNDRV_TIMER_CLASS_GLOBAL:
snd_iprintf(buffer, "G%i: ", timer->tmr_device); snd_iprintf(buffer, "G%i: ", timer->tmr_device);
...@@ -1185,6 +1217,14 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, ...@@ -1185,6 +1217,14 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
wake_up(&tu->qchange_sleep); wake_up(&tu->qchange_sleep);
} }
static void snd_timer_user_disconnect(struct snd_timer_instance *timeri)
{
struct snd_timer_user *tu = timeri->callback_data;
tu->disconnected = true;
wake_up(&tu->qchange_sleep);
}
static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
unsigned long resolution, unsigned long resolution,
unsigned long ticks) unsigned long ticks)
...@@ -1558,6 +1598,7 @@ static int snd_timer_user_tselect(struct file *file, ...@@ -1558,6 +1598,7 @@ static int snd_timer_user_tselect(struct file *file,
? snd_timer_user_tinterrupt : snd_timer_user_interrupt; ? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
tu->timeri->ccallback = snd_timer_user_ccallback; tu->timeri->ccallback = snd_timer_user_ccallback;
tu->timeri->callback_data = (void *)tu; tu->timeri->callback_data = (void *)tu;
tu->timeri->disconnect = snd_timer_user_disconnect;
} }
__err: __err:
...@@ -1876,6 +1917,10 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, ...@@ -1876,6 +1917,10 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
remove_wait_queue(&tu->qchange_sleep, &wait); remove_wait_queue(&tu->qchange_sleep, &wait);
if (tu->disconnected) {
err = -ENODEV;
break;
}
if (signal_pending(current)) { if (signal_pending(current)) {
err = -ERESTARTSYS; err = -ERESTARTSYS;
break; break;
...@@ -1925,6 +1970,8 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait) ...@@ -1925,6 +1970,8 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
mask = 0; mask = 0;
if (tu->qused) if (tu->qused)
mask |= POLLIN | POLLRDNORM; mask |= POLLIN | POLLRDNORM;
if (tu->disconnected)
mask |= POLLERR;
return mask; return mask;
} }
......
...@@ -306,7 +306,7 @@ int snd_hdac_i915_init(struct hdac_bus *bus) ...@@ -306,7 +306,7 @@ int snd_hdac_i915_init(struct hdac_bus *bus)
out_err: out_err:
kfree(acomp); kfree(acomp);
bus->audio_component = NULL; bus->audio_component = NULL;
dev_err(dev, "failed to add i915 component master (%d)\n", ret); dev_info(dev, "failed to add i915 component master (%d)\n", ret);
return ret; return ret;
} }
......
...@@ -174,14 +174,40 @@ static inline bool codec_probed(struct hda_codec *codec) ...@@ -174,14 +174,40 @@ static inline bool codec_probed(struct hda_codec *codec)
return device_attach(hda_codec_dev(codec)) > 0 && codec->preset; return device_attach(hda_codec_dev(codec)) > 0 && codec->preset;
} }
/* try to auto-load and bind the codec module */ /* try to auto-load codec module */
static void codec_bind_module(struct hda_codec *codec) static void request_codec_module(struct hda_codec *codec)
{ {
#ifdef MODULE #ifdef MODULE
char modalias[32]; char modalias[32];
const char *mod = NULL;
switch (codec->probe_id) {
case HDA_CODEC_ID_GENERIC_HDMI:
#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI)
mod = "snd-hda-codec-hdmi";
#endif
break;
case HDA_CODEC_ID_GENERIC:
#if IS_MODULE(CONFIG_SND_HDA_GENERIC)
mod = "snd-hda-codec-generic";
#endif
break;
default:
snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias));
mod = modalias;
break;
}
if (mod)
request_module(mod);
#endif /* MODULE */
}
snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias)); /* try to auto-load and bind the codec module */
request_module(modalias); static void codec_bind_module(struct hda_codec *codec)
{
#ifdef MODULE
request_codec_module(codec);
if (codec_probed(codec)) if (codec_probed(codec))
return; return;
#endif #endif
...@@ -218,17 +244,13 @@ static int codec_bind_generic(struct hda_codec *codec) ...@@ -218,17 +244,13 @@ static int codec_bind_generic(struct hda_codec *codec)
if (is_likely_hdmi_codec(codec)) { if (is_likely_hdmi_codec(codec)) {
codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI; codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI;
#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI) request_codec_module(codec);
request_module("snd-hda-codec-hdmi");
#endif
if (codec_probed(codec)) if (codec_probed(codec))
return 0; return 0;
} }
codec->probe_id = HDA_CODEC_ID_GENERIC; codec->probe_id = HDA_CODEC_ID_GENERIC;
#if IS_MODULE(CONFIG_SND_HDA_GENERIC) request_codec_module(codec);
request_module("snd-hda-codec-generic");
#endif
if (codec_probed(codec)) if (codec_probed(codec))
return 0; return 0;
return -ENODEV; return -ENODEV;
......
...@@ -2078,9 +2078,11 @@ static int azx_probe_continue(struct azx *chip) ...@@ -2078,9 +2078,11 @@ static int azx_probe_continue(struct azx *chip)
* for other chips, still continue probing as other * for other chips, still continue probing as other
* codecs can be on the same link. * codecs can be on the same link.
*/ */
if (CONTROLLER_IN_GPU(pci)) if (CONTROLLER_IN_GPU(pci)) {
dev_err(chip->card->dev,
"HSW/BDW HD-audio HDMI/DP requires binding with gfx driver\n");
goto out_free; goto out_free;
else } else
goto skip_i915; goto skip_i915;
} }
...@@ -2149,9 +2151,17 @@ static int azx_probe_continue(struct azx *chip) ...@@ -2149,9 +2151,17 @@ static int azx_probe_continue(struct azx *chip)
static void azx_remove(struct pci_dev *pci) static void azx_remove(struct pci_dev *pci)
{ {
struct snd_card *card = pci_get_drvdata(pci); struct snd_card *card = pci_get_drvdata(pci);
struct azx *chip;
struct hda_intel *hda;
if (card) {
/* flush the pending probing work */
chip = card->private_data;
hda = container_of(chip, struct hda_intel, chip);
flush_work(&hda->probe_work);
if (card)
snd_card_free(card); snd_card_free(card);
}
} }
static void azx_shutdown(struct pci_dev *pci) static void azx_shutdown(struct pci_dev *pci)
......
...@@ -6566,6 +6566,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { ...@@ -6566,6 +6566,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_BASS_1A), SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_BASS_1A),
SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A),
SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16), SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16),
SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16), SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16),
......
...@@ -221,6 +221,8 @@ static int snd_at73c213_pcm_open(struct snd_pcm_substream *substream) ...@@ -221,6 +221,8 @@ static int snd_at73c213_pcm_open(struct snd_pcm_substream *substream)
runtime->hw = snd_at73c213_playback_hw; runtime->hw = snd_at73c213_playback_hw;
chip->substream = substream; chip->substream = substream;
clk_enable(chip->ssc->clk);
return 0; return 0;
} }
...@@ -228,6 +230,7 @@ static int snd_at73c213_pcm_close(struct snd_pcm_substream *substream) ...@@ -228,6 +230,7 @@ static int snd_at73c213_pcm_close(struct snd_pcm_substream *substream)
{ {
struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
chip->substream = NULL; chip->substream = NULL;
clk_disable(chip->ssc->clk);
return 0; return 0;
} }
...@@ -897,6 +900,8 @@ static int snd_at73c213_dev_init(struct snd_card *card, ...@@ -897,6 +900,8 @@ static int snd_at73c213_dev_init(struct snd_card *card,
chip->card = card; chip->card = card;
chip->irq = -1; chip->irq = -1;
clk_enable(chip->ssc->clk);
retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip); retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip);
if (retval) { if (retval) {
dev_dbg(&chip->spi->dev, "unable to request irq %d\n", irq); dev_dbg(&chip->spi->dev, "unable to request irq %d\n", irq);
...@@ -935,6 +940,8 @@ static int snd_at73c213_dev_init(struct snd_card *card, ...@@ -935,6 +940,8 @@ static int snd_at73c213_dev_init(struct snd_card *card,
free_irq(chip->irq, chip); free_irq(chip->irq, chip);
chip->irq = -1; chip->irq = -1;
out: out:
clk_disable(chip->ssc->clk);
return retval; return retval;
} }
...@@ -1012,7 +1019,9 @@ static int snd_at73c213_remove(struct spi_device *spi) ...@@ -1012,7 +1019,9 @@ static int snd_at73c213_remove(struct spi_device *spi)
int retval; int retval;
/* Stop playback. */ /* Stop playback. */
clk_enable(chip->ssc->clk);
ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS)); ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
clk_disable(chip->ssc->clk);
/* Mute sound. */ /* Mute sound. */
retval = snd_at73c213_write_reg(chip, DAC_LMPG, 0x3f); retval = snd_at73c213_write_reg(chip, DAC_LMPG, 0x3f);
...@@ -1080,6 +1089,7 @@ static int snd_at73c213_suspend(struct device *dev) ...@@ -1080,6 +1089,7 @@ static int snd_at73c213_suspend(struct device *dev)
struct snd_at73c213 *chip = card->private_data; struct snd_at73c213 *chip = card->private_data;
ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS)); ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
clk_disable(chip->ssc->clk);
clk_disable(chip->board->dac_clk); clk_disable(chip->board->dac_clk);
return 0; return 0;
...@@ -1091,6 +1101,7 @@ static int snd_at73c213_resume(struct device *dev) ...@@ -1091,6 +1101,7 @@ static int snd_at73c213_resume(struct device *dev)
struct snd_at73c213 *chip = card->private_data; struct snd_at73c213 *chip = card->private_data;
clk_enable(chip->board->dac_clk); clk_enable(chip->board->dac_clk);
clk_enable(chip->ssc->clk);
ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN)); ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN));
return 0; return 0;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册