summaryrefslogtreecommitdiff
path: root/sound/core/timer.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-05-09 18:26:55 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2019-05-09 18:26:55 +0300
commite57ccca1ba33e1d92cc3bbf8b6304a46948844b0 (patch)
treea988a1f7d1d3250f57761dbea365482300a7b3b2 /sound/core/timer.c
parenta2d635decbfa9c1e4ae15cb05b68b2559f7f827c (diff)
parented97c988bdc61ab6fb5d1f5f02a709844557b68f (diff)
downloadlinux-e57ccca1ba33e1d92cc3bbf8b6304a46948844b0.tar.xz
Merge tag 'sound-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "The most significant changes at this cycle are the Sound Open Firmware support from Intel for the common DSP framework along with its support for Intel platforms. It's a door opened to a real "free" firmware (in the sense of FOSS), and other parties show interests in it. In addition to SOF, we've got a bunch of updates and fixes as usual. Some highlights are below. ALSA core: - Cleanups and fixes in ALSA timer code to cover some races spotted by syzkaller - Cleanups and fixes in ALSA sequencer code to cover some races, again unsurprisingly, spotted by syzkaller - Optimize the common page allocation helper with alloc_pages_exact() ASoC: - Add SOF core support, as well as Intel SOF platform support - Generic card driver improvements: support for MCLK/sample rate ratio and pin switches - A big set of improvements to TLV320AIC32x4 drivers - New drivers for Freescale audio mixers, several Intel machines, several Mediatek machines, Meson G12A, Spreadtrum compressed audio and DMA devices HD-audio: - A few Realtek codec fixes for reducing pop noises - Quirks for Chromebooks - Workaround for faulty connection report on AMD/Nvidia HDMI Others: - A quirk for Focusrite Scarlett Solo USB-audio - Add support for MOTU 8pre FireWire - 24bit sample format support in aloop - GUS patch format support (finally, over a decade) in native emux synth code" * tag 'sound-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (375 commits) ASoC: SOF: Fix unused variable warnings ALSA: line6: toneport: Fix broken usage of timer for delayed execution ALSA: aica: Fix a long-time build breakage ALSA: hda/realtek - Support low power consumption for ALC256 ASoC: stm32: i2s: update pcm hardware constraints ASoC: codec: hdac_hdmi: no checking monitor in hw_params ASoC: mediatek: mt6358: save PGA for mixer control ASoC: mediatek: mt6358: save output volume for mixer controls ASoC: mediatek: mt6358: initialize setting when ramping volume ASoC: SOF: core: fix undefined nocodec reference ASoC: SOF: xtensa: fix undefined references ASoC: SOF: Propagate sof_get_ctrl_copy_params() error properly ALSA: hdea/realtek - Headset fixup for System76 Gazelle (gaze14) ALSA: hda/intel: add CometLake PCI IDs ALSA: hda/realtek - Support low power consumption for ALC295 ASoC: rockchip: Fix an uninitialized variable compile warning ASoC: SOF: Fix a compile warning with CONFIG_PCI=n ASoC: da7219: Fix a compile warning at CONFIG_COMMON_CLK=n ASoC: sound/soc/sof/: fix kconfig dependency warning ASoC: stm32: spdifrx: change trace level on iec control ...
Diffstat (limited to 'sound/core/timer.c')
-rw-r--r--sound/core/timer.c181
1 files changed, 110 insertions, 71 deletions
diff --git a/sound/core/timer.c b/sound/core/timer.c
index b842b61f66c2..e3973957b392 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -38,6 +38,7 @@
/* internal flags */
#define SNDRV_TIMER_IFLG_PAUSED 0x00010000
+#define SNDRV_TIMER_IFLG_DEAD 0x00020000
#if IS_ENABLED(CONFIG_SND_HRTIMER)
#define DEFAULT_TIMER_LIMIT 4
@@ -254,19 +255,20 @@ int snd_timer_open(struct snd_timer_instance **ti,
struct snd_timer_instance *timeri = NULL;
int err;
+ mutex_lock(&register_mutex);
if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
/* open a slave instance */
if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE ||
tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) {
pr_debug("ALSA: timer: invalid slave class %i\n",
tid->dev_sclass);
- return -EINVAL;
+ err = -EINVAL;
+ goto unlock;
}
- mutex_lock(&register_mutex);
timeri = snd_timer_instance_new(owner, NULL);
if (!timeri) {
- mutex_unlock(&register_mutex);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto unlock;
}
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = tid->device;
@@ -277,13 +279,10 @@ int snd_timer_open(struct snd_timer_instance **ti,
snd_timer_close_locked(timeri);
timeri = NULL;
}
- mutex_unlock(&register_mutex);
- *ti = timeri;
- return err;
+ goto unlock;
}
/* open a master instance */
- mutex_lock(&register_mutex);
timer = snd_timer_find(tid);
#ifdef CONFIG_MODULES
if (!timer) {
@@ -294,25 +293,26 @@ int snd_timer_open(struct snd_timer_instance **ti,
}
#endif
if (!timer) {
- mutex_unlock(&register_mutex);
- return -ENODEV;
+ err = -ENODEV;
+ goto unlock;
}
if (!list_empty(&timer->open_list_head)) {
timeri = list_entry(timer->open_list_head.next,
struct snd_timer_instance, open_list);
if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
- mutex_unlock(&register_mutex);
- return -EBUSY;
+ err = -EBUSY;
+ timeri = NULL;
+ goto unlock;
}
}
if (timer->num_instances >= timer->max_instances) {
- mutex_unlock(&register_mutex);
- return -EBUSY;
+ err = -EBUSY;
+ goto unlock;
}
timeri = snd_timer_instance_new(owner, timer);
if (!timeri) {
- mutex_unlock(&register_mutex);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto unlock;
}
/* take a card refcount for safe disconnection */
if (timer->card)
@@ -321,16 +321,16 @@ int snd_timer_open(struct snd_timer_instance **ti,
timeri->slave_id = slave_id;
if (list_empty(&timer->open_list_head) && timer->hw.open) {
- int err = timer->hw.open(timer);
+ err = timer->hw.open(timer);
if (err) {
kfree(timeri->owner);
kfree(timeri);
+ timeri = NULL;
if (timer->card)
put_device(&timer->card->card_dev);
module_put(timer->module);
- mutex_unlock(&register_mutex);
- return err;
+ goto unlock;
}
}
@@ -341,6 +341,8 @@ int snd_timer_open(struct snd_timer_instance **ti,
snd_timer_close_locked(timeri);
timeri = NULL;
}
+
+ unlock:
mutex_unlock(&register_mutex);
*ti = timeri;
return err;
@@ -353,15 +355,20 @@ EXPORT_SYMBOL(snd_timer_open);
*/
static int snd_timer_close_locked(struct snd_timer_instance *timeri)
{
- struct snd_timer *timer = NULL;
+ struct snd_timer *timer = timeri->timer;
struct snd_timer_instance *slave, *tmp;
+ if (timer) {
+ spin_lock_irq(&timer->lock);
+ timeri->flags |= SNDRV_TIMER_IFLG_DEAD;
+ spin_unlock_irq(&timer->lock);
+ }
+
list_del(&timeri->open_list);
/* force to stop the timer */
snd_timer_stop(timeri);
- timer = timeri->timer;
if (timer) {
timer->num_instances--;
/* wait, until the active callback is finished */
@@ -497,6 +504,10 @@ static int snd_timer_start1(struct snd_timer_instance *timeri,
return -EINVAL;
spin_lock_irqsave(&timer->lock, flags);
+ if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
+ result = -EINVAL;
+ goto unlock;
+ }
if (timer->card && timer->card->shutdown) {
result = -ENODEV;
goto unlock;
@@ -541,11 +552,16 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri,
bool start)
{
unsigned long flags;
+ int err;
spin_lock_irqsave(&slave_active_lock, flags);
+ if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
+ err = -EINVAL;
+ goto unlock;
+ }
if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
- spin_unlock_irqrestore(&slave_active_lock, flags);
- return -EBUSY;
+ err = -EBUSY;
+ goto unlock;
}
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
if (timeri->master && timeri->timer) {
@@ -556,8 +572,10 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri,
SNDRV_TIMER_EVENT_CONTINUE);
spin_unlock(&timeri->timer->lock);
}
+ err = 1; /* delayed start */
+ unlock:
spin_unlock_irqrestore(&slave_active_lock, flags);
- return 1; /* delayed start */
+ return err;
}
/* stop/pause a master timer */
@@ -720,6 +738,46 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
timer->sticks = ticks;
}
+/* call callbacks in timer ack list */
+static void snd_timer_process_callbacks(struct snd_timer *timer,
+ struct list_head *head)
+{
+ struct snd_timer_instance *ti;
+ unsigned long resolution, ticks;
+
+ while (!list_empty(head)) {
+ ti = list_first_entry(head, struct snd_timer_instance,
+ ack_list);
+
+ /* remove from ack_list and make empty */
+ list_del_init(&ti->ack_list);
+
+ if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) {
+ ticks = ti->pticks;
+ ti->pticks = 0;
+ resolution = ti->resolution;
+ ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
+ spin_unlock(&timer->lock);
+ if (ti->callback)
+ ti->callback(ti, resolution, ticks);
+ spin_lock(&timer->lock);
+ ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
+ }
+ }
+}
+
+/* clear pending instances from ack list */
+static void snd_timer_clear_callbacks(struct snd_timer *timer,
+ struct list_head *head)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&timer->lock, flags);
+ while (!list_empty(head))
+ list_del_init(head->next);
+ spin_unlock_irqrestore(&timer->lock, flags);
+}
+
/*
* timer tasklet
*
@@ -727,34 +785,15 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l
static void snd_timer_tasklet(unsigned long arg)
{
struct snd_timer *timer = (struct snd_timer *) arg;
- struct snd_timer_instance *ti;
- struct list_head *p;
- unsigned long resolution, ticks;
unsigned long flags;
- if (timer->card && timer->card->shutdown)
+ if (timer->card && timer->card->shutdown) {
+ snd_timer_clear_callbacks(timer, &timer->sack_list_head);
return;
+ }
spin_lock_irqsave(&timer->lock, flags);
- /* now process all callbacks */
- while (!list_empty(&timer->sack_list_head)) {
- p = timer->sack_list_head.next; /* get first item */
- ti = list_entry(p, struct snd_timer_instance, ack_list);
-
- /* remove from ack_list and make empty */
- list_del_init(p);
-
- ticks = ti->pticks;
- ti->pticks = 0;
- resolution = ti->resolution;
-
- ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
- spin_unlock(&timer->lock);
- if (ti->callback)
- ti->callback(ti, resolution, ticks);
- spin_lock(&timer->lock);
- ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
- }
+ snd_timer_process_callbacks(timer, &timer->sack_list_head);
spin_unlock_irqrestore(&timer->lock, flags);
}
@@ -767,16 +806,18 @@ static void snd_timer_tasklet(unsigned long arg)
void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
{
struct snd_timer_instance *ti, *ts, *tmp;
- unsigned long resolution, ticks;
- struct list_head *p, *ack_list_head;
+ unsigned long resolution;
+ struct list_head *ack_list_head;
unsigned long flags;
int use_tasklet = 0;
if (timer == NULL)
return;
- if (timer->card && timer->card->shutdown)
+ if (timer->card && timer->card->shutdown) {
+ snd_timer_clear_callbacks(timer, &timer->ack_list_head);
return;
+ }
spin_lock_irqsave(&timer->lock, flags);
@@ -790,6 +831,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
*/
list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
active_list) {
+ if (ti->flags & SNDRV_TIMER_IFLG_DEAD)
+ continue;
if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
continue;
ti->pticks += ticks_left;
@@ -839,23 +882,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
}
/* now process all fast callbacks */
- while (!list_empty(&timer->ack_list_head)) {
- p = timer->ack_list_head.next; /* get first item */
- ti = list_entry(p, struct snd_timer_instance, ack_list);
-
- /* remove from ack_list and make empty */
- list_del_init(p);
-
- ticks = ti->pticks;
- ti->pticks = 0;
-
- ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
- spin_unlock(&timer->lock);
- if (ti->callback)
- ti->callback(ti, resolution, ticks);
- spin_lock(&timer->lock);
- ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
- }
+ snd_timer_process_callbacks(timer, &timer->ack_list_head);
/* do we have any slow callbacks? */
use_tasklet = !list_empty(&timer->sack_list_head);
@@ -1882,7 +1909,10 @@ static int snd_timer_user_start(struct file *file)
snd_timer_stop(tu->timeri);
tu->timeri->lost = 0;
tu->last_resolution = 0;
- return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0;
+ err = snd_timer_start(tu->timeri, tu->ticks);
+ if (err < 0)
+ return err;
+ return 0;
}
static int snd_timer_user_stop(struct file *file)
@@ -1893,7 +1923,10 @@ static int snd_timer_user_stop(struct file *file)
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
- return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
+ err = snd_timer_stop(tu->timeri);
+ if (err < 0)
+ return err;
+ return 0;
}
static int snd_timer_user_continue(struct file *file)
@@ -1908,7 +1941,10 @@ static int snd_timer_user_continue(struct file *file)
if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
return snd_timer_user_start(file);
tu->timeri->lost = 0;
- return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
+ err = snd_timer_continue(tu->timeri);
+ if (err < 0)
+ return err;
+ return 0;
}
static int snd_timer_user_pause(struct file *file)
@@ -1919,7 +1955,10 @@ static int snd_timer_user_pause(struct file *file)
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
- return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
+ err = snd_timer_pause(tu->timeri);
+ if (err < 0)
+ return err;
+ return 0;
}
enum {