Skip to content

Commit

Permalink
Merge branch 'topic/misc' into topic/pcsp-fix
Browse files Browse the repository at this point in the history
Conflicts:
	sound/drivers/pcsp/pcsp_lib.c
  • Loading branch information
Takashi Iwai committed Nov 26, 2008
2 parents ed31348 + bc4a68f commit e7dd8c1
Show file tree
Hide file tree
Showing 11 changed files with 359 additions and 303 deletions.
28 changes: 25 additions & 3 deletions include/sound/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
* snd_printk - printk wrapper
* @fmt: format string
*
* Works like print() but prints the file and the line of the caller
* Works like printk() but prints the file and the line of the caller
* when configured with CONFIG_SND_VERBOSE_PRINTK.
*/
#define snd_printk(fmt, args...) \
Expand All @@ -380,18 +380,40 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
printk(fmt ,##args)
#endif

/**
* snd_BUG - give a BUG warning message and stack trace
*
* Calls WARN() if CONFIG_SND_DEBUG is set.
* Ignored when CONFIG_SND_DEBUG is not set.
*/
#define snd_BUG() WARN(1, "BUG?\n")

/**
* snd_BUG_ON - debugging check macro
* @cond: condition to evaluate
*
* When CONFIG_SND_DEBUG is set, this macro evaluates the given condition,
* and call WARN() and returns the value if it's non-zero.
*
* When CONFIG_SND_DEBUG is not set, this just returns zero, and the given
* condition is ignored.
*
* NOTE: the argument won't be evaluated at all when CONFIG_SND_DEBUG=n.
* Thus, don't put any statement that influences on the code behavior,
* such as pre/post increment, to the argument of this macro.
* If you want to evaluate and give a warning, use standard WARN_ON().
*/
#define snd_BUG_ON(cond) WARN((cond), "BUG? (%s)\n", __stringify(cond))

#else /* !CONFIG_SND_DEBUG */

#define snd_printd(fmt, args...) do { } while (0)
#define snd_BUG() do { } while (0)
static inline int __snd_bug_on(void)
static inline int __snd_bug_on(int cond)
{
return 0;
}
#define snd_BUG_ON(cond) __snd_bug_on() /* always false */
#define snd_BUG_ON(cond) __snd_bug_on(0 && (cond)) /* always false */

#endif /* CONFIG_SND_DEBUG */

Expand Down
2 changes: 1 addition & 1 deletion include/sound/version.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/* include/version.h */
#define CONFIG_SND_VERSION "1.0.18rc3"
#define CONFIG_SND_VERSION "1.0.18a"
#define CONFIG_SND_DATE ""
4 changes: 2 additions & 2 deletions sound/core/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ int snd_device_free(struct snd_card *card, void *device_data)
kfree(dev);
return 0;
}
snd_printd("device free %p (from %p), not found\n", device_data,
snd_printd("device free %p (from %pF), not found\n", device_data,
__builtin_return_address(0));
return -ENXIO;
}
Expand Down Expand Up @@ -135,7 +135,7 @@ int snd_device_disconnect(struct snd_card *card, void *device_data)
}
return 0;
}
snd_printd("device disconnect %p (from %p), not found\n", device_data,
snd_printd("device disconnect %p (from %pF), not found\n", device_data,
__builtin_return_address(0));
return -ENXIO;
}
Expand Down
8 changes: 3 additions & 5 deletions sound/drivers/pcsp/pcsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
return -EINVAL;

hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
pcsp_chip.timer.cb_mode = HRTIMER_CB_SOFTIRQ;
pcsp_chip.timer.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED;
pcsp_chip.timer.function = pcsp_do_timer;

card = snd_card_new(index, id, THIS_MODULE, 0);
Expand Down Expand Up @@ -188,10 +188,8 @@ static int __devexit pcsp_remove(struct platform_device *dev)

static void pcsp_stop_beep(struct snd_pcsp *chip)
{
spin_lock_irq(&chip->substream_lock);
if (!chip->playback_substream)
pcspkr_stop_sound();
spin_unlock_irq(&chip->substream_lock);
pcsp_sync_stop(chip);
pcspkr_stop_sound();
}

#ifdef CONFIG_PM
Expand Down
1 change: 1 addition & 0 deletions sound/drivers/pcsp/pcsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ struct snd_pcsp {
extern struct snd_pcsp pcsp_chip;

extern enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle);
extern void pcsp_sync_stop(struct snd_pcsp *chip);

extern int snd_pcsp_new_pcm(struct snd_pcsp *chip);
extern int snd_pcsp_new_mixer(struct snd_pcsp *chip);
Expand Down
95 changes: 52 additions & 43 deletions sound/drivers/pcsp/pcsp_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <sound/pcm.h>
#include <asm/io.h>
#include "pcsp.h"
Expand All @@ -19,6 +20,22 @@ MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "

#define DMIX_WANTS_S16 1

/*
* Call snd_pcm_period_elapsed in a tasklet
* This avoids spinlock messes and long-running irq contexts
*/
static void pcsp_call_pcm_elapsed(unsigned long priv)
{
if (atomic_read(&pcsp_chip.timer_active)) {
struct snd_pcm_substream *substream;
substream = pcsp_chip.playback_substream;
if (substream)
snd_pcm_period_elapsed(substream);
}
}

static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0);

enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
{
unsigned char timer_cnt, val;
Expand All @@ -28,41 +45,23 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
unsigned long flags;

if (chip->thalf) {
outb(chip->val61, 0x61);
chip->thalf = 0;
if (!atomic_read(&chip->timer_active))
return HRTIMER_NORESTART;
goto stop;
hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer),
ktime_set(0, chip->ns_rem));
return HRTIMER_RESTART;
}

spin_lock_irq(&chip->substream_lock);
/* Takashi Iwai says regarding this extra lock:
If the irq handler handles some data on the DMA buffer, it should
do snd_pcm_stream_lock().
That protects basically against all races among PCM callbacks, yes.
However, there are two remaining issues:
1. The substream pointer you try to lock isn't protected _before_
this lock yet.
2. snd_pcm_period_elapsed() itself acquires the lock.
The requirement of another lock is because of 1. When you get
chip->playback_substream, it's not protected.
Keeping this lock while snd_pcm_period_elapsed() assures the substream
is still protected (at least, not released). And the other status is
handled properly inside snd_pcm_stream_lock() in
snd_pcm_period_elapsed().
*/
if (!chip->playback_substream)
goto exit_nr_unlock1;
substream = chip->playback_substream;
snd_pcm_stream_lock(substream);
if (!atomic_read(&chip->timer_active))
goto exit_nr_unlock2;
goto stop;
substream = chip->playback_substream;
if (!substream)
goto stop;

runtime = substream->runtime;
fmt_size = snd_pcm_format_physical_width(runtime->format) >> 3;
Expand All @@ -87,6 +86,8 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)

period_bytes = snd_pcm_lib_period_bytes(substream);
buffer_bytes = snd_pcm_lib_buffer_bytes(substream);

spin_lock_irqsave(&chip->substream_lock, flags);
chip->playback_ptr += PCSP_INDEX_INC() * fmt_size;
periods_elapsed = chip->playback_ptr - chip->period_ptr;
if (periods_elapsed < 0) {
Expand All @@ -102,18 +103,15 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
* or ALSA will BUG on us. */
chip->playback_ptr %= buffer_bytes;

snd_pcm_stream_unlock(substream);

if (periods_elapsed) {
snd_pcm_period_elapsed(substream);
chip->period_ptr += periods_elapsed * period_bytes;
chip->period_ptr %= buffer_bytes;
tasklet_schedule(&pcsp_pcm_tasklet);
}

spin_unlock_irq(&chip->substream_lock);
spin_unlock_irqrestore(&chip->substream_lock, flags);

if (!atomic_read(&chip->timer_active))
return HRTIMER_NORESTART;
goto stop;

chip->ns_rem = PCSP_PERIOD_NS();
ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
Expand All @@ -122,10 +120,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
ktime_set(0, ns));
return HRTIMER_RESTART;

exit_nr_unlock2:
snd_pcm_stream_unlock(substream);
exit_nr_unlock1:
spin_unlock_irq(&chip->substream_lock);
stop:
return HRTIMER_NORESTART;
}

Expand Down Expand Up @@ -165,26 +160,35 @@ static void pcsp_stop_playing(struct snd_pcsp *chip)
spin_unlock(&i8253_lock);
}

/*
* Force to stop and sync the stream
*/
void pcsp_sync_stop(struct snd_pcsp *chip)
{
local_irq_disable();
pcsp_stop_playing(chip);
local_irq_enable();
hrtimer_cancel(&chip->timer);
tasklet_kill(&pcsp_pcm_tasklet);
}

static int snd_pcsp_playback_close(struct snd_pcm_substream *substream)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
#if PCSP_DEBUG
printk(KERN_INFO "PCSP: close called\n");
#endif
if (atomic_read(&chip->timer_active)) {
printk(KERN_ERR "PCSP: timer still active\n");
pcsp_stop_playing(chip);
}
spin_lock_irq(&chip->substream_lock);
pcsp_sync_stop(chip);
chip->playback_substream = NULL;
spin_unlock_irq(&chip->substream_lock);
return 0;
}

static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
int err;
pcsp_sync_stop(chip);
err = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
if (err < 0)
Expand All @@ -194,9 +198,11 @@ static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,

static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
#if PCSP_DEBUG
printk(KERN_INFO "PCSP: hw_free called\n");
#endif
pcsp_sync_stop(chip);
return snd_pcm_lib_free_pages(substream);
}

Expand All @@ -212,6 +218,7 @@ static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
snd_pcm_lib_period_bytes(substream),
substream->runtime->periods);
#endif
pcsp_sync_stop(chip);
chip->playback_ptr = 0;
chip->period_ptr = 0;
return 0;
Expand Down Expand Up @@ -242,7 +249,11 @@ static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream
*substream)
{
struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
return bytes_to_frames(substream->runtime, chip->playback_ptr);
unsigned int pos;
spin_lock(&chip->substream_lock);
pos = chip->playback_ptr;
spin_unlock(&chip->substream_lock);
return bytes_to_frames(substream->runtime, pos);
}

static struct snd_pcm_hardware snd_pcsp_playback = {
Expand Down Expand Up @@ -279,9 +290,7 @@ static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
return -EBUSY;
}
runtime->hw = snd_pcsp_playback;
spin_lock_irq(&chip->substream_lock);
chip->playback_substream = substream;
spin_unlock_irq(&chip->substream_lock);
return 0;
}

Expand Down
2 changes: 2 additions & 0 deletions sound/pci/ac97/ac97_patch.c
Original file line number Diff line number Diff line change
Expand Up @@ -2832,6 +2832,8 @@ static int patch_alc655(struct snd_ac97 * ac97)
val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */
else
val |= (1 << 1); /* Pin 47 is spdif input pin */
/* this seems missing on some hardwares */
ac97->ext_id |= AC97_EI_SPDIF;
}
val &= ~(1 << 12); /* vref enable */
snd_ac97_write_cache(ac97, 0x7a, val);
Expand Down
Loading

0 comments on commit e7dd8c1

Please sign in to comment.