Commit babd90b2 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6:
  [ALSA] hda - Fix ASUS P5GD1 model
  [ALSA] hda - Fix ALC262 fujitsu model
  snd-pcsp: use HRTIMER_CB_SOFTIRQ
parents 424de91d 186c3117
...@@ -96,7 +96,7 @@ static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev) ...@@ -96,7 +96,7 @@ static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
return -EINVAL; return -EINVAL;
hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
pcsp_chip.timer.cb_mode = HRTIMER_CB_IRQSAFE; pcsp_chip.timer.cb_mode = HRTIMER_CB_SOFTIRQ;
pcsp_chip.timer.function = pcsp_do_timer; pcsp_chip.timer.function = pcsp_do_timer;
card = snd_card_new(index, id, THIS_MODULE, 0); card = snd_card_new(index, id, THIS_MODULE, 0);
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <linux/interrupt.h>
#include <asm/io.h> #include <asm/io.h>
#include "pcsp.h" #include "pcsp.h"
...@@ -20,34 +19,8 @@ MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround " ...@@ -20,34 +19,8 @@ MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
#define DMIX_WANTS_S16 1 #define DMIX_WANTS_S16 1
static void pcsp_start_timer(unsigned long dummy)
{
hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
}
/*
* We need the hrtimer_start as a tasklet to avoid
* the nasty locking problem. :(
* The problem:
* - The timer handler is called with the cpu_base->lock
* already held by hrtimer code.
* - snd_pcm_period_elapsed() takes the
* substream->self_group.lock.
* So far so good.
* But the snd_pcsp_trigger() is called with the
* substream->self_group.lock held, and it calls
* hrtimer_start(), which takes the cpu_base->lock.
* You see the problem. We have the code pathes
* which take two locks in a reverse order. This
* can deadlock and the lock validator complains.
* The only solution I could find was to move the
* hrtimer_start() into a tasklet. -stsp
*/
static DECLARE_TASKLET(pcsp_start_timer_tasklet, pcsp_start_timer, 0);
enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
{ {
unsigned long flags;
unsigned char timer_cnt, val; unsigned char timer_cnt, val;
int fmt_size, periods_elapsed; int fmt_size, periods_elapsed;
u64 ns; u64 ns;
...@@ -66,9 +39,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) ...@@ -66,9 +39,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
return HRTIMER_RESTART; return HRTIMER_RESTART;
} }
/* hrtimer calls us from both hardirq and softirq contexts, spin_lock_irq(&chip->substream_lock);
* so irqsave :( */
spin_lock_irqsave(&chip->substream_lock, flags);
/* Takashi Iwai says regarding this extra lock: /* Takashi Iwai says regarding this extra lock:
If the irq handler handles some data on the DMA buffer, it should If the irq handler handles some data on the DMA buffer, it should
...@@ -139,7 +110,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) ...@@ -139,7 +110,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
chip->period_ptr %= buffer_bytes; chip->period_ptr %= buffer_bytes;
} }
spin_unlock_irqrestore(&chip->substream_lock, flags); spin_unlock_irq(&chip->substream_lock);
if (!atomic_read(&chip->timer_active)) if (!atomic_read(&chip->timer_active))
return HRTIMER_NORESTART; return HRTIMER_NORESTART;
...@@ -153,7 +124,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) ...@@ -153,7 +124,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
exit_nr_unlock2: exit_nr_unlock2:
snd_pcm_stream_unlock(substream); snd_pcm_stream_unlock(substream);
exit_nr_unlock1: exit_nr_unlock1:
spin_unlock_irqrestore(&chip->substream_lock, flags); spin_unlock_irq(&chip->substream_lock);
return HRTIMER_NORESTART; return HRTIMER_NORESTART;
} }
...@@ -174,7 +145,7 @@ static void pcsp_start_playing(struct snd_pcsp *chip) ...@@ -174,7 +145,7 @@ static void pcsp_start_playing(struct snd_pcsp *chip)
atomic_set(&chip->timer_active, 1); atomic_set(&chip->timer_active, 1);
chip->thalf = 0; chip->thalf = 0;
tasklet_schedule(&pcsp_start_timer_tasklet); hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
} }
static void pcsp_stop_playing(struct snd_pcsp *chip) static void pcsp_stop_playing(struct snd_pcsp *chip)
......
...@@ -2981,7 +2981,7 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = { ...@@ -2981,7 +2981,7 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = {
/* SND_PCI_QUIRK(0x1043, 0x1964, "ASUS", ALC880_ASUS_DIG), */ /* SND_PCI_QUIRK(0x1043, 0x1964, "ASUS", ALC880_ASUS_DIG), */
SND_PCI_QUIRK(0x1043, 0x1973, "ASUS", ALC880_ASUS_DIG), SND_PCI_QUIRK(0x1043, 0x1973, "ASUS", ALC880_ASUS_DIG),
SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS", ALC880_ASUS_DIG), SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS", ALC880_ASUS_DIG),
SND_PCI_QUIRK(0x1043, 0x814e, "ASUS", ALC880_ASUS), SND_PCI_QUIRK(0x1043, 0x814e, "ASUS P5GD1 w/SPDIF", ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG), SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG),
SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST), SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST),
SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST), SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST),
...@@ -8757,35 +8757,39 @@ static struct hda_input_mux alc262_HP_D7000_capture_source = { ...@@ -8757,35 +8757,39 @@ static struct hda_input_mux alc262_HP_D7000_capture_source = {
}, },
}; };
/* mute/unmute internal speaker according to the hp jack and mute state */ /* mute/unmute internal speaker according to the hp jacks and mute state */
static void alc262_fujitsu_automute(struct hda_codec *codec, int force) static void alc262_fujitsu_automute(struct hda_codec *codec, int force)
{ {
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
unsigned int mute; unsigned int mute;
if (force || !spec->sense_updated) { if (force || !spec->sense_updated) {
unsigned int present_int_hp, present_dock_hp; unsigned int present;
/* need to execute and sync at first */ /* need to execute and sync at first */
snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0); snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0);
present_int_hp = snd_hda_codec_read(codec, 0x14, 0, /* check laptop HP jack */
AC_VERB_GET_PIN_SENSE, 0); present = snd_hda_codec_read(codec, 0x14, 0,
snd_hda_codec_read(codec, 0x1B, 0, AC_VERB_SET_PIN_SENSE, 0); AC_VERB_GET_PIN_SENSE, 0);
present_dock_hp = snd_hda_codec_read(codec, 0x1b, 0, /* need to execute and sync at first */
AC_VERB_GET_PIN_SENSE, 0); snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
spec->jack_present = (present_int_hp & 0x80000000) != 0; /* check docking HP jack */
spec->jack_present |= (present_dock_hp & 0x80000000) != 0; present |= snd_hda_codec_read(codec, 0x1b, 0,
AC_VERB_GET_PIN_SENSE, 0);
if (present & AC_PINSENSE_PRESENCE)
spec->jack_present = 1;
else
spec->jack_present = 0;
spec->sense_updated = 1; spec->sense_updated = 1;
} }
if (spec->jack_present) { /* unmute internal speaker only if both HPs are unplugged and
/* mute internal speaker */ * master switch is on
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, */
HDA_AMP_MUTE, HDA_AMP_MUTE); if (spec->jack_present)
} else { mute = HDA_AMP_MUTE;
/* unmute internal speaker if necessary */ else
mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0); mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
HDA_AMP_MUTE, mute); HDA_AMP_MUTE, mute);
}
} }
/* unsolicited event for HP jack sensing */ /* unsolicited event for HP jack sensing */
...@@ -8797,6 +8801,11 @@ static void alc262_fujitsu_unsol_event(struct hda_codec *codec, ...@@ -8797,6 +8801,11 @@ static void alc262_fujitsu_unsol_event(struct hda_codec *codec,
alc262_fujitsu_automute(codec, 1); alc262_fujitsu_automute(codec, 1);
} }
static void alc262_fujitsu_init_hook(struct hda_codec *codec)
{
alc262_fujitsu_automute(codec, 1);
}
/* bind volumes of both NID 0x0c and 0x0d */ /* bind volumes of both NID 0x0c and 0x0d */
static struct hda_bind_ctls alc262_fujitsu_bind_master_vol = { static struct hda_bind_ctls alc262_fujitsu_bind_master_vol = {
.ops = &snd_hda_bind_vol, .ops = &snd_hda_bind_vol,
...@@ -9570,6 +9579,7 @@ static struct alc_config_preset alc262_presets[] = { ...@@ -9570,6 +9579,7 @@ static struct alc_config_preset alc262_presets[] = {
.channel_mode = alc262_modes, .channel_mode = alc262_modes,
.input_mux = &alc262_fujitsu_capture_source, .input_mux = &alc262_fujitsu_capture_source,
.unsol_event = alc262_fujitsu_unsol_event, .unsol_event = alc262_fujitsu_unsol_event,
.init_hook = alc262_fujitsu_init_hook,
}, },
[ALC262_HP_BPC] = { [ALC262_HP_BPC] = {
.mixers = { alc262_HP_BPC_mixer }, .mixers = { alc262_HP_BPC_mixer },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment