summaryrefslogtreecommitdiff
path: root/sound/pci/hda
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda')
-rw-r--r--sound/pci/hda/Kconfig5
-rw-r--r--sound/pci/hda/Makefile1
-rw-r--r--sound/pci/hda/hda_codec.c92
-rw-r--r--sound/pci/hda/hda_codec.h3
-rw-r--r--sound/pci/hda/hda_intel.c128
-rw-r--r--sound/pci/hda/hda_intel_trace.h62
-rw-r--r--sound/pci/hda/hda_jack.c24
-rw-r--r--sound/pci/hda/hda_jack.h2
-rw-r--r--sound/pci/hda/hda_local.h8
-rw-r--r--sound/pci/hda/patch_cirrus.c5
-rw-r--r--sound/pci/hda/patch_conexant.c44
-rw-r--r--sound/pci/hda/patch_hdmi.c7
-rw-r--r--sound/pci/hda/patch_realtek.c7
-rw-r--r--sound/pci/hda/patch_sigmatel.c7
14 files changed, 352 insertions, 43 deletions
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 7105c3de1bca..6eeb8897624b 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -37,8 +37,8 @@ config SND_HDA_HWDEP
with codecs for debugging purposes.
config SND_HDA_RECONFIG
- bool "Allow dynamic codec reconfiguration (EXPERIMENTAL)"
- depends on SND_HDA_HWDEP && EXPERIMENTAL
+ bool "Allow dynamic codec reconfiguration"
+ depends on SND_HDA_HWDEP
help
Say Y here to enable the HD-audio codec re-configuration feature.
This adds the sysfs interfaces to allow user to clear the whole
@@ -72,7 +72,6 @@ config SND_HDA_INPUT_JACK
config SND_HDA_PATCH_LOADER
bool "Support initialization patch loading for HD-audio"
- depends on EXPERIMENTAL
select FW_LOADER
select SND_HDA_HWDEP
select SND_HDA_RECONFIG
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index bd4149f1aaf4..24a251497a1f 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -8,6 +8,7 @@ snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
# for trace-points
CFLAGS_hda_codec.o := -I$(src)
+CFLAGS_hda_intel.o := -I$(src)
snd-hda-codec-realtek-objs := patch_realtek.o
snd-hda-codec-cmedia-objs := patch_cmedia.o
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 70d4848b5cd0..2da787519513 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1135,6 +1135,19 @@ static void restore_shutup_pins(struct hda_codec *codec)
}
#endif
+static void hda_jackpoll_work(struct work_struct *work)
+{
+ struct hda_codec *codec =
+ container_of(work, struct hda_codec, jackpoll_work.work);
+ if (!codec->jackpoll_interval)
+ return;
+
+ snd_hda_jack_set_dirty_all(codec);
+ snd_hda_jack_poll_all(codec);
+ queue_delayed_work(codec->bus->workq, &codec->jackpoll_work,
+ codec->jackpoll_interval);
+}
+
static void init_hda_cache(struct hda_cache_rec *cache,
unsigned int record_size);
static void free_hda_cache(struct hda_cache_rec *cache);
@@ -1190,6 +1203,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
{
if (!codec)
return;
+ cancel_delayed_work_sync(&codec->jackpoll_work);
snd_hda_jack_tbl_clear(codec);
restore_init_pincfgs(codec);
#ifdef CONFIG_PM
@@ -1273,6 +1287,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
+ INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
#ifdef CONFIG_PM
spin_lock_init(&codec->power_lock);
@@ -2151,12 +2166,12 @@ EXPORT_SYMBOL_HDA(snd_hda_set_vmaster_tlv);
/* find a mixer control element with the given name */
static struct snd_kcontrol *
-_snd_hda_find_mixer_ctl(struct hda_codec *codec,
- const char *name, int idx)
+find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx)
{
struct snd_ctl_elem_id id;
memset(&id, 0, sizeof(id));
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ id.device = dev;
id.index = idx;
if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
return NULL;
@@ -2174,15 +2189,16 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec,
struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
const char *name)
{
- return _snd_hda_find_mixer_ctl(codec, name, 0);
+ return find_mixer_ctl(codec, name, 0, 0);
}
EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
-static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name)
+static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name,
+ int dev)
{
int idx;
for (idx = 0; idx < 16; idx++) { /* 16 ctlrs should be large enough */
- if (!_snd_hda_find_mixer_ctl(codec, name, idx))
+ if (!find_mixer_ctl(codec, name, dev, idx))
return idx;
}
return -EBUSY;
@@ -2349,7 +2365,7 @@ int snd_hda_codec_reset(struct hda_codec *codec)
return -EBUSY;
/* OK, let it free */
-
+ cancel_delayed_work_sync(&codec->jackpoll_work);
#ifdef CONFIG_PM
cancel_delayed_work_sync(&codec->power_work);
codec->power_on = 0;
@@ -3133,26 +3149,48 @@ static struct snd_kcontrol_new dig_mixes[] = {
};
/**
- * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls
+ * snd_hda_create_dig_out_ctls - create Output SPDIF-related controls
* @codec: the HDA codec
- * @nid: audio out widget NID
- *
- * Creates controls related with the SPDIF output.
- * Called from each patch supporting the SPDIF out.
+ * @associated_nid: NID that new ctls associated with
+ * @cvt_nid: converter NID
+ * @type: HDA_PCM_TYPE_*
+ * Creates controls related with the digital output.
+ * Called from each patch supporting the digital out.
*
* Returns 0 if successful, or a negative error code.
*/
-int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
- hda_nid_t associated_nid,
- hda_nid_t cvt_nid)
+int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
+ hda_nid_t associated_nid,
+ hda_nid_t cvt_nid,
+ int type)
{
int err;
struct snd_kcontrol *kctl;
struct snd_kcontrol_new *dig_mix;
- int idx;
+ int idx, dev = 0;
+ const int spdif_pcm_dev = 1;
struct hda_spdif_out *spdif;
- idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch");
+ if (codec->primary_dig_out_type == HDA_PCM_TYPE_HDMI &&
+ type == HDA_PCM_TYPE_SPDIF) {
+ dev = spdif_pcm_dev;
+ } else if (codec->primary_dig_out_type == HDA_PCM_TYPE_SPDIF &&
+ type == HDA_PCM_TYPE_HDMI) {
+ for (idx = 0; idx < codec->spdif_out.used; idx++) {
+ spdif = snd_array_elem(&codec->spdif_out, idx);
+ for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
+ kctl = find_mixer_ctl(codec, dig_mix->name, 0, idx);
+ if (!kctl)
+ break;
+ kctl->id.device = spdif_pcm_dev;
+ }
+ }
+ codec->primary_dig_out_type = HDA_PCM_TYPE_HDMI;
+ }
+ if (!codec->primary_dig_out_type)
+ codec->primary_dig_out_type = type;
+
+ idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", dev);
if (idx < 0) {
printk(KERN_ERR "hda_codec: too many IEC958 outputs\n");
return -EBUSY;
@@ -3162,6 +3200,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
kctl = snd_ctl_new1(dig_mix, codec);
if (!kctl)
return -ENOMEM;
+ kctl->id.device = dev;
kctl->id.index = idx;
kctl->private_value = codec->spdif_out.used - 1;
err = snd_hda_ctl_add(codec, associated_nid, kctl);
@@ -3174,7 +3213,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
spdif->status = convert_to_spdif_status(spdif->ctls);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
+EXPORT_SYMBOL_HDA(snd_hda_create_dig_out_ctls);
/* get the hda_spdif_out entry from the given NID
* call within spdif_mutex lock
@@ -3349,7 +3388,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
struct snd_kcontrol_new *dig_mix;
int idx;
- idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch");
+ idx = find_empty_mixer_ctl_idx(codec, "IEC958 Capture Switch", 0);
if (idx < 0) {
printk(KERN_ERR "hda_codec: too many IEC958 inputs\n");
return -EBUSY;
@@ -3646,7 +3685,6 @@ static void hda_call_codec_resume(struct hda_codec *codec)
restore_pincfgs(codec); /* restore all current pin configs */
restore_shutup_pins(codec);
hda_exec_init_verbs(codec);
- snd_hda_jack_set_dirty_all(codec);
if (codec->patch_ops.resume)
codec->patch_ops.resume(codec);
else {
@@ -3655,7 +3693,13 @@ static void hda_call_codec_resume(struct hda_codec *codec)
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
}
- snd_hda_jack_report_sync(codec);
+
+ if (codec->jackpoll_interval)
+ hda_jackpoll_work(&codec->jackpoll_work.work);
+ else {
+ snd_hda_jack_set_dirty_all(codec);
+ snd_hda_jack_report_sync(codec);
+ }
snd_hda_power_down(codec); /* flag down before returning */
}
#endif /* CONFIG_PM */
@@ -3737,7 +3781,10 @@ int snd_hda_codec_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
- snd_hda_jack_report_sync(codec); /* call at the last init point */
+ if (codec->jackpoll_interval)
+ hda_jackpoll_work(&codec->jackpoll_work.work);
+ else
+ snd_hda_jack_report_sync(codec); /* call at the last init point */
return 0;
}
@@ -4449,7 +4496,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
addr = codec->addr;
else if (!idx && !knew->index) {
idx = find_empty_mixer_ctl_idx(codec,
- knew->name);
+ knew->name, 0);
if (idx <= 0)
return err;
} else
@@ -5128,6 +5175,7 @@ int snd_hda_suspend(struct hda_bus *bus)
struct hda_codec *codec;
list_for_each_entry(codec, &bus->codec_list, list) {
+ cancel_delayed_work_sync(&codec->jackpoll_work);
if (hda_codec_is_power_on(codec))
hda_call_codec_suspend(codec, false);
}
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 507fe8a917b6..62d4229c7b95 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -836,6 +836,7 @@ struct hda_codec {
struct mutex hash_mutex;
struct snd_array spdif_out;
unsigned int spdif_in_enable; /* SPDIF input enable? */
+ int primary_dig_out_type; /* primary digital out PCM type */
const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
struct snd_array init_pins; /* initial (BIOS) pin configurations */
struct snd_array driver_pins; /* pin configs set by codec parser */
@@ -884,6 +885,8 @@ struct hda_codec {
/* jack detection */
struct snd_array jacktbl;
+ unsigned long jackpoll_interval; /* In jiffies. Zero means no poll, rely on unsol events */
+ struct delayed_work jackpoll_work;
#ifdef CONFIG_SND_HDA_INPUT_JACK
/* jack detection */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 72b085ae7d46..bffaaeb0b4ba 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -47,6 +47,9 @@
#include <linux/reboot.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
+#include <linux/clocksource.h>
+#include <linux/time.h>
+
#ifdef CONFIG_X86
/* for snoop control */
#include <asm/pgtable.h>
@@ -68,6 +71,7 @@ static int position_fix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int probe_only[SNDRV_CARDS];
+static int jackpoll_ms[SNDRV_CARDS];
static bool single_cmd;
static int enable_msi = -1;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
@@ -95,6 +99,8 @@ module_param_array(probe_mask, int, NULL, 0444);
MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
module_param_array(probe_only, int, NULL, 0444);
MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization.");
+module_param_array(jackpoll_ms, int, NULL, 0444);
+MODULE_PARM_DESC(jackpoll_ms, "Ms between polling for jack events (default = 0, using unsol events only)");
module_param(single_cmd, bool, 0444);
MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
"(for debugging only).");
@@ -416,6 +422,9 @@ struct azx_dev {
unsigned int insufficient :1;
unsigned int wc_marked:1;
unsigned int no_period_wakeup:1;
+
+ struct timecounter azx_tc;
+ struct cyclecounter azx_cc;
};
/* CORB/RIRB */
@@ -518,6 +527,9 @@ struct azx {
struct list_head list;
};
+#define CREATE_TRACE_POINTS
+#include "hda_intel_trace.h"
+
/* driver types */
enum {
AZX_DRIVER_ICH,
@@ -829,8 +841,9 @@ static void azx_update_rirb(struct azx *chip)
smp_wmb();
chip->rirb.cmds[addr]--;
} else
- snd_printk(KERN_ERR SFX "spurious response %#x:%#x, "
+ snd_printk(KERN_ERR SFX "%s: spurious response %#x:%#x, "
"last cmd=%#08x\n",
+ pci_name(chip->pci),
res, res_ex,
chip->last_cmd[addr]);
}
@@ -1582,6 +1595,22 @@ static void azx_bus_reset(struct hda_bus *bus)
bus->in_reset = 0;
}
+static int get_jackpoll_interval(struct azx *chip)
+{
+ int i = jackpoll_ms[chip->dev_index];
+ unsigned int j;
+ if (i == 0)
+ return 0;
+ if (i < 50 || i > 60000)
+ j = 0;
+ else
+ j = msecs_to_jiffies(i);
+ if (j == 0)
+ snd_printk(KERN_WARNING SFX
+ "jackpoll_ms value out of range: %d\n", i);
+ return j;
+}
+
/*
* Codec initialization
*/
@@ -1666,6 +1695,7 @@ static int DELAYED_INIT_MARK azx_codec_create(struct azx *chip, const char *mode
err = snd_hda_codec_new(chip->bus, c, &codec);
if (err < 0)
continue;
+ codec->jackpoll_interval = get_jackpoll_interval(chip);
codec->beep_mode = chip->beep_mode;
codecs++;
}
@@ -1728,6 +1758,64 @@ static inline void azx_release_device(struct azx_dev *azx_dev)
azx_dev->opened = 0;
}
+static cycle_t azx_cc_read(const struct cyclecounter *cc)
+{
+ struct azx_dev *azx_dev = container_of(cc, struct azx_dev, azx_cc);
+ struct snd_pcm_substream *substream = azx_dev->substream;
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+
+ return azx_readl(chip, WALLCLK);
+}
+
+static void azx_timecounter_init(struct snd_pcm_substream *substream,
+ bool force, cycle_t last)
+{
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct timecounter *tc = &azx_dev->azx_tc;
+ struct cyclecounter *cc = &azx_dev->azx_cc;
+ u64 nsec;
+
+ cc->read = azx_cc_read;
+ cc->mask = CLOCKSOURCE_MASK(32);
+
+ /*
+ * Converting from 24 MHz to ns means applying a 125/3 factor.
+ * To avoid any saturation issues in intermediate operations,
+ * the 125 factor is applied first. The division is applied
+ * last after reading the timecounter value.
+ * Applying the 1/3 factor as part of the multiplication
+ * requires at least 20 bits for a decent precision, however
+ * overflows occur after about 4 hours or less, not a option.
+ */
+
+ cc->mult = 125; /* saturation after 195 years */
+ cc->shift = 0;
+
+ nsec = 0; /* audio time is elapsed time since trigger */
+ timecounter_init(tc, cc, nsec);
+ if (force)
+ /*
+ * force timecounter to use predefined value,
+ * used for synchronized starts
+ */
+ tc->cycle_last = last;
+}
+
+static int azx_get_wallclock_tstamp(struct snd_pcm_substream *substream,
+ struct timespec *ts)
+{
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ u64 nsec;
+
+ nsec = timecounter_read(&azx_dev->azx_tc);
+ nsec = div_u64(nsec, 3); /* can be optimized */
+
+ *ts = ns_to_timespec(nsec);
+
+ return 0;
+}
+
static struct snd_pcm_hardware azx_pcm_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
@@ -1737,6 +1825,7 @@ static struct snd_pcm_hardware azx_pcm_hw = {
/* SNDRV_PCM_INFO_RESUME |*/
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_SYNC_START |
+ SNDRV_PCM_INFO_HAS_WALL_CLOCK |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_48000,
@@ -1776,6 +1865,12 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
runtime->hw.rates = hinfo->rates;
snd_pcm_limit_hw_rates(runtime);
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+ /* avoid wrap-around with wall-clock */
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
+ 20,
+ 178000000);
+
if (chip->align_buffer_size)
/* constrain buffer sizes to be multiple of 128
bytes. This is more efficient in terms of memory
@@ -1815,6 +1910,12 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
mutex_unlock(&chip->open_mutex);
return -EINVAL;
}
+
+ /* disable WALLCLOCK timestamps for capture streams
+ until we figure out how to handle digital inputs */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK;
+
spin_lock_irqsave(&chip->reg_lock, flags);
azx_dev->substream = substream;
azx_dev->running = 0;
@@ -1961,6 +2062,9 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
int rstart = 0, start, nsync = 0, sbits = 0;
int nwait, timeout;
+ azx_dev = get_azx_dev(substream);
+ trace_azx_pcm_trigger(chip, azx_dev, cmd);
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
rstart = 1;
@@ -2051,6 +2155,22 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
azx_readl(chip, OLD_SSYNC) & ~sbits);
else
azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits);
+ if (start) {
+ azx_timecounter_init(substream, 0, 0);
+ if (nsync > 1) {
+ cycle_t cycle_last;
+
+ /* same start cycle for master and group */
+ azx_dev = get_azx_dev(substream);
+ cycle_last = azx_dev->azx_tc.cycle_last;
+
+ snd_pcm_group_for_each_entry(s, substream) {
+ if (s->pcm->card != substream->pcm->card)
+ continue;
+ azx_timecounter_init(s, 1, cycle_last);
+ }
+ }
+ }
spin_unlock(&chip->reg_lock);
return 0;
}
@@ -2117,6 +2237,7 @@ static unsigned int azx_get_position(struct azx *chip,
{
unsigned int pos;
int stream = azx_dev->substream->stream;
+ int delay = 0;
switch (chip->position_fix[stream]) {
case POS_FIX_LPIB:
@@ -2150,7 +2271,6 @@ static unsigned int azx_get_position(struct azx *chip,
chip->position_fix[stream] == POS_FIX_POSBUF &&
(chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) {
unsigned int lpib_pos = azx_sd_readl(azx_dev, SD_LPIB);
- int delay;
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
delay = pos - lpib_pos;
else
@@ -2168,6 +2288,7 @@ static unsigned int azx_get_position(struct azx *chip,
azx_dev->substream->runtime->delay =
bytes_to_frames(azx_dev->substream->runtime, delay);
}
+ trace_azx_get_position(chip, azx_dev, pos, delay);
return pos;
}
@@ -2193,13 +2314,11 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
{
u32 wallclk;
unsigned int pos;
- int stream;
wallclk = azx_readl(chip, WALLCLK) - azx_dev->start_wallclk;
if (wallclk < (azx_dev->period_wallclk * 2) / 3)
return -1; /* bogus (too early) interrupt */
- stream = azx_dev->substream->stream;
pos = azx_get_position(chip, azx_dev, true);
if (WARN_ONCE(!azx_dev->period_bytes,
@@ -2290,6 +2409,7 @@ static struct snd_pcm_ops azx_pcm_ops = {
.prepare = azx_pcm_prepare,
.trigger = azx_pcm_trigger,
.pointer = azx_pcm_pointer,
+ .wall_clock = azx_get_wallclock_tstamp,
.mmap = azx_pcm_mmap,
.page = snd_pcm_sgbuf_ops_page,
};
diff --git a/sound/pci/hda/hda_intel_trace.h b/sound/pci/hda/hda_intel_trace.h
new file mode 100644
index 000000000000..7b5e4c2cf9d5
--- /dev/null
+++ b/sound/pci/hda/hda_intel_trace.h
@@ -0,0 +1,62 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hda_intel
+#define TRACE_INCLUDE_FILE hda_intel_trace
+
+#if !defined(_TRACE_HDA_INTEL_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_HDA_INTEL_H
+
+#include <linux/tracepoint.h>
+
+struct azx;
+struct azx_dev;
+
+TRACE_EVENT(azx_pcm_trigger,
+
+ TP_PROTO(struct azx *chip, struct azx_dev *dev, int cmd),
+
+ TP_ARGS(chip, dev, cmd),
+
+ TP_STRUCT__entry(
+ __field( int, card )
+ __field( int, idx )
+ __field( int, cmd )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (chip)->card->number;
+ __entry->idx = (dev)->index;
+ __entry->cmd = cmd;
+ ),
+
+ TP_printk("[%d:%d] cmd=%d", __entry->card, __entry->idx, __entry->cmd)
+);
+
+TRACE_EVENT(azx_get_position,
+
+ TP_PROTO(struct azx *chip, struct azx_dev *dev, unsigned int pos, unsigned int delay),
+
+ TP_ARGS(chip, dev, pos, delay),
+
+ TP_STRUCT__entry(
+ __field( int, card )
+ __field( int, idx )
+ __field( unsigned int, pos )
+ __field( unsigned int, delay )
+ ),
+
+ TP_fast_assign(
+ __entry->card = (chip)->card->number;
+ __entry->idx = (dev)->index;
+ __entry->pos = pos;
+ __entry->delay = delay;
+ ),
+
+ TP_printk("[%d:%d] pos=%u, delay=%u", __entry->card, __entry->idx, __entry->pos, __entry->delay)
+);
+
+#endif /* _TRACE_HDA_INTEL_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#include <trace/define_trace.h>
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c
index 5c690cb873d4..5bdbadaa20fe 100644
--- a/sound/pci/hda/hda_jack.c
+++ b/sound/pci/hda/hda_jack.c
@@ -206,6 +206,8 @@ int snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid,
jack->action = action;
if (cb)
jack->callback = cb;
+ if (codec->jackpoll_interval > 0)
+ return 0; /* No unsol if we're polling instead */
return snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | jack->tag);
@@ -439,3 +441,25 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
}
EXPORT_SYMBOL_HDA(snd_hda_jack_unsol_event);
+void snd_hda_jack_poll_all(struct hda_codec *codec)
+{
+ struct hda_jack_tbl *jack = codec->jacktbl.list;
+ int i, changes = 0;
+
+ for (i = 0; i < codec->jacktbl.used; i++, jack++) {
+ unsigned int old_sense;
+ if (!jack->nid || !jack->jack_dirty || jack->phantom_jack)
+ continue;
+ old_sense = get_jack_plug_state(jack->pin_sense);
+ jack_detect_update(codec, jack);
+ if (old_sense == get_jack_plug_state(jack->pin_sense))
+ continue;
+ changes = 1;
+ if (jack->callback)
+ jack->callback(codec, jack);
+ }
+ if (changes)
+ snd_hda_jack_report_sync(codec);
+}
+EXPORT_SYMBOL_HDA(snd_hda_jack_poll_all);
+
diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h
index af8dd4724da5..4487785adf34 100644
--- a/sound/pci/hda/hda_jack.h
+++ b/sound/pci/hda/hda_jack.h
@@ -84,4 +84,6 @@ void snd_hda_jack_report_sync(struct hda_codec *codec);
void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res);
+void snd_hda_jack_poll_all(struct hda_codec *codec);
+
#endif /* __SOUND_HDA_JACK_H */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 09dbdc37f781..8c43198b7f56 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -240,9 +240,11 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
/*
* SPDIF I/O
*/
-int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
- hda_nid_t associated_nid,
- hda_nid_t cvt_nid);
+int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
+ hda_nid_t associated_nid,
+ hda_nid_t cvt_nid, int type);
+#define snd_hda_create_spdif_out_ctls(codec, anid, cnid) \
+ snd_hda_create_dig_out_ctls(codec, anid, cnid, HDA_PCM_TYPE_SPDIF)
int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
/*
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 61a71131711c..a7f8790ae885 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -873,8 +873,9 @@ static int build_digital_output(struct hda_codec *codec)
if (!spec->multiout.dig_out_nid)
return 0;
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid,
- spec->multiout.dig_out_nid);
+ err = snd_hda_create_dig_out_ctls(codec, spec->multiout.dig_out_nid,
+ spec->multiout.dig_out_nid,
+ spec->pcm_rec[1].pcm_type);
if (err < 0)
return err;
err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 03b1dc317ff0..252d4197f221 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -4405,7 +4405,10 @@ static const struct hda_codec_ops cx_auto_patch_ops = {
enum {
CXT_PINCFG_LENOVO_X200,
CXT_PINCFG_LENOVO_TP410,
+ CXT_PINCFG_LEMOTE_A1004,
+ CXT_PINCFG_LEMOTE_A1205,
CXT_FIXUP_STEREO_DMIC,
+ CXT_FIXUP_INC_MIC_BOOST,
};
static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
@@ -4415,6 +4418,19 @@ static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
spec->fixup_stereo_dmic = 1;
}
+static void cxt5066_increase_mic_boost(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+
+ snd_hda_override_amp_caps(codec, 0x17, HDA_OUTPUT,
+ (0x3 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x4 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
+}
+
/* ThinkPad X200 & co with cxt5051 */
static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
{ 0x16, 0x042140ff }, /* HP (seq# overridden) */
@@ -4432,6 +4448,18 @@ static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = {
{}
};
+/* Lemote A1004/A1205 with cxt5066 */
+static const struct hda_pintbl cxt_pincfg_lemote[] = {
+ { 0x1a, 0x90a10020 }, /* Internal mic */
+ { 0x1b, 0x03a11020 }, /* External mic */
+ { 0x1d, 0x400101f0 }, /* Not used */
+ { 0x1e, 0x40a701f0 }, /* Not used */
+ { 0x20, 0x404501f0 }, /* Not used */
+ { 0x22, 0x404401f0 }, /* Not used */
+ { 0x23, 0x40a701f0 }, /* Not used */
+ {}
+};
+
static const struct hda_fixup cxt_fixups[] = {
[CXT_PINCFG_LENOVO_X200] = {
.type = HDA_FIXUP_PINS,
@@ -4441,10 +4469,24 @@ static const struct hda_fixup cxt_fixups[] = {
.type = HDA_FIXUP_PINS,
.v.pins = cxt_pincfg_lenovo_tp410,
},
+ [CXT_PINCFG_LEMOTE_A1004] = {
+ .type = HDA_FIXUP_PINS,
+ .chained = true,
+ .chain_id = CXT_FIXUP_INC_MIC_BOOST,
+ .v.pins = cxt_pincfg_lemote,
+ },
+ [CXT_PINCFG_LEMOTE_A1205] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = cxt_pincfg_lemote,
+ },
[CXT_FIXUP_STEREO_DMIC] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_stereo_dmic,
},
+ [CXT_FIXUP_INC_MIC_BOOST] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt5066_increase_mic_boost,
+ },
};
static const struct snd_pci_quirk cxt5051_fixups[] = {
@@ -4461,6 +4503,8 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
+ SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004),
+ SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205),
{}
};
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 71555cc54db1..39ca1005995d 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1589,9 +1589,10 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
- err = snd_hda_create_spdif_out_ctls(codec,
- per_pin->pin_nid,
- per_pin->mux_nids[0]);
+ err = snd_hda_create_dig_out_ctls(codec,
+ per_pin->pin_nid,
+ per_pin->mux_nids[0],
+ HDA_PCM_TYPE_HDMI);
if (err < 0)
return err;
snd_hda_spdif_ctls_unassign(codec, pin_idx);
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index f7397ad02a0d..96c08792d210 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -1836,9 +1836,10 @@ static int __alc_build_controls(struct hda_codec *codec)
return err;
}
if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->multiout.dig_out_nid,
- spec->multiout.dig_out_nid);
+ err = snd_hda_create_dig_out_ctls(codec,
+ spec->multiout.dig_out_nid,
+ spec->multiout.dig_out_nid,
+ spec->pcm_rec[1].pcm_type);
if (err < 0)
return err;
if (!spec->no_analog) {
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 9ba8af056170..962a948f4f10 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -1136,9 +1136,10 @@ static int stac92xx_build_controls(struct hda_codec *codec)
}
if (spec->multiout.dig_out_nid) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->multiout.dig_out_nid,
- spec->multiout.dig_out_nid);
+ err = snd_hda_create_dig_out_ctls(codec,
+ spec->multiout.dig_out_nid,
+ spec->multiout.dig_out_nid,
+ spec->autocfg.dig_out_type[0]);
if (err < 0)
return err;
err = snd_hda_create_spdif_share_sw(codec,