sound fixes for 4.5-rc1
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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJWoe6+AAoJEGwxgFQ9KSmk+nAQAJziI0aIgVRoppcazN8KiC0F hF2Xwi+3ZMwWRNPDkt8XkL2fN1qdvyhJFmwj5MKvPWe2PUaDOw4IyGHNZncHG7QW fCjdLcnN+3eNdXWC8tZD7k72i6sJn7781ronlvYccu2FMuoBfsilxOM5N6mQrHo+ Lr46Pjdvyw9EgO7CYr/cBWr2ax8nm0UYBHGZq5eyHwvML3j6r0uwn9YpS+g5RX+d uPcgjdcsfDlfiZpwJX+uGEzyES7io57keSmXHfFDpfM3Jqz26STcpEq9mc/PtF2m lmDYQ94llNIUrsWm4fObuNgzcKDfywJh+ZXBZzahekSjIQcLAl76RCsJAXRyiIzI iVPomBQHlMthWt5K5dYlUiYtclIaVN3XD/c3xeksu0aUiiPapp6M8UVL8lqCWy63 hwj/lOTXv3rO9Ji7b57FUJF0zva/5lzQooxWoiOLhbACD9vVLBuxjKn2nys7xu0F LoMFYWROgWvROvGNY1LhXsM31LTkO24QJgq6G7l7eWnutZzV/k/AHzBmUmrkwLu3 NRhP1F1uWwMBeh4V6DFRPz1ze+kw07DzocQHR3bfgm5Hmbfr/qZm5qGQYXoRVN7C wGYM1J938zknBpjl909XgUVGMQumqg9edlOj17qMWJIgRCP9fTkXPpvrDCp/n+L3 nuQDdUZ6TNCvNSrXrzY+ =6OeM -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
e7cc3edd17
11 changed files with 124 additions and 23 deletions
|
@ -104,6 +104,7 @@ struct snd_timer_instance {
|
|||
int event,
|
||||
struct timespec * tstamp,
|
||||
unsigned long resolution);
|
||||
void (*disconnect)(struct snd_timer_instance *timeri);
|
||||
void *callback_data;
|
||||
unsigned long ticks; /* auto-load ticks when expired */
|
||||
unsigned long cticks; /* current ticks */
|
||||
|
|
|
@ -1405,6 +1405,8 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
|
|||
return -EFAULT;
|
||||
if (tlv.length < sizeof(unsigned int) * 2)
|
||||
return -EINVAL;
|
||||
if (!tlv.numid)
|
||||
return -EINVAL;
|
||||
down_read(&card->controls_rwsem);
|
||||
kctl = snd_ctl_find_numid(card, tlv.numid);
|
||||
if (kctl == NULL) {
|
||||
|
|
|
@ -90,7 +90,7 @@ static int snd_hrtimer_start(struct snd_timer *t)
|
|||
struct snd_hrtimer *stime = t->private_data;
|
||||
|
||||
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_MODE_REL);
|
||||
atomic_set(&stime->running, 1);
|
||||
|
@ -101,6 +101,7 @@ static int snd_hrtimer_stop(struct snd_timer *t)
|
|||
{
|
||||
struct snd_hrtimer *stime = t->private_data;
|
||||
atomic_set(&stime->running, 0);
|
||||
hrtimer_try_to_cancel(&stime->hrt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -255,10 +255,15 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
|
|||
if (! (runtime = substream->runtime))
|
||||
return -ENOTTY;
|
||||
|
||||
/* only fifo_size is different, so just copy all */
|
||||
data = memdup_user(data32, sizeof(*data32));
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
data = kmalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* 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)
|
||||
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
|
|||
struct snd_seq_port_info *data;
|
||||
mm_segment_t fs;
|
||||
|
||||
data = memdup_user(data32, sizeof(*data32));
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
data = kmalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!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))
|
||||
goto error;
|
||||
data->kernel = NULL;
|
||||
|
|
|
@ -65,6 +65,7 @@ struct snd_timer_user {
|
|||
int qtail;
|
||||
int qused;
|
||||
int queue_size;
|
||||
bool disconnected;
|
||||
struct snd_timer_read *queue;
|
||||
struct snd_timer_tread *tqueue;
|
||||
spinlock_t qlock;
|
||||
|
@ -290,6 +291,9 @@ int snd_timer_open(struct snd_timer_instance **ti,
|
|||
mutex_unlock(®ister_mutex);
|
||||
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_id = slave_id;
|
||||
if (list_empty(&timer->open_list_head) && timer->hw.open)
|
||||
|
@ -359,6 +363,9 @@ int snd_timer_close(struct snd_timer_instance *timeri)
|
|||
}
|
||||
spin_unlock(&timer->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(®ister_mutex);
|
||||
}
|
||||
out:
|
||||
|
@ -474,6 +481,8 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
|
|||
timer = timeri->timer;
|
||||
if (timer == NULL)
|
||||
return -EINVAL;
|
||||
if (timer->card && timer->card->shutdown)
|
||||
return -ENODEV;
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
timeri->ticks = timeri->cticks = ticks;
|
||||
timeri->pticks = 0;
|
||||
|
@ -505,6 +514,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
|
|||
spin_lock_irqsave(&timer->lock, flags);
|
||||
list_del_init(&timeri->ack_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) &&
|
||||
!(--timer->running)) {
|
||||
timer->hw.stop(timer);
|
||||
|
@ -565,6 +578,8 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
|
|||
timer = timeri->timer;
|
||||
if (! timer)
|
||||
return -EINVAL;
|
||||
if (timer->card && timer->card->shutdown)
|
||||
return -ENODEV;
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
if (!timeri->cticks)
|
||||
timeri->cticks = 1;
|
||||
|
@ -628,6 +643,9 @@ static void snd_timer_tasklet(unsigned long arg)
|
|||
unsigned long resolution, ticks;
|
||||
unsigned long flags;
|
||||
|
||||
if (timer->card && timer->card->shutdown)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
/* now process all callbacks */
|
||||
while (!list_empty(&timer->sack_list_head)) {
|
||||
|
@ -668,6 +686,9 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
|
|||
if (timer == NULL)
|
||||
return;
|
||||
|
||||
if (timer->card && timer->card->shutdown)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
|
||||
/* remember the current resolution */
|
||||
|
@ -881,8 +902,15 @@ static int snd_timer_dev_register(struct snd_device *dev)
|
|||
static int snd_timer_dev_disconnect(struct snd_device *device)
|
||||
{
|
||||
struct snd_timer *timer = device->device_data;
|
||||
struct snd_timer_instance *ti;
|
||||
|
||||
mutex_lock(®ister_mutex);
|
||||
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(®ister_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
@ -893,6 +921,8 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
|
|||
unsigned long resolution = 0;
|
||||
struct snd_timer_instance *ti, *ts;
|
||||
|
||||
if (timer->card && timer->card->shutdown)
|
||||
return;
|
||||
if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
|
||||
return;
|
||||
if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
|
||||
|
@ -1051,6 +1081,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
|
|||
|
||||
mutex_lock(®ister_mutex);
|
||||
list_for_each_entry(timer, &snd_timer_list, device_list) {
|
||||
if (timer->card && timer->card->shutdown)
|
||||
continue;
|
||||
switch (timer->tmr_class) {
|
||||
case SNDRV_TIMER_CLASS_GLOBAL:
|
||||
snd_iprintf(buffer, "G%i: ", timer->tmr_device);
|
||||
|
@ -1185,6 +1217,14 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
|
|||
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,
|
||||
unsigned long resolution,
|
||||
unsigned long ticks)
|
||||
|
@ -1558,6 +1598,7 @@ static int snd_timer_user_tselect(struct file *file,
|
|||
? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
|
||||
tu->timeri->ccallback = snd_timer_user_ccallback;
|
||||
tu->timeri->callback_data = (void *)tu;
|
||||
tu->timeri->disconnect = snd_timer_user_disconnect;
|
||||
}
|
||||
|
||||
__err:
|
||||
|
@ -1876,6 +1917,10 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
|
|||
|
||||
remove_wait_queue(&tu->qchange_sleep, &wait);
|
||||
|
||||
if (tu->disconnected) {
|
||||
err = -ENODEV;
|
||||
break;
|
||||
}
|
||||
if (signal_pending(current)) {
|
||||
err = -ERESTARTSYS;
|
||||
break;
|
||||
|
@ -1925,6 +1970,8 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
|
|||
mask = 0;
|
||||
if (tu->qused)
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
if (tu->disconnected)
|
||||
mask |= POLLERR;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
|
|
@ -306,7 +306,7 @@ int snd_hdac_i915_init(struct hdac_bus *bus)
|
|||
out_err:
|
||||
kfree(acomp);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -174,14 +174,40 @@ static inline bool codec_probed(struct hda_codec *codec)
|
|||
return device_attach(hda_codec_dev(codec)) > 0 && codec->preset;
|
||||
}
|
||||
|
||||
/* try to auto-load codec module */
|
||||
static void request_codec_module(struct hda_codec *codec)
|
||||
{
|
||||
#ifdef MODULE
|
||||
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 */
|
||||
}
|
||||
|
||||
/* try to auto-load and bind the codec module */
|
||||
static void codec_bind_module(struct hda_codec *codec)
|
||||
{
|
||||
#ifdef MODULE
|
||||
char modalias[32];
|
||||
|
||||
snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias));
|
||||
request_module(modalias);
|
||||
request_codec_module(codec);
|
||||
if (codec_probed(codec))
|
||||
return;
|
||||
#endif
|
||||
|
@ -218,17 +244,13 @@ static int codec_bind_generic(struct hda_codec *codec)
|
|||
|
||||
if (is_likely_hdmi_codec(codec)) {
|
||||
codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI;
|
||||
#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI)
|
||||
request_module("snd-hda-codec-hdmi");
|
||||
#endif
|
||||
request_codec_module(codec);
|
||||
if (codec_probed(codec))
|
||||
return 0;
|
||||
}
|
||||
|
||||
codec->probe_id = HDA_CODEC_ID_GENERIC;
|
||||
#if IS_MODULE(CONFIG_SND_HDA_GENERIC)
|
||||
request_module("snd-hda-codec-generic");
|
||||
#endif
|
||||
request_codec_module(codec);
|
||||
if (codec_probed(codec))
|
||||
return 0;
|
||||
return -ENODEV;
|
||||
|
|
|
@ -2078,9 +2078,11 @@ static int azx_probe_continue(struct azx *chip)
|
|||
* for other chips, still continue probing as other
|
||||
* 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;
|
||||
else
|
||||
} else
|
||||
goto skip_i915;
|
||||
}
|
||||
|
||||
|
@ -2149,9 +2151,17 @@ static int azx_probe_continue(struct azx *chip)
|
|||
static void azx_remove(struct pci_dev *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);
|
||||
}
|
||||
}
|
||||
|
||||
static void azx_shutdown(struct pci_dev *pci)
|
||||
|
|
|
@ -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(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
|
||||
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, 0x15a7, "ASUS UX51VZH", 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)
|
|||
runtime->hw = snd_at73c213_playback_hw;
|
||||
chip->substream = substream;
|
||||
|
||||
clk_enable(chip->ssc->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -228,6 +230,7 @@ static int snd_at73c213_pcm_close(struct snd_pcm_substream *substream)
|
|||
{
|
||||
struct snd_at73c213 *chip = snd_pcm_substream_chip(substream);
|
||||
chip->substream = NULL;
|
||||
clk_disable(chip->ssc->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -897,6 +900,8 @@ static int snd_at73c213_dev_init(struct snd_card *card,
|
|||
chip->card = card;
|
||||
chip->irq = -1;
|
||||
|
||||
clk_enable(chip->ssc->clk);
|
||||
|
||||
retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip);
|
||||
if (retval) {
|
||||
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,
|
|||
free_irq(chip->irq, chip);
|
||||
chip->irq = -1;
|
||||
out:
|
||||
clk_disable(chip->ssc->clk);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -1012,7 +1019,9 @@ static int snd_at73c213_remove(struct spi_device *spi)
|
|||
int retval;
|
||||
|
||||
/* Stop playback. */
|
||||
clk_enable(chip->ssc->clk);
|
||||
ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
|
||||
clk_disable(chip->ssc->clk);
|
||||
|
||||
/* Mute sound. */
|
||||
retval = snd_at73c213_write_reg(chip, DAC_LMPG, 0x3f);
|
||||
|
@ -1080,6 +1089,7 @@ static int snd_at73c213_suspend(struct device *dev)
|
|||
struct snd_at73c213 *chip = card->private_data;
|
||||
|
||||
ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS));
|
||||
clk_disable(chip->ssc->clk);
|
||||
clk_disable(chip->board->dac_clk);
|
||||
|
||||
return 0;
|
||||
|
@ -1091,6 +1101,7 @@ static int snd_at73c213_resume(struct device *dev)
|
|||
struct snd_at73c213 *chip = card->private_data;
|
||||
|
||||
clk_enable(chip->board->dac_clk);
|
||||
clk_enable(chip->ssc->clk);
|
||||
ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN));
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Reference in a new issue