Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit babd90b2 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* '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
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -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);
+4 −33
Original line number Original line Diff line number Diff line
@@ -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 "


#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)
		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)
		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)
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)
	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)
+29 −19
Original line number Original line Diff line number Diff line
@@ -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,36 +8757,40 @@ 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 */
		present = snd_hda_codec_read(codec, 0x14, 0,
					     AC_VERB_GET_PIN_SENSE, 0);
					     AC_VERB_GET_PIN_SENSE, 0);
		snd_hda_codec_read(codec, 0x1B, 0, AC_VERB_SET_PIN_SENSE, 0);
		/* need to execute and sync at first */
		present_dock_hp = snd_hda_codec_read(codec, 0x1b, 0,
		snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
		/* check docking HP jack */
		present |= snd_hda_codec_read(codec, 0x1b, 0,
					      AC_VERB_GET_PIN_SENSE, 0);
					      AC_VERB_GET_PIN_SENSE, 0);
		spec->jack_present = (present_int_hp & 0x80000000) != 0;
		if (present & AC_PINSENSE_PRESENCE)
		spec->jack_present |= (present_dock_hp & 0x80000000) != 0;
			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 */
static void alc262_fujitsu_unsol_event(struct hda_codec *codec,
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[] = {
		.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 },