diff --git a/[refs] b/[refs]
index abda4e452097..4a37503ef2eb 100644
--- a/[refs]
+++ b/[refs]
@@ -1,2 +1,2 @@
---
-refs/heads/master: f3c90242a3b9e32f510229c4c1313df6ca7f1667
+refs/heads/master: 43cd8bf1c8d8f6e897ed0f2c4bd50a4266b5c36e
diff --git a/trunk/Documentation/DocBook/writing-an-alsa-driver.tmpl b/trunk/Documentation/DocBook/writing-an-alsa-driver.tmpl
index bd6fee22c4dd..fb32aead5a0b 100644
--- a/trunk/Documentation/DocBook/writing-an-alsa-driver.tmpl
+++ b/trunk/Documentation/DocBook/writing-an-alsa-driver.tmpl
@@ -871,8 +871,9 @@
This function itself doesn't allocate the data space. The data
must be allocated manually beforehand, and its pointer is passed
- as the argument. This pointer (chip in the
- above example) is used as the identifier for the instance.
+ as the argument. This pointer is used as the
+ (chip identifier in the above example)
+ for the instance.
@@ -2303,7 +2304,7 @@ struct _snd_pcm_runtime {
SNDRV_PCM_INFO_XXX. Here, at least, you
have to specify whether the mmap is supported and which
interleaved format is supported.
- When the hardware supports mmap, add the
+ When the is supported, add the
SNDRV_PCM_INFO_MMAP flag here. When the
hardware supports the interleaved or the non-interleaved
formats, SNDRV_PCM_INFO_INTERLEAVED or
@@ -2897,7 +2898,7 @@ struct _snd_pcm_runtime {
When the pcm supports the pause operation (given in the info
- field of the hardware table), the PAUSE_PUSH
+ field of the hardware table), the PAUSE_PUSE
and PAUSE_RELEASE commands must be
handled here, too. The former is the command to pause the pcm,
and the latter to restart the pcm again.
@@ -3084,7 +3085,7 @@ struct _snd_pcm_runtime {
High frequency timer interrupts
- This happens when the hardware doesn't generate interrupts
+ This happense when the hardware doesn't generate interrupts
at the period boundary but issues timer interrupts at a fixed
timer rate (e.g. es1968 or ymfpci drivers).
In this case, you need to check the current hardware
@@ -3250,19 +3251,18 @@ struct _snd_pcm_runtime {
Example of Hardware Constraints for Channels
bits[0] == SNDRV_PCM_FMTBIT_S16_LE) {
- ch.min = ch.max = 1;
- ch.integer = 1;
- return snd_interval_refine(c, &ch);
+ snd_mask_any(&fmt); /* Init the struct */
+ if (c->min < 2) {
+ fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_LE;
+ return snd_mask_refine(f, &fmt);
}
return 0;
}
@@ -3278,35 +3278,35 @@ struct _snd_pcm_runtime {
runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- hw_rule_channels_by_format, NULL,
- SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ hw_rule_channels_by_format, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+ -1);
]]>
- The rule function is called when an application sets the PCM
- format, and it refines the number of channels accordingly.
- But an application may set the number of channels before
- setting the format. Thus you also need to define the inverse rule:
+ The rule function is called when an application sets the number of
+ channels. But an application can set the format before the number of
+ channels. Thus you also need to define the inverse rule:
- Example of Hardware Constraints for Formats
+ Example of Hardware Constraints for Channels
min < 2) {
- fmt.bits[0] &= SNDRV_PCM_FMTBIT_S16_LE;
- return snd_mask_refine(f, &fmt);
+ snd_interval_any(&ch);
+ if (f->bits[0] == SNDRV_PCM_FMTBIT_S16_LE) {
+ ch.min = ch.max = 1;
+ ch.integer = 1;
+ return snd_interval_refine(c, &ch);
}
return 0;
}
@@ -3321,8 +3321,8 @@ struct _snd_pcm_runtime {
runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
- hw_rule_format_by_channels, NULL,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ hw_rule_format_by_channels, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ -1);
]]>
diff --git a/trunk/Documentation/sound/alsa/ALSA-Configuration.txt b/trunk/Documentation/sound/alsa/ALSA-Configuration.txt
index ce6581c8ca26..b9cfd339a6fa 100644
--- a/trunk/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/trunk/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -890,9 +890,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
enable_msi - Enable Message Signaled Interrupt (MSI) (default = off)
power_save - Automatic power-saving timeout (in second, 0 =
disable)
- power_save_controller - Support runtime D3 of HD-audio controller
- (-1 = on for supported chip (default), false = off,
- true = force to on even for unsupported hardware)
+ power_save_controller - Reset HD-audio controller in power-saving mode
+ (default = on)
align_buffer_size - Force rounding of buffer/period sizes to multiples
of 128 bytes. This is more efficient in terms of memory
access but isn't required by the HDA spec and prevents
diff --git a/trunk/Documentation/sound/alsa/HD-Audio-Models.txt b/trunk/Documentation/sound/alsa/HD-Audio-Models.txt
index bb8b0dc532b8..16dfe57f1731 100644
--- a/trunk/Documentation/sound/alsa/HD-Audio-Models.txt
+++ b/trunk/Documentation/sound/alsa/HD-Audio-Models.txt
@@ -53,7 +53,7 @@ ALC882/883/885/888/889
acer-aspire-8930g Acer Aspire 8330G/6935G
acer-aspire Acer Aspire others
inv-dmic Inverted internal mic workaround
- no-primary-hp VAIO Z/VGC-LN51JGB workaround (for fixed speaker DAC)
+ no-primary-hp VAIO Z workaround (for fixed speaker DAC)
ALC861/660
==========
diff --git a/trunk/Documentation/sound/alsa/HD-Audio.txt b/trunk/Documentation/sound/alsa/HD-Audio.txt
index d4faa63ff352..7813c06a5c71 100644
--- a/trunk/Documentation/sound/alsa/HD-Audio.txt
+++ b/trunk/Documentation/sound/alsa/HD-Audio.txt
@@ -176,14 +176,14 @@ support the automatic probing (yet as of 2.6.28). And, BIOS is often,
yes, pretty often broken. It sets up wrong values and screws up the
driver.
-The preset model (or recently called as "fix-up") is provided
-basically to overcome such a situation. When the matching preset
-model is found in the white-list, the driver assumes the static
-configuration of that preset with the correct pin setup, etc.
-Thus, if you have a newer machine with a slightly different PCI SSID
-(or codec SSID) from the existing one, you may have a good chance to
-re-use the same model. You can pass the `model` option to specify the
-preset model instead of PCI (and codec-) SSID look-up.
+The preset model is provided basically to overcome such a situation.
+When the matching preset model is found in the white-list, the driver
+assumes the static configuration of that preset and builds the mixer
+elements and PCM streams based on the static information. Thus, if
+you have a newer machine with a slightly different PCI SSID from the
+existing one, you may have a good chance to re-use the same model.
+You can pass the `model` option to specify the preset model instead of
+PCI SSID look-up.
What `model` option values are available depends on the codec chip.
Check your codec chip from the codec proc file (see "Codec Proc-File"
@@ -199,12 +199,17 @@ non-working HD-audio hardware is to check HD-audio codec and several
different `model` option values. If you have any luck, some of them
might suit with your device well.
-There are a few special model option values:
-- when 'nofixup' is passed, the device-specific fixups in the codec
- parser are skipped.
-- when `generic` is passed, the codec-specific parser is skipped and
- only the generic parser is used.
+Some codecs such as ALC880 have a special model option `model=test`.
+This configures the driver to provide as many mixer controls as
+possible for every single pin feature except for the unsolicited
+events (and maybe some other specials). Adjust each mixer element and
+try the I/O in the way of trial-and-error until figuring out the whole
+I/O pin mappings.
+Note that `model=generic` has a special meaning. It means to use the
+generic parser regardless of the codec. Usually the codec-specific
+parser is much better than the generic parser (as now). Thus this
+option is more about the debugging purpose.
Speaker and Headphone Output
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -382,8 +387,9 @@ init_verbs::
(separated with a space).
hints::
Shows / stores hint strings for codec parsers for any use.
- Its format is `key = value`. For example, passing `jack_detect = no`
- will disable the jack detection of the machine completely.
+ Its format is `key = value`. For example, passing `hp_detect = yes`
+ to IDT/STAC codec parser will result in the disablement of the
+ headphone detection.
init_pin_configs::
Shows the initial pin default config values set by BIOS.
driver_pin_configs::
@@ -415,61 +421,6 @@ re-configure based on that state, run like below:
------------------------------------------------------------------------
-Hint Strings
-~~~~~~~~~~~~
-The codec parser have several switches and adjustment knobs for
-matching better with the actual codec or device behavior. Many of
-them can be adjusted dynamically via "hints" strings as mentioned in
-the section above. For example, by passing `jack_detect = no` string
-via sysfs or a patch file, you can disable the jack detection, thus
-the codec parser will skip the features like auto-mute or mic
-auto-switch. As a boolean value, either `yes`, `no`, `true`, `false`,
-`1` or `0` can be passed.
-
-The generic parser supports the following hints:
-
-- jack_detect (bool): specify whether the jack detection is available
- at all on this machine; default true
-- inv_jack_detect (bool): indicates that the jack detection logic is
- inverted
-- trigger_sense (bool): indicates that the jack detection needs the
- explicit call of AC_VERB_SET_PIN_SENSE verb
-- inv_eapd (bool): indicates that the EAPD is implemented in the
- inverted logic
-- pcm_format_first (bool): sets the PCM format before the stream tag
- and channel ID
-- sticky_stream (bool): keep the PCM format, stream tag and ID as long
- as possible; default true
-- spdif_status_reset (bool): reset the SPDIF status bits at each time
- the SPDIF stream is set up
-- pin_amp_workaround (bool): the output pin may have multiple amp
- values
-- single_adc_amp (bool): ADCs can have only single input amps
-- auto_mute (bool): enable/disable the headphone auto-mute feature;
- default true
-- auto_mic (bool): enable/disable the mic auto-switch feature; default
- true
-- line_in_auto_switch (bool): enable/disable the line-in auto-switch
- feature; default false
-- need_dac_fix (bool): limits the DACs depending on the channel count
-- primary_hp (bool): probe headphone jacks as the primary outputs;
- default true
-- multi_cap_vol (bool): provide multiple capture volumes
-- inv_dmic_split (bool): provide split internal mic volume/switch for
- phase-inverted digital mics
-- indep_hp (bool): provide the independent headphone PCM stream and
- the corresponding mixer control, if available
-- add_stereo_mix_input (bool): add the stereo mix (analog-loopback
- mix) to the input mux if available
-- add_out_jack_modes (bool): add "xxx Jack Mode" enum controls to each
- output jack for allowing to change the headphone amp capability
-- add_in_jack_modes (bool): add "xxx Jack Mode" enum controls to each
- input jack for allowing to change the mic bias vref
-- power_down_unused (bool): power down the unused widgets
-- mixer_nid (int): specifies the widget NID of the analog-loopback
- mixer
-
-
Early Patching
~~~~~~~~~~~~~~
When CONFIG_SND_HDA_PATCH_LOADER=y is set, you can pass a "patch" as a
@@ -494,7 +445,7 @@ A patch file is a plain text file which looks like below:
0x20 0x400 0xff
[hint]
- jack_detect = no
+ hp_detect = yes
------------------------------------------------------------------------
The file needs to have a line `[codec]`. The next line should contain
@@ -580,13 +531,6 @@ cable is unplugged. Thus, if you hear noises, suspect first the
power-saving. See /sys/module/snd_hda_intel/parameters/power_save to
check the current value. If it's non-zero, the feature is turned on.
-The recent kernel supports the runtime PM for the HD-audio controller
-chip, too. It means that the HD-audio controller is also powered up /
-down dynamically. The feature is enabled only for certain controller
-chips like Intel LynxPoint. You can enable/disable this feature
-forcibly by setting `power_save_controller` option, which is also
-available at /sys/module/snd_hda_intel/parameters directory.
-
Tracepoints
~~~~~~~~~~~
@@ -643,9 +587,8 @@ The latest development codes for HD-audio are found on sound git tree:
- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
The master branch or for-next branches can be used as the main
-development branches in general while the development for the current
-and next kernels are found in for-linus and for-next branches,
-respectively.
+development branches in general while the HD-audio specific patches
+are committed in topic/hda branch.
If you are using the latest Linus tree, it'd be better to pull the
above GIT tree onto it. If you are using the older kernels, an easy
@@ -756,11 +699,7 @@ won't be always updated. For example, the volume values are usually
cached in the driver, and thus changing the widget amp value directly
via hda-verb won't change the mixer value.
-The hda-verb program is included now in alsa-tools:
-
-- git://git.alsa-project.org/alsa-tools.git
-
-Also, the old stand-alone package is found in the ftp directory:
+The hda-verb program is found in the ftp directory:
- ftp://ftp.suse.com/pub/people/tiwai/misc/
@@ -838,18 +777,3 @@ A git repository is available:
See README file in the tarball for more details about hda-emu
program.
-
-
-hda-jack-retask
-~~~~~~~~~~~~~~~
-hda-jack-retask is a user-friendly GUI program to manipulate the
-HD-audio pin control for jack retasking. If you have a problem about
-the jack assignment, try this program and check whether you can get
-useful results. Once when you figure out the proper pin assignment,
-it can be fixed either in the driver code statically or via passing a
-firmware patch file (see "Early Patching" section).
-
-The program is included in alsa-tools now:
-
-- git://git.alsa-project.org/alsa-tools.git
-
diff --git a/trunk/Documentation/sound/alsa/compress_offload.txt b/trunk/Documentation/sound/alsa/compress_offload.txt
index 0bcc55155911..90e9b3a11abc 100644
--- a/trunk/Documentation/sound/alsa/compress_offload.txt
+++ b/trunk/Documentation/sound/alsa/compress_offload.txt
@@ -145,52 +145,6 @@ Modifications include:
- Addition of encoding options when required (derived from OpenMAX IL)
- Addition of rateControlSupported (missing in OpenMAX AL)
-Gapless Playback
-================
-When playing thru an album, the decoders have the ability to skip the encoder
-delay and padding and directly move from one track content to another. The end
-user can perceive this as gapless playback as we dont have silence while
-switching from one track to another
-
-Also, there might be low-intensity noises due to encoding. Perfect gapless is
-difficult to reach with all types of compressed data, but works fine with most
-music content. The decoder needs to know the encoder delay and encoder padding.
-So we need to pass this to DSP. This metadata is extracted from ID3/MP4 headers
-and are not present by default in the bitstream, hence the need for a new
-interface to pass this information to the DSP. Also DSP and userspace needs to
-switch from one track to another and start using data for second track.
-
-The main additions are:
-
-- set_metadata
-This routine sets the encoder delay and encoder padding. This can be used by
-decoder to strip the silence. This needs to be set before the data in the track
-is written.
-
-- set_next_track
-This routine tells DSP that metadata and write operation sent after this would
-correspond to subsequent track
-
-- partial drain
-This is called when end of file is reached. The userspace can inform DSP that
-EOF is reached and now DSP can start skipping padding delay. Also next write
-data would belong to next track
-
-Sequence flow for gapless would be:
-- Open
-- Get caps / codec caps
-- Set params
-- Set metadata of the first track
-- Fill data of the first track
-- Trigger start
-- User-space finished sending all,
-- Indicaite next track data by sending set_next_track
-- Set metadata of the next track
-- then call partial_drain to flush most of buffer in DSP
-- Fill data of the next track
-- DSP switches to second track
-(note: order for partial_drain and write for next track can be reversed as well)
-
Not supported:
- Support for VoIP/circuit-switched calls is not the target of this
diff --git a/trunk/include/sound/compress_driver.h b/trunk/include/sound/compress_driver.h
index ff6c74153fa1..f2912abacdf3 100644
--- a/trunk/include/sound/compress_driver.h
+++ b/trunk/include/sound/compress_driver.h
@@ -71,8 +71,6 @@ struct snd_compr_runtime {
* @runtime: pointer to runtime structure
* @device: device pointer
* @direction: stream direction, playback/recording
- * @metadata_set: metadata set flag, true when set
- * @next_track: has userspace signall next track transistion, true when set
* @private_data: pointer to DSP private data
*/
struct snd_compr_stream {
@@ -81,8 +79,6 @@ struct snd_compr_stream {
struct snd_compr_runtime *runtime;
struct snd_compr *device;
enum snd_compr_direction direction;
- bool metadata_set;
- bool next_track;
void *private_data;
};
@@ -114,10 +110,6 @@ struct snd_compr_ops {
struct snd_compr_params *params);
int (*get_params)(struct snd_compr_stream *stream,
struct snd_codec *params);
- int (*set_metadata)(struct snd_compr_stream *stream,
- struct snd_compr_metadata *metadata);
- int (*get_metadata)(struct snd_compr_stream *stream,
- struct snd_compr_metadata *metadata);
int (*trigger)(struct snd_compr_stream *stream, int cmd);
int (*pointer)(struct snd_compr_stream *stream,
struct snd_compr_tstamp *tstamp);
diff --git a/trunk/include/sound/core.h b/trunk/include/sound/core.h
index 7cede2d6aa86..93896ad1fcdd 100644
--- a/trunk/include/sound/core.h
+++ b/trunk/include/sound/core.h
@@ -394,11 +394,8 @@ void __snd_printk(unsigned int level, const char *file, int line,
#else /* !CONFIG_SND_DEBUG */
-__printf(1, 2)
-static inline void snd_printd(const char *format, ...) {}
-__printf(2, 3)
-static inline void _snd_printd(int level, const char *format, ...) {}
-
+#define snd_printd(fmt, args...) do { } while (0)
+#define _snd_printd(level, fmt, args...) do { } while (0)
#define snd_BUG() do { } while (0)
static inline int __snd_bug_on(int cond)
{
@@ -419,8 +416,7 @@ static inline int __snd_bug_on(int cond)
#define snd_printdd(format, args...) \
__snd_printk(2, __FILE__, __LINE__, format, ##args)
#else
-__printf(1, 2)
-static inline void snd_printdd(const char *format, ...) {}
+#define snd_printdd(format, args...) do { } while (0)
#endif
@@ -458,7 +454,6 @@ struct snd_pci_quirk {
#define SND_PCI_QUIRK_MASK(vend, mask, dev, xname, val) \
{_SND_PCI_QUIRK_ID_MASK(vend, mask, dev), \
.value = (val), .name = (xname)}
-#define snd_pci_quirk_name(q) ((q)->name)
#else
#define SND_PCI_QUIRK(vend,dev,xname,val) \
{_SND_PCI_QUIRK_ID(vend, dev), .value = (val)}
@@ -466,7 +461,6 @@ struct snd_pci_quirk {
{_SND_PCI_QUIRK_ID_MASK(vend, mask, dev), .value = (val)}
#define SND_PCI_QUIRK_VENDOR(vend, xname, val) \
{_SND_PCI_QUIRK_ID_MASK(vend, 0, 0), .value = (val)}
-#define snd_pci_quirk_name(q) ""
#endif
const struct snd_pci_quirk *
diff --git a/trunk/include/sound/memalloc.h b/trunk/include/sound/memalloc.h
index cf15b8213df7..844af65af626 100644
--- a/trunk/include/sound/memalloc.h
+++ b/trunk/include/sound/memalloc.h
@@ -37,7 +37,7 @@ struct snd_dma_device {
#ifndef snd_dma_pci_data
#define snd_dma_pci_data(pci) (&(pci)->dev)
#define snd_dma_isa_data() NULL
-#define snd_dma_continuous_data(x) ((struct device *)(__force unsigned long)(x))
+#define snd_dma_continuous_data(x) ((struct device *)(unsigned long)(x))
#endif
diff --git a/trunk/include/uapi/sound/compress_offload.h b/trunk/include/uapi/sound/compress_offload.h
index d630163b9a2e..05341a43fedf 100644
--- a/trunk/include/uapi/sound/compress_offload.h
+++ b/trunk/include/uapi/sound/compress_offload.h
@@ -30,7 +30,7 @@
#include
-#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 1)
+#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 0)
/**
* struct snd_compressed_buffer: compressed buffer
* @fragment_size: size of buffer fragment in bytes
@@ -121,27 +121,6 @@ struct snd_compr_codec_caps {
struct snd_codec_desc descriptor[MAX_NUM_CODEC_DESCRIPTORS];
};
-/**
- * @SNDRV_COMPRESS_ENCODER_PADDING: no of samples appended by the encoder at the
- * end of the track
- * @SNDRV_COMPRESS_ENCODER_DELAY: no of samples inserted by the encoder at the
- * beginning of the track
- */
-enum {
- SNDRV_COMPRESS_ENCODER_PADDING = 1,
- SNDRV_COMPRESS_ENCODER_DELAY = 2,
-};
-
-/**
- * struct snd_compr_metadata: compressed stream metadata
- * @key: key id
- * @value: key value
- */
-struct snd_compr_metadata {
- __u32 key;
- __u32 value[8];
-};
-
/**
* compress path ioctl definitions
* SNDRV_COMPRESS_GET_CAPS: Query capability of DSP
@@ -166,10 +145,6 @@ struct snd_compr_metadata {
struct snd_compr_codec_caps)
#define SNDRV_COMPRESS_SET_PARAMS _IOW('C', 0x12, struct snd_compr_params)
#define SNDRV_COMPRESS_GET_PARAMS _IOR('C', 0x13, struct snd_codec)
-#define SNDRV_COMPRESS_SET_METADATA _IOW('C', 0x14,\
- struct snd_compr_metadata)
-#define SNDRV_COMPRESS_GET_METADATA _IOWR('C', 0x15,\
- struct snd_compr_metadata)
#define SNDRV_COMPRESS_TSTAMP _IOR('C', 0x20, struct snd_compr_tstamp)
#define SNDRV_COMPRESS_AVAIL _IOR('C', 0x21, struct snd_compr_avail)
#define SNDRV_COMPRESS_PAUSE _IO('C', 0x30)
@@ -177,14 +152,10 @@ struct snd_compr_metadata {
#define SNDRV_COMPRESS_START _IO('C', 0x32)
#define SNDRV_COMPRESS_STOP _IO('C', 0x33)
#define SNDRV_COMPRESS_DRAIN _IO('C', 0x34)
-#define SNDRV_COMPRESS_NEXT_TRACK _IO('C', 0x35)
-#define SNDRV_COMPRESS_PARTIAL_DRAIN _IO('C', 0x36)
/*
* TODO
* 1. add mmap support
*
*/
#define SND_COMPR_TRIGGER_DRAIN 7 /*FIXME move this to pcm.h */
-#define SND_COMPR_TRIGGER_NEXT_TRACK 8
-#define SND_COMPR_TRIGGER_PARTIAL_DRAIN 9
#endif
diff --git a/trunk/sound/core/compress_offload.c b/trunk/sound/core/compress_offload.c
index c84abc886e90..ad11dc994792 100644
--- a/trunk/sound/core/compress_offload.c
+++ b/trunk/sound/core/compress_offload.c
@@ -144,17 +144,16 @@ static int snd_compr_free(struct inode *inode, struct file *f)
return 0;
}
-static int snd_compr_update_tstamp(struct snd_compr_stream *stream,
+static void snd_compr_update_tstamp(struct snd_compr_stream *stream,
struct snd_compr_tstamp *tstamp)
{
if (!stream->ops->pointer)
- return -ENOTSUPP;
+ return;
stream->ops->pointer(stream, tstamp);
pr_debug("dsp consumed till %d total %d bytes\n",
tstamp->byte_offset, tstamp->copied_total);
stream->runtime->hw_pointer = tstamp->byte_offset;
stream->runtime->total_bytes_transferred = tstamp->copied_total;
- return 0;
}
static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
@@ -162,9 +161,7 @@ static size_t snd_compr_calc_avail(struct snd_compr_stream *stream,
{
long avail_calc; /*this needs to be signed variable */
- memset(avail, 0, sizeof(*avail));
snd_compr_update_tstamp(stream, &avail->tstamp);
- /* Still need to return avail even if tstamp can't be filled in */
/* FIXME: This needs to be different for capture stream,
available is # of compressed data, for playback it's
@@ -486,8 +483,6 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
if (retval)
goto out;
stream->runtime->state = SNDRV_PCM_STATE_SETUP;
- stream->metadata_set = false;
- stream->next_track = false;
} else {
return -EPERM;
}
@@ -519,60 +514,14 @@ snd_compr_get_params(struct snd_compr_stream *stream, unsigned long arg)
return retval;
}
-static int
-snd_compr_get_metadata(struct snd_compr_stream *stream, unsigned long arg)
-{
- struct snd_compr_metadata metadata;
- int retval;
-
- if (!stream->ops->get_metadata)
- return -ENXIO;
-
- if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
- return -EFAULT;
-
- retval = stream->ops->get_metadata(stream, &metadata);
- if (retval != 0)
- return retval;
-
- if (copy_to_user((void __user *)arg, &metadata, sizeof(metadata)))
- return -EFAULT;
-
- return 0;
-}
-
-static int
-snd_compr_set_metadata(struct snd_compr_stream *stream, unsigned long arg)
-{
- struct snd_compr_metadata metadata;
- int retval;
-
- if (!stream->ops->set_metadata)
- return -ENXIO;
- /*
- * we should allow parameter change only when stream has been
- * opened not in other cases
- */
- if (copy_from_user(&metadata, (void __user *)arg, sizeof(metadata)))
- return -EFAULT;
-
- retval = stream->ops->set_metadata(stream, &metadata);
- stream->metadata_set = true;
-
- return retval;
-}
-
static inline int
snd_compr_tstamp(struct snd_compr_stream *stream, unsigned long arg)
{
- struct snd_compr_tstamp tstamp = {0};
- int ret;
+ struct snd_compr_tstamp tstamp;
- ret = snd_compr_update_tstamp(stream, &tstamp);
- if (ret == 0)
- ret = copy_to_user((struct snd_compr_tstamp __user *)arg,
- &tstamp, sizeof(tstamp)) ? -EFAULT : 0;
- return ret;
+ snd_compr_update_tstamp(stream, &tstamp);
+ return copy_to_user((struct snd_compr_tstamp __user *)arg,
+ &tstamp, sizeof(tstamp)) ? -EFAULT : 0;
}
static int snd_compr_pause(struct snd_compr_stream *stream)
@@ -645,44 +594,6 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
return retval;
}
-static int snd_compr_next_track(struct snd_compr_stream *stream)
-{
- int retval;
-
- /* only a running stream can transition to next track */
- if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
- return -EPERM;
-
- /* you can signal next track isf this is intended to be a gapless stream
- * and current track metadata is set
- */
- if (stream->metadata_set == false)
- return -EPERM;
-
- retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_NEXT_TRACK);
- if (retval != 0)
- return retval;
- stream->metadata_set = false;
- stream->next_track = true;
- return 0;
-}
-
-static int snd_compr_partial_drain(struct snd_compr_stream *stream)
-{
- int retval;
- if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
- stream->runtime->state == SNDRV_PCM_STATE_SETUP)
- return -EPERM;
- /* stream can be drained only when next track has been signalled */
- if (stream->next_track == false)
- return -EPERM;
-
- retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
-
- stream->next_track = false;
- return retval;
-}
-
static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
struct snd_compr_file *data = f->private_data;
@@ -712,12 +623,6 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
case _IOC_NR(SNDRV_COMPRESS_GET_PARAMS):
retval = snd_compr_get_params(stream, arg);
break;
- case _IOC_NR(SNDRV_COMPRESS_SET_METADATA):
- retval = snd_compr_set_metadata(stream, arg);
- break;
- case _IOC_NR(SNDRV_COMPRESS_GET_METADATA):
- retval = snd_compr_get_metadata(stream, arg);
- break;
case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
retval = snd_compr_tstamp(stream, arg);
break;
@@ -739,13 +644,6 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
case _IOC_NR(SNDRV_COMPRESS_DRAIN):
retval = snd_compr_drain(stream);
break;
- case _IOC_NR(SNDRV_COMPRESS_PARTIAL_DRAIN):
- retval = snd_compr_partial_drain(stream);
- break;
- case _IOC_NR(SNDRV_COMPRESS_NEXT_TRACK):
- retval = snd_compr_next_track(stream);
- break;
-
}
mutex_unlock(&stream->device->lock);
return retval;
diff --git a/trunk/sound/drivers/aloop.c b/trunk/sound/drivers/aloop.c
index 64d534710b51..3d822328d383 100644
--- a/trunk/sound/drivers/aloop.c
+++ b/trunk/sound/drivers/aloop.c
@@ -286,14 +286,12 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
loopback_active_notify(dpcm);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- case SNDRV_PCM_TRIGGER_SUSPEND:
spin_lock(&cable->lock);
cable->pause |= stream;
loopback_timer_stop(dpcm);
spin_unlock(&cable->lock);
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- case SNDRV_PCM_TRIGGER_RESUME:
spin_lock(&cable->lock);
dpcm->last_jiffies = jiffies;
cable->pause &= ~stream;
@@ -565,8 +563,7 @@ static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
static struct snd_pcm_hardware loopback_pcm_hardware =
{
.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME),
+ SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |
SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE),
diff --git a/trunk/sound/drivers/vx/vx_core.c b/trunk/sound/drivers/vx/vx_core.c
index c39961c11401..de5055a3b0d0 100644
--- a/trunk/sound/drivers/vx/vx_core.c
+++ b/trunk/sound/drivers/vx/vx_core.c
@@ -52,6 +52,7 @@ MODULE_LICENSE("GPL");
int snd_vx_check_reg_bit(struct vx_core *chip, int reg, int mask, int bit, int time)
{
unsigned long end_time = jiffies + (time * HZ + 999) / 1000;
+#ifdef CONFIG_SND_DEBUG
static char *reg_names[VX_REG_MAX] = {
"ICR", "CVR", "ISR", "IVR", "RXH", "RXM", "RXL",
"DMA", "CDSP", "RFREQ", "RUER/V2", "DATA", "MEMIRQ",
@@ -59,7 +60,7 @@ int snd_vx_check_reg_bit(struct vx_core *chip, int reg, int mask, int bit, int t
"MIC3", "INTCSR", "CNTRL", "GPIOC",
"LOFREQ", "HIFREQ", "CSUER", "RUER"
};
-
+#endif
do {
if ((snd_vx_inb(chip, reg) & mask) == bit)
return 0;
diff --git a/trunk/sound/pci/Kconfig b/trunk/sound/pci/Kconfig
index fe6fa93a6262..947cfb4eb30c 100644
--- a/trunk/sound/pci/Kconfig
+++ b/trunk/sound/pci/Kconfig
@@ -678,7 +678,6 @@ config SND_LOLA
config SND_LX6464ES
tristate "Digigram LX6464ES"
- depends on HAS_IOPORT
select SND_PCM
help
Say Y here to include support for Digigram LX6464ES boards.
diff --git a/trunk/sound/pci/ali5451/ali5451.c b/trunk/sound/pci/ali5451/ali5451.c
index e760af9d1fb6..136a393b70ab 100644
--- a/trunk/sound/pci/ali5451/ali5451.c
+++ b/trunk/sound/pci/ali5451/ali5451.c
@@ -1435,7 +1435,7 @@ static snd_pcm_uframes_t snd_ali_pointer(struct snd_pcm_substream *substream)
spin_lock(&codec->reg_lock);
if (!pvoice->running) {
- spin_unlock(&codec->reg_lock);
+ spin_unlock_irq(&codec->reg_lock);
return 0;
}
outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR));
diff --git a/trunk/sound/pci/atiixp.c b/trunk/sound/pci/atiixp.c
index 6e78c6789858..a67743183aaf 100644
--- a/trunk/sound/pci/atiixp.c
+++ b/trunk/sound/pci/atiixp.c
@@ -567,9 +567,8 @@ static int ac97_probing_bugs(struct pci_dev *pci)
q = snd_pci_quirk_lookup(pci, atiixp_quirks);
if (q) {
- snd_printdd(KERN_INFO
- "Atiixp quirk for %s. Forcing codec %d\n",
- snd_pci_quirk_name(q), q->value);
+ snd_printdd(KERN_INFO "Atiixp quirk for %s. "
+ "Forcing codec %d\n", q->name, q->value);
return q->value;
}
/* this hardware doesn't need workarounds. Probe for codec */
diff --git a/trunk/sound/pci/au88x0/au88x0_pcm.c b/trunk/sound/pci/au88x0/au88x0_pcm.c
index b46dc9b24dbd..a4184bb27761 100644
--- a/trunk/sound/pci/au88x0/au88x0_pcm.c
+++ b/trunk/sound/pci/au88x0/au88x0_pcm.c
@@ -650,29 +650,6 @@ static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
snd_dma_pci_data(chip->pci_dev),
0x10000, 0x10000);
- switch (VORTEX_PCM_TYPE(pcm)) {
- case VORTEX_PCM_ADB:
- err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- snd_pcm_std_chmaps,
- VORTEX_IS_QUAD(chip) ? 4 : 2,
- 0, NULL);
- if (err < 0)
- return err;
- err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
- snd_pcm_std_chmaps, 2, 0, NULL);
- if (err < 0)
- return err;
- break;
-#ifdef CHIP_AU8830
- case VORTEX_PCM_A3D:
- err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- snd_pcm_std_chmaps, 1, 0, NULL);
- if (err < 0)
- return err;
- break;
-#endif
- };
-
if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) {
for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) {
kctl = snd_ctl_new1(&snd_vortex_mixer_spdif[i], chip);
diff --git a/trunk/sound/pci/hda/Kconfig b/trunk/sound/pci/hda/Kconfig
index 80a7d44bcf81..6eeb8897624b 100644
--- a/trunk/sound/pci/hda/Kconfig
+++ b/trunk/sound/pci/hda/Kconfig
@@ -15,9 +15,6 @@ menuconfig SND_HDA_INTEL
if SND_HDA_INTEL
-config SND_HDA_DSP_LOADER
- bool
-
config SND_HDA_PREALLOC_SIZE
int "Pre-allocated buffer size for HD-audio driver"
range 0 32768
@@ -89,7 +86,6 @@ config SND_HDA_PATCH_LOADER
config SND_HDA_CODEC_REALTEK
bool "Build Realtek HD-audio codec support"
default y
- select SND_HDA_GENERIC
help
Say Y here to include Realtek HD-audio codec support in
snd-hda-intel driver, such as ALC880.
@@ -102,7 +98,6 @@ config SND_HDA_CODEC_REALTEK
config SND_HDA_CODEC_ANALOG
bool "Build Analog Device HD-audio codec support"
default y
- select SND_HDA_GENERIC
help
Say Y here to include Analog Device HD-audio codec support in
snd-hda-intel driver, such as AD1986A.
@@ -115,7 +110,6 @@ config SND_HDA_CODEC_ANALOG
config SND_HDA_CODEC_SIGMATEL
bool "Build IDT/Sigmatel HD-audio codec support"
default y
- select SND_HDA_GENERIC
help
Say Y here to include IDT (Sigmatel) HD-audio codec support in
snd-hda-intel driver, such as STAC9200.
@@ -128,7 +122,6 @@ config SND_HDA_CODEC_SIGMATEL
config SND_HDA_CODEC_VIA
bool "Build VIA HD-audio codec support"
default y
- select SND_HDA_GENERIC
help
Say Y here to include VIA HD-audio codec support in
snd-hda-intel driver, such as VT1708.
@@ -154,8 +147,8 @@ config SND_HDA_CODEC_HDMI
config SND_HDA_CODEC_CIRRUS
bool "Build Cirrus Logic codec support"
+ depends on SND_HDA_INTEL
default y
- select SND_HDA_GENERIC
help
Say Y here to include Cirrus Logic codec support in
snd-hda-intel driver, such as CS4206.
@@ -168,7 +161,6 @@ config SND_HDA_CODEC_CIRRUS
config SND_HDA_CODEC_CONEXANT
bool "Build Conexant HD-audio codec support"
default y
- select SND_HDA_GENERIC
help
Say Y here to include Conexant HD-audio codec support in
snd-hda-intel driver, such as CX20549.
@@ -180,8 +172,8 @@ config SND_HDA_CODEC_CONEXANT
config SND_HDA_CODEC_CA0110
bool "Build Creative CA0110-IBG codec support"
+ depends on SND_HDA_INTEL
default y
- select SND_HDA_GENERIC
help
Say Y here to include Creative CA0110-IBG codec support in
snd-hda-intel driver, found on some Creative X-Fi cards.
@@ -193,6 +185,7 @@ config SND_HDA_CODEC_CA0110
config SND_HDA_CODEC_CA0132
bool "Build Creative CA0132 codec support"
+ depends on SND_HDA_INTEL
default y
help
Say Y here to include Creative CA0132 codec support in
@@ -203,21 +196,9 @@ config SND_HDA_CODEC_CA0132
snd-hda-codec-ca0132.
This module is automatically loaded at probing.
-config SND_HDA_CODEC_CA0132_DSP
- bool "Support new DSP code for CA0132 codec"
- depends on SND_HDA_CODEC_CA0132 && FW_LOADER
- select SND_HDA_DSP_LOADER
- help
- Say Y here to enable the DSP for Creative CA0132 for extended
- features like equalizer or echo cancellation.
-
- Note that this option requires the external firmware file
- (ctefx.bin).
-
config SND_HDA_CODEC_CMEDIA
bool "Build C-Media HD-audio codec support"
default y
- select SND_HDA_GENERIC
help
Say Y here to include C-Media HD-audio codec support in
snd-hda-intel driver, such as CMI9880.
diff --git a/trunk/sound/pci/hda/ca0132_regs.h b/trunk/sound/pci/hda/ca0132_regs.h
deleted file mode 100644
index 07e760937d3c..000000000000
--- a/trunk/sound/pci/hda/ca0132_regs.h
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * HD audio interface patch for Creative CA0132 chip.
- * CA0132 registers defines.
- *
- * Copyright (c) 2011, Creative Technology Ltd.
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef __CA0132_REGS_H
-#define __CA0312_REGS_H
-
-#define DSP_CHIP_OFFSET 0x100000
-#define DSP_DBGCNTL_MODULE_OFFSET 0xE30
-#define DSP_DBGCNTL_INST_OFFSET \
- (DSP_CHIP_OFFSET + DSP_DBGCNTL_MODULE_OFFSET)
-
-#define DSP_DBGCNTL_EXEC_LOBIT 0x0
-#define DSP_DBGCNTL_EXEC_HIBIT 0x3
-#define DSP_DBGCNTL_EXEC_MASK 0xF
-
-#define DSP_DBGCNTL_SS_LOBIT 0x4
-#define DSP_DBGCNTL_SS_HIBIT 0x7
-#define DSP_DBGCNTL_SS_MASK 0xF0
-
-#define DSP_DBGCNTL_STATE_LOBIT 0xA
-#define DSP_DBGCNTL_STATE_HIBIT 0xD
-#define DSP_DBGCNTL_STATE_MASK 0x3C00
-
-#define XRAM_CHIP_OFFSET 0x0
-#define XRAM_XRAM_CHANNEL_COUNT 0xE000
-#define XRAM_XRAM_MODULE_OFFSET 0x0
-#define XRAM_XRAM_CHAN_INCR 4
-#define XRAM_XRAM_INST_OFFSET(_chan) \
- (XRAM_CHIP_OFFSET + XRAM_XRAM_MODULE_OFFSET + \
- (_chan * XRAM_XRAM_CHAN_INCR))
-
-#define YRAM_CHIP_OFFSET 0x40000
-#define YRAM_YRAM_CHANNEL_COUNT 0x8000
-#define YRAM_YRAM_MODULE_OFFSET 0x0
-#define YRAM_YRAM_CHAN_INCR 4
-#define YRAM_YRAM_INST_OFFSET(_chan) \
- (YRAM_CHIP_OFFSET + YRAM_YRAM_MODULE_OFFSET + \
- (_chan * YRAM_YRAM_CHAN_INCR))
-
-#define UC_CHIP_OFFSET 0x80000
-#define UC_UC_CHANNEL_COUNT 0x10000
-#define UC_UC_MODULE_OFFSET 0x0
-#define UC_UC_CHAN_INCR 4
-#define UC_UC_INST_OFFSET(_chan) \
- (UC_CHIP_OFFSET + UC_UC_MODULE_OFFSET + \
- (_chan * UC_UC_CHAN_INCR))
-
-#define AXRAM_CHIP_OFFSET 0x3C000
-#define AXRAM_AXRAM_CHANNEL_COUNT 0x1000
-#define AXRAM_AXRAM_MODULE_OFFSET 0x0
-#define AXRAM_AXRAM_CHAN_INCR 4
-#define AXRAM_AXRAM_INST_OFFSET(_chan) \
- (AXRAM_CHIP_OFFSET + AXRAM_AXRAM_MODULE_OFFSET + \
- (_chan * AXRAM_AXRAM_CHAN_INCR))
-
-#define AYRAM_CHIP_OFFSET 0x78000
-#define AYRAM_AYRAM_CHANNEL_COUNT 0x1000
-#define AYRAM_AYRAM_MODULE_OFFSET 0x0
-#define AYRAM_AYRAM_CHAN_INCR 4
-#define AYRAM_AYRAM_INST_OFFSET(_chan) \
- (AYRAM_CHIP_OFFSET + AYRAM_AYRAM_MODULE_OFFSET + \
- (_chan * AYRAM_AYRAM_CHAN_INCR))
-
-#define DSPDMAC_CHIP_OFFSET 0x110000
-#define DSPDMAC_DMA_CFG_CHANNEL_COUNT 12
-#define DSPDMAC_DMACFG_MODULE_OFFSET 0xF00
-#define DSPDMAC_DMACFG_CHAN_INCR 0x10
-#define DSPDMAC_DMACFG_INST_OFFSET(_chan) \
- (DSPDMAC_CHIP_OFFSET + DSPDMAC_DMACFG_MODULE_OFFSET + \
- (_chan * DSPDMAC_DMACFG_CHAN_INCR))
-
-#define DSPDMAC_DMACFG_DBADR_LOBIT 0x0
-#define DSPDMAC_DMACFG_DBADR_HIBIT 0x10
-#define DSPDMAC_DMACFG_DBADR_MASK 0x1FFFF
-#define DSPDMAC_DMACFG_LP_LOBIT 0x11
-#define DSPDMAC_DMACFG_LP_HIBIT 0x11
-#define DSPDMAC_DMACFG_LP_MASK 0x20000
-
-#define DSPDMAC_DMACFG_AINCR_LOBIT 0x12
-#define DSPDMAC_DMACFG_AINCR_HIBIT 0x12
-#define DSPDMAC_DMACFG_AINCR_MASK 0x40000
-
-#define DSPDMAC_DMACFG_DWR_LOBIT 0x13
-#define DSPDMAC_DMACFG_DWR_HIBIT 0x13
-#define DSPDMAC_DMACFG_DWR_MASK 0x80000
-
-#define DSPDMAC_DMACFG_AJUMP_LOBIT 0x14
-#define DSPDMAC_DMACFG_AJUMP_HIBIT 0x17
-#define DSPDMAC_DMACFG_AJUMP_MASK 0xF00000
-
-#define DSPDMAC_DMACFG_AMODE_LOBIT 0x18
-#define DSPDMAC_DMACFG_AMODE_HIBIT 0x19
-#define DSPDMAC_DMACFG_AMODE_MASK 0x3000000
-
-#define DSPDMAC_DMACFG_LK_LOBIT 0x1A
-#define DSPDMAC_DMACFG_LK_HIBIT 0x1A
-#define DSPDMAC_DMACFG_LK_MASK 0x4000000
-
-#define DSPDMAC_DMACFG_AICS_LOBIT 0x1B
-#define DSPDMAC_DMACFG_AICS_HIBIT 0x1F
-#define DSPDMAC_DMACFG_AICS_MASK 0xF8000000
-
-#define DSPDMAC_DMACFG_LP_SINGLE 0
-#define DSPDMAC_DMACFG_LP_LOOPING 1
-
-#define DSPDMAC_DMACFG_AINCR_XANDY 0
-#define DSPDMAC_DMACFG_AINCR_XORY 1
-
-#define DSPDMAC_DMACFG_DWR_DMA_RD 0
-#define DSPDMAC_DMACFG_DWR_DMA_WR 1
-
-#define DSPDMAC_DMACFG_AMODE_LINEAR 0
-#define DSPDMAC_DMACFG_AMODE_RSV1 1
-#define DSPDMAC_DMACFG_AMODE_WINTLV 2
-#define DSPDMAC_DMACFG_AMODE_GINTLV 3
-
-#define DSPDMAC_DSP_ADR_OFS_CHANNEL_COUNT 12
-#define DSPDMAC_DSPADROFS_MODULE_OFFSET 0xF04
-#define DSPDMAC_DSPADROFS_CHAN_INCR 0x10
-#define DSPDMAC_DSPADROFS_INST_OFFSET(_chan) \
- (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADROFS_MODULE_OFFSET + \
- (_chan * DSPDMAC_DSPADROFS_CHAN_INCR))
-
-#define DSPDMAC_DSPADROFS_COFS_LOBIT 0x0
-#define DSPDMAC_DSPADROFS_COFS_HIBIT 0xF
-#define DSPDMAC_DSPADROFS_COFS_MASK 0xFFFF
-
-#define DSPDMAC_DSPADROFS_BOFS_LOBIT 0x10
-#define DSPDMAC_DSPADROFS_BOFS_HIBIT 0x1F
-#define DSPDMAC_DSPADROFS_BOFS_MASK 0xFFFF0000
-
-#define DSPDMAC_DSP_ADR_WOFS_CHANNEL_COUNT 12
-#define DSPDMAC_DSPADRWOFS_MODULE_OFFSET 0xF04
-#define DSPDMAC_DSPADRWOFS_CHAN_INCR 0x10
-
-#define DSPDMAC_DSPADRWOFS_INST_OFFSET(_chan) \
- (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRWOFS_MODULE_OFFSET + \
- (_chan * DSPDMAC_DSPADRWOFS_CHAN_INCR))
-
-#define DSPDMAC_DSPADRWOFS_WCOFS_LOBIT 0x0
-#define DSPDMAC_DSPADRWOFS_WCOFS_HIBIT 0xA
-#define DSPDMAC_DSPADRWOFS_WCOFS_MASK 0x7FF
-
-#define DSPDMAC_DSPADRWOFS_WCBFR_LOBIT 0xB
-#define DSPDMAC_DSPADRWOFS_WCBFR_HIBIT 0xF
-#define DSPDMAC_DSPADRWOFS_WCBFR_MASK 0xF800
-
-#define DSPDMAC_DSPADRWOFS_WBOFS_LOBIT 0x10
-#define DSPDMAC_DSPADRWOFS_WBOFS_HIBIT 0x1A
-#define DSPDMAC_DSPADRWOFS_WBOFS_MASK 0x7FF0000
-
-#define DSPDMAC_DSPADRWOFS_WBBFR_LOBIT 0x1B
-#define DSPDMAC_DSPADRWOFS_WBBFR_HIBIT 0x1F
-#define DSPDMAC_DSPADRWOFS_WBBFR_MASK 0xF8000000
-
-#define DSPDMAC_DSP_ADR_GOFS_CHANNEL_COUNT 12
-#define DSPDMAC_DSPADRGOFS_MODULE_OFFSET 0xF04
-#define DSPDMAC_DSPADRGOFS_CHAN_INCR 0x10
-#define DSPDMAC_DSPADRGOFS_INST_OFFSET(_chan) \
- (DSPDMAC_CHIP_OFFSET + DSPDMAC_DSPADRGOFS_MODULE_OFFSET + \
- (_chan * DSPDMAC_DSPADRGOFS_CHAN_INCR))
-
-#define DSPDMAC_DSPADRGOFS_GCOFS_LOBIT 0x0
-#define DSPDMAC_DSPADRGOFS_GCOFS_HIBIT 0x9
-#define DSPDMAC_DSPADRGOFS_GCOFS_MASK 0x3FF
-
-#define DSPDMAC_DSPADRGOFS_GCS_LOBIT 0xA
-#define DSPDMAC_DSPADRGOFS_GCS_HIBIT 0xC
-#define DSPDMAC_DSPADRGOFS_GCS_MASK 0x1C00
-
-#define DSPDMAC_DSPADRGOFS_GCBFR_LOBIT 0xD
-#define DSPDMAC_DSPADRGOFS_GCBFR_HIBIT 0xF
-#define DSPDMAC_DSPADRGOFS_GCBFR_MASK 0xE000
-
-#define DSPDMAC_DSPADRGOFS_GBOFS_LOBIT 0x10
-#define DSPDMAC_DSPADRGOFS_GBOFS_HIBIT 0x19
-#define DSPDMAC_DSPADRGOFS_GBOFS_MASK 0x3FF0000
-
-#define DSPDMAC_DSPADRGOFS_GBS_LOBIT 0x1A
-#define DSPDMAC_DSPADRGOFS_GBS_HIBIT 0x1C
-#define DSPDMAC_DSPADRGOFS_GBS_MASK 0x1C000000
-
-#define DSPDMAC_DSPADRGOFS_GBBFR_LOBIT 0x1D
-#define DSPDMAC_DSPADRGOFS_GBBFR_HIBIT 0x1F
-#define DSPDMAC_DSPADRGOFS_GBBFR_MASK 0xE0000000
-
-#define DSPDMAC_XFR_CNT_CHANNEL_COUNT 12
-#define DSPDMAC_XFRCNT_MODULE_OFFSET 0xF08
-#define DSPDMAC_XFRCNT_CHAN_INCR 0x10
-
-#define DSPDMAC_XFRCNT_INST_OFFSET(_chan) \
- (DSPDMAC_CHIP_OFFSET + DSPDMAC_XFRCNT_MODULE_OFFSET + \
- (_chan * DSPDMAC_XFRCNT_CHAN_INCR))
-
-#define DSPDMAC_XFRCNT_CCNT_LOBIT 0x0
-#define DSPDMAC_XFRCNT_CCNT_HIBIT 0xF
-#define DSPDMAC_XFRCNT_CCNT_MASK 0xFFFF
-
-#define DSPDMAC_XFRCNT_BCNT_LOBIT 0x10
-#define DSPDMAC_XFRCNT_BCNT_HIBIT 0x1F
-#define DSPDMAC_XFRCNT_BCNT_MASK 0xFFFF0000
-
-#define DSPDMAC_IRQ_CNT_CHANNEL_COUNT 12
-#define DSPDMAC_IRQCNT_MODULE_OFFSET 0xF0C
-#define DSPDMAC_IRQCNT_CHAN_INCR 0x10
-#define DSPDMAC_IRQCNT_INST_OFFSET(_chan) \
- (DSPDMAC_CHIP_OFFSET + DSPDMAC_IRQCNT_MODULE_OFFSET + \
- (_chan * DSPDMAC_IRQCNT_CHAN_INCR))
-
-#define DSPDMAC_IRQCNT_CICNT_LOBIT 0x0
-#define DSPDMAC_IRQCNT_CICNT_HIBIT 0xF
-#define DSPDMAC_IRQCNT_CICNT_MASK 0xFFFF
-
-#define DSPDMAC_IRQCNT_BICNT_LOBIT 0x10
-#define DSPDMAC_IRQCNT_BICNT_HIBIT 0x1F
-#define DSPDMAC_IRQCNT_BICNT_MASK 0xFFFF0000
-
-#define DSPDMAC_AUD_CHSEL_CHANNEL_COUNT 12
-#define DSPDMAC_AUDCHSEL_MODULE_OFFSET 0xFC0
-#define DSPDMAC_AUDCHSEL_CHAN_INCR 0x4
-#define DSPDMAC_AUDCHSEL_INST_OFFSET(_chan) \
- (DSPDMAC_CHIP_OFFSET + DSPDMAC_AUDCHSEL_MODULE_OFFSET + \
- (_chan * DSPDMAC_AUDCHSEL_CHAN_INCR))
-
-#define DSPDMAC_AUDCHSEL_ACS_LOBIT 0x0
-#define DSPDMAC_AUDCHSEL_ACS_HIBIT 0x1F
-#define DSPDMAC_AUDCHSEL_ACS_MASK 0xFFFFFFFF
-
-#define DSPDMAC_CHNLSTART_MODULE_OFFSET 0xFF0
-#define DSPDMAC_CHNLSTART_INST_OFFSET \
- (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTART_MODULE_OFFSET)
-
-#define DSPDMAC_CHNLSTART_EN_LOBIT 0x0
-#define DSPDMAC_CHNLSTART_EN_HIBIT 0xB
-#define DSPDMAC_CHNLSTART_EN_MASK 0xFFF
-
-#define DSPDMAC_CHNLSTART_VAI1_LOBIT 0xC
-#define DSPDMAC_CHNLSTART_VAI1_HIBIT 0xF
-#define DSPDMAC_CHNLSTART_VAI1_MASK 0xF000
-
-#define DSPDMAC_CHNLSTART_DIS_LOBIT 0x10
-#define DSPDMAC_CHNLSTART_DIS_HIBIT 0x1B
-#define DSPDMAC_CHNLSTART_DIS_MASK 0xFFF0000
-
-#define DSPDMAC_CHNLSTART_VAI2_LOBIT 0x1C
-#define DSPDMAC_CHNLSTART_VAI2_HIBIT 0x1F
-#define DSPDMAC_CHNLSTART_VAI2_MASK 0xF0000000
-
-#define DSPDMAC_CHNLSTATUS_MODULE_OFFSET 0xFF4
-#define DSPDMAC_CHNLSTATUS_INST_OFFSET \
- (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLSTATUS_MODULE_OFFSET)
-
-#define DSPDMAC_CHNLSTATUS_ISC_LOBIT 0x0
-#define DSPDMAC_CHNLSTATUS_ISC_HIBIT 0xB
-#define DSPDMAC_CHNLSTATUS_ISC_MASK 0xFFF
-
-#define DSPDMAC_CHNLSTATUS_AOO_LOBIT 0xC
-#define DSPDMAC_CHNLSTATUS_AOO_HIBIT 0xC
-#define DSPDMAC_CHNLSTATUS_AOO_MASK 0x1000
-
-#define DSPDMAC_CHNLSTATUS_AOU_LOBIT 0xD
-#define DSPDMAC_CHNLSTATUS_AOU_HIBIT 0xD
-#define DSPDMAC_CHNLSTATUS_AOU_MASK 0x2000
-
-#define DSPDMAC_CHNLSTATUS_AIO_LOBIT 0xE
-#define DSPDMAC_CHNLSTATUS_AIO_HIBIT 0xE
-#define DSPDMAC_CHNLSTATUS_AIO_MASK 0x4000
-
-#define DSPDMAC_CHNLSTATUS_AIU_LOBIT 0xF
-#define DSPDMAC_CHNLSTATUS_AIU_HIBIT 0xF
-#define DSPDMAC_CHNLSTATUS_AIU_MASK 0x8000
-
-#define DSPDMAC_CHNLSTATUS_IEN_LOBIT 0x10
-#define DSPDMAC_CHNLSTATUS_IEN_HIBIT 0x1B
-#define DSPDMAC_CHNLSTATUS_IEN_MASK 0xFFF0000
-
-#define DSPDMAC_CHNLSTATUS_VAI0_LOBIT 0x1C
-#define DSPDMAC_CHNLSTATUS_VAI0_HIBIT 0x1F
-#define DSPDMAC_CHNLSTATUS_VAI0_MASK 0xF0000000
-
-#define DSPDMAC_CHNLPROP_MODULE_OFFSET 0xFF8
-#define DSPDMAC_CHNLPROP_INST_OFFSET \
- (DSPDMAC_CHIP_OFFSET + DSPDMAC_CHNLPROP_MODULE_OFFSET)
-
-#define DSPDMAC_CHNLPROP_DCON_LOBIT 0x0
-#define DSPDMAC_CHNLPROP_DCON_HIBIT 0xB
-#define DSPDMAC_CHNLPROP_DCON_MASK 0xFFF
-
-#define DSPDMAC_CHNLPROP_FFS_LOBIT 0xC
-#define DSPDMAC_CHNLPROP_FFS_HIBIT 0xC
-#define DSPDMAC_CHNLPROP_FFS_MASK 0x1000
-
-#define DSPDMAC_CHNLPROP_NAJ_LOBIT 0xD
-#define DSPDMAC_CHNLPROP_NAJ_HIBIT 0xD
-#define DSPDMAC_CHNLPROP_NAJ_MASK 0x2000
-
-#define DSPDMAC_CHNLPROP_ENH_LOBIT 0xE
-#define DSPDMAC_CHNLPROP_ENH_HIBIT 0xE
-#define DSPDMAC_CHNLPROP_ENH_MASK 0x4000
-
-#define DSPDMAC_CHNLPROP_MSPCE_LOBIT 0x10
-#define DSPDMAC_CHNLPROP_MSPCE_HIBIT 0x1B
-#define DSPDMAC_CHNLPROP_MSPCE_MASK 0xFFF0000
-
-#define DSPDMAC_CHNLPROP_AC_LOBIT 0x1C
-#define DSPDMAC_CHNLPROP_AC_HIBIT 0x1F
-#define DSPDMAC_CHNLPROP_AC_MASK 0xF0000000
-
-#define DSPDMAC_ACTIVE_MODULE_OFFSET 0xFFC
-#define DSPDMAC_ACTIVE_INST_OFFSET \
- (DSPDMAC_CHIP_OFFSET + DSPDMAC_ACTIVE_MODULE_OFFSET)
-
-#define DSPDMAC_ACTIVE_AAR_LOBIT 0x0
-#define DSPDMAC_ACTIVE_AAR_HIBIT 0xB
-#define DSPDMAC_ACTIVE_AAR_MASK 0xFFF
-
-#define DSPDMAC_ACTIVE_WFR_LOBIT 0xC
-#define DSPDMAC_ACTIVE_WFR_HIBIT 0x17
-#define DSPDMAC_ACTIVE_WFR_MASK 0xFFF000
-
-#define DSP_AUX_MEM_BASE 0xE000
-#define INVALID_CHIP_ADDRESS (~0U)
-
-#define X_SIZE (XRAM_XRAM_CHANNEL_COUNT * XRAM_XRAM_CHAN_INCR)
-#define Y_SIZE (YRAM_YRAM_CHANNEL_COUNT * YRAM_YRAM_CHAN_INCR)
-#define AX_SIZE (AXRAM_AXRAM_CHANNEL_COUNT * AXRAM_AXRAM_CHAN_INCR)
-#define AY_SIZE (AYRAM_AYRAM_CHANNEL_COUNT * AYRAM_AYRAM_CHAN_INCR)
-#define UC_SIZE (UC_UC_CHANNEL_COUNT * UC_UC_CHAN_INCR)
-
-#define XEXT_SIZE (X_SIZE + AX_SIZE)
-#define YEXT_SIZE (Y_SIZE + AY_SIZE)
-
-#define U64K 0x10000UL
-
-#define X_END (XRAM_CHIP_OFFSET + X_SIZE)
-#define X_EXT (XRAM_CHIP_OFFSET + XEXT_SIZE)
-#define AX_END (XRAM_CHIP_OFFSET + U64K*4)
-
-#define Y_END (YRAM_CHIP_OFFSET + Y_SIZE)
-#define Y_EXT (YRAM_CHIP_OFFSET + YEXT_SIZE)
-#define AY_END (YRAM_CHIP_OFFSET + U64K*4)
-
-#define UC_END (UC_CHIP_OFFSET + UC_SIZE)
-
-#define X_RANGE_MAIN(a, s) \
- (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_END))
-#define X_RANGE_AUX(a, s) \
- (((a) >= X_END) && ((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END))
-#define X_RANGE_EXT(a, s) \
- (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < X_EXT))
-#define X_RANGE_ALL(a, s) \
- (((a)+((s)-1)*XRAM_XRAM_CHAN_INCR < AX_END))
-
-#define Y_RANGE_MAIN(a, s) \
- (((a) >= YRAM_CHIP_OFFSET) && \
- ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_END))
-#define Y_RANGE_AUX(a, s) \
- (((a) >= Y_END) && \
- ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END))
-#define Y_RANGE_EXT(a, s) \
- (((a) >= YRAM_CHIP_OFFSET) && \
- ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < Y_EXT))
-#define Y_RANGE_ALL(a, s) \
- (((a) >= YRAM_CHIP_OFFSET) && \
- ((a)+((s)-1)*YRAM_YRAM_CHAN_INCR < AY_END))
-
-#define UC_RANGE(a, s) \
- (((a) >= UC_CHIP_OFFSET) && \
- ((a)+((s)-1)*UC_UC_CHAN_INCR < UC_END))
-
-#define X_OFF(a) \
- (((a) - XRAM_CHIP_OFFSET) / XRAM_XRAM_CHAN_INCR)
-#define AX_OFF(a) \
- (((a) % (AXRAM_AXRAM_CHANNEL_COUNT * \
- AXRAM_AXRAM_CHAN_INCR)) / AXRAM_AXRAM_CHAN_INCR)
-
-#define Y_OFF(a) \
- (((a) - YRAM_CHIP_OFFSET) / YRAM_YRAM_CHAN_INCR)
-#define AY_OFF(a) \
- (((a) % (AYRAM_AYRAM_CHANNEL_COUNT * \
- AYRAM_AYRAM_CHAN_INCR)) / AYRAM_AYRAM_CHAN_INCR)
-
-#define UC_OFF(a) (((a) - UC_CHIP_OFFSET) / UC_UC_CHAN_INCR)
-
-#define X_EXT_MAIN_SIZE(a) (XRAM_XRAM_CHANNEL_COUNT - X_OFF(a))
-#define X_EXT_AUX_SIZE(a, s) ((s) - X_EXT_MAIN_SIZE(a))
-
-#define Y_EXT_MAIN_SIZE(a) (YRAM_YRAM_CHANNEL_COUNT - Y_OFF(a))
-#define Y_EXT_AUX_SIZE(a, s) ((s) - Y_EXT_MAIN_SIZE(a))
-
-#endif
diff --git a/trunk/sound/pci/hda/hda_auto_parser.c b/trunk/sound/pci/hda/hda_auto_parser.c
index a3ea76a4c9d2..7da883a464e3 100644
--- a/trunk/sound/pci/hda/hda_auto_parser.c
+++ b/trunk/sound/pci/hda/hda_auto_parser.c
@@ -97,28 +97,6 @@ static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
}
}
-/* check whether the given pin has a proper pin I/O capability bit */
-static bool check_pincap_validity(struct hda_codec *codec, hda_nid_t pin,
- unsigned int dev)
-{
- unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
-
- /* some old hardware don't return the proper pincaps */
- if (!pincap)
- return true;
-
- switch (dev) {
- case AC_JACK_LINE_OUT:
- case AC_JACK_SPEAKER:
- case AC_JACK_HP_OUT:
- case AC_JACK_SPDIF_OUT:
- case AC_JACK_DIG_OTHER_OUT:
- return !!(pincap & AC_PINCAP_OUT);
- default:
- return !!(pincap & AC_PINCAP_IN);
- }
-}
-
/*
* Parse all pin widgets and store the useful pin nids to cfg
*
@@ -148,9 +126,6 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
struct auto_out_pin hp_out[ARRAY_SIZE(cfg->hp_pins)];
int i;
- if (!snd_hda_get_int_hint(codec, "parser_flags", &i))
- cond_flags = i;
-
memset(cfg, 0, sizeof(*cfg));
memset(line_out, 0, sizeof(line_out));
@@ -181,14 +156,10 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
/* workaround for buggy BIOS setups */
if (dev == AC_JACK_LINE_OUT) {
- if (conn == AC_JACK_PORT_FIXED ||
- conn == AC_JACK_PORT_BOTH)
+ if (conn == AC_JACK_PORT_FIXED)
dev = AC_JACK_SPEAKER;
}
- if (!check_pincap_validity(codec, nid, dev))
- continue;
-
switch (dev) {
case AC_JACK_LINE_OUT:
seq = get_defcfg_sequence(def_conf);
@@ -392,7 +363,7 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec,
{
unsigned int def_conf;
static const char * const mic_names[] = {
- "Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic"
+ "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
};
int attr;
@@ -423,8 +394,6 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec,
return "SPDIF In";
case AC_JACK_DIG_OTHER_IN:
return "Digital In";
- case AC_JACK_HP_OUT:
- return "Headphone Mic";
default:
return "Misc";
}
@@ -583,9 +552,6 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
return 1;
}
-#define is_hdmi_cfg(conf) \
- (get_defcfg_location(conf) == AC_JACK_LOC_HDMI)
-
/**
* snd_hda_get_pin_label - Get a label for the given I/O pin
*
@@ -606,7 +572,6 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
const char *name = NULL;
int i;
- bool hdmi;
if (indexp)
*indexp = 0;
@@ -625,18 +590,16 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
label, maxlen, indexp);
case AC_JACK_SPDIF_OUT:
case AC_JACK_DIG_OTHER_OUT:
- hdmi = is_hdmi_cfg(def_conf);
- name = hdmi ? "HDMI" : "SPDIF";
- if (cfg && indexp)
- for (i = 0; i < cfg->dig_outs; i++) {
- hda_nid_t pin = cfg->dig_out_pins[i];
- unsigned int c;
- if (pin == nid)
- break;
- c = snd_hda_codec_get_pincfg(codec, pin);
- if (hdmi == is_hdmi_cfg(c))
- (*indexp)++;
- }
+ if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI)
+ name = "HDMI";
+ else
+ name = "SPDIF";
+ if (cfg && indexp) {
+ i = find_idx_in_nid_list(nid, cfg->dig_out_pins,
+ cfg->dig_outs);
+ if (i >= 0)
+ *indexp = i;
+ }
break;
default:
if (cfg) {
@@ -659,27 +622,28 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_HDA(snd_hda_get_pin_label);
-int snd_hda_add_verbs(struct hda_codec *codec,
- const struct hda_verb *list)
+int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
+ const struct hda_verb *list)
{
const struct hda_verb **v;
- v = snd_array_new(&codec->verbs);
+ v = snd_array_new(&spec->verbs);
if (!v)
return -ENOMEM;
*v = list;
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_add_verbs);
+EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs);
-void snd_hda_apply_verbs(struct hda_codec *codec)
+void snd_hda_gen_apply_verbs(struct hda_codec *codec)
{
+ struct hda_gen_spec *spec = codec->spec;
int i;
- for (i = 0; i < codec->verbs.used; i++) {
- struct hda_verb **v = snd_array_elem(&codec->verbs, i);
+ for (i = 0; i < spec->verbs.used; i++) {
+ struct hda_verb **v = snd_array_elem(&spec->verbs, i);
snd_hda_sequence_write(codec, *v);
}
}
-EXPORT_SYMBOL_HDA(snd_hda_apply_verbs);
+EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs);
void snd_hda_apply_pincfgs(struct hda_codec *codec,
const struct hda_pintbl *cfg)
@@ -689,22 +653,20 @@ void snd_hda_apply_pincfgs(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs);
-static void set_pin_targets(struct hda_codec *codec,
- const struct hda_pintbl *cfg)
+void snd_hda_apply_fixup(struct hda_codec *codec, int action)
{
- for (; cfg->nid; cfg++)
- snd_hda_set_pin_ctl_cache(codec, cfg->nid, cfg->val);
-}
+ struct hda_gen_spec *spec = codec->spec;
+ int id = spec->fixup_id;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ const char *modelname = spec->fixup_name;
+#endif
+ int depth = 0;
-static void apply_fixup(struct hda_codec *codec, int id, int action, int depth)
-{
- const char *modelname = codec->fixup_name;
+ if (!spec->fixup_list)
+ return;
while (id >= 0) {
- const struct hda_fixup *fix = codec->fixup_list + id;
-
- if (fix->chained_before)
- apply_fixup(codec, fix->chain_id, action, depth + 1);
+ const struct hda_fixup *fix = spec->fixup_list + id;
switch (fix->type) {
case HDA_FIXUP_PINS:
@@ -721,7 +683,7 @@ static void apply_fixup(struct hda_codec *codec, int id, int action, int depth)
snd_printdd(KERN_INFO SFX
"%s: Apply fix-verbs for %s\n",
codec->chip_name, modelname);
- snd_hda_add_verbs(codec, fix->v.verbs);
+ snd_hda_gen_add_verbs(codec->spec, fix->v.verbs);
break;
case HDA_FIXUP_FUNC:
if (!fix->v.func)
@@ -731,33 +693,19 @@ static void apply_fixup(struct hda_codec *codec, int id, int action, int depth)
codec->chip_name, modelname);
fix->v.func(codec, fix, action);
break;
- case HDA_FIXUP_PINCTLS:
- if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins)
- break;
- snd_printdd(KERN_INFO SFX
- "%s: Apply pinctl for %s\n",
- codec->chip_name, modelname);
- set_pin_targets(codec, fix->v.pins);
- break;
default:
snd_printk(KERN_ERR SFX
"%s: Invalid fixup type %d\n",
codec->chip_name, fix->type);
break;
}
- if (!fix->chained || fix->chained_before)
+ if (!fix->chained)
break;
if (++depth > 10)
break;
id = fix->chain_id;
}
}
-
-void snd_hda_apply_fixup(struct hda_codec *codec, int action)
-{
- if (codec->fixup_list)
- apply_fixup(codec, codec->fixup_id, action, 0);
-}
EXPORT_SYMBOL_HDA(snd_hda_apply_fixup);
void snd_hda_pick_fixup(struct hda_codec *codec,
@@ -765,14 +713,15 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
const struct snd_pci_quirk *quirk,
const struct hda_fixup *fixlist)
{
+ struct hda_gen_spec *spec = codec->spec;
const struct snd_pci_quirk *q;
int id = -1;
const char *name = NULL;
/* when model=nofixup is given, don't pick up any fixups */
if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
- codec->fixup_list = NULL;
- codec->fixup_id = -1;
+ spec->fixup_list = NULL;
+ spec->fixup_id = -1;
return;
}
@@ -810,10 +759,10 @@ void snd_hda_pick_fixup(struct hda_codec *codec,
}
}
- codec->fixup_id = id;
+ spec->fixup_id = id;
if (id >= 0) {
- codec->fixup_list = fixlist;
- codec->fixup_name = name;
+ spec->fixup_list = fixlist;
+ spec->fixup_name = name;
}
}
EXPORT_SYMBOL_HDA(snd_hda_pick_fixup);
diff --git a/trunk/sound/pci/hda/hda_auto_parser.h b/trunk/sound/pci/hda/hda_auto_parser.h
index f74807138b49..632ad0ad3007 100644
--- a/trunk/sound/pci/hda/hda_auto_parser.h
+++ b/trunk/sound/pci/hda/hda_auto_parser.h
@@ -51,9 +51,8 @@ enum {
INPUT_PIN_ATTR_INT, /* internal mic/line-in */
INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
- INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
- INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT,
+ INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
};
int snd_hda_get_input_pin_attr(unsigned int def_conf);
@@ -90,4 +89,82 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
+/*
+ */
+
+struct hda_gen_spec {
+ /* fix-up list */
+ int fixup_id;
+ const struct hda_fixup *fixup_list;
+ const char *fixup_name;
+
+ /* additional init verbs */
+ struct snd_array verbs;
+};
+
+
+/*
+ * Fix-up pin default configurations and add default verbs
+ */
+
+struct hda_pintbl {
+ hda_nid_t nid;
+ u32 val;
+};
+
+struct hda_model_fixup {
+ const int id;
+ const char *name;
+};
+
+struct hda_fixup {
+ int type;
+ bool chained;
+ int chain_id;
+ union {
+ const struct hda_pintbl *pins;
+ const struct hda_verb *verbs;
+ void (*func)(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action);
+ } v;
+};
+
+/* fixup types */
+enum {
+ HDA_FIXUP_INVALID,
+ HDA_FIXUP_PINS,
+ HDA_FIXUP_VERBS,
+ HDA_FIXUP_FUNC,
+};
+
+/* fixup action definitions */
+enum {
+ HDA_FIXUP_ACT_PRE_PROBE,
+ HDA_FIXUP_ACT_PROBE,
+ HDA_FIXUP_ACT_INIT,
+ HDA_FIXUP_ACT_BUILD,
+};
+
+int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
+ const struct hda_verb *list);
+void snd_hda_gen_apply_verbs(struct hda_codec *codec);
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+ const struct hda_pintbl *cfg);
+void snd_hda_apply_fixup(struct hda_codec *codec, int action);
+void snd_hda_pick_fixup(struct hda_codec *codec,
+ const struct hda_model_fixup *models,
+ const struct snd_pci_quirk *quirk,
+ const struct hda_fixup *fixlist);
+
+static inline void snd_hda_gen_init(struct hda_gen_spec *spec)
+{
+ snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8);
+}
+
+static inline void snd_hda_gen_free(struct hda_gen_spec *spec)
+{
+ snd_array_free(&spec->verbs);
+}
+
#endif /* __SOUND_HDA_AUTO_PARSER_H */
diff --git a/trunk/sound/pci/hda/hda_codec.c b/trunk/sound/pci/hda/hda_codec.c
index 04b57383e8cb..822df971972c 100644
--- a/trunk/sound/pci/hda/hda_codec.c
+++ b/trunk/sound/pci/hda/hda_codec.c
@@ -222,14 +222,8 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
again:
snd_hda_power_up(codec);
mutex_lock(&bus->cmd_mutex);
- for (;;) {
- trace_hda_send_cmd(codec, cmd);
- err = bus->ops.command(bus, cmd);
- if (err != -EAGAIN)
- break;
- /* process pending verbs */
- bus->ops.get_response(bus, codec->addr);
- }
+ trace_hda_send_cmd(codec, cmd);
+ err = bus->ops.command(bus, cmd);
if (!err && res) {
*res = bus->ops.get_response(bus, codec->addr);
trace_hda_get_response(codec, *res);
@@ -334,116 +328,32 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
-/* connection list element */
-struct hda_conn_list {
- struct list_head list;
- int len;
- hda_nid_t nid;
- hda_nid_t conns[0];
-};
-
/* look up the cached results */
-static struct hda_conn_list *
-lookup_conn_list(struct hda_codec *codec, hda_nid_t nid)
+static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid)
{
- struct hda_conn_list *p;
- list_for_each_entry(p, &codec->conn_list, list) {
- if (p->nid == nid)
+ int i, len;
+ for (i = 0; i < array->used; ) {
+ hda_nid_t *p = snd_array_elem(array, i);
+ if (nid == *p)
return p;
+ len = p[1];
+ i += len + 2;
}
return NULL;
}
-static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
- const hda_nid_t *list)
-{
- struct hda_conn_list *p;
-
- p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL);
- if (!p)
- return -ENOMEM;
- p->len = len;
- p->nid = nid;
- memcpy(p->conns, list, len * sizeof(hda_nid_t));
- list_add(&p->list, &codec->conn_list);
- return 0;
-}
-
-static void remove_conn_list(struct hda_codec *codec)
-{
- while (!list_empty(&codec->conn_list)) {
- struct hda_conn_list *p;
- p = list_first_entry(&codec->conn_list, typeof(*p), list);
- list_del(&p->list);
- kfree(p);
- }
-}
-
/* read the connection and add to the cache */
static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
{
- hda_nid_t list[32];
- hda_nid_t *result = list;
+ hda_nid_t list[HDA_MAX_CONNECTIONS];
int len;
len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list));
- if (len == -ENOSPC) {
- len = snd_hda_get_num_raw_conns(codec, nid);
- result = kmalloc(sizeof(hda_nid_t) * len, GFP_KERNEL);
- if (!result)
- return -ENOMEM;
- len = snd_hda_get_raw_connections(codec, nid, result, len);
- }
- if (len >= 0)
- len = snd_hda_override_conn_list(codec, nid, len, result);
- if (result != list)
- kfree(result);
- return len;
+ if (len < 0)
+ return len;
+ return snd_hda_override_conn_list(codec, nid, len, list);
}
-/**
- * snd_hda_get_conn_list - get connection list
- * @codec: the HDA codec
- * @nid: NID to parse
- * @len: number of connection list entries
- * @listp: the pointer to store NID list
- *
- * Parses the connection list of the given widget and stores the pointer
- * to the list of NIDs.
- *
- * Returns the number of connections, or a negative error code.
- *
- * Note that the returned pointer isn't protected against the list
- * modification. If snd_hda_override_conn_list() might be called
- * concurrently, protect with a mutex appropriately.
- */
-int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
- const hda_nid_t **listp)
-{
- bool added = false;
-
- for (;;) {
- int err;
- const struct hda_conn_list *p;
-
- /* if the connection-list is already cached, read it */
- p = lookup_conn_list(codec, nid);
- if (p) {
- if (listp)
- *listp = p->conns;
- return p->len;
- }
- if (snd_BUG_ON(added))
- return -EINVAL;
-
- err = read_and_add_raw_conns(codec, nid);
- if (err < 0)
- return err;
- added = true;
- }
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
-
/**
* snd_hda_get_connections - copy connection list
* @codec: the HDA codec
@@ -459,44 +369,42 @@ EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns)
{
- const hda_nid_t *list;
- int len = snd_hda_get_conn_list(codec, nid, &list);
+ struct snd_array *array = &codec->conn_lists;
+ int len;
+ hda_nid_t *p;
+ bool added = false;
- if (len > 0 && conn_list) {
- if (len > max_conns) {
+ again:
+ mutex_lock(&codec->hash_mutex);
+ len = -1;
+ /* if the connection-list is already cached, read it */
+ p = lookup_conn_list(array, nid);
+ if (p) {
+ len = p[1];
+ if (conn_list && len > max_conns) {
snd_printk(KERN_ERR "hda_codec: "
"Too many connections %d for NID 0x%x\n",
len, nid);
+ mutex_unlock(&codec->hash_mutex);
return -EINVAL;
}
- memcpy(conn_list, list, len * sizeof(hda_nid_t));
+ if (conn_list && len)
+ memcpy(conn_list, p + 2, len * sizeof(hda_nid_t));
}
+ mutex_unlock(&codec->hash_mutex);
+ if (len >= 0)
+ return len;
+ if (snd_BUG_ON(added))
+ return -EINVAL;
- return len;
+ len = read_and_add_raw_conns(codec, nid);
+ if (len < 0)
+ return len;
+ added = true;
+ goto again;
}
EXPORT_SYMBOL_HDA(snd_hda_get_connections);
-/* return CONNLIST_LEN parameter of the given widget */
-static unsigned int get_num_conns(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int parm;
-
- if (!(wcaps & AC_WCAP_CONN_LIST) &&
- get_wcaps_type(wcaps) != AC_WID_VOL_KNB)
- return 0;
-
- parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
- if (parm == -1)
- parm = 0;
- return parm;
-}
-
-int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid)
-{
- return get_num_conns(codec, nid) & AC_CLIST_LENGTH;
-}
-
/**
* snd_hda_get_raw_connections - copy connection list without cache
* @codec: the HDA codec
@@ -514,16 +422,18 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
unsigned int parm;
int i, conn_len, conns;
unsigned int shift, num_elems, mask;
+ unsigned int wcaps;
hda_nid_t prev_nid;
- int null_count = 0;
if (snd_BUG_ON(!conn_list || max_conns <= 0))
return -EINVAL;
- parm = get_num_conns(codec, nid);
- if (!parm)
+ wcaps = get_wcaps(codec, nid);
+ if (!(wcaps & AC_WCAP_CONN_LIST) &&
+ get_wcaps_type(wcaps) != AC_WID_VOL_KNB)
return 0;
+ parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
if (parm & AC_CLIST_LONG) {
/* long form */
shift = 16;
@@ -564,7 +474,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
}
range_val = !!(parm & (1 << (shift-1))); /* ranges */
val = parm & mask;
- if (val == 0 && null_count++) { /* no second chance */
+ if (val == 0) {
snd_printk(KERN_WARNING "hda_codec: "
"invalid CONNECT_LIST verb %x[%i]:%x\n",
nid, i, parm);
@@ -580,13 +490,21 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
continue;
}
for (n = prev_nid + 1; n <= val; n++) {
- if (conns >= max_conns)
- return -ENOSPC;
+ if (conns >= max_conns) {
+ snd_printk(KERN_ERR "hda_codec: "
+ "Too many connections %d for NID 0x%x\n",
+ conns, nid);
+ return -EINVAL;
+ }
conn_list[conns++] = n;
}
} else {
- if (conns >= max_conns)
- return -ENOSPC;
+ if (conns >= max_conns) {
+ snd_printk(KERN_ERR "hda_codec: "
+ "Too many connections %d for NID 0x%x\n",
+ conns, nid);
+ return -EINVAL;
+ }
conn_list[conns++] = val;
}
prev_nid = val;
@@ -594,6 +512,15 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
return conns;
}
+static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
+{
+ hda_nid_t *p = snd_array_new(array);
+ if (!p)
+ return false;
+ *p = nid;
+ return true;
+}
+
/**
* snd_hda_override_conn_list - add/modify the connection-list to cache
* @codec: the HDA codec
@@ -609,15 +536,28 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
const hda_nid_t *list)
{
- struct hda_conn_list *p;
+ struct snd_array *array = &codec->conn_lists;
+ hda_nid_t *p;
+ int i, old_used;
- p = lookup_conn_list(codec, nid);
- if (p) {
- list_del(&p->list);
- kfree(p);
- }
+ mutex_lock(&codec->hash_mutex);
+ p = lookup_conn_list(array, nid);
+ if (p)
+ *p = -1; /* invalidate the old entry */
+
+ old_used = array->used;
+ if (!add_conn_list(array, nid) || !add_conn_list(array, len))
+ goto error_add;
+ for (i = 0; i < len; i++)
+ if (!add_conn_list(array, list[i]))
+ goto error_add;
+ mutex_unlock(&codec->hash_mutex);
+ return 0;
- return add_conn_list(codec, nid, len, list);
+ error_add:
+ array->used = old_used;
+ mutex_unlock(&codec->hash_mutex);
+ return -ENOMEM;
}
EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
@@ -635,16 +575,16 @@ EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid, int recursive)
{
- const hda_nid_t *conn;
+ hda_nid_t conn[HDA_MAX_NUM_INPUTS];
int i, nums;
- nums = snd_hda_get_conn_list(codec, mux, &conn);
+ nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
for (i = 0; i < nums; i++)
if (conn[i] == nid)
return i;
if (!recursive)
return -1;
- if (recursive > 10) {
+ if (recursive > 5) {
snd_printd("hda_codec: too deep connection for 0x%x\n", nid);
return -1;
}
@@ -1106,16 +1046,9 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
struct hda_pincfg *pin;
#ifdef CONFIG_SND_HDA_HWDEP
- {
- unsigned int cfg = 0;
- mutex_lock(&codec->user_mutex);
- pin = look_up_pincfg(codec, &codec->user_pins, nid);
- if (pin)
- cfg = pin->cfg;
- mutex_unlock(&codec->user_mutex);
- if (cfg)
- return cfg;
- }
+ pin = look_up_pincfg(codec, &codec->user_pins, nid);
+ if (pin)
+ return pin->cfg;
#endif
pin = look_up_pincfg(codec, &codec->driver_pins, nid);
if (pin)
@@ -1127,32 +1060,6 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
}
EXPORT_SYMBOL_HDA(snd_hda_codec_get_pincfg);
-/* remember the current pinctl target value */
-int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
- unsigned int val)
-{
- struct hda_pincfg *pin;
-
- pin = look_up_pincfg(codec, &codec->init_pins, nid);
- if (!pin)
- return -EINVAL;
- pin->target = val;
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_set_pin_target);
-
-/* return the current pinctl target value */
-int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid)
-{
- struct hda_pincfg *pin;
-
- pin = look_up_pincfg(codec, &codec->init_pins, nid);
- if (!pin)
- return 0;
- return pin->target;
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_get_pin_target);
-
/**
* snd_hda_shutup_pins - Shut up all pins
* @codec: the HDA codec
@@ -1272,8 +1179,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
snd_array_free(&codec->mixers);
snd_array_free(&codec->nids);
snd_array_free(&codec->cvt_setups);
+ snd_array_free(&codec->conn_lists);
snd_array_free(&codec->spdif_out);
- remove_conn_list(codec);
codec->bus->caddr_tbl[codec->addr] = NULL;
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
@@ -1296,8 +1203,6 @@ static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec,
static unsigned int hda_set_power_state(struct hda_codec *codec,
unsigned int power_state);
-static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid,
- unsigned int power_state);
/**
* snd_hda_codec_new - create a HDA codec
@@ -1345,11 +1250,9 @@ int snd_hda_codec_new(struct hda_bus *bus,
snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
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);
snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
- snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
- INIT_LIST_HEAD(&codec->conn_list);
-
INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
#ifdef CONFIG_PM
@@ -1418,7 +1321,6 @@ int snd_hda_codec_new(struct hda_bus *bus,
#endif
codec->epss = snd_hda_codec_get_supported_ps(codec, fg,
AC_PWRST_EPSS);
- codec->power_filter = default_power_filter;
/* power-up all before initialization */
hda_set_power_state(codec, AC_PWRST_D0);
@@ -1441,30 +1343,6 @@ int snd_hda_codec_new(struct hda_bus *bus,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_new);
-int snd_hda_codec_update_widgets(struct hda_codec *codec)
-{
- hda_nid_t fg;
- int err;
-
- /* Assume the function group node does not change,
- * only the widget nodes may change.
- */
- kfree(codec->wcaps);
- fg = codec->afg ? codec->afg : codec->mfg;
- err = read_widget_caps(codec, fg);
- if (err < 0) {
- snd_printk(KERN_ERR "hda_codec: cannot malloc\n");
- return err;
- }
-
- snd_array_free(&codec->init_pins);
- err = read_pin_defaults(codec);
-
- return err;
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_update_widgets);
-
-
/**
* snd_hda_codec_configure - (Re-)configure the HD-audio codec
* @codec: the HDA codec
@@ -1573,7 +1451,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
"NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
nid, stream_tag, channel_id, format);
p = get_hda_cvt_setup(codec, nid);
- if (!p || p->active)
+ if (!p)
return;
if (codec->pcm_format_first)
@@ -1620,7 +1498,7 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
p = get_hda_cvt_setup(codec, nid);
- if (p && p->active) {
+ if (p) {
/* here we just clear the active flag when do_now isn't set;
* actual clean-ups will be done later in
* purify_inactive_streams() called from snd_hda_codec_prpapre()
@@ -1732,7 +1610,6 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
cur = snd_array_index(&cache->buf, info);
info->key = key;
info->val = 0;
- info->dirty = 0;
idx = key % (u16)ARRAY_SIZE(cache->hash);
info->next = cache->hash[idx];
cache->hash[idx] = cur;
@@ -1887,7 +1764,7 @@ EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps);
*/
static struct hda_amp_info *
update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int index, bool init_only)
+ int direction, int index)
{
struct hda_amp_info *info;
unsigned int parm, val = 0;
@@ -1913,15 +1790,14 @@ update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
}
info->vol[ch] = val;
info->head.val |= INFO_AMP_VOL(ch);
- } else if (init_only)
- return NULL;
+ }
return info;
}
/*
* write the current volume in info to the h/w
*/
-static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps,
+static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
hda_nid_t nid, int ch, int direction, int index,
int val)
{
@@ -1930,8 +1806,8 @@ static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps,
parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
parm |= index << AC_AMP_SET_INDEX_SHIFT;
- if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) &&
- (amp_caps & AC_AMPCAP_MIN_MUTE))
+ if ((val & HDA_AMP_MUTE) && !(info->amp_caps & AC_AMPCAP_MUTE) &&
+ (info->amp_caps & AC_AMPCAP_MIN_MUTE))
; /* set the zero value as a fake mute */
else
parm |= val;
@@ -1955,7 +1831,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
unsigned int val = 0;
mutex_lock(&codec->hash_mutex);
- info = update_amp_hash(codec, nid, ch, direction, index, false);
+ info = update_amp_hash(codec, nid, ch, direction, index);
if (info)
val = info->vol[ch];
mutex_unlock(&codec->hash_mutex);
@@ -1963,20 +1839,30 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
-static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int idx, int mask, int val,
- bool init_only)
+/**
+ * snd_hda_codec_amp_update - update the AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP value with a bit mask.
+ * Returns 0 if the value is unchanged, 1 if changed.
+ */
+int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
+ int direction, int idx, int mask, int val)
{
struct hda_amp_info *info;
- unsigned int caps;
- unsigned int cache_only;
if (snd_BUG_ON(mask & ~0xff))
mask &= 0xff;
val &= mask;
mutex_lock(&codec->hash_mutex);
- info = update_amp_hash(codec, nid, ch, direction, idx, init_only);
+ info = update_amp_hash(codec, nid, ch, direction, idx);
if (!info) {
mutex_unlock(&codec->hash_mutex);
return 0;
@@ -1987,32 +1873,10 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
return 0;
}
info->vol[ch] = val;
- cache_only = info->head.dirty = codec->cached_write;
- caps = info->amp_caps;
mutex_unlock(&codec->hash_mutex);
- if (!cache_only)
- put_vol_mute(codec, caps, nid, ch, direction, idx, val);
+ put_vol_mute(codec, info, nid, ch, direction, idx, val);
return 1;
}
-
-/**
- * snd_hda_codec_amp_update - update the AMP value
- * @codec: HD-audio codec
- * @nid: NID to read the AMP value
- * @ch: channel (left=0 or right=1)
- * @direction: #HDA_INPUT or #HDA_OUTPUT
- * @idx: the index value (only for input direction)
- * @mask: bit mask to set
- * @val: the bits value to set
- *
- * Update the AMP value with a bit mask.
- * Returns 0 if the value is unchanged, 1 if changed.
- */
-int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int idx, int mask, int val)
-{
- return codec_amp_update(codec, nid, ch, direction, idx, mask, val, false);
-}
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
/**
@@ -2041,31 +1905,7 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
-/* Works like snd_hda_codec_amp_update() but it writes the value only at
- * the first access. If the amp was already initialized / updated beforehand,
- * this does nothing.
- */
-int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
- int dir, int idx, int mask, int val)
-{
- return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true);
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init);
-
-int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
- int dir, int idx, int mask, int val)
-{
- int ch, ret = 0;
-
- if (snd_BUG_ON(mask & ~0xff))
- mask &= 0xff;
- for (ch = 0; ch < 2; ch++)
- ret |= snd_hda_codec_amp_init(codec, nid, ch, dir,
- idx, mask, val);
- return ret;
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init_stereo);
-
+#ifdef CONFIG_PM
/**
* snd_hda_codec_resume_amp - Resume all AMP commands from the cache
* @codec: HD-audio codec
@@ -2074,40 +1914,28 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init_stereo);
*/
void snd_hda_codec_resume_amp(struct hda_codec *codec)
{
+ struct hda_amp_info *buffer = codec->amp_cache.buf.list;
int i;
- mutex_lock(&codec->hash_mutex);
- codec->cached_write = 0;
- for (i = 0; i < codec->amp_cache.buf.used; i++) {
- struct hda_amp_info *buffer;
- u32 key;
+ for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) {
+ u32 key = buffer->head.key;
hda_nid_t nid;
unsigned int idx, dir, ch;
- struct hda_amp_info info;
-
- buffer = snd_array_elem(&codec->amp_cache.buf, i);
- if (!buffer->head.dirty)
- continue;
- buffer->head.dirty = 0;
- info = *buffer;
- key = info.head.key;
if (!key)
continue;
nid = key & 0xff;
idx = (key >> 16) & 0xff;
dir = (key >> 24) & 0xff;
for (ch = 0; ch < 2; ch++) {
- if (!(info.head.val & INFO_AMP_VOL(ch)))
+ if (!(buffer->head.val & INFO_AMP_VOL(ch)))
continue;
- mutex_unlock(&codec->hash_mutex);
- put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx,
- info.vol[ch]);
- mutex_lock(&codec->hash_mutex);
+ put_vol_mute(codec, buffer, nid, ch, dir, idx,
+ buffer->vol[ch]);
}
}
- mutex_unlock(&codec->hash_mutex);
}
EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
+#endif /* CONFIG_PM */
static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int ofs)
@@ -2332,12 +2160,11 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
static int find_empty_mixer_ctl_idx(struct hda_codec *codec, const char *name,
- int start_idx)
+ int dev)
{
- int i, idx;
- /* 16 ctlrs should be large enough */
- for (i = 0, idx = start_idx; i < 16; i++, idx++) {
- if (!find_mixer_ctl(codec, name, 0, idx))
+ int idx;
+ for (idx = 0; idx < 16; idx++) { /* 16 ctlrs should be large enough */
+ if (!find_mixer_ctl(codec, name, dev, idx))
return idx;
}
return -EBUSY;
@@ -2535,7 +2362,6 @@ int snd_hda_codec_reset(struct hda_codec *codec)
snd_array_free(&codec->driver_pins);
snd_array_free(&codec->cvt_setups);
snd_array_free(&codec->spdif_out);
- snd_array_free(&codec->verbs);
codec->num_pcms = 0;
codec->pcm_info = NULL;
codec->preset = NULL;
@@ -3306,29 +3132,30 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
int err;
struct snd_kcontrol *kctl;
struct snd_kcontrol_new *dig_mix;
- int idx = 0;
- const int spdif_index = 16;
+ int idx, dev = 0;
+ const int spdif_pcm_dev = 1;
struct hda_spdif_out *spdif;
- struct hda_bus *bus = codec->bus;
- if (bus->primary_dig_out_type == HDA_PCM_TYPE_HDMI &&
+ if (codec->primary_dig_out_type == HDA_PCM_TYPE_HDMI &&
type == HDA_PCM_TYPE_SPDIF) {
- idx = spdif_index;
- } else if (bus->primary_dig_out_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) {
- /* suppose a single SPDIF device */
- for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
- kctl = find_mixer_ctl(codec, dig_mix->name, 0, 0);
- if (!kctl)
- break;
- kctl->id.index = spdif_index;
+ 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;
+ }
}
- bus->primary_dig_out_type = HDA_PCM_TYPE_HDMI;
+ codec->primary_dig_out_type = HDA_PCM_TYPE_HDMI;
}
- if (!bus->primary_dig_out_type)
- bus->primary_dig_out_type = type;
+ if (!codec->primary_dig_out_type)
+ codec->primary_dig_out_type = type;
- idx = find_empty_mixer_ctl_idx(codec, "IEC958 Playback Switch", idx);
+ 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;
@@ -3338,6 +3165,7 @@ int snd_hda_create_dig_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);
@@ -3547,11 +3375,12 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
}
EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
+#ifdef CONFIG_PM
/*
* command cache
*/
-/* build a 31bit cache key with the widget id and the command parameter */
+/* build a 32bit cache key with the widget id and the command parameter */
#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid)
#define get_cmd_cache_nid(key) ((key) & 0xff)
#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff)
@@ -3571,28 +3400,20 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
int direct, unsigned int verb, unsigned int parm)
{
- int err;
+ int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
struct hda_cache_head *c;
u32 key;
- unsigned int cache_only;
-
- cache_only = codec->cached_write;
- if (!cache_only) {
- err = snd_hda_codec_write(codec, nid, direct, verb, parm);
- if (err < 0)
- return err;
- }
+ if (err < 0)
+ return err;
/* parm may contain the verb stuff for get/set amp */
verb = verb | (parm >> 8);
parm &= 0xff;
key = build_cmd_cache_key(nid, verb);
mutex_lock(&codec->bus->cmd_mutex);
c = get_alloc_hash(&codec->cmd_cache, key);
- if (c) {
+ if (c)
c->val = parm;
- c->dirty = cache_only;
- }
mutex_unlock(&codec->bus->cmd_mutex);
return 0;
}
@@ -3641,27 +3462,16 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache);
*/
void snd_hda_codec_resume_cache(struct hda_codec *codec)
{
+ struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
int i;
- mutex_lock(&codec->hash_mutex);
- codec->cached_write = 0;
- for (i = 0; i < codec->cmd_cache.buf.used; i++) {
- struct hda_cache_head *buffer;
- u32 key;
-
- buffer = snd_array_elem(&codec->cmd_cache.buf, i);
- key = buffer->key;
+ for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) {
+ u32 key = buffer->key;
if (!key)
continue;
- if (!buffer->dirty)
- continue;
- buffer->dirty = 0;
- mutex_unlock(&codec->hash_mutex);
snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0,
get_cmd_cache_cmd(key), buffer->val);
- mutex_lock(&codec->hash_mutex);
}
- mutex_unlock(&codec->hash_mutex);
}
EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
@@ -3682,36 +3492,32 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
seq->param);
}
EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
-
-/**
- * snd_hda_codec_flush_cache - Execute all pending (cached) amps / verbs
- * @codec: HD-audio codec
- */
-void snd_hda_codec_flush_cache(struct hda_codec *codec)
-{
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
-}
-EXPORT_SYMBOL_HDA(snd_hda_codec_flush_cache);
+#endif /* CONFIG_PM */
void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state)
+ unsigned int power_state,
+ bool eapd_workaround)
{
hda_nid_t nid = codec->start_nid;
int i;
for (i = 0; i < codec->num_nodes; i++, nid++) {
unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int state = power_state;
if (!(wcaps & AC_WCAP_POWER))
continue;
- if (codec->power_filter) {
- state = codec->power_filter(codec, nid, power_state);
- if (state != power_state && power_state == AC_PWRST_D3)
+ /* don't power down the widget if it controls eapd and
+ * EAPD_BTLENABLE is set.
+ */
+ if (eapd_workaround && power_state == AC_PWRST_D3 &&
+ get_wcaps_type(wcaps) == AC_WID_PIN &&
+ (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) {
+ int eapd = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_EAPD_BTLENABLE, 0);
+ if (eapd & 0x02)
continue;
}
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
- state);
+ power_state);
}
}
EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);
@@ -3758,21 +3564,6 @@ static unsigned int hda_sync_power_state(struct hda_codec *codec,
return state;
}
-/* don't power down the widget if it controls eapd and EAPD_BTLENABLE is set */
-static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid,
- unsigned int power_state)
-{
- if (power_state == AC_PWRST_D3 &&
- get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN &&
- (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) {
- int eapd = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_EAPD_BTLENABLE, 0);
- if (eapd & 0x02)
- return AC_PWRST_D0;
- }
- return power_state;
-}
-
/*
* set power state of the codec, and return the power state
*/
@@ -3798,7 +3589,8 @@ static unsigned int hda_set_power_state(struct hda_codec *codec,
snd_hda_codec_read(codec, fg, 0,
AC_VERB_SET_POWER_STATE,
power_state);
- snd_hda_codec_set_power_to_all(codec, fg, power_state);
+ snd_hda_codec_set_power_to_all(codec, fg, power_state,
+ true);
}
state = hda_sync_power_state(codec, fg, power_state);
if (!(state & AC_PWRST_ERROR))
@@ -3808,32 +3600,6 @@ static unsigned int hda_set_power_state(struct hda_codec *codec,
return state;
}
-/* sync power states of all widgets;
- * this is called at the end of codec parsing
- */
-static void sync_power_up_states(struct hda_codec *codec)
-{
- hda_nid_t nid = codec->start_nid;
- int i;
-
- /* don't care if no or standard filter is used */
- if (!codec->power_filter || codec->power_filter == default_power_filter)
- return;
-
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- unsigned int wcaps = get_wcaps(codec, nid);
- unsigned int target;
- if (!(wcaps & AC_WCAP_POWER))
- continue;
- target = codec->power_filter(codec, nid, AC_PWRST_D0);
- if (target == AC_PWRST_D0)
- continue;
- if (!snd_hda_check_power_state(codec, nid, target))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE, target);
- }
-}
-
#ifdef CONFIG_SND_HDA_HWDEP
/* execute additional init verbs */
static void hda_exec_init_verbs(struct hda_codec *codec)
@@ -3874,22 +3640,6 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq)
return state;
}
-/* mark all entries of cmd and amp caches dirty */
-static void hda_mark_cmd_cache_dirty(struct hda_codec *codec)
-{
- int i;
- for (i = 0; i < codec->cmd_cache.buf.used; i++) {
- struct hda_cache_head *cmd;
- cmd = snd_array_elem(&codec->cmd_cache.buf, i);
- cmd->dirty = 1;
- }
- for (i = 0; i < codec->amp_cache.buf.used; i++) {
- struct hda_amp_info *amp;
- amp = snd_array_elem(&codec->amp_cache.buf, i);
- amp->head.dirty = 1;
- }
-}
-
/*
* kick up codec; used both from PM and power-save
*/
@@ -3897,8 +3647,6 @@ static void hda_call_codec_resume(struct hda_codec *codec)
{
codec->in_pm = 1;
- hda_mark_cmd_cache_dirty(codec);
-
/* set as if powered on for avoiding re-entering the resume
* in the resume / power-save sequence
*/
@@ -4021,7 +3769,6 @@ int snd_hda_codec_build_controls(struct hda_codec *codec)
hda_jackpoll_work(&codec->jackpoll_work.work);
else
snd_hda_jack_report_sync(codec); /* call at the last init point */
- sync_power_up_states(codec);
return 0;
}
@@ -5373,62 +5120,23 @@ unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin)
}
EXPORT_SYMBOL_HDA(snd_hda_get_default_vref);
-/* correct the pin ctl value for matching with the pin cap */
-unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
- hda_nid_t pin, unsigned int val)
-{
- static unsigned int cap_lists[][2] = {
- { AC_PINCTL_VREF_100, AC_PINCAP_VREF_100 },
- { AC_PINCTL_VREF_80, AC_PINCAP_VREF_80 },
- { AC_PINCTL_VREF_50, AC_PINCAP_VREF_50 },
- { AC_PINCTL_VREF_GRD, AC_PINCAP_VREF_GRD },
- };
- unsigned int cap;
-
- if (!val)
- return 0;
- cap = snd_hda_query_pin_caps(codec, pin);
- if (!cap)
- return val; /* don't know what to do... */
-
- if (val & AC_PINCTL_OUT_EN) {
- if (!(cap & AC_PINCAP_OUT))
- val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
- else if ((val & AC_PINCTL_HP_EN) && !(cap & AC_PINCAP_HP_DRV))
- val &= ~AC_PINCTL_HP_EN;
- }
-
- if (val & AC_PINCTL_IN_EN) {
- if (!(cap & AC_PINCAP_IN))
- val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
- else {
- unsigned int vcap, vref;
- int i;
- vcap = (cap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
- vref = val & AC_PINCTL_VREFEN;
- for (i = 0; i < ARRAY_SIZE(cap_lists); i++) {
- if (vref == cap_lists[i][0] &&
- !(vcap & cap_lists[i][1])) {
- if (i == ARRAY_SIZE(cap_lists) - 1)
- vref = AC_PINCTL_VREF_HIZ;
- else
- vref = cap_lists[i + 1][0];
- }
- }
- val &= ~AC_PINCTL_VREFEN;
- val |= vref;
- }
- }
-
- return val;
-}
-EXPORT_SYMBOL_HDA(snd_hda_correct_pin_ctl);
-
int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
unsigned int val, bool cached)
{
- val = snd_hda_correct_pin_ctl(codec, pin, val);
- snd_hda_codec_set_pin_target(codec, pin, val);
+ if (val) {
+ unsigned int cap = snd_hda_query_pin_caps(codec, pin);
+ if (cap && (val & AC_PINCTL_OUT_EN)) {
+ if (!(cap & AC_PINCAP_OUT))
+ val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ else if ((val & AC_PINCTL_HP_EN) &&
+ !(cap & AC_PINCAP_HP_DRV))
+ val &= ~AC_PINCTL_HP_EN;
+ }
+ if (cap && (val & AC_PINCTL_IN_EN)) {
+ if (!(cap & AC_PINCAP_IN))
+ val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
+ }
+ }
if (cached)
return snd_hda_codec_update_cache(codec, pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, val);
diff --git a/trunk/sound/pci/hda/hda_codec.h b/trunk/sound/pci/hda/hda_codec.h
index 23ca1722aff1..8665540e55aa 100644
--- a/trunk/sound/pci/hda/hda_codec.h
+++ b/trunk/sound/pci/hda/hda_codec.h
@@ -551,6 +551,9 @@ enum {
AC_JACK_PORT_BOTH,
};
+/* max. connections to a widget */
+#define HDA_MAX_CONNECTIONS 32
+
/* max. codec address */
#define HDA_MAX_CODEC_ADDRESS 0x0f
@@ -615,17 +618,6 @@ struct hda_bus_ops {
/* notify power-up/down from codec to controller */
void (*pm_notify)(struct hda_bus *bus, bool power_up);
#endif
-#ifdef CONFIG_SND_HDA_DSP_LOADER
- /* prepare DSP transfer */
- int (*load_dsp_prepare)(struct hda_bus *bus, unsigned int format,
- unsigned int byte_size,
- struct snd_dma_buffer *bufp);
- /* start/stop DSP transfer */
- void (*load_dsp_trigger)(struct hda_bus *bus, bool start);
- /* clean up DSP transfer */
- void (*load_dsp_cleanup)(struct hda_bus *bus,
- struct snd_dma_buffer *dmab);
-#endif
};
/* template to pass to the bus constructor */
@@ -679,8 +671,6 @@ struct hda_bus {
unsigned int response_reset:1; /* controller was reset */
unsigned int in_reset:1; /* during reset operation */
unsigned int power_keep_link_on:1; /* don't power off HDA link */
-
- int primary_dig_out_type; /* primary digital out PCM type */
};
/*
@@ -729,10 +719,9 @@ struct hda_codec_ops {
/* record for amp information cache */
struct hda_cache_head {
- u32 key:31; /* hash key */
- u32 dirty:1;
+ u32 key; /* hash key */
u16 val; /* assigned value */
- u16 next;
+ u16 next; /* next link; -1 = terminal */
};
struct hda_amp_info {
@@ -841,20 +830,20 @@ struct hda_codec {
struct hda_cache_rec amp_cache; /* cache for amp access */
struct hda_cache_rec cmd_cache; /* cache for other commands */
- struct list_head conn_list; /* linked-list of connection-list */
+ struct snd_array conn_lists; /* connection-list array */
struct mutex spdif_mutex;
struct mutex control_mutex;
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 */
struct snd_array cvt_setups; /* audio convert setups */
#ifdef CONFIG_SND_HDA_HWDEP
- struct mutex user_mutex;
struct snd_hwdep *hwdep; /* assigned hwdep device */
struct snd_array init_verbs; /* additional init verbs */
struct snd_array hints; /* additional hints */
@@ -876,11 +865,8 @@ struct hda_codec {
unsigned int pins_shutup:1; /* pins are shut up */
unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
unsigned int no_jack_detect:1; /* Machine has no jack-detection */
- unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */
- unsigned int inv_jack_detect:1; /* broken h/w: inverted detection bit */
unsigned int pcm_format_first:1; /* PCM format must be set first */
unsigned int epss:1; /* supporting EPSS? */
- unsigned int cached_write:1; /* write only to caches */
#ifdef CONFIG_PM
unsigned int power_on :1; /* current (global) power-state */
unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */
@@ -895,10 +881,6 @@ struct hda_codec {
spinlock_t power_lock;
#endif
- /* filter the requested power state per nid */
- unsigned int (*power_filter)(struct hda_codec *codec, hda_nid_t nid,
- unsigned int power_state);
-
/* codec-specific additional proc output */
void (*proc_widget_hook)(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid);
@@ -912,14 +894,6 @@ struct hda_codec {
/* jack detection */
struct snd_array jacks;
#endif
-
- /* fix-up list */
- int fixup_id;
- const struct hda_fixup *fixup_list;
- const char *fixup_name;
-
- /* additional init verbs */
- struct snd_array verbs;
};
/* direction */
@@ -936,7 +910,6 @@ int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
struct hda_codec **codecp);
int snd_hda_codec_configure(struct hda_codec *codec);
-int snd_hda_codec_update_widgets(struct hda_codec *codec);
/*
* low level functions
@@ -957,11 +930,8 @@ snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_get_connections(codec, nid, NULL, 0);
}
-int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
-int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
- const hda_nid_t **listp);
int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
const hda_nid_t *list);
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
@@ -982,6 +952,7 @@ void snd_hda_sequence_write(struct hda_codec *codec,
int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
/* cached write */
+#ifdef CONFIG_PM
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
int direct, unsigned int verb, unsigned int parm);
void snd_hda_sequence_write_cache(struct hda_codec *codec,
@@ -989,14 +960,17 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
int direct, unsigned int verb, unsigned int parm);
void snd_hda_codec_resume_cache(struct hda_codec *codec);
-/* both for cmd & amp caches */
-void snd_hda_codec_flush_cache(struct hda_codec *codec);
+#else
+#define snd_hda_codec_write_cache snd_hda_codec_write
+#define snd_hda_codec_update_cache snd_hda_codec_write
+#define snd_hda_sequence_write_cache snd_hda_sequence_write
+#endif
/* the struct for codec->pin_configs */
struct hda_pincfg {
hda_nid_t nid;
- unsigned char ctrl; /* original pin control value */
- unsigned char target; /* target pin control value */
+ unsigned char ctrl; /* current pin control value */
+ unsigned char pad; /* reserved */
unsigned int cfg; /* default configuration */
};
@@ -1062,7 +1036,8 @@ extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[];
void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
void snd_hda_bus_reboot_notify(struct hda_bus *bus);
void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state);
+ unsigned int power_state,
+ bool eapd_workaround);
int snd_hda_lock_devices(struct hda_bus *bus);
void snd_hda_unlock_devices(struct hda_bus *bus);
@@ -1161,40 +1136,6 @@ static inline void snd_hda_power_sync(struct hda_codec *codec)
int snd_hda_load_patch(struct hda_bus *bus, size_t size, const void *buf);
#endif
-#ifdef CONFIG_SND_HDA_DSP_LOADER
-static inline int
-snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
- unsigned int size,
- struct snd_dma_buffer *bufp)
-{
- return codec->bus->ops.load_dsp_prepare(codec->bus, format, size, bufp);
-}
-static inline void
-snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start)
-{
- return codec->bus->ops.load_dsp_trigger(codec->bus, start);
-}
-static inline void
-snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
- struct snd_dma_buffer *dmab)
-{
- return codec->bus->ops.load_dsp_cleanup(codec->bus, dmab);
-}
-#else
-static inline int
-snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format,
- unsigned int size,
- struct snd_dma_buffer *bufp)
-{
- return -ENOSYS;
-}
-static inline void
-snd_hda_codec_load_dsp_trigger(struct hda_codec *codec, bool start) {}
-static inline void
-snd_hda_codec_load_dsp_cleanup(struct hda_codec *codec,
- struct snd_dma_buffer *dmab) {}
-#endif
-
/*
* Codec modularization
*/
diff --git a/trunk/sound/pci/hda/hda_generic.c b/trunk/sound/pci/hda/hda_generic.c
index 78897d05d80f..b81d3d0b952d 100644
--- a/trunk/sound/pci/hda/hda_generic.c
+++ b/trunk/sound/pci/hda/hda_generic.c
@@ -23,4907 +23,1063 @@
#include
#include
#include
-#include
-#include
-#include
-#include
-#include
#include
-#include
#include "hda_codec.h"
#include "hda_local.h"
-#include "hda_auto_parser.h"
-#include "hda_jack.h"
-#include "hda_generic.h"
+/* widget node for parsing */
+struct hda_gnode {
+ hda_nid_t nid; /* NID of this widget */
+ unsigned short nconns; /* number of input connections */
+ hda_nid_t *conn_list;
+ hda_nid_t slist[2]; /* temporay list */
+ unsigned int wid_caps; /* widget capabilities */
+ unsigned char type; /* widget type */
+ unsigned char pin_ctl; /* pin controls */
+ unsigned char checked; /* the flag indicates that the node is already parsed */
+ unsigned int pin_caps; /* pin widget capabilities */
+ unsigned int def_cfg; /* default configuration */
+ unsigned int amp_out_caps; /* AMP out capabilities */
+ unsigned int amp_in_caps; /* AMP in capabilities */
+ struct list_head list;
+};
-/* initialize hda_gen_spec struct */
-int snd_hda_gen_spec_init(struct hda_gen_spec *spec)
-{
- snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
- snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
- snd_array_init(&spec->loopback_list, sizeof(struct hda_amp_list), 8);
- mutex_init(&spec->pcm_mutex);
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init);
+/* patch-specific record */
-struct snd_kcontrol_new *
-snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name,
- const struct snd_kcontrol_new *temp)
-{
- struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls);
- if (!knew)
- return NULL;
- *knew = *temp;
- if (name)
- knew->name = kstrdup(name, GFP_KERNEL);
- else if (knew->name)
- knew->name = kstrdup(knew->name, GFP_KERNEL);
- if (!knew->name)
- return NULL;
- return knew;
-}
-EXPORT_SYMBOL_HDA(snd_hda_gen_add_kctl);
+#define MAX_PCM_VOLS 2
+struct pcm_vol {
+ struct hda_gnode *node; /* Node for PCM volume */
+ unsigned int index; /* connection of PCM volume */
+};
-static void free_kctls(struct hda_gen_spec *spec)
-{
- if (spec->kctls.list) {
- struct snd_kcontrol_new *kctl = spec->kctls.list;
- int i;
- for (i = 0; i < spec->kctls.used; i++)
- kfree(kctl[i].name);
- }
- snd_array_free(&spec->kctls);
-}
+struct hda_gspec {
+ struct hda_gnode *dac_node[2]; /* DAC node */
+ struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */
+ struct pcm_vol pcm_vol[MAX_PCM_VOLS]; /* PCM volumes */
+ unsigned int pcm_vol_nodes; /* number of PCM volumes */
-void snd_hda_gen_spec_free(struct hda_gen_spec *spec)
-{
- if (!spec)
- return;
- free_kctls(spec);
- snd_array_free(&spec->paths);
- snd_array_free(&spec->loopback_list);
-}
-EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free);
+ struct hda_gnode *adc_node; /* ADC node */
+ struct hda_gnode *cap_vol_node; /* Node for capture volume */
+ unsigned int cur_cap_src; /* current capture source */
+ struct hda_input_mux input_mux;
-/*
- * store user hints
- */
-static void parse_user_hints(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- int val;
+ unsigned int def_amp_in_caps;
+ unsigned int def_amp_out_caps;
- val = snd_hda_get_bool_hint(codec, "jack_detect");
- if (val >= 0)
- codec->no_jack_detect = !val;
- val = snd_hda_get_bool_hint(codec, "inv_jack_detect");
- if (val >= 0)
- codec->inv_jack_detect = !!val;
- val = snd_hda_get_bool_hint(codec, "trigger_sense");
- if (val >= 0)
- codec->no_trigger_sense = !val;
- val = snd_hda_get_bool_hint(codec, "inv_eapd");
- if (val >= 0)
- codec->inv_eapd = !!val;
- val = snd_hda_get_bool_hint(codec, "pcm_format_first");
- if (val >= 0)
- codec->pcm_format_first = !!val;
- val = snd_hda_get_bool_hint(codec, "sticky_stream");
- if (val >= 0)
- codec->no_sticky_stream = !val;
- val = snd_hda_get_bool_hint(codec, "spdif_status_reset");
- if (val >= 0)
- codec->spdif_status_reset = !!val;
- val = snd_hda_get_bool_hint(codec, "pin_amp_workaround");
- if (val >= 0)
- codec->pin_amp_workaround = !!val;
- val = snd_hda_get_bool_hint(codec, "single_adc_amp");
- if (val >= 0)
- codec->single_adc_amp = !!val;
+ struct hda_pcm pcm_rec; /* PCM information */
- val = snd_hda_get_bool_hint(codec, "auto_mute");
- if (val >= 0)
- spec->suppress_auto_mute = !val;
- val = snd_hda_get_bool_hint(codec, "auto_mic");
- if (val >= 0)
- spec->suppress_auto_mic = !val;
- val = snd_hda_get_bool_hint(codec, "line_in_auto_switch");
- if (val >= 0)
- spec->line_in_auto_switch = !!val;
- val = snd_hda_get_bool_hint(codec, "need_dac_fix");
- if (val >= 0)
- spec->need_dac_fix = !!val;
- val = snd_hda_get_bool_hint(codec, "primary_hp");
- if (val >= 0)
- spec->no_primary_hp = !val;
- val = snd_hda_get_bool_hint(codec, "multi_cap_vol");
- if (val >= 0)
- spec->multi_cap_vol = !!val;
- val = snd_hda_get_bool_hint(codec, "inv_dmic_split");
- if (val >= 0)
- spec->inv_dmic_split = !!val;
- val = snd_hda_get_bool_hint(codec, "indep_hp");
- if (val >= 0)
- spec->indep_hp = !!val;
- val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input");
- if (val >= 0)
- spec->add_stereo_mix_input = !!val;
- val = snd_hda_get_bool_hint(codec, "add_out_jack_modes");
- if (val >= 0)
- spec->add_out_jack_modes = !!val;
- val = snd_hda_get_bool_hint(codec, "add_in_jack_modes");
- if (val >= 0)
- spec->add_in_jack_modes = !!val;
- val = snd_hda_get_bool_hint(codec, "power_down_unused");
- if (val >= 0)
- spec->power_down_unused = !!val;
+ struct list_head nid_list; /* list of widgets */
- if (!snd_hda_get_int_hint(codec, "mixer_nid", &val))
- spec->mixer_nid = val;
-}
+#ifdef CONFIG_PM
+#define MAX_LOOPBACK_AMPS 7
+ struct hda_loopback_check loopback;
+ int num_loopbacks;
+ struct hda_amp_list loopback_list[MAX_LOOPBACK_AMPS + 1];
+#endif
+};
/*
- * pin control value accesses
+ * retrieve the default device type from the default config value
*/
+#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> \
+ AC_DEFCFG_DEVICE_SHIFT)
+#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> \
+ AC_DEFCFG_LOCATION_SHIFT)
+#define defcfg_port_conn(node) (((node)->def_cfg & AC_DEFCFG_PORT_CONN) >> \
+ AC_DEFCFG_PORT_CONN_SHIFT)
-#define update_pin_ctl(codec, pin, val) \
- snd_hda_codec_update_cache(codec, pin, 0, \
- AC_VERB_SET_PIN_WIDGET_CONTROL, val)
-
-/* restore the pinctl based on the cached value */
-static inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin)
+/*
+ * destructor
+ */
+static void snd_hda_generic_free(struct hda_codec *codec)
{
- update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin));
-}
+ struct hda_gspec *spec = codec->spec;
+ struct hda_gnode *node, *n;
-/* set the pinctl target value and write it if requested */
-static void set_pin_target(struct hda_codec *codec, hda_nid_t pin,
- unsigned int val, bool do_write)
-{
- if (!pin)
+ if (! spec)
return;
- val = snd_hda_correct_pin_ctl(codec, pin, val);
- snd_hda_codec_set_pin_target(codec, pin, val);
- if (do_write)
- update_pin_ctl(codec, pin, val);
+ /* free all widgets */
+ list_for_each_entry_safe(node, n, &spec->nid_list, list) {
+ if (node->conn_list != node->slist)
+ kfree(node->conn_list);
+ kfree(node);
+ }
+ kfree(spec);
}
-/* set pinctl target values for all given pins */
-static void set_pin_targets(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins, unsigned int val)
-{
- int i;
- for (i = 0; i < num_pins; i++)
- set_pin_target(codec, pins[i], val, false);
-}
/*
- * parsing paths
+ * add a new widget node and read its attributes
*/
-
-/* return the position of NID in the list, or -1 if not found */
-static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid)
{
- int i;
- for (i = 0; i < nums; i++)
- if (list[i] == nid)
- return i;
- return -1;
-}
+ struct hda_gnode *node;
+ int nconns;
+ hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
-/* return true if the given NID is contained in the path */
-static bool is_nid_contained(struct nid_path *path, hda_nid_t nid)
-{
- return find_idx_in_nid_list(nid, path->path, path->depth) >= 0;
-}
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (node == NULL)
+ return -ENOMEM;
+ node->nid = nid;
+ node->wid_caps = get_wcaps(codec, nid);
+ node->type = get_wcaps_type(node->wid_caps);
+ if (node->wid_caps & AC_WCAP_CONN_LIST) {
+ nconns = snd_hda_get_connections(codec, nid, conn_list,
+ HDA_MAX_CONNECTIONS);
+ if (nconns < 0) {
+ kfree(node);
+ return nconns;
+ }
+ } else {
+ nconns = 0;
+ }
+ if (nconns <= ARRAY_SIZE(node->slist))
+ node->conn_list = node->slist;
+ else {
+ node->conn_list = kmalloc(sizeof(hda_nid_t) * nconns,
+ GFP_KERNEL);
+ if (! node->conn_list) {
+ snd_printk(KERN_ERR "hda-generic: cannot malloc\n");
+ kfree(node);
+ return -ENOMEM;
+ }
+ }
+ memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t));
+ node->nconns = nconns;
-static struct nid_path *get_nid_path(struct hda_codec *codec,
- hda_nid_t from_nid, hda_nid_t to_nid,
- int anchor_nid)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i;
+ if (node->type == AC_WID_PIN) {
+ node->pin_caps = snd_hda_query_pin_caps(codec, node->nid);
+ node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid);
+ }
- for (i = 0; i < spec->paths.used; i++) {
- struct nid_path *path = snd_array_elem(&spec->paths, i);
- if (path->depth <= 0)
- continue;
- if ((!from_nid || path->path[0] == from_nid) &&
- (!to_nid || path->path[path->depth - 1] == to_nid)) {
- if (!anchor_nid ||
- (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) ||
- (anchor_nid < 0 && !is_nid_contained(path, anchor_nid)))
- return path;
- }
+ if (node->wid_caps & AC_WCAP_OUT_AMP) {
+ if (node->wid_caps & AC_WCAP_AMP_OVRD)
+ node->amp_out_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_OUT_CAP);
+ if (! node->amp_out_caps)
+ node->amp_out_caps = spec->def_amp_out_caps;
}
- return NULL;
+ if (node->wid_caps & AC_WCAP_IN_AMP) {
+ if (node->wid_caps & AC_WCAP_AMP_OVRD)
+ node->amp_in_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_IN_CAP);
+ if (! node->amp_in_caps)
+ node->amp_in_caps = spec->def_amp_in_caps;
+ }
+ list_add_tail(&node->list, &spec->nid_list);
+ return 0;
}
-/* get the path between the given NIDs;
- * passing 0 to either @pin or @dac behaves as a wildcard
+/*
+ * build the AFG subtree
*/
-struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
- hda_nid_t from_nid, hda_nid_t to_nid)
+static int build_afg_tree(struct hda_codec *codec)
{
- return get_nid_path(codec, from_nid, to_nid, 0);
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_nid_path);
+ struct hda_gspec *spec = codec->spec;
+ int i, nodes, err;
+ hda_nid_t nid;
-/* get the index number corresponding to the path instance;
- * the index starts from 1, for easier checking the invalid value
- */
-int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct nid_path *array = spec->paths.list;
- ssize_t idx;
+ if (snd_BUG_ON(!spec))
+ return -EINVAL;
- if (!spec->paths.used)
- return 0;
- idx = path - array;
- if (idx < 0 || idx >= spec->paths.used)
- return 0;
- return idx + 1;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_path_idx);
+ spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP);
+ spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP);
-/* get the path instance corresponding to the given index number */
-struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx)
-{
- struct hda_gen_spec *spec = codec->spec;
+ nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+ if (! nid || nodes < 0) {
+ printk(KERN_ERR "Invalid AFG subtree\n");
+ return -EINVAL;
+ }
+
+ /* parse all nodes belonging to the AFG */
+ for (i = 0; i < nodes; i++, nid++) {
+ if ((err = add_new_node(codec, spec, nid)) < 0)
+ return err;
+ }
- if (idx <= 0 || idx > spec->paths.used)
- return NULL;
- return snd_array_elem(&spec->paths, idx - 1);
+ return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_get_path_from_idx);
-/* check whether the given DAC is already found in any existing paths */
-static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
+
+/*
+ * look for the node record for the given NID
+ */
+/* FIXME: should avoid the braindead linear search */
+static struct hda_gnode *hda_get_node(struct hda_gspec *spec, hda_nid_t nid)
{
- struct hda_gen_spec *spec = codec->spec;
- int i;
+ struct hda_gnode *node;
- for (i = 0; i < spec->paths.used; i++) {
- struct nid_path *path = snd_array_elem(&spec->paths, i);
- if (path->path[0] == nid)
- return true;
+ list_for_each_entry(node, &spec->nid_list, list) {
+ if (node->nid == nid)
+ return node;
}
- return false;
+ return NULL;
}
-/* check whether the given two widgets can be connected */
-static bool is_reachable_path(struct hda_codec *codec,
- hda_nid_t from_nid, hda_nid_t to_nid)
-{
- if (!from_nid || !to_nid)
- return false;
- return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0;
+/*
+ * unmute (and set max vol) the output amplifier
+ */
+static int unmute_output(struct hda_codec *codec, struct hda_gnode *node)
+{
+ unsigned int val, ofs;
+ snd_printdd("UNMUTE OUT: NID=0x%x\n", node->nid);
+ val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+ if (val >= ofs)
+ val -= ofs;
+ snd_hda_codec_amp_stereo(codec, node->nid, HDA_OUTPUT, 0, 0xff, val);
+ return 0;
}
-/* nid, dir and idx */
-#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19))
-
-/* check whether the given ctl is already assigned in any path elements */
-static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i;
-
- val &= AMP_VAL_COMPARE_MASK;
- for (i = 0; i < spec->paths.used; i++) {
- struct nid_path *path = snd_array_elem(&spec->paths, i);
- if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val)
- return true;
- }
- return false;
+/*
+ * unmute (and set max vol) the input amplifier
+ */
+static int unmute_input(struct hda_codec *codec, struct hda_gnode *node, unsigned int index)
+{
+ unsigned int val, ofs;
+ snd_printdd("UNMUTE IN: NID=0x%x IDX=0x%x\n", node->nid, index);
+ val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+ if (val >= ofs)
+ val -= ofs;
+ snd_hda_codec_amp_stereo(codec, node->nid, HDA_INPUT, index, 0xff, val);
+ return 0;
}
-/* check whether a control with the given (nid, dir, idx) was assigned */
-static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
- int dir, int idx, int type)
+/*
+ * select the input connection of the given node.
+ */
+static int select_input_connection(struct hda_codec *codec, struct hda_gnode *node,
+ unsigned int index)
{
- unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir);
- return is_ctl_used(codec, val, type);
+ snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index);
+ return snd_hda_codec_write_cache(codec, node->nid, 0,
+ AC_VERB_SET_CONNECT_SEL, index);
}
-static void print_nid_path(const char *pfx, struct nid_path *path)
+/*
+ * clear checked flag of each node in the node list
+ */
+static void clear_check_flags(struct hda_gspec *spec)
{
- char buf[40];
- int i;
+ struct hda_gnode *node;
-
- buf[0] = 0;
- for (i = 0; i < path->depth; i++) {
- char tmp[4];
- sprintf(tmp, ":%02x", path->path[i]);
- strlcat(buf, tmp, sizeof(buf));
+ list_for_each_entry(node, &spec->nid_list, list) {
+ node->checked = 0;
}
- snd_printdd("%s path: depth=%d %s\n", pfx, path->depth, buf);
}
-/* called recursively */
-static bool __parse_nid_path(struct hda_codec *codec,
- hda_nid_t from_nid, hda_nid_t to_nid,
- int anchor_nid, struct nid_path *path,
- int depth)
+/*
+ * parse the output path recursively until reach to an audio output widget
+ *
+ * returns 0 if not found, 1 if found, or a negative error code.
+ */
+static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec,
+ struct hda_gnode *node, int dac_idx)
{
- const hda_nid_t *conn;
- int i, nums;
+ int i, err;
+ struct hda_gnode *child;
- if (to_nid == anchor_nid)
- anchor_nid = 0; /* anchor passed */
- else if (to_nid == (hda_nid_t)(-anchor_nid))
- return false; /* hit the exclusive nid */
+ if (node->checked)
+ return 0;
- nums = snd_hda_get_conn_list(codec, to_nid, &conn);
- for (i = 0; i < nums; i++) {
- if (conn[i] != from_nid) {
- /* special case: when from_nid is 0,
- * try to find an empty DAC
- */
- if (from_nid ||
- get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT ||
- is_dac_already_used(codec, conn[i]))
- continue;
+ node->checked = 1;
+ if (node->type == AC_WID_AUD_OUT) {
+ if (node->wid_caps & AC_WCAP_DIGITAL) {
+ snd_printdd("Skip Digital OUT node %x\n", node->nid);
+ return 0;
}
- /* anchor is not requested or already passed? */
- if (anchor_nid <= 0)
- goto found;
- }
- if (depth >= MAX_NID_PATH_DEPTH)
- return false;
- for (i = 0; i < nums; i++) {
- unsigned int type;
- type = get_wcaps_type(get_wcaps(codec, conn[i]));
- if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN ||
- type == AC_WID_PIN)
- continue;
- if (__parse_nid_path(codec, from_nid, conn[i],
- anchor_nid, path, depth + 1))
- goto found;
+ snd_printdd("AUD_OUT found %x\n", node->nid);
+ if (spec->dac_node[dac_idx]) {
+ /* already DAC node is assigned, just unmute & connect */
+ return node == spec->dac_node[dac_idx];
+ }
+ spec->dac_node[dac_idx] = node;
+ if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
+ spec->pcm_vol_nodes < MAX_PCM_VOLS) {
+ spec->pcm_vol[spec->pcm_vol_nodes].node = node;
+ spec->pcm_vol[spec->pcm_vol_nodes].index = 0;
+ spec->pcm_vol_nodes++;
+ }
+ return 1; /* found */
}
- return false;
-
- found:
- path->path[path->depth] = conn[i];
- path->idx[path->depth + 1] = i;
- if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX)
- path->multi[path->depth + 1] = 1;
- path->depth++;
- return true;
-}
-/* parse the widget path from the given nid to the target nid;
- * when @from_nid is 0, try to find an empty DAC;
- * when @anchor_nid is set to a positive value, only paths through the widget
- * with the given value are evaluated.
- * when @anchor_nid is set to a negative value, paths through the widget
- * with the negative of given value are excluded, only other paths are chosen.
- * when @anchor_nid is zero, no special handling about path selection.
- */
-bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
- hda_nid_t to_nid, int anchor_nid,
- struct nid_path *path)
-{
- if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) {
- path->path[path->depth] = to_nid;
- path->depth++;
- return true;
+ for (i = 0; i < node->nconns; i++) {
+ child = hda_get_node(spec, node->conn_list[i]);
+ if (! child)
+ continue;
+ err = parse_output_path(codec, spec, child, dac_idx);
+ if (err < 0)
+ return err;
+ else if (err > 0) {
+ /* found one,
+ * select the path, unmute both input and output
+ */
+ if (node->nconns > 1)
+ select_input_connection(codec, node, i);
+ unmute_input(codec, node, i);
+ unmute_output(codec, node);
+ if (spec->dac_node[dac_idx] &&
+ spec->pcm_vol_nodes < MAX_PCM_VOLS &&
+ !(spec->dac_node[dac_idx]->wid_caps &
+ AC_WCAP_OUT_AMP)) {
+ if ((node->wid_caps & AC_WCAP_IN_AMP) ||
+ (node->wid_caps & AC_WCAP_OUT_AMP)) {
+ int n = spec->pcm_vol_nodes;
+ spec->pcm_vol[n].node = node;
+ spec->pcm_vol[n].index = i;
+ spec->pcm_vol_nodes++;
+ }
+ }
+ return 1;
+ }
}
- return false;
+ return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_parse_nid_path);
/*
- * parse the path between the given NIDs and add to the path list.
- * if no valid path is found, return NULL
+ * Look for the output PIN widget with the given jack type
+ * and parse the output path to that PIN.
+ *
+ * Returns the PIN node when the path to DAC is established.
*/
-struct nid_path *
-snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
- hda_nid_t to_nid, int anchor_nid)
+static struct hda_gnode *parse_output_jack(struct hda_codec *codec,
+ struct hda_gspec *spec,
+ int jack_type)
{
- struct hda_gen_spec *spec = codec->spec;
- struct nid_path *path;
-
- if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid))
- return NULL;
-
- /* check whether the path has been already added */
- path = get_nid_path(codec, from_nid, to_nid, anchor_nid);
- if (path)
- return path;
+ struct hda_gnode *node;
+ int err;
- path = snd_array_new(&spec->paths);
- if (!path)
- return NULL;
- memset(path, 0, sizeof(*path));
- if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path))
- return path;
- /* push back */
- spec->paths.used--;
+ list_for_each_entry(node, &spec->nid_list, list) {
+ if (node->type != AC_WID_PIN)
+ continue;
+ /* output capable? */
+ if (! (node->pin_caps & AC_PINCAP_OUT))
+ continue;
+ if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
+ continue; /* unconnected */
+ if (jack_type >= 0) {
+ if (jack_type != defcfg_type(node))
+ continue;
+ if (node->wid_caps & AC_WCAP_DIGITAL)
+ continue; /* skip SPDIF */
+ } else {
+ /* output as default? */
+ if (! (node->pin_ctl & AC_PINCTL_OUT_EN))
+ continue;
+ }
+ clear_check_flags(spec);
+ err = parse_output_path(codec, spec, node, 0);
+ if (err < 0)
+ return NULL;
+ if (! err && spec->out_pin_node[0]) {
+ err = parse_output_path(codec, spec, node, 1);
+ if (err < 0)
+ return NULL;
+ }
+ if (err > 0) {
+ /* unmute the PIN output */
+ unmute_output(codec, node);
+ /* set PIN-Out enable */
+ snd_hda_codec_write_cache(codec, node->nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ AC_PINCTL_OUT_EN |
+ ((node->pin_caps & AC_PINCAP_HP_DRV) ?
+ AC_PINCTL_HP_EN : 0));
+ return node;
+ }
+ }
return NULL;
}
-EXPORT_SYMBOL_HDA(snd_hda_add_new_path);
-/* clear the given path as invalid so that it won't be picked up later */
-static void invalidate_nid_path(struct hda_codec *codec, int idx)
-{
- struct nid_path *path = snd_hda_get_path_from_idx(codec, idx);
- if (!path)
- return;
- memset(path, 0, sizeof(*path));
-}
-/* look for an empty DAC slot */
-static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin,
- bool is_digital)
+/*
+ * parse outputs
+ */
+static int parse_output(struct hda_codec *codec)
{
- struct hda_gen_spec *spec = codec->spec;
- bool cap_digital;
- int i;
+ struct hda_gspec *spec = codec->spec;
+ struct hda_gnode *node;
- for (i = 0; i < spec->num_all_dacs; i++) {
- hda_nid_t nid = spec->all_dacs[i];
- if (!nid || is_dac_already_used(codec, nid))
- continue;
- cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL);
- if (is_digital != cap_digital)
- continue;
- if (is_reachable_path(codec, nid, pin))
- return nid;
+ /*
+ * Look for the output PIN widget
+ */
+ /* first, look for the line-out pin */
+ node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT);
+ if (node) /* found, remember the PIN node */
+ spec->out_pin_node[0] = node;
+ else {
+ /* if no line-out is found, try speaker out */
+ node = parse_output_jack(codec, spec, AC_JACK_SPEAKER);
+ if (node)
+ spec->out_pin_node[0] = node;
+ }
+ /* look for the HP-out pin */
+ node = parse_output_jack(codec, spec, AC_JACK_HP_OUT);
+ if (node) {
+ if (! spec->out_pin_node[0])
+ spec->out_pin_node[0] = node;
+ else
+ spec->out_pin_node[1] = node;
}
- return 0;
-}
-/* replace the channels in the composed amp value with the given number */
-static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs)
-{
- val &= ~(0x3U << 16);
- val |= chs << 16;
- return val;
-}
+ if (! spec->out_pin_node[0]) {
+ /* no line-out or HP pins found,
+ * then choose for the first output pin
+ */
+ spec->out_pin_node[0] = parse_output_jack(codec, spec, -1);
+ if (! spec->out_pin_node[0])
+ snd_printd("hda_generic: no proper output path found\n");
+ }
-/* check whether the widget has the given amp capability for the direction */
-static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
- int dir, unsigned int bits)
-{
- if (!nid)
- return false;
- if (get_wcaps(codec, nid) & (1 << (dir + 1)))
- if (query_amp_caps(codec, nid, dir) & bits)
- return true;
- return false;
+ return 0;
}
-static bool same_amp_caps(struct hda_codec *codec, hda_nid_t nid1,
- hda_nid_t nid2, int dir)
+/*
+ * input MUX
+ */
+
+/* control callbacks */
+static int capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
- if (!(get_wcaps(codec, nid1) & (1 << (dir + 1))))
- return !(get_wcaps(codec, nid2) & (1 << (dir + 1)));
- return (query_amp_caps(codec, nid1, dir) ==
- query_amp_caps(codec, nid2, dir));
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gspec *spec = codec->spec;
+ return snd_hda_input_mux_info(&spec->input_mux, uinfo);
}
-#define nid_has_mute(codec, nid, dir) \
- check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
-#define nid_has_volume(codec, nid, dir) \
- check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
-
-/* look for a widget suitable for assigning a mute switch in the path */
-static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec,
- struct nid_path *path)
+static int capture_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- int i;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gspec *spec = codec->spec;
- for (i = path->depth - 1; i >= 0; i--) {
- if (nid_has_mute(codec, path->path[i], HDA_OUTPUT))
- return path->path[i];
- if (i != path->depth - 1 && i != 0 &&
- nid_has_mute(codec, path->path[i], HDA_INPUT))
- return path->path[i];
- }
+ ucontrol->value.enumerated.item[0] = spec->cur_cap_src;
return 0;
}
-/* look for a widget suitable for assigning a volume ctl in the path */
-static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec,
- struct nid_path *path)
+static int capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
- int i;
-
- for (i = path->depth - 1; i >= 0; i--) {
- if (nid_has_volume(codec, path->path[i], HDA_OUTPUT))
- return path->path[i];
- }
- return 0;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct hda_gspec *spec = codec->spec;
+ return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol,
+ spec->adc_node->nid, &spec->cur_cap_src);
}
/*
- * path activation / deactivation
+ * return the string name of the given input PIN widget
*/
-
-/* can have the amp-in capability? */
-static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx)
-{
- hda_nid_t nid = path->path[idx];
- unsigned int caps = get_wcaps(codec, nid);
- unsigned int type = get_wcaps_type(caps);
-
- if (!(caps & AC_WCAP_IN_AMP))
- return false;
- if (type == AC_WID_PIN && idx > 0) /* only for input pins */
- return false;
- return true;
+static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
+{
+ unsigned int location = defcfg_location(node);
+ switch (defcfg_type(node)) {
+ case AC_JACK_LINE_IN:
+ if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+ return "Front Line";
+ return "Line";
+ case AC_JACK_CD:
+#if 0
+ if (pinctl)
+ *pinctl |= AC_PINCTL_VREF_GRD;
+#endif
+ return "CD";
+ case AC_JACK_AUX:
+ if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+ return "Front Aux";
+ return "Aux";
+ case AC_JACK_MIC_IN:
+ if (pinctl &&
+ (node->pin_caps &
+ (AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT)))
+ *pinctl |= AC_PINCTL_VREF_80;
+ if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+ return "Front Mic";
+ return "Mic";
+ case AC_JACK_SPDIF_IN:
+ return "SPDIF";
+ case AC_JACK_DIG_OTHER_IN:
+ return "Digital";
+ }
+ return NULL;
}
-/* can have the amp-out capability? */
-static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx)
+/*
+ * parse the nodes recursively until reach to the input PIN
+ *
+ * returns 0 if not found, 1 if found, or a negative error code.
+ */
+static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
+ struct hda_gnode *node, int idx)
{
- hda_nid_t nid = path->path[idx];
- unsigned int caps = get_wcaps(codec, nid);
- unsigned int type = get_wcaps_type(caps);
-
- if (!(caps & AC_WCAP_OUT_AMP))
- return false;
- if (type == AC_WID_PIN && !idx) /* only for output pins */
- return false;
- return true;
-}
+ int i, err;
+ unsigned int pinctl;
+ const char *type;
-/* check whether the given (nid,dir,idx) is active */
-static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
- unsigned int dir, unsigned int idx)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i, n;
+ if (node->checked)
+ return 0;
- for (n = 0; n < spec->paths.used; n++) {
- struct nid_path *path = snd_array_elem(&spec->paths, n);
- if (!path->active)
- continue;
- for (i = 0; i < path->depth; i++) {
- if (path->path[i] == nid) {
- if (dir == HDA_OUTPUT || path->idx[i] == idx)
- return true;
- break;
+ node->checked = 1;
+ if (node->type != AC_WID_PIN) {
+ for (i = 0; i < node->nconns; i++) {
+ struct hda_gnode *child;
+ child = hda_get_node(spec, node->conn_list[i]);
+ if (! child)
+ continue;
+ err = parse_adc_sub_nodes(codec, spec, child, idx);
+ if (err < 0)
+ return err;
+ if (err > 0) {
+ /* found one,
+ * select the path, unmute both input and output
+ */
+ if (node->nconns > 1)
+ select_input_connection(codec, node, i);
+ unmute_input(codec, node, i);
+ unmute_output(codec, node);
+ return err;
}
}
+ return 0;
}
- return false;
-}
-/* get the default amp value for the target state */
-static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
- int dir, unsigned int caps, bool enable)
-{
- unsigned int val = 0;
+ /* input capable? */
+ if (! (node->pin_caps & AC_PINCAP_IN))
+ return 0;
- if (caps & AC_AMPCAP_NUM_STEPS) {
- /* set to 0dB */
- if (enable)
- val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+ if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
+ return 0; /* unconnected */
+
+ if (node->wid_caps & AC_WCAP_DIGITAL)
+ return 0; /* skip SPDIF */
+
+ if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) {
+ snd_printk(KERN_ERR "hda_generic: Too many items for capture\n");
+ return -EINVAL;
}
- if (caps & AC_AMPCAP_MUTE) {
- if (!enable)
- val |= HDA_AMP_MUTE;
+
+ pinctl = AC_PINCTL_IN_EN;
+ /* create a proper capture source label */
+ type = get_input_type(node, &pinctl);
+ if (! type) {
+ /* input as default? */
+ if (! (node->pin_ctl & AC_PINCTL_IN_EN))
+ return 0;
+ type = "Input";
}
- return val;
-}
+ snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL);
-/* initialize the amp value (only at the first time) */
-static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx)
-{
- unsigned int caps = query_amp_caps(codec, nid, dir);
- int val = get_amp_val_to_activate(codec, nid, dir, caps, false);
- snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
+ /* unmute the PIN external input */
+ unmute_input(codec, node, 0); /* index = 0? */
+ /* set PIN-In enable */
+ snd_hda_codec_write_cache(codec, node->nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+
+ return 1; /* found */
}
-/* calculate amp value mask we can modify;
- * if the given amp is controlled by mixers, don't touch it
+/*
+ * parse input
*/
-static unsigned int get_amp_mask_to_modify(struct hda_codec *codec,
- hda_nid_t nid, int dir, int idx,
- unsigned int caps)
+static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
{
- unsigned int mask = 0xff;
+ struct hda_gspec *spec = codec->spec;
+ struct hda_gnode *node;
+ int i, err;
- if (caps & AC_AMPCAP_MUTE) {
- if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_MUTE_CTL))
- mask &= ~0x80;
+ snd_printdd("AUD_IN = %x\n", adc_node->nid);
+ clear_check_flags(spec);
+
+ // awk added - fixed no recording due to muted widget
+ unmute_input(codec, adc_node, 0);
+
+ /*
+ * check each connection of the ADC
+ * if it reaches to a proper input PIN, add the path as the
+ * input path.
+ */
+ /* first, check the direct connections to PIN widgets */
+ for (i = 0; i < adc_node->nconns; i++) {
+ node = hda_get_node(spec, adc_node->conn_list[i]);
+ if (node && node->type == AC_WID_PIN) {
+ err = parse_adc_sub_nodes(codec, spec, node, i);
+ if (err < 0)
+ return err;
+ }
}
- if (caps & AC_AMPCAP_NUM_STEPS) {
- if (is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) ||
- is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL))
- mask &= ~0x7f;
+ /* ... then check the rests, more complicated connections */
+ for (i = 0; i < adc_node->nconns; i++) {
+ node = hda_get_node(spec, adc_node->conn_list[i]);
+ if (node && node->type != AC_WID_PIN) {
+ err = parse_adc_sub_nodes(codec, spec, node, i);
+ if (err < 0)
+ return err;
+ }
}
- return mask;
-}
-
-static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
- int idx, int idx_to_check, bool enable)
-{
- unsigned int caps;
- unsigned int mask, val;
-
- if (!enable && is_active_nid(codec, nid, dir, idx_to_check))
- return;
- caps = query_amp_caps(codec, nid, dir);
- val = get_amp_val_to_activate(codec, nid, dir, caps, enable);
- mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps);
- if (!mask)
- return;
+ if (! spec->input_mux.num_items)
+ return 0; /* no input path found... */
- val &= mask;
- snd_hda_codec_amp_stereo(codec, nid, dir, idx, mask, val);
-}
+ snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items);
+ for (i = 0; i < spec->input_mux.num_items; i++)
+ snd_printdd(" [%s] IDX=0x%x\n", spec->input_mux.items[i].label,
+ spec->input_mux.items[i].index);
-static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
- int i, bool enable)
-{
- hda_nid_t nid = path->path[i];
- init_amp(codec, nid, HDA_OUTPUT, 0);
- activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
+ spec->adc_node = adc_node;
+ return 1;
}
-static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
- int i, bool enable, bool add_aamix)
+/*
+ * parse input
+ */
+static int parse_input(struct hda_codec *codec)
{
- struct hda_gen_spec *spec = codec->spec;
- const hda_nid_t *conn;
- int n, nums, idx;
- int type;
- hda_nid_t nid = path->path[i];
-
- nums = snd_hda_get_conn_list(codec, nid, &conn);
- type = get_wcaps_type(get_wcaps(codec, nid));
- if (type == AC_WID_PIN ||
- (type == AC_WID_AUD_IN && codec->single_adc_amp)) {
- nums = 1;
- idx = 0;
- } else
- idx = path->idx[i];
-
- for (n = 0; n < nums; n++)
- init_amp(codec, nid, HDA_INPUT, n);
+ struct hda_gspec *spec = codec->spec;
+ struct hda_gnode *node;
+ int err;
- /* here is a little bit tricky in comparison with activate_amp_out();
- * when aa-mixer is available, we need to enable the path as well
+ /*
+ * At first we look for an audio input widget.
+ * If it reaches to certain input PINs, we take it as the
+ * input path.
*/
- for (n = 0; n < nums; n++) {
- if (n != idx && (!add_aamix || conn[n] != spec->mixer_merge_nid))
- continue;
- activate_amp(codec, nid, HDA_INPUT, n, idx, enable);
+ list_for_each_entry(node, &spec->nid_list, list) {
+ if (node->wid_caps & AC_WCAP_DIGITAL)
+ continue; /* skip SPDIF */
+ if (node->type == AC_WID_AUD_IN) {
+ err = parse_input_path(codec, node);
+ if (err < 0)
+ return err;
+ else if (err > 0)
+ return 0;
+ }
}
+ snd_printd("hda_generic: no proper input path found\n");
+ return 0;
}
-/* activate or deactivate the given path
- * if @add_aamix is set, enable the input from aa-mix NID as well (if any)
- */
-void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
- bool enable, bool add_aamix)
+#ifdef CONFIG_PM
+static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx)
{
- struct hda_gen_spec *spec = codec->spec;
- int i;
+ struct hda_gspec *spec = codec->spec;
+ struct hda_amp_list *p;
- if (!enable)
- path->active = false;
-
- for (i = path->depth - 1; i >= 0; i--) {
- hda_nid_t nid = path->path[i];
- if (enable && spec->power_down_unused) {
- /* make sure the widget is powered up */
- if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- }
- if (enable && path->multi[i])
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- path->idx[i]);
- if (has_amp_in(codec, path, i))
- activate_amp_in(codec, path, i, enable, add_aamix);
- if (has_amp_out(codec, path, i))
- activate_amp_out(codec, path, i, enable);
+ if (spec->num_loopbacks >= MAX_LOOPBACK_AMPS) {
+ snd_printk(KERN_ERR "hda_generic: Too many loopback ctls\n");
+ return;
}
-
- if (enable)
- path->active = true;
+ p = &spec->loopback_list[spec->num_loopbacks++];
+ p->nid = nid;
+ p->dir = dir;
+ p->idx = idx;
+ spec->loopback.amplist = spec->loopback_list;
}
-EXPORT_SYMBOL_HDA(snd_hda_activate_path);
+#else
+#define add_input_loopback(codec,nid,dir,idx)
+#endif
-/* if the given path is inactive, put widgets into D3 (only if suitable) */
-static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path)
+/*
+ * create mixer controls if possible
+ */
+static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
+ unsigned int index, const char *type,
+ const char *dir_sfx, int is_loopback)
{
- struct hda_gen_spec *spec = codec->spec;
- bool changed;
- int i;
-
- if (!spec->power_down_unused || path->active)
- return;
+ char name[32];
+ int err;
+ int created = 0;
+ struct snd_kcontrol_new knew;
- for (i = 0; i < path->depth; i++) {
- hda_nid_t nid = path->path[i];
- if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D3)) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- changed = true;
- }
+ if (type)
+ sprintf(name, "%s %s Switch", type, dir_sfx);
+ else
+ sprintf(name, "%s Switch", dir_sfx);
+ if ((node->wid_caps & AC_WCAP_IN_AMP) &&
+ (node->amp_in_caps & AC_AMPCAP_MUTE)) {
+ knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);
+ if (is_loopback)
+ add_input_loopback(codec, node->nid, HDA_INPUT, index);
+ snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
+ err = snd_hda_ctl_add(codec, node->nid,
+ snd_ctl_new1(&knew, codec));
+ if (err < 0)
+ return err;
+ created = 1;
+ } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
+ (node->amp_out_caps & AC_AMPCAP_MUTE)) {
+ knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);
+ if (is_loopback)
+ add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
+ snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
+ err = snd_hda_ctl_add(codec, node->nid,
+ snd_ctl_new1(&knew, codec));
+ if (err < 0)
+ return err;
+ created = 1;
}
- if (changed) {
- msleep(10);
- snd_hda_codec_read(codec, path->path[0], 0,
- AC_VERB_GET_POWER_STATE, 0);
+ if (type)
+ sprintf(name, "%s %s Volume", type, dir_sfx);
+ else
+ sprintf(name, "%s Volume", dir_sfx);
+ if ((node->wid_caps & AC_WCAP_IN_AMP) &&
+ (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
+ knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
+ snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
+ err = snd_hda_ctl_add(codec, node->nid,
+ snd_ctl_new1(&knew, codec));
+ if (err < 0)
+ return err;
+ created = 1;
+ } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
+ (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
+ knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
+ snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
+ err = snd_hda_ctl_add(codec, node->nid,
+ snd_ctl_new1(&knew, codec));
+ if (err < 0)
+ return err;
+ created = 1;
}
-}
-/* turn on/off EAPD on the given pin */
-static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable)
-{
- struct hda_gen_spec *spec = codec->spec;
- if (spec->own_eapd_ctl ||
- !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD))
- return;
- if (codec->inv_eapd)
- enable = !enable;
- snd_hda_codec_update_cache(codec, pin, 0,
- AC_VERB_SET_EAPD_BTLENABLE,
- enable ? 0x02 : 0x00);
+ return created;
}
-/* re-initialize the path specified by the given path index */
-static void resume_path_from_idx(struct hda_codec *codec, int path_idx)
-{
- struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx);
- if (path)
- snd_hda_activate_path(codec, path, path->active, false);
+/*
+ * check whether the controls with the given name and direction suffix already exist
+ */
+static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir)
+{
+ struct snd_ctl_elem_id id;
+ memset(&id, 0, sizeof(id));
+ sprintf(id.name, "%s %s Volume", type, dir);
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ if (snd_ctl_find_id(codec->bus->card, &id))
+ return 1;
+ sprintf(id.name, "%s %s Switch", type, dir);
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ if (snd_ctl_find_id(codec->bus->card, &id))
+ return 1;
+ return 0;
}
-
/*
- * Helper functions for creating mixer ctl elements
+ * build output mixer controls
*/
-
-enum {
- HDA_CTL_WIDGET_VOL,
- HDA_CTL_WIDGET_MUTE,
- HDA_CTL_BIND_MUTE,
-};
-static const struct snd_kcontrol_new control_templates[] = {
- HDA_CODEC_VOLUME(NULL, 0, 0, 0),
- HDA_CODEC_MUTE(NULL, 0, 0, 0),
- HDA_BIND_MUTE(NULL, 0, 0, 0),
-};
-
-/* add dynamic controls from template */
-static struct snd_kcontrol_new *
-add_control(struct hda_gen_spec *spec, int type, const char *name,
- int cidx, unsigned long val)
+static int create_output_mixers(struct hda_codec *codec,
+ const char * const *names)
{
- struct snd_kcontrol_new *knew;
+ struct hda_gspec *spec = codec->spec;
+ int i, err;
- knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]);
- if (!knew)
- return NULL;
- knew->index = cidx;
- if (get_amp_nid_(val))
- knew->subdevice = HDA_SUBDEV_AMP_FLAG;
- knew->private_value = val;
- return knew;
+ for (i = 0; i < spec->pcm_vol_nodes; i++) {
+ err = create_mixer(codec, spec->pcm_vol[i].node,
+ spec->pcm_vol[i].index,
+ names[i], "Playback", 0);
+ if (err < 0)
+ return err;
+ }
+ return 0;
}
-static int add_control_with_pfx(struct hda_gen_spec *spec, int type,
- const char *pfx, const char *dir,
- const char *sfx, int cidx, unsigned long val)
+static int build_output_controls(struct hda_codec *codec)
{
- char name[32];
- snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
- if (!add_control(spec, type, name, cidx, val))
- return -ENOMEM;
- return 0;
-}
+ struct hda_gspec *spec = codec->spec;
+ static const char * const types_speaker[] = { "Speaker", "Headphone" };
+ static const char * const types_line[] = { "Front", "Headphone" };
-#define add_pb_vol_ctrl(spec, type, pfx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
-#define add_pb_sw_ctrl(spec, type, pfx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
-#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
-#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
-
-static int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx,
- unsigned int chs, struct nid_path *path)
-{
- unsigned int val;
- if (!path)
- return 0;
- val = path->ctls[NID_PATH_VOL_CTL];
- if (!val)
- return 0;
- val = amp_val_replace_channels(val, chs);
- return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val);
-}
-
-/* return the channel bits suitable for the given path->ctls[] */
-static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path,
- int type)
-{
- int chs = 1; /* mono (left only) */
- if (path) {
- hda_nid_t nid = get_amp_nid_(path->ctls[type]);
- if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO))
- chs = 3; /* stereo */
- }
- return chs;
-}
-
-static int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx,
- struct nid_path *path)
-{
- int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL);
- return add_vol_ctl(codec, pfx, cidx, chs, path);
-}
-
-/* create a mute-switch for the given mixer widget;
- * if it has multiple sources (e.g. DAC and loopback), create a bind-mute
- */
-static int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx,
- unsigned int chs, struct nid_path *path)
-{
- unsigned int val;
- int type = HDA_CTL_WIDGET_MUTE;
-
- if (!path)
- return 0;
- val = path->ctls[NID_PATH_MUTE_CTL];
- if (!val)
- return 0;
- val = amp_val_replace_channels(val, chs);
- if (get_amp_direction_(val) == HDA_INPUT) {
- hda_nid_t nid = get_amp_nid_(val);
- int nums = snd_hda_get_num_conns(codec, nid);
- if (nums > 1) {
- type = HDA_CTL_BIND_MUTE;
- val |= nums << 19;
- }
- }
- return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
-}
-
-static int add_stereo_sw(struct hda_codec *codec, const char *pfx,
- int cidx, struct nid_path *path)
-{
- int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL);
- return add_sw_ctl(codec, pfx, cidx, chs, path);
-}
-
-/* any ctl assigned to the path with the given index? */
-static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type)
-{
- struct nid_path *path = snd_hda_get_path_from_idx(codec, path_idx);
- return path && path->ctls[ctl_type];
-}
-
-static const char * const channel_name[4] = {
- "Front", "Surround", "CLFE", "Side"
-};
-
-/* give some appropriate ctl name prefix for the given line out channel */
-static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
- int *index, int ctl_type)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- *index = 0;
- if (cfg->line_outs == 1 && !spec->multi_ios &&
- !cfg->hp_outs && !cfg->speaker_outs)
- return spec->vmaster_mute.hook ? "PCM" : "Master";
-
- /* if there is really a single DAC used in the whole output paths,
- * use it master (or "PCM" if a vmaster hook is present)
- */
- if (spec->multiout.num_dacs == 1 && !spec->mixer_nid &&
- !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0])
- return spec->vmaster_mute.hook ? "PCM" : "Master";
-
- /* multi-io channels */
- if (ch >= cfg->line_outs)
- return channel_name[ch];
-
- switch (cfg->line_out_type) {
- case AUTO_PIN_SPEAKER_OUT:
- /* if the primary channel vol/mute is shared with HP volume,
- * don't name it as Speaker
- */
- if (!ch && cfg->hp_outs &&
- !path_has_mixer(codec, spec->hp_paths[0], ctl_type))
- break;
- if (cfg->line_outs == 1)
- return "Speaker";
- if (cfg->line_outs == 2)
- return ch ? "Bass Speaker" : "Speaker";
- break;
- case AUTO_PIN_HP_OUT:
- /* if the primary channel vol/mute is shared with spk volume,
- * don't name it as Headphone
- */
- if (!ch && cfg->speaker_outs &&
- !path_has_mixer(codec, spec->speaker_paths[0], ctl_type))
- break;
- /* for multi-io case, only the primary out */
- if (ch && spec->multi_ios)
- break;
- *index = ch;
- return "Headphone";
- }
-
- /* for a single channel output, we don't have to name the channel */
- if (cfg->line_outs == 1 && !spec->multi_ios)
- return "PCM";
-
- if (ch >= ARRAY_SIZE(channel_name)) {
- snd_BUG();
- return "PCM";
- }
-
- return channel_name[ch];
-}
-
-/*
- * Parse output paths
- */
-
-/* badness definition */
-enum {
- /* No primary DAC is found for the main output */
- BAD_NO_PRIMARY_DAC = 0x10000,
- /* No DAC is found for the extra output */
- BAD_NO_DAC = 0x4000,
- /* No possible multi-ios */
- BAD_MULTI_IO = 0x120,
- /* No individual DAC for extra output */
- BAD_NO_EXTRA_DAC = 0x102,
- /* No individual DAC for extra surrounds */
- BAD_NO_EXTRA_SURR_DAC = 0x101,
- /* Primary DAC shared with main surrounds */
- BAD_SHARED_SURROUND = 0x100,
- /* Primary DAC shared with main CLFE */
- BAD_SHARED_CLFE = 0x10,
- /* Primary DAC shared with extra surrounds */
- BAD_SHARED_EXTRA_SURROUND = 0x10,
- /* Volume widget is shared */
- BAD_SHARED_VOL = 0x10,
-};
-
-/* look for widgets in the given path which are appropriate for
- * volume and mute controls, and assign the values to ctls[].
- *
- * When no appropriate widget is found in the path, the badness value
- * is incremented depending on the situation. The function returns the
- * total badness for both volume and mute controls.
- */
-static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path)
-{
- hda_nid_t nid;
- unsigned int val;
- int badness = 0;
-
- if (!path)
- return BAD_SHARED_VOL * 2;
-
- if (path->ctls[NID_PATH_VOL_CTL] ||
- path->ctls[NID_PATH_MUTE_CTL])
- return 0; /* already evaluated */
-
- nid = look_for_out_vol_nid(codec, path);
- if (nid) {
- val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- if (is_ctl_used(codec, val, NID_PATH_VOL_CTL))
- badness += BAD_SHARED_VOL;
- else
- path->ctls[NID_PATH_VOL_CTL] = val;
- } else
- badness += BAD_SHARED_VOL;
- nid = look_for_out_mute_nid(codec, path);
- if (nid) {
- unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
- if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT ||
- nid_has_mute(codec, nid, HDA_OUTPUT))
- val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- else
- val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
- if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL))
- badness += BAD_SHARED_VOL;
- else
- path->ctls[NID_PATH_MUTE_CTL] = val;
- } else
- badness += BAD_SHARED_VOL;
- return badness;
-}
-
-struct badness_table {
- int no_primary_dac; /* no primary DAC */
- int no_dac; /* no secondary DACs */
- int shared_primary; /* primary DAC is shared with main output */
- int shared_surr; /* secondary DAC shared with main or primary */
- int shared_clfe; /* third DAC shared with main or primary */
- int shared_surr_main; /* secondary DAC sahred with main/DAC0 */
-};
-
-static struct badness_table main_out_badness = {
- .no_primary_dac = BAD_NO_PRIMARY_DAC,
- .no_dac = BAD_NO_DAC,
- .shared_primary = BAD_NO_PRIMARY_DAC,
- .shared_surr = BAD_SHARED_SURROUND,
- .shared_clfe = BAD_SHARED_CLFE,
- .shared_surr_main = BAD_SHARED_SURROUND,
-};
-
-static struct badness_table extra_out_badness = {
- .no_primary_dac = BAD_NO_DAC,
- .no_dac = BAD_NO_DAC,
- .shared_primary = BAD_NO_EXTRA_DAC,
- .shared_surr = BAD_SHARED_EXTRA_SURROUND,
- .shared_clfe = BAD_SHARED_EXTRA_SURROUND,
- .shared_surr_main = BAD_NO_EXTRA_SURR_DAC,
-};
-
-/* get the DAC of the primary output corresponding to the given array index */
-static hda_nid_t get_primary_out(struct hda_codec *codec, int idx)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- if (cfg->line_outs > idx)
- return spec->private_dac_nids[idx];
- idx -= cfg->line_outs;
- if (spec->multi_ios > idx)
- return spec->multi_io[idx].dac;
- return 0;
-}
-
-/* return the DAC if it's reachable, otherwise zero */
-static inline hda_nid_t try_dac(struct hda_codec *codec,
- hda_nid_t dac, hda_nid_t pin)
-{
- return is_reachable_path(codec, dac, pin) ? dac : 0;
-}
-
-/* try to assign DACs to pins and return the resultant badness */
-static int try_assign_dacs(struct hda_codec *codec, int num_outs,
- const hda_nid_t *pins, hda_nid_t *dacs,
- int *path_idx,
- const struct badness_table *bad)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i, j;
- int badness = 0;
- hda_nid_t dac;
-
- if (!num_outs)
- return 0;
-
- for (i = 0; i < num_outs; i++) {
- struct nid_path *path;
- hda_nid_t pin = pins[i];
-
- path = snd_hda_get_path_from_idx(codec, path_idx[i]);
- if (path) {
- badness += assign_out_path_ctls(codec, path);
- continue;
- }
-
- dacs[i] = look_for_dac(codec, pin, false);
- if (!dacs[i] && !i) {
- /* try to steal the DAC of surrounds for the front */
- for (j = 1; j < num_outs; j++) {
- if (is_reachable_path(codec, dacs[j], pin)) {
- dacs[0] = dacs[j];
- dacs[j] = 0;
- invalidate_nid_path(codec, path_idx[j]);
- path_idx[j] = 0;
- break;
- }
- }
- }
- dac = dacs[i];
- if (!dac) {
- if (num_outs > 2)
- dac = try_dac(codec, get_primary_out(codec, i), pin);
- if (!dac)
- dac = try_dac(codec, dacs[0], pin);
- if (!dac)
- dac = try_dac(codec, get_primary_out(codec, i), pin);
- if (dac) {
- if (!i)
- badness += bad->shared_primary;
- else if (i == 1)
- badness += bad->shared_surr;
- else
- badness += bad->shared_clfe;
- } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) {
- dac = spec->private_dac_nids[0];
- badness += bad->shared_surr_main;
- } else if (!i)
- badness += bad->no_primary_dac;
- else
- badness += bad->no_dac;
- }
- if (!dac)
- continue;
- path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid);
- if (!path && !i && spec->mixer_nid) {
- /* try with aamix */
- path = snd_hda_add_new_path(codec, dac, pin, 0);
- }
- if (!path) {
- dac = dacs[i] = 0;
- badness += bad->no_dac;
- } else {
- /* print_nid_path("output", path); */
- path->active = true;
- path_idx[i] = snd_hda_get_path_idx(codec, path);
- badness += assign_out_path_ctls(codec, path);
- }
- }
-
- return badness;
-}
-
-/* return NID if the given pin has only a single connection to a certain DAC */
-static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i;
- hda_nid_t nid_found = 0;
-
- for (i = 0; i < spec->num_all_dacs; i++) {
- hda_nid_t nid = spec->all_dacs[i];
- if (!nid || is_dac_already_used(codec, nid))
- continue;
- if (is_reachable_path(codec, nid, pin)) {
- if (nid_found)
- return 0;
- nid_found = nid;
- }
- }
- return nid_found;
-}
-
-/* check whether the given pin can be a multi-io pin */
-static bool can_be_multiio_pin(struct hda_codec *codec,
- unsigned int location, hda_nid_t nid)
-{
- unsigned int defcfg, caps;
-
- defcfg = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX)
- return false;
- if (location && get_defcfg_location(defcfg) != location)
- return false;
- caps = snd_hda_query_pin_caps(codec, nid);
- if (!(caps & AC_PINCAP_OUT))
- return false;
- return true;
-}
-
-/* count the number of input pins that are capable to be multi-io */
-static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
- unsigned int location = get_defcfg_location(defcfg);
- int type, i;
- int num_pins = 0;
-
- for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
- for (i = 0; i < cfg->num_inputs; i++) {
- if (cfg->inputs[i].type != type)
- continue;
- if (can_be_multiio_pin(codec, location,
- cfg->inputs[i].pin))
- num_pins++;
- }
- }
- return num_pins;
-}
-
-/*
- * multi-io helper
- *
- * When hardwired is set, try to fill ony hardwired pins, and returns
- * zero if any pins are filled, non-zero if nothing found.
- * When hardwired is off, try to fill possible input pins, and returns
- * the badness value.
- */
-static int fill_multi_ios(struct hda_codec *codec,
- hda_nid_t reference_pin,
- bool hardwired)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int type, i, j, num_pins, old_pins;
- unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
- unsigned int location = get_defcfg_location(defcfg);
- int badness = 0;
- struct nid_path *path;
-
- old_pins = spec->multi_ios;
- if (old_pins >= 2)
- goto end_fill;
-
- num_pins = count_multiio_pins(codec, reference_pin);
- if (num_pins < 2)
- goto end_fill;
-
- for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- hda_nid_t dac = 0;
-
- if (cfg->inputs[i].type != type)
- continue;
- if (!can_be_multiio_pin(codec, location, nid))
- continue;
- for (j = 0; j < spec->multi_ios; j++) {
- if (nid == spec->multi_io[j].pin)
- break;
- }
- if (j < spec->multi_ios)
- continue;
-
- if (hardwired)
- dac = get_dac_if_single(codec, nid);
- else if (!dac)
- dac = look_for_dac(codec, nid, false);
- if (!dac) {
- badness++;
- continue;
- }
- path = snd_hda_add_new_path(codec, dac, nid,
- -spec->mixer_nid);
- if (!path) {
- badness++;
- continue;
- }
- /* print_nid_path("multiio", path); */
- spec->multi_io[spec->multi_ios].pin = nid;
- spec->multi_io[spec->multi_ios].dac = dac;
- spec->out_paths[cfg->line_outs + spec->multi_ios] =
- snd_hda_get_path_idx(codec, path);
- spec->multi_ios++;
- if (spec->multi_ios >= 2)
- break;
- }
- }
- end_fill:
- if (badness)
- badness = BAD_MULTI_IO;
- if (old_pins == spec->multi_ios) {
- if (hardwired)
- return 1; /* nothing found */
- else
- return badness; /* no badness if nothing found */
- }
- if (!hardwired && spec->multi_ios < 2) {
- /* cancel newly assigned paths */
- spec->paths.used -= spec->multi_ios - old_pins;
- spec->multi_ios = old_pins;
- return badness;
- }
-
- /* assign volume and mute controls */
- for (i = old_pins; i < spec->multi_ios; i++) {
- path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]);
- badness += assign_out_path_ctls(codec, path);
- }
-
- return badness;
-}
-
-/* map DACs for all pins in the list if they are single connections */
-static bool map_singles(struct hda_codec *codec, int outs,
- const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i;
- bool found = false;
- for (i = 0; i < outs; i++) {
- struct nid_path *path;
- hda_nid_t dac;
- if (dacs[i])
- continue;
- dac = get_dac_if_single(codec, pins[i]);
- if (!dac)
- continue;
- path = snd_hda_add_new_path(codec, dac, pins[i],
- -spec->mixer_nid);
- if (!path && !i && spec->mixer_nid)
- path = snd_hda_add_new_path(codec, dac, pins[i], 0);
- if (path) {
- dacs[i] = dac;
- found = true;
- /* print_nid_path("output", path); */
- path->active = true;
- path_idx[i] = snd_hda_get_path_idx(codec, path);
- }
- }
- return found;
-}
-
-/* create a new path including aamix if available, and return its index */
-static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct nid_path *path;
- hda_nid_t dac, pin;
-
- path = snd_hda_get_path_from_idx(codec, path_idx);
- if (!path || !path->depth ||
- is_nid_contained(path, spec->mixer_nid))
- return 0;
- dac = path->path[0];
- pin = path->path[path->depth - 1];
- path = snd_hda_add_new_path(codec, dac, pin, spec->mixer_nid);
- if (!path) {
- if (dac != spec->multiout.dac_nids[0])
- dac = spec->multiout.dac_nids[0];
- else if (spec->multiout.hp_out_nid[0])
- dac = spec->multiout.hp_out_nid[0];
- else if (spec->multiout.extra_out_nid[0])
- dac = spec->multiout.extra_out_nid[0];
- if (dac)
- path = snd_hda_add_new_path(codec, dac, pin,
- spec->mixer_nid);
- }
- if (!path)
- return 0;
- /* print_nid_path("output-aamix", path); */
- path->active = false; /* unused as default */
- return snd_hda_get_path_idx(codec, path);
-}
-
-/* fill the empty entries in the dac array for speaker/hp with the
- * shared dac pointed by the paths
- */
-static void refill_shared_dacs(struct hda_codec *codec, int num_outs,
- hda_nid_t *dacs, int *path_idx)
-{
- struct nid_path *path;
- int i;
-
- for (i = 0; i < num_outs; i++) {
- if (dacs[i])
- continue;
- path = snd_hda_get_path_from_idx(codec, path_idx[i]);
- if (!path)
- continue;
- dacs[i] = path->path[0];
- }
-}
-
-/* fill in the dac_nids table from the parsed pin configuration */
-static int fill_and_eval_dacs(struct hda_codec *codec,
- bool fill_hardwired,
- bool fill_mio_first)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i, err, badness;
-
- /* set num_dacs once to full for look_for_dac() */
- spec->multiout.num_dacs = cfg->line_outs;
- spec->multiout.dac_nids = spec->private_dac_nids;
- memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
- memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid));
- memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
- spec->multi_ios = 0;
- snd_array_free(&spec->paths);
-
- /* clear path indices */
- memset(spec->out_paths, 0, sizeof(spec->out_paths));
- memset(spec->hp_paths, 0, sizeof(spec->hp_paths));
- memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths));
- memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths));
- memset(spec->digout_paths, 0, sizeof(spec->digout_paths));
- memset(spec->input_paths, 0, sizeof(spec->input_paths));
- memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths));
- memset(&spec->digin_path, 0, sizeof(spec->digin_path));
-
- badness = 0;
-
- /* fill hard-wired DACs first */
- if (fill_hardwired) {
- bool mapped;
- do {
- mapped = map_singles(codec, cfg->line_outs,
- cfg->line_out_pins,
- spec->private_dac_nids,
- spec->out_paths);
- mapped |= map_singles(codec, cfg->hp_outs,
- cfg->hp_pins,
- spec->multiout.hp_out_nid,
- spec->hp_paths);
- mapped |= map_singles(codec, cfg->speaker_outs,
- cfg->speaker_pins,
- spec->multiout.extra_out_nid,
- spec->speaker_paths);
- if (fill_mio_first && cfg->line_outs == 1 &&
- cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- err = fill_multi_ios(codec, cfg->line_out_pins[0], true);
- if (!err)
- mapped = true;
- }
- } while (mapped);
- }
-
- badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins,
- spec->private_dac_nids, spec->out_paths,
- &main_out_badness);
-
- if (fill_mio_first &&
- cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- /* try to fill multi-io first */
- err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
- if (err < 0)
- return err;
- /* we don't count badness at this stage yet */
- }
-
- if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
- err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins,
- spec->multiout.hp_out_nid,
- spec->hp_paths,
- &extra_out_badness);
- if (err < 0)
- return err;
- badness += err;
- }
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- err = try_assign_dacs(codec, cfg->speaker_outs,
- cfg->speaker_pins,
- spec->multiout.extra_out_nid,
- spec->speaker_paths,
- &extra_out_badness);
- if (err < 0)
- return err;
- badness += err;
- }
- if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
- if (err < 0)
- return err;
- badness += err;
- }
-
- if (spec->mixer_nid) {
- spec->aamix_out_paths[0] =
- check_aamix_out_path(codec, spec->out_paths[0]);
- if (cfg->line_out_type != AUTO_PIN_HP_OUT)
- spec->aamix_out_paths[1] =
- check_aamix_out_path(codec, spec->hp_paths[0]);
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
- spec->aamix_out_paths[2] =
- check_aamix_out_path(codec, spec->speaker_paths[0]);
- }
-
- if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
- spec->multi_ios = 1; /* give badness */
-
- /* re-count num_dacs and squash invalid entries */
- spec->multiout.num_dacs = 0;
- for (i = 0; i < cfg->line_outs; i++) {
- if (spec->private_dac_nids[i])
- spec->multiout.num_dacs++;
- else {
- memmove(spec->private_dac_nids + i,
- spec->private_dac_nids + i + 1,
- sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
- spec->private_dac_nids[cfg->line_outs - 1] = 0;
- }
- }
-
- spec->ext_channel_count = spec->min_channel_count =
- spec->multiout.num_dacs * 2;
-
- if (spec->multi_ios == 2) {
- for (i = 0; i < 2; i++)
- spec->private_dac_nids[spec->multiout.num_dacs++] =
- spec->multi_io[i].dac;
- } else if (spec->multi_ios) {
- spec->multi_ios = 0;
- badness += BAD_MULTI_IO;
- }
-
- /* re-fill the shared DAC for speaker / headphone */
- if (cfg->line_out_type != AUTO_PIN_HP_OUT)
- refill_shared_dacs(codec, cfg->hp_outs,
- spec->multiout.hp_out_nid,
- spec->hp_paths);
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
- refill_shared_dacs(codec, cfg->speaker_outs,
- spec->multiout.extra_out_nid,
- spec->speaker_paths);
-
- return badness;
-}
-
-#define DEBUG_BADNESS
-
-#ifdef DEBUG_BADNESS
-#define debug_badness snd_printdd
-#else
-#define debug_badness(...)
-#endif
-
-#ifdef DEBUG_BADNESS
-static inline void print_nid_path_idx(struct hda_codec *codec,
- const char *pfx, int idx)
-{
- struct nid_path *path;
-
- path = snd_hda_get_path_from_idx(codec, idx);
- if (path)
- print_nid_path(pfx, path);
-}
-
-static void debug_show_configs(struct hda_codec *codec,
- struct auto_pin_cfg *cfg)
-{
- struct hda_gen_spec *spec = codec->spec;
- static const char * const lo_type[3] = { "LO", "SP", "HP" };
- int i;
-
- debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x (type %s)\n",
- cfg->line_out_pins[0], cfg->line_out_pins[1],
- cfg->line_out_pins[2], cfg->line_out_pins[3],
- spec->multiout.dac_nids[0],
- spec->multiout.dac_nids[1],
- spec->multiout.dac_nids[2],
- spec->multiout.dac_nids[3],
- lo_type[cfg->line_out_type]);
- for (i = 0; i < cfg->line_outs; i++)
- print_nid_path_idx(codec, " out", spec->out_paths[i]);
- if (spec->multi_ios > 0)
- debug_badness("multi_ios(%d) = %x/%x : %x/%x\n",
- spec->multi_ios,
- spec->multi_io[0].pin, spec->multi_io[1].pin,
- spec->multi_io[0].dac, spec->multi_io[1].dac);
- for (i = 0; i < spec->multi_ios; i++)
- print_nid_path_idx(codec, " mio",
- spec->out_paths[cfg->line_outs + i]);
- if (cfg->hp_outs)
- debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
- cfg->hp_pins[0], cfg->hp_pins[1],
- cfg->hp_pins[2], cfg->hp_pins[3],
- spec->multiout.hp_out_nid[0],
- spec->multiout.hp_out_nid[1],
- spec->multiout.hp_out_nid[2],
- spec->multiout.hp_out_nid[3]);
- for (i = 0; i < cfg->hp_outs; i++)
- print_nid_path_idx(codec, " hp ", spec->hp_paths[i]);
- if (cfg->speaker_outs)
- debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
- cfg->speaker_pins[0], cfg->speaker_pins[1],
- cfg->speaker_pins[2], cfg->speaker_pins[3],
- spec->multiout.extra_out_nid[0],
- spec->multiout.extra_out_nid[1],
- spec->multiout.extra_out_nid[2],
- spec->multiout.extra_out_nid[3]);
- for (i = 0; i < cfg->speaker_outs; i++)
- print_nid_path_idx(codec, " spk", spec->speaker_paths[i]);
- for (i = 0; i < 3; i++)
- print_nid_path_idx(codec, " mix", spec->aamix_out_paths[i]);
-}
-#else
-#define debug_show_configs(codec, cfg) /* NOP */
-#endif
-
-/* find all available DACs of the codec */
-static void fill_all_dac_nids(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i;
- hda_nid_t nid = codec->start_nid;
-
- spec->num_all_dacs = 0;
- memset(spec->all_dacs, 0, sizeof(spec->all_dacs));
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT)
- continue;
- if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) {
- snd_printk(KERN_ERR "hda: Too many DACs!\n");
- break;
- }
- spec->all_dacs[spec->num_all_dacs++] = nid;
- }
-}
-
-static int parse_output_paths(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- struct auto_pin_cfg *best_cfg;
- unsigned int val;
- int best_badness = INT_MAX;
- int badness;
- bool fill_hardwired = true, fill_mio_first = true;
- bool best_wired = true, best_mio = true;
- bool hp_spk_swapped = false;
-
- best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL);
- if (!best_cfg)
- return -ENOMEM;
- *best_cfg = *cfg;
-
- for (;;) {
- badness = fill_and_eval_dacs(codec, fill_hardwired,
- fill_mio_first);
- if (badness < 0) {
- kfree(best_cfg);
- return badness;
- }
- debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n",
- cfg->line_out_type, fill_hardwired, fill_mio_first,
- badness);
- debug_show_configs(codec, cfg);
- if (badness < best_badness) {
- best_badness = badness;
- *best_cfg = *cfg;
- best_wired = fill_hardwired;
- best_mio = fill_mio_first;
- }
- if (!badness)
- break;
- fill_mio_first = !fill_mio_first;
- if (!fill_mio_first)
- continue;
- fill_hardwired = !fill_hardwired;
- if (!fill_hardwired)
- continue;
- if (hp_spk_swapped)
- break;
- hp_spk_swapped = true;
- if (cfg->speaker_outs > 0 &&
- cfg->line_out_type == AUTO_PIN_HP_OUT) {
- cfg->hp_outs = cfg->line_outs;
- memcpy(cfg->hp_pins, cfg->line_out_pins,
- sizeof(cfg->hp_pins));
- cfg->line_outs = cfg->speaker_outs;
- memcpy(cfg->line_out_pins, cfg->speaker_pins,
- sizeof(cfg->speaker_pins));
- cfg->speaker_outs = 0;
- memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
- cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
- fill_hardwired = true;
- continue;
- }
- if (cfg->hp_outs > 0 &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
- cfg->speaker_outs = cfg->line_outs;
- memcpy(cfg->speaker_pins, cfg->line_out_pins,
- sizeof(cfg->speaker_pins));
- cfg->line_outs = cfg->hp_outs;
- memcpy(cfg->line_out_pins, cfg->hp_pins,
- sizeof(cfg->hp_pins));
- cfg->hp_outs = 0;
- memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
- cfg->line_out_type = AUTO_PIN_HP_OUT;
- fill_hardwired = true;
- continue;
- }
- break;
- }
-
- if (badness) {
- debug_badness("==> restoring best_cfg\n");
- *cfg = *best_cfg;
- fill_and_eval_dacs(codec, best_wired, best_mio);
- }
- debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n",
- cfg->line_out_type, best_wired, best_mio);
- debug_show_configs(codec, cfg);
-
- if (cfg->line_out_pins[0]) {
- struct nid_path *path;
- path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]);
- if (path)
- spec->vmaster_nid = look_for_out_vol_nid(codec, path);
- if (spec->vmaster_nid)
- snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
- HDA_OUTPUT, spec->vmaster_tlv);
- }
-
- /* set initial pinctl targets */
- if (spec->prefer_hp_amp || cfg->line_out_type == AUTO_PIN_HP_OUT)
- val = PIN_HP;
- else
- val = PIN_OUT;
- set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins, val);
- if (cfg->line_out_type != AUTO_PIN_HP_OUT)
- set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP);
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- val = spec->prefer_hp_amp ? PIN_HP : PIN_OUT;
- set_pin_targets(codec, cfg->speaker_outs,
- cfg->speaker_pins, val);
- }
-
- kfree(best_cfg);
- return 0;
-}
-
-/* add playback controls from the parsed DAC table */
-static int create_multi_out_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i, err, noutputs;
-
- noutputs = cfg->line_outs;
- if (spec->multi_ios > 0 && cfg->line_outs < 3)
- noutputs += spec->multi_ios;
-
- for (i = 0; i < noutputs; i++) {
- const char *name;
- int index;
- struct nid_path *path;
-
- path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]);
- if (!path)
- continue;
-
- name = get_line_out_pfx(codec, i, &index, NID_PATH_VOL_CTL);
- if (!name || !strcmp(name, "CLFE")) {
- /* Center/LFE */
- err = add_vol_ctl(codec, "Center", 0, 1, path);
- if (err < 0)
- return err;
- err = add_vol_ctl(codec, "LFE", 0, 2, path);
- if (err < 0)
- return err;
- } else {
- err = add_stereo_vol(codec, name, index, path);
- if (err < 0)
- return err;
- }
-
- name = get_line_out_pfx(codec, i, &index, NID_PATH_MUTE_CTL);
- if (!name || !strcmp(name, "CLFE")) {
- err = add_sw_ctl(codec, "Center", 0, 1, path);
- if (err < 0)
- return err;
- err = add_sw_ctl(codec, "LFE", 0, 2, path);
- if (err < 0)
- return err;
- } else {
- err = add_stereo_sw(codec, name, index, path);
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-static int create_extra_out(struct hda_codec *codec, int path_idx,
- const char *pfx, int cidx)
-{
- struct nid_path *path;
- int err;
-
- path = snd_hda_get_path_from_idx(codec, path_idx);
- if (!path)
- return 0;
- err = add_stereo_vol(codec, pfx, cidx, path);
- if (err < 0)
- return err;
- err = add_stereo_sw(codec, pfx, cidx, path);
- if (err < 0)
- return err;
- return 0;
-}
-
-/* add playback controls for speaker and HP outputs */
-static int create_extra_outs(struct hda_codec *codec, int num_pins,
- const int *paths, const char *pfx)
-{
- int i;
-
- for (i = 0; i < num_pins; i++) {
- const char *name;
- char tmp[44];
- int err, idx = 0;
-
- if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker"))
- name = "Bass Speaker";
- else if (num_pins >= 3) {
- snprintf(tmp, sizeof(tmp), "%s %s",
- pfx, channel_name[i]);
- name = tmp;
- } else {
- name = pfx;
- idx = i;
- }
- err = create_extra_out(codec, paths[i], name, idx);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-static int create_hp_out_ctls(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- return create_extra_outs(codec, spec->autocfg.hp_outs,
- spec->hp_paths,
- "Headphone");
-}
-
-static int create_speaker_out_ctls(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- return create_extra_outs(codec, spec->autocfg.speaker_outs,
- spec->speaker_paths,
- "Speaker");
-}
-
-/*
- * independent HP controls
- */
-
-static int indep_hp_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
-}
-
-static int indep_hp_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
- ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled;
- return 0;
-}
-
-static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
- int nomix_path_idx, int mix_path_idx,
- int out_type);
-
-static int indep_hp_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
- unsigned int select = ucontrol->value.enumerated.item[0];
- int ret = 0;
-
- mutex_lock(&spec->pcm_mutex);
- if (spec->active_streams) {
- ret = -EBUSY;
- goto unlock;
- }
-
- if (spec->indep_hp_enabled != select) {
- hda_nid_t *dacp;
- if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
- dacp = &spec->private_dac_nids[0];
- else
- dacp = &spec->multiout.hp_out_nid[0];
-
- /* update HP aamix paths in case it conflicts with indep HP */
- if (spec->have_aamix_ctl) {
- if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
- update_aamix_paths(codec, spec->aamix_mode,
- spec->out_paths[0],
- spec->aamix_out_paths[0],
- spec->autocfg.line_out_type);
- else
- update_aamix_paths(codec, spec->aamix_mode,
- spec->hp_paths[0],
- spec->aamix_out_paths[1],
- AUTO_PIN_HP_OUT);
- }
-
- spec->indep_hp_enabled = select;
- if (spec->indep_hp_enabled)
- *dacp = 0;
- else
- *dacp = spec->alt_dac_nid;
-
- /* update HP auto-mute state too */
- if (spec->hp_automute_hook)
- spec->hp_automute_hook(codec, NULL);
- else
- snd_hda_gen_hp_automute(codec, NULL);
-
- ret = 1;
- }
- unlock:
- mutex_unlock(&spec->pcm_mutex);
- return ret;
-}
-
-static const struct snd_kcontrol_new indep_hp_ctl = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Independent HP",
- .info = indep_hp_info,
- .get = indep_hp_get,
- .put = indep_hp_put,
-};
-
-
-static int create_indep_hp_ctls(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- hda_nid_t dac;
-
- if (!spec->indep_hp)
- return 0;
- if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
- dac = spec->multiout.dac_nids[0];
- else
- dac = spec->multiout.hp_out_nid[0];
- if (!dac) {
- spec->indep_hp = 0;
- return 0;
- }
-
- spec->indep_hp_enabled = false;
- spec->alt_dac_nid = dac;
- if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl))
- return -ENOMEM;
- return 0;
-}
-
-/*
- * channel mode enum control
- */
-
-static int ch_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
- int chs;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = spec->multi_ios + 1;
- if (uinfo->value.enumerated.item > spec->multi_ios)
- uinfo->value.enumerated.item = spec->multi_ios;
- chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count;
- sprintf(uinfo->value.enumerated.name, "%dch", chs);
- return 0;
-}
-
-static int ch_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
- ucontrol->value.enumerated.item[0] =
- (spec->ext_channel_count - spec->min_channel_count) / 2;
- return 0;
-}
-
-static inline struct nid_path *
-get_multiio_path(struct hda_codec *codec, int idx)
-{
- struct hda_gen_spec *spec = codec->spec;
- return snd_hda_get_path_from_idx(codec,
- spec->out_paths[spec->autocfg.line_outs + idx]);
-}
-
-static void update_automute_all(struct hda_codec *codec);
-
-static int set_multi_io(struct hda_codec *codec, int idx, bool output)
-{
- struct hda_gen_spec *spec = codec->spec;
- hda_nid_t nid = spec->multi_io[idx].pin;
- struct nid_path *path;
-
- path = get_multiio_path(codec, idx);
- if (!path)
- return -EINVAL;
-
- if (path->active == output)
- return 0;
-
- if (output) {
- set_pin_target(codec, nid, PIN_OUT, true);
- snd_hda_activate_path(codec, path, true, true);
- set_pin_eapd(codec, nid, true);
- } else {
- set_pin_eapd(codec, nid, false);
- snd_hda_activate_path(codec, path, false, true);
- set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true);
- path_power_down_sync(codec, path);
- }
-
- /* update jack retasking in case it modifies any of them */
- update_automute_all(codec);
-
- return 0;
-}
-
-static int ch_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
- int i, ch;
-
- ch = ucontrol->value.enumerated.item[0];
- if (ch < 0 || ch > spec->multi_ios)
- return -EINVAL;
- if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2)
- return 0;
- spec->ext_channel_count = ch * 2 + spec->min_channel_count;
- for (i = 0; i < spec->multi_ios; i++)
- set_multi_io(codec, i, i < ch);
- spec->multiout.max_channels = max(spec->ext_channel_count,
- spec->const_channel_count);
- if (spec->need_dac_fix)
- spec->multiout.num_dacs = spec->multiout.max_channels / 2;
- return 1;
-}
-
-static const struct snd_kcontrol_new channel_mode_enum = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Channel Mode",
- .info = ch_mode_info,
- .get = ch_mode_get,
- .put = ch_mode_put,
-};
-
-static int create_multi_channel_mode(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
-
- if (spec->multi_ios > 0) {
- if (!snd_hda_gen_add_kctl(spec, NULL, &channel_mode_enum))
- return -ENOMEM;
- }
- return 0;
-}
-
-/*
- * aamix loopback enable/disable switch
- */
-
-#define loopback_mixing_info indep_hp_info
-
-static int loopback_mixing_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
- ucontrol->value.enumerated.item[0] = spec->aamix_mode;
- return 0;
-}
-
-static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
- int nomix_path_idx, int mix_path_idx,
- int out_type)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct nid_path *nomix_path, *mix_path;
-
- nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx);
- mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx);
- if (!nomix_path || !mix_path)
- return;
-
- /* if HP aamix path is driven from a different DAC and the
- * independent HP mode is ON, can't turn on aamix path
- */
- if (out_type == AUTO_PIN_HP_OUT && spec->indep_hp_enabled &&
- mix_path->path[0] != spec->alt_dac_nid)
- do_mix = false;
-
- if (do_mix) {
- snd_hda_activate_path(codec, nomix_path, false, true);
- snd_hda_activate_path(codec, mix_path, true, true);
- path_power_down_sync(codec, nomix_path);
- } else {
- snd_hda_activate_path(codec, mix_path, false, true);
- snd_hda_activate_path(codec, nomix_path, true, true);
- path_power_down_sync(codec, mix_path);
- }
-}
-
-static int loopback_mixing_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
- unsigned int val = ucontrol->value.enumerated.item[0];
-
- if (val == spec->aamix_mode)
- return 0;
- spec->aamix_mode = val;
- update_aamix_paths(codec, val, spec->out_paths[0],
- spec->aamix_out_paths[0],
- spec->autocfg.line_out_type);
- update_aamix_paths(codec, val, spec->hp_paths[0],
- spec->aamix_out_paths[1],
- AUTO_PIN_HP_OUT);
- update_aamix_paths(codec, val, spec->speaker_paths[0],
- spec->aamix_out_paths[2],
- AUTO_PIN_SPEAKER_OUT);
- return 1;
-}
-
-static const struct snd_kcontrol_new loopback_mixing_enum = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Loopback Mixing",
- .info = loopback_mixing_info,
- .get = loopback_mixing_get,
- .put = loopback_mixing_put,
-};
-
-static int create_loopback_mixing_ctl(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
-
- if (!spec->mixer_nid)
- return 0;
- if (!(spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
- spec->aamix_out_paths[2]))
- return 0;
- if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
- return -ENOMEM;
- spec->have_aamix_ctl = 1;
- return 0;
-}
-
-/*
- * shared headphone/mic handling
- */
-
-static void call_update_outputs(struct hda_codec *codec);
-
-/* for shared I/O, change the pin-control accordingly */
-static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
-{
- struct hda_gen_spec *spec = codec->spec;
- unsigned int val;
- hda_nid_t pin = spec->autocfg.inputs[1].pin;
- /* NOTE: this assumes that there are only two inputs, the
- * first is the real internal mic and the second is HP/mic jack.
- */
-
- val = snd_hda_get_default_vref(codec, pin);
-
- /* This pin does not have vref caps - let's enable vref on pin 0x18
- instead, as suggested by Realtek */
- if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) {
- const hda_nid_t vref_pin = spec->shared_mic_vref_pin;
- unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
- if (vref_val != AC_PINCTL_VREF_HIZ)
- snd_hda_set_pin_ctl_cache(codec, vref_pin,
- PIN_IN | (set_as_mic ? vref_val : 0));
- }
-
- val = set_as_mic ? val | PIN_IN : PIN_HP;
- set_pin_target(codec, pin, val, true);
-
- spec->automute_speaker = !set_as_mic;
- call_update_outputs(codec);
-}
-
-/* create a shared input with the headphone out */
-static int create_shared_input(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int defcfg;
- hda_nid_t nid;
-
- /* only one internal input pin? */
- if (cfg->num_inputs != 1)
- return 0;
- defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin);
- if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
- return 0;
-
- if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */
- else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT)
- nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */
- else
- return 0; /* both not available */
-
- if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN))
- return 0; /* no input */
-
- cfg->inputs[1].pin = nid;
- cfg->inputs[1].type = AUTO_PIN_MIC;
- cfg->num_inputs = 2;
- spec->shared_mic_hp = 1;
- snd_printdd("hda-codec: Enable shared I/O jack on NID 0x%x\n", nid);
- return 0;
-}
-
-/*
- * output jack mode
- */
-static int out_jack_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- static const char * const texts[] = {
- "Line Out", "Headphone Out",
- };
- return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts);
-}
-
-static int out_jack_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- if (snd_hda_codec_get_pin_target(codec, nid) == PIN_HP)
- ucontrol->value.enumerated.item[0] = 1;
- else
- ucontrol->value.enumerated.item[0] = 0;
- return 0;
-}
-
-static int out_jack_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned int val;
-
- val = ucontrol->value.enumerated.item[0] ? PIN_HP : PIN_OUT;
- if (snd_hda_codec_get_pin_target(codec, nid) == val)
- return 0;
- snd_hda_set_pin_ctl_cache(codec, nid, val);
- return 1;
-}
-
-static const struct snd_kcontrol_new out_jack_mode_enum = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .info = out_jack_mode_info,
- .get = out_jack_mode_get,
- .put = out_jack_mode_put,
-};
-
-static bool find_kctl_name(struct hda_codec *codec, const char *name, int idx)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->kctls.used; i++) {
- struct snd_kcontrol_new *kctl = snd_array_elem(&spec->kctls, i);
- if (!strcmp(kctl->name, name) && kctl->index == idx)
- return true;
- }
- return false;
-}
-
-static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin,
- char *name, size_t name_len)
-{
- struct hda_gen_spec *spec = codec->spec;
- int idx = 0;
-
- snd_hda_get_pin_label(codec, pin, &spec->autocfg, name, name_len, &idx);
- strlcat(name, " Jack Mode", name_len);
-
- for (; find_kctl_name(codec, name, idx); idx++)
- ;
-}
-
-static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < num_pins; i++) {
- hda_nid_t pin = pins[i];
- unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
- if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV)) {
- struct snd_kcontrol_new *knew;
- char name[44];
- get_jack_mode_name(codec, pin, name, sizeof(name));
- knew = snd_hda_gen_add_kctl(spec, name,
- &out_jack_mode_enum);
- if (!knew)
- return -ENOMEM;
- knew->private_value = pin;
- }
- }
-
- return 0;
-}
-
-/*
- * input jack mode
- */
-
-/* from AC_PINCTL_VREF_HIZ to AC_PINCTL_VREF_100 */
-#define NUM_VREFS 6
-
-static const char * const vref_texts[NUM_VREFS] = {
- "Line In", "Mic 50pc Bias", "Mic 0V Bias",
- "", "Mic 80pc Bias", "Mic 100pc Bias"
-};
-
-static unsigned int get_vref_caps(struct hda_codec *codec, hda_nid_t pin)
-{
- unsigned int pincap;
-
- pincap = snd_hda_query_pin_caps(codec, pin);
- pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
- /* filter out unusual vrefs */
- pincap &= ~(AC_PINCAP_VREF_GRD | AC_PINCAP_VREF_100);
- return pincap;
-}
-
-/* convert from the enum item index to the vref ctl index (0=HIZ, 1=50%...) */
-static int get_vref_idx(unsigned int vref_caps, unsigned int item_idx)
-{
- unsigned int i, n = 0;
-
- for (i = 0; i < NUM_VREFS; i++) {
- if (vref_caps & (1 << i)) {
- if (n == item_idx)
- return i;
- n++;
- }
- }
- return 0;
-}
-
-/* convert back from the vref ctl index to the enum item index */
-static int cvt_from_vref_idx(unsigned int vref_caps, unsigned int idx)
-{
- unsigned int i, n = 0;
-
- for (i = 0; i < NUM_VREFS; i++) {
- if (i == idx)
- return n;
- if (vref_caps & (1 << i))
- n++;
- }
- return 0;
-}
-
-static int in_jack_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned int vref_caps = get_vref_caps(codec, nid);
-
- snd_hda_enum_helper_info(kcontrol, uinfo, hweight32(vref_caps),
- vref_texts);
- /* set the right text */
- strcpy(uinfo->value.enumerated.name,
- vref_texts[get_vref_idx(vref_caps, uinfo->value.enumerated.item)]);
- return 0;
-}
-
-static int in_jack_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned int vref_caps = get_vref_caps(codec, nid);
- unsigned int idx;
-
- idx = snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_VREFEN;
- ucontrol->value.enumerated.item[0] = cvt_from_vref_idx(vref_caps, idx);
- return 0;
-}
-
-static int in_jack_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned int vref_caps = get_vref_caps(codec, nid);
- unsigned int val, idx;
-
- val = snd_hda_codec_get_pin_target(codec, nid);
- idx = cvt_from_vref_idx(vref_caps, val & AC_PINCTL_VREFEN);
- if (idx == ucontrol->value.enumerated.item[0])
- return 0;
-
- val &= ~AC_PINCTL_VREFEN;
- val |= get_vref_idx(vref_caps, ucontrol->value.enumerated.item[0]);
- snd_hda_set_pin_ctl_cache(codec, nid, val);
- return 1;
-}
-
-static const struct snd_kcontrol_new in_jack_mode_enum = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .info = in_jack_mode_info,
- .get = in_jack_mode_get,
- .put = in_jack_mode_put,
-};
-
-static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
-{
- struct hda_gen_spec *spec = codec->spec;
- unsigned int defcfg;
- struct snd_kcontrol_new *knew;
- char name[44];
-
- /* no jack mode for fixed pins */
- defcfg = snd_hda_codec_get_pincfg(codec, pin);
- if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT)
- return 0;
-
- /* no multiple vref caps? */
- if (hweight32(get_vref_caps(codec, pin)) <= 1)
- return 0;
-
- get_jack_mode_name(codec, pin, name, sizeof(name));
- knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum);
- if (!knew)
- return -ENOMEM;
- knew->private_value = pin;
- return 0;
-}
-
-
-/*
- * Parse input paths
- */
-
-/* add the powersave loopback-list entry */
-static int add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx)
-{
- struct hda_amp_list *list;
-
- list = snd_array_new(&spec->loopback_list);
- if (!list)
- return -ENOMEM;
- list->nid = mix;
- list->dir = HDA_INPUT;
- list->idx = idx;
- spec->loopback.amplist = spec->loopback_list.list;
- return 0;
-}
-
-/* create input playback/capture controls for the given pin */
-static int new_analog_input(struct hda_codec *codec, int input_idx,
- hda_nid_t pin, const char *ctlname, int ctlidx,
- hda_nid_t mix_nid)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct nid_path *path;
- unsigned int val;
- int err, idx;
-
- if (!nid_has_volume(codec, mix_nid, HDA_INPUT) &&
- !nid_has_mute(codec, mix_nid, HDA_INPUT))
- return 0; /* no need for analog loopback */
-
- path = snd_hda_add_new_path(codec, pin, mix_nid, 0);
- if (!path)
- return -EINVAL;
- print_nid_path("loopback", path);
- spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path);
-
- idx = path->idx[path->depth - 1];
- if (nid_has_volume(codec, mix_nid, HDA_INPUT)) {
- val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
- err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, val);
- if (err < 0)
- return err;
- path->ctls[NID_PATH_VOL_CTL] = val;
- }
-
- if (nid_has_mute(codec, mix_nid, HDA_INPUT)) {
- val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
- err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, val);
- if (err < 0)
- return err;
- path->ctls[NID_PATH_MUTE_CTL] = val;
- }
-
- path->active = true;
- err = add_loopback_list(spec, mix_nid, idx);
- if (err < 0)
- return err;
-
- if (spec->mixer_nid != spec->mixer_merge_nid &&
- !spec->loopback_merge_path) {
- path = snd_hda_add_new_path(codec, spec->mixer_nid,
- spec->mixer_merge_nid, 0);
- if (path) {
- print_nid_path("loopback-merge", path);
- path->active = true;
- spec->loopback_merge_path =
- snd_hda_get_path_idx(codec, path);
- }
- }
-
- return 0;
-}
-
-static int is_input_pin(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
- return (pincap & AC_PINCAP_IN) != 0;
-}
-
-/* Parse the codec tree and retrieve ADCs */
-static int fill_adc_nids(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- hda_nid_t nid;
- hda_nid_t *adc_nids = spec->adc_nids;
- int max_nums = ARRAY_SIZE(spec->adc_nids);
- int i, nums = 0;
-
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
- unsigned int caps = get_wcaps(codec, nid);
- int type = get_wcaps_type(caps);
-
- if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL))
- continue;
- adc_nids[nums] = nid;
- if (++nums >= max_nums)
- break;
- }
- spec->num_adc_nids = nums;
-
- /* copy the detected ADCs to all_adcs[] */
- spec->num_all_adcs = nums;
- memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t));
-
- return nums;
-}
-
-/* filter out invalid adc_nids that don't give all active input pins;
- * if needed, check whether dynamic ADC-switching is available
- */
-static int check_dyn_adc_switch(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->input_mux;
- unsigned int ok_bits;
- int i, n, nums;
-
- again:
- nums = 0;
- ok_bits = 0;
- for (n = 0; n < spec->num_adc_nids; n++) {
- for (i = 0; i < imux->num_items; i++) {
- if (!spec->input_paths[i][n])
- break;
- }
- if (i >= imux->num_items) {
- ok_bits |= (1 << n);
- nums++;
- }
- }
-
- if (!ok_bits) {
- if (spec->shared_mic_hp) {
- spec->shared_mic_hp = 0;
- imux->num_items = 1;
- goto again;
- }
-
- /* check whether ADC-switch is possible */
- for (i = 0; i < imux->num_items; i++) {
- for (n = 0; n < spec->num_adc_nids; n++) {
- if (spec->input_paths[i][n]) {
- spec->dyn_adc_idx[i] = n;
- break;
- }
- }
- }
-
- snd_printdd("hda-codec: enabling ADC switching\n");
- spec->dyn_adc_switch = 1;
- } else if (nums != spec->num_adc_nids) {
- /* shrink the invalid adcs and input paths */
- nums = 0;
- for (n = 0; n < spec->num_adc_nids; n++) {
- if (!(ok_bits & (1 << n)))
- continue;
- if (n != nums) {
- spec->adc_nids[nums] = spec->adc_nids[n];
- for (i = 0; i < imux->num_items; i++) {
- invalidate_nid_path(codec,
- spec->input_paths[i][nums]);
- spec->input_paths[i][nums] =
- spec->input_paths[i][n];
- }
- }
- nums++;
- }
- spec->num_adc_nids = nums;
- }
-
- if (imux->num_items == 1 || spec->shared_mic_hp) {
- snd_printdd("hda-codec: reducing to a single ADC\n");
- spec->num_adc_nids = 1; /* reduce to a single ADC */
- }
-
- /* single index for individual volumes ctls */
- if (!spec->dyn_adc_switch && spec->multi_cap_vol)
- spec->num_adc_nids = 1;
-
- return 0;
-}
-
-/* parse capture source paths from the given pin and create imux items */
-static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin,
- int cfg_idx, int num_adcs,
- const char *label, int anchor)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->input_mux;
- int imux_idx = imux->num_items;
- bool imux_added = false;
- int c;
-
- for (c = 0; c < num_adcs; c++) {
- struct nid_path *path;
- hda_nid_t adc = spec->adc_nids[c];
-
- if (!is_reachable_path(codec, pin, adc))
- continue;
- path = snd_hda_add_new_path(codec, pin, adc, anchor);
- if (!path)
- continue;
- print_nid_path("input", path);
- spec->input_paths[imux_idx][c] =
- snd_hda_get_path_idx(codec, path);
-
- if (!imux_added) {
- spec->imux_pins[imux->num_items] = pin;
- snd_hda_add_imux_item(imux, label, cfg_idx, NULL);
- imux_added = true;
- }
- }
-
- return 0;
-}
-
-/*
- * create playback/capture controls for input pins
- */
-
-/* fill the label for each input at first */
-static int fill_input_pin_labels(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t pin = cfg->inputs[i].pin;
- const char *label;
- int j, idx;
-
- if (!is_input_pin(codec, pin))
- continue;
-
- label = hda_get_autocfg_input_label(codec, cfg, i);
- idx = 0;
- for (j = i - 1; j >= 0; j--) {
- if (spec->input_labels[j] &&
- !strcmp(spec->input_labels[j], label)) {
- idx = spec->input_label_idxs[j] + 1;
- break;
- }
- }
-
- spec->input_labels[i] = label;
- spec->input_label_idxs[i] = idx;
- }
-
- return 0;
-}
-
-#define CFG_IDX_MIX 99 /* a dummy cfg->input idx for stereo mix */
-
-static int create_input_ctls(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- const struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t mixer = spec->mixer_nid;
- int num_adcs;
- int i, err;
- unsigned int val;
-
- num_adcs = fill_adc_nids(codec);
- if (num_adcs < 0)
- return 0;
-
- err = fill_input_pin_labels(codec);
- if (err < 0)
- return err;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t pin;
-
- pin = cfg->inputs[i].pin;
- if (!is_input_pin(codec, pin))
- continue;
-
- val = PIN_IN;
- if (cfg->inputs[i].type == AUTO_PIN_MIC)
- val |= snd_hda_get_default_vref(codec, pin);
- set_pin_target(codec, pin, val, false);
-
- if (mixer) {
- if (is_reachable_path(codec, pin, mixer)) {
- err = new_analog_input(codec, i, pin,
- spec->input_labels[i],
- spec->input_label_idxs[i],
- mixer);
- if (err < 0)
- return err;
- }
- }
-
- err = parse_capture_source(codec, pin, i, num_adcs,
- spec->input_labels[i], -mixer);
- if (err < 0)
- return err;
-
- if (spec->add_in_jack_modes) {
- err = create_in_jack_mode(codec, pin);
- if (err < 0)
- return err;
- }
- }
-
- if (mixer && spec->add_stereo_mix_input) {
- err = parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs,
- "Stereo Mix", 0);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-
-/*
- * input source mux
- */
-
-/* get the input path specified by the given adc and imux indices */
-static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx)
-{
- struct hda_gen_spec *spec = codec->spec;
- if (imux_idx < 0 || imux_idx >= HDA_MAX_NUM_INPUTS) {
- snd_BUG();
- return NULL;
- }
- if (spec->dyn_adc_switch)
- adc_idx = spec->dyn_adc_idx[imux_idx];
- if (adc_idx < 0 || adc_idx >= AUTO_CFG_MAX_INS) {
- snd_BUG();
- return NULL;
- }
- return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]);
-}
-
-static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
- unsigned int idx);
-
-static int mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
- return snd_hda_input_mux_info(&spec->input_mux, uinfo);
-}
-
-static int mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
- /* the ctls are created at once with multiple counts */
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
- return 0;
-}
-
-static int mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- return mux_select(codec, adc_idx,
- ucontrol->value.enumerated.item[0]);
-}
-
-static const struct snd_kcontrol_new cap_src_temp = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Input Source",
- .info = mux_enum_info,
- .get = mux_enum_get,
- .put = mux_enum_put,
-};
-
-/*
- * capture volume and capture switch ctls
- */
-
-typedef int (*put_call_t)(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-
-/* call the given amp update function for all amps in the imux list at once */
-static int cap_put_caller(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol,
- put_call_t func, int type)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
- const struct hda_input_mux *imux;
- struct nid_path *path;
- int i, adc_idx, err = 0;
-
- imux = &spec->input_mux;
- adc_idx = kcontrol->id.index;
- mutex_lock(&codec->control_mutex);
- /* we use the cache-only update at first since multiple input paths
- * may shared the same amp; by updating only caches, the redundant
- * writes to hardware can be reduced.
- */
- codec->cached_write = 1;
- for (i = 0; i < imux->num_items; i++) {
- path = get_input_path(codec, adc_idx, i);
- if (!path || !path->ctls[type])
- continue;
- kcontrol->private_value = path->ctls[type];
- err = func(kcontrol, ucontrol);
- if (err < 0)
- goto error;
- }
- error:
- codec->cached_write = 0;
- mutex_unlock(&codec->control_mutex);
- snd_hda_codec_flush_cache(codec); /* flush the updates */
- if (err >= 0 && spec->cap_sync_hook)
- spec->cap_sync_hook(codec, ucontrol);
- return err;
-}
-
-/* capture volume ctl callbacks */
-#define cap_vol_info snd_hda_mixer_amp_volume_info
-#define cap_vol_get snd_hda_mixer_amp_volume_get
-#define cap_vol_tlv snd_hda_mixer_amp_tlv
-
-static int cap_vol_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return cap_put_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_volume_put,
- NID_PATH_VOL_CTL);
-}
-
-static const struct snd_kcontrol_new cap_vol_temp = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Volume",
- .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ |
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK),
- .info = cap_vol_info,
- .get = cap_vol_get,
- .put = cap_vol_put,
- .tlv = { .c = cap_vol_tlv },
-};
-
-/* capture switch ctl callbacks */
-#define cap_sw_info snd_ctl_boolean_stereo_info
-#define cap_sw_get snd_hda_mixer_amp_switch_get
-
-static int cap_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- return cap_put_caller(kcontrol, ucontrol,
- snd_hda_mixer_amp_switch_put,
- NID_PATH_MUTE_CTL);
-}
-
-static const struct snd_kcontrol_new cap_sw_temp = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Switch",
- .info = cap_sw_info,
- .get = cap_sw_get,
- .put = cap_sw_put,
-};
-
-static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path)
-{
- hda_nid_t nid;
- int i, depth;
-
- path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0;
- for (depth = 0; depth < 3; depth++) {
- if (depth >= path->depth)
- return -EINVAL;
- i = path->depth - depth - 1;
- nid = path->path[i];
- if (!path->ctls[NID_PATH_VOL_CTL]) {
- if (nid_has_volume(codec, nid, HDA_OUTPUT))
- path->ctls[NID_PATH_VOL_CTL] =
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- else if (nid_has_volume(codec, nid, HDA_INPUT)) {
- int idx = path->idx[i];
- if (!depth && codec->single_adc_amp)
- idx = 0;
- path->ctls[NID_PATH_VOL_CTL] =
- HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
- }
- }
- if (!path->ctls[NID_PATH_MUTE_CTL]) {
- if (nid_has_mute(codec, nid, HDA_OUTPUT))
- path->ctls[NID_PATH_MUTE_CTL] =
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- else if (nid_has_mute(codec, nid, HDA_INPUT)) {
- int idx = path->idx[i];
- if (!depth && codec->single_adc_amp)
- idx = 0;
- path->ctls[NID_PATH_MUTE_CTL] =
- HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT);
- }
- }
- }
- return 0;
-}
-
-static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int val;
- int i;
-
- if (!spec->inv_dmic_split)
- return false;
- for (i = 0; i < cfg->num_inputs; i++) {
- if (cfg->inputs[i].pin != nid)
- continue;
- if (cfg->inputs[i].type != AUTO_PIN_MIC)
- return false;
- val = snd_hda_codec_get_pincfg(codec, nid);
- return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT;
- }
- return false;
-}
-
-/* capture switch put callback for a single control with hook call */
-static int cap_single_sw_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
- int ret;
-
- ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- if (ret < 0)
- return ret;
-
- if (spec->cap_sync_hook)
- spec->cap_sync_hook(codec, ucontrol);
-
- return ret;
-}
-
-static int add_single_cap_ctl(struct hda_codec *codec, const char *label,
- int idx, bool is_switch, unsigned int ctl,
- bool inv_dmic)
-{
- struct hda_gen_spec *spec = codec->spec;
- char tmpname[44];
- int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL;
- const char *sfx = is_switch ? "Switch" : "Volume";
- unsigned int chs = inv_dmic ? 1 : 3;
- struct snd_kcontrol_new *knew;
-
- if (!ctl)
- return 0;
-
- if (label)
- snprintf(tmpname, sizeof(tmpname),
- "%s Capture %s", label, sfx);
- else
- snprintf(tmpname, sizeof(tmpname),
- "Capture %s", sfx);
- knew = add_control(spec, type, tmpname, idx,
- amp_val_replace_channels(ctl, chs));
- if (!knew)
- return -ENOMEM;
- if (is_switch)
- knew->put = cap_single_sw_put;
- if (!inv_dmic)
- return 0;
-
- /* Make independent right kcontrol */
- if (label)
- snprintf(tmpname, sizeof(tmpname),
- "Inverted %s Capture %s", label, sfx);
- else
- snprintf(tmpname, sizeof(tmpname),
- "Inverted Capture %s", sfx);
- knew = add_control(spec, type, tmpname, idx,
- amp_val_replace_channels(ctl, 2));
- if (!knew)
- return -ENOMEM;
- if (is_switch)
- knew->put = cap_single_sw_put;
- return 0;
-}
-
-/* create single (and simple) capture volume and switch controls */
-static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx,
- unsigned int vol_ctl, unsigned int sw_ctl,
- bool inv_dmic)
-{
- int err;
- err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic);
- if (err < 0)
- return err;
- err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic);
- if (err < 0)
- return err;
- return 0;
-}
-
-/* create bound capture volume and switch controls */
-static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx,
- unsigned int vol_ctl, unsigned int sw_ctl)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct snd_kcontrol_new *knew;
-
- if (vol_ctl) {
- knew = snd_hda_gen_add_kctl(spec, NULL, &cap_vol_temp);
- if (!knew)
- return -ENOMEM;
- knew->index = idx;
- knew->private_value = vol_ctl;
- knew->subdevice = HDA_SUBDEV_AMP_FLAG;
- }
- if (sw_ctl) {
- knew = snd_hda_gen_add_kctl(spec, NULL, &cap_sw_temp);
- if (!knew)
- return -ENOMEM;
- knew->index = idx;
- knew->private_value = sw_ctl;
- knew->subdevice = HDA_SUBDEV_AMP_FLAG;
- }
- return 0;
-}
-
-/* return the vol ctl when used first in the imux list */
-static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type)
-{
- struct nid_path *path;
- unsigned int ctl;
- int i;
-
- path = get_input_path(codec, 0, idx);
- if (!path)
- return 0;
- ctl = path->ctls[type];
- if (!ctl)
- return 0;
- for (i = 0; i < idx - 1; i++) {
- path = get_input_path(codec, 0, i);
- if (path && path->ctls[type] == ctl)
- return 0;
- }
- return ctl;
-}
-
-/* create individual capture volume and switch controls per input */
-static int create_multi_cap_vol_ctl(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->input_mux;
- int i, err, type;
-
- for (i = 0; i < imux->num_items; i++) {
- bool inv_dmic;
- int idx;
-
- idx = imux->items[i].index;
- if (idx >= spec->autocfg.num_inputs)
- continue;
- inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]);
-
- for (type = 0; type < 2; type++) {
- err = add_single_cap_ctl(codec,
- spec->input_labels[idx],
- spec->input_label_idxs[idx],
- type,
- get_first_cap_ctl(codec, i, type),
- inv_dmic);
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-
-static int create_capture_mixers(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->input_mux;
- int i, n, nums, err;
-
- if (spec->dyn_adc_switch)
- nums = 1;
- else
- nums = spec->num_adc_nids;
-
- if (!spec->auto_mic && imux->num_items > 1) {
- struct snd_kcontrol_new *knew;
- const char *name;
- name = nums > 1 ? "Input Source" : "Capture Source";
- knew = snd_hda_gen_add_kctl(spec, name, &cap_src_temp);
- if (!knew)
- return -ENOMEM;
- knew->count = nums;
- }
-
- for (n = 0; n < nums; n++) {
- bool multi = false;
- bool multi_cap_vol = spec->multi_cap_vol;
- bool inv_dmic = false;
- int vol, sw;
-
- vol = sw = 0;
- for (i = 0; i < imux->num_items; i++) {
- struct nid_path *path;
- path = get_input_path(codec, n, i);
- if (!path)
- continue;
- parse_capvol_in_path(codec, path);
- if (!vol)
- vol = path->ctls[NID_PATH_VOL_CTL];
- else if (vol != path->ctls[NID_PATH_VOL_CTL]) {
- multi = true;
- if (!same_amp_caps(codec, vol,
- path->ctls[NID_PATH_VOL_CTL], HDA_INPUT))
- multi_cap_vol = true;
- }
- if (!sw)
- sw = path->ctls[NID_PATH_MUTE_CTL];
- else if (sw != path->ctls[NID_PATH_MUTE_CTL]) {
- multi = true;
- if (!same_amp_caps(codec, sw,
- path->ctls[NID_PATH_MUTE_CTL], HDA_INPUT))
- multi_cap_vol = true;
- }
- if (is_inv_dmic_pin(codec, spec->imux_pins[i]))
- inv_dmic = true;
- }
-
- if (!multi)
- err = create_single_cap_vol_ctl(codec, n, vol, sw,
- inv_dmic);
- else if (!multi_cap_vol)
- err = create_bind_cap_vol_ctl(codec, n, vol, sw);
- else
- err = create_multi_cap_vol_ctl(codec);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-/*
- * add mic boosts if needed
- */
-
-/* check whether the given amp is feasible as a boost volume */
-static bool check_boost_vol(struct hda_codec *codec, hda_nid_t nid,
- int dir, int idx)
-{
- unsigned int step;
-
- if (!nid_has_volume(codec, nid, dir) ||
- is_ctl_associated(codec, nid, dir, idx, NID_PATH_VOL_CTL) ||
- is_ctl_associated(codec, nid, dir, idx, NID_PATH_BOOST_CTL))
- return false;
-
- step = (query_amp_caps(codec, nid, dir) & AC_AMPCAP_STEP_SIZE)
- >> AC_AMPCAP_STEP_SIZE_SHIFT;
- if (step < 0x20)
- return false;
- return true;
-}
-
-/* look for a boost amp in a widget close to the pin */
-static unsigned int look_for_boost_amp(struct hda_codec *codec,
- struct nid_path *path)
-{
- unsigned int val = 0;
- hda_nid_t nid;
- int depth;
-
- for (depth = 0; depth < 3; depth++) {
- if (depth >= path->depth - 1)
- break;
- nid = path->path[depth];
- if (depth && check_boost_vol(codec, nid, HDA_OUTPUT, 0)) {
- val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- break;
- } else if (check_boost_vol(codec, nid, HDA_INPUT,
- path->idx[depth])) {
- val = HDA_COMPOSE_AMP_VAL(nid, 3, path->idx[depth],
- HDA_INPUT);
- break;
- }
- }
-
- return val;
-}
-
-static int parse_mic_boost(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- struct hda_input_mux *imux = &spec->input_mux;
- int i;
-
- if (!spec->num_adc_nids)
- return 0;
-
- for (i = 0; i < imux->num_items; i++) {
- struct nid_path *path;
- unsigned int val;
- int idx;
- char boost_label[44];
-
- idx = imux->items[i].index;
- if (idx >= imux->num_items)
- continue;
-
- /* check only line-in and mic pins */
- if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
- continue;
-
- path = get_input_path(codec, 0, i);
- if (!path)
- continue;
-
- val = look_for_boost_amp(codec, path);
- if (!val)
- continue;
-
- /* create a boost control */
- snprintf(boost_label, sizeof(boost_label),
- "%s Boost Volume", spec->input_labels[idx]);
- if (!add_control(spec, HDA_CTL_WIDGET_VOL, boost_label,
- spec->input_label_idxs[idx], val))
- return -ENOMEM;
-
- path->ctls[NID_PATH_BOOST_CTL] = val;
- }
- return 0;
-}
-
-/*
- * parse digital I/Os and set up NIDs in BIOS auto-parse mode
- */
-static void parse_digital(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct nid_path *path;
- int i, nums;
- hda_nid_t dig_nid, pin;
-
- /* support multiple SPDIFs; the secondary is set up as a slave */
- nums = 0;
- for (i = 0; i < spec->autocfg.dig_outs; i++) {
- pin = spec->autocfg.dig_out_pins[i];
- dig_nid = look_for_dac(codec, pin, true);
- if (!dig_nid)
- continue;
- path = snd_hda_add_new_path(codec, dig_nid, pin, 0);
- if (!path)
- continue;
- print_nid_path("digout", path);
- path->active = true;
- spec->digout_paths[i] = snd_hda_get_path_idx(codec, path);
- set_pin_target(codec, pin, PIN_OUT, false);
- if (!nums) {
- spec->multiout.dig_out_nid = dig_nid;
- spec->dig_out_type = spec->autocfg.dig_out_type[0];
- } else {
- spec->multiout.slave_dig_outs = spec->slave_dig_outs;
- if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
- break;
- spec->slave_dig_outs[nums - 1] = dig_nid;
- }
- nums++;
- }
-
- if (spec->autocfg.dig_in_pin) {
- pin = spec->autocfg.dig_in_pin;
- dig_nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
- unsigned int wcaps = get_wcaps(codec, dig_nid);
- if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
- continue;
- if (!(wcaps & AC_WCAP_DIGITAL))
- continue;
- path = snd_hda_add_new_path(codec, pin, dig_nid, 0);
- if (path) {
- print_nid_path("digin", path);
- path->active = true;
- spec->dig_in_nid = dig_nid;
- spec->digin_path = snd_hda_get_path_idx(codec, path);
- set_pin_target(codec, pin, PIN_IN, false);
- break;
- }
- }
- }
-}
-
-
-/*
- * input MUX handling
- */
-
-static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur);
-
-/* select the given imux item; either unmute exclusively or select the route */
-static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
- unsigned int idx)
-{
- struct hda_gen_spec *spec = codec->spec;
- const struct hda_input_mux *imux;
- struct nid_path *old_path, *path;
-
- imux = &spec->input_mux;
- if (!imux->num_items)
- return 0;
-
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- if (spec->cur_mux[adc_idx] == idx)
- return 0;
-
- old_path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]);
- if (!old_path)
- return 0;
- if (old_path->active)
- snd_hda_activate_path(codec, old_path, false, false);
-
- spec->cur_mux[adc_idx] = idx;
-
- if (spec->shared_mic_hp)
- update_shared_mic_hp(codec, spec->cur_mux[adc_idx]);
-
- if (spec->dyn_adc_switch)
- dyn_adc_pcm_resetup(codec, idx);
-
- path = get_input_path(codec, adc_idx, idx);
- if (!path)
- return 0;
- if (path->active)
- return 0;
- snd_hda_activate_path(codec, path, true, false);
- if (spec->cap_sync_hook)
- spec->cap_sync_hook(codec, NULL);
- path_power_down_sync(codec, old_path);
- return 1;
-}
-
-
-/*
- * Jack detections for HP auto-mute and mic-switch
- */
-
-/* check each pin in the given array; returns true if any of them is plugged */
-static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
-{
- int i, present = 0;
-
- for (i = 0; i < num_pins; i++) {
- hda_nid_t nid = pins[i];
- if (!nid)
- break;
- /* don't detect pins retasked as inputs */
- if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN)
- continue;
- present |= snd_hda_jack_detect(codec, nid);
- }
- return present;
-}
-
-/* standard HP/line-out auto-mute helper */
-static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
- bool mute)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < num_pins; i++) {
- hda_nid_t nid = pins[i];
- unsigned int val;
- if (!nid)
- break;
- /* don't reset VREF value in case it's controlling
- * the amp (see alc861_fixup_asus_amp_vref_0f())
- */
- if (spec->keep_vref_in_automute)
- val = snd_hda_codec_get_pin_target(codec, nid) & ~PIN_HP;
- else
- val = 0;
- if (!mute)
- val |= snd_hda_codec_get_pin_target(codec, nid);
- /* here we call update_pin_ctl() so that the pinctl is changed
- * without changing the pinctl target value;
- * the original target value will be still referred at the
- * init / resume again
- */
- update_pin_ctl(codec, nid, val);
- set_pin_eapd(codec, nid, !mute);
- }
-}
-
-/* Toggle outputs muting */
-void snd_hda_gen_update_outputs(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- int on;
-
- /* Control HP pins/amps depending on master_mute state;
- * in general, HP pins/amps control should be enabled in all cases,
- * but currently set only for master_mute, just to be safe
- */
- if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */
- do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
- spec->autocfg.hp_pins, spec->master_mute);
-
- if (!spec->automute_speaker)
- on = 0;
- else
- on = spec->hp_jack_present | spec->line_jack_present;
- on |= spec->master_mute;
- spec->speaker_muted = on;
- do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
- spec->autocfg.speaker_pins, on);
-
- /* toggle line-out mutes if needed, too */
- /* if LO is a copy of either HP or Speaker, don't need to handle it */
- if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] ||
- spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0])
- return;
- if (!spec->automute_lo)
- on = 0;
- else
- on = spec->hp_jack_present;
- on |= spec->master_mute;
- spec->line_out_muted = on;
- do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
- spec->autocfg.line_out_pins, on);
-}
-EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs);
-
-static void call_update_outputs(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- if (spec->automute_hook)
- spec->automute_hook(codec);
- else
- snd_hda_gen_update_outputs(codec);
-}
-
-/* standard HP-automute helper */
-void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
-{
- struct hda_gen_spec *spec = codec->spec;
- hda_nid_t *pins = spec->autocfg.hp_pins;
- int num_pins = ARRAY_SIZE(spec->autocfg.hp_pins);
-
- /* No detection for the first HP jack during indep-HP mode */
- if (spec->indep_hp_enabled) {
- pins++;
- num_pins--;
- }
-
- spec->hp_jack_present = detect_jacks(codec, num_pins, pins);
- if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo))
- return;
- call_update_outputs(codec);
-}
-EXPORT_SYMBOL_HDA(snd_hda_gen_hp_automute);
-
-/* standard line-out-automute helper */
-void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
-{
- struct hda_gen_spec *spec = codec->spec;
-
- if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
- return;
- /* check LO jack only when it's different from HP */
- if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0])
- return;
-
- spec->line_jack_present =
- detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
- spec->autocfg.line_out_pins);
- if (!spec->automute_speaker || !spec->detect_lo)
- return;
- call_update_outputs(codec);
-}
-EXPORT_SYMBOL_HDA(snd_hda_gen_line_automute);
-
-/* standard mic auto-switch helper */
-void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i;
-
- if (!spec->auto_mic)
- return;
-
- for (i = spec->am_num_entries - 1; i > 0; i--) {
- hda_nid_t pin = spec->am_entry[i].pin;
- /* don't detect pins retasked as outputs */
- if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN)
- continue;
- if (snd_hda_jack_detect(codec, pin)) {
- mux_select(codec, 0, spec->am_entry[i].idx);
- return;
- }
- }
- mux_select(codec, 0, spec->am_entry[0].idx);
-}
-EXPORT_SYMBOL_HDA(snd_hda_gen_mic_autoswitch);
-
-/* update jack retasking */
-static void update_automute_all(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
-
- if (spec->hp_automute_hook)
- spec->hp_automute_hook(codec, NULL);
- else
- snd_hda_gen_hp_automute(codec, NULL);
- if (spec->line_automute_hook)
- spec->line_automute_hook(codec, NULL);
- else
- snd_hda_gen_line_automute(codec, NULL);
- if (spec->mic_autoswitch_hook)
- spec->mic_autoswitch_hook(codec, NULL);
- else
- snd_hda_gen_mic_autoswitch(codec, NULL);
-}
-
-/*
- * Auto-Mute mode mixer enum support
- */
-static int automute_mode_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
- static const char * const texts3[] = {
- "Disabled", "Speaker Only", "Line Out+Speaker"
- };
-
- if (spec->automute_speaker_possible && spec->automute_lo_possible)
- return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
- return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
-}
-
-static int automute_mode_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
- unsigned int val = 0;
- if (spec->automute_speaker)
- val++;
- if (spec->automute_lo)
- val++;
-
- ucontrol->value.enumerated.item[0] = val;
- return 0;
-}
-
-static int automute_mode_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct hda_gen_spec *spec = codec->spec;
-
- switch (ucontrol->value.enumerated.item[0]) {
- case 0:
- if (!spec->automute_speaker && !spec->automute_lo)
- return 0;
- spec->automute_speaker = 0;
- spec->automute_lo = 0;
- break;
+ switch (spec->pcm_vol_nodes) {
case 1:
- if (spec->automute_speaker_possible) {
- if (!spec->automute_lo && spec->automute_speaker)
- return 0;
- spec->automute_speaker = 1;
- spec->automute_lo = 0;
- } else if (spec->automute_lo_possible) {
- if (spec->automute_lo)
- return 0;
- spec->automute_lo = 1;
- } else
- return -EINVAL;
- break;
+ return create_mixer(codec, spec->pcm_vol[0].node,
+ spec->pcm_vol[0].index,
+ "Master", "Playback", 0);
case 2:
- if (!spec->automute_lo_possible || !spec->automute_speaker_possible)
- return -EINVAL;
- if (spec->automute_speaker && spec->automute_lo)
- return 0;
- spec->automute_speaker = 1;
- spec->automute_lo = 1;
- break;
- default:
- return -EINVAL;
- }
- call_update_outputs(codec);
- return 1;
-}
-
-static const struct snd_kcontrol_new automute_mode_enum = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Auto-Mute Mode",
- .info = automute_mode_info,
- .get = automute_mode_get,
- .put = automute_mode_put,
-};
-
-static int add_automute_mode_enum(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
-
- if (!snd_hda_gen_add_kctl(spec, NULL, &automute_mode_enum))
- return -ENOMEM;
- return 0;
-}
-
-/*
- * Check the availability of HP/line-out auto-mute;
- * Set up appropriately if really supported
- */
-static int check_auto_mute_availability(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int present = 0;
- int i, err;
-
- if (spec->suppress_auto_mute)
- return 0;
-
- if (cfg->hp_pins[0])
- present++;
- if (cfg->line_out_pins[0])
- present++;
- if (cfg->speaker_pins[0])
- present++;
- if (present < 2) /* need two different output types */
- return 0;
-
- if (!cfg->speaker_pins[0] &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
- memcpy(cfg->speaker_pins, cfg->line_out_pins,
- sizeof(cfg->speaker_pins));
- cfg->speaker_outs = cfg->line_outs;
- }
-
- if (!cfg->hp_pins[0] &&
- cfg->line_out_type == AUTO_PIN_HP_OUT) {
- memcpy(cfg->hp_pins, cfg->line_out_pins,
- sizeof(cfg->hp_pins));
- cfg->hp_outs = cfg->line_outs;
- }
-
- for (i = 0; i < cfg->hp_outs; i++) {
- hda_nid_t nid = cfg->hp_pins[i];
- if (!is_jack_detectable(codec, nid))
- continue;
- snd_printdd("hda-codec: Enable HP auto-muting on NID 0x%x\n",
- nid);
- snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT,
- spec->hp_automute_hook ?
- spec->hp_automute_hook :
- snd_hda_gen_hp_automute);
- spec->detect_hp = 1;
- }
-
- if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) {
- if (cfg->speaker_outs)
- for (i = 0; i < cfg->line_outs; i++) {
- hda_nid_t nid = cfg->line_out_pins[i];
- if (!is_jack_detectable(codec, nid))
- continue;
- snd_printdd("hda-codec: Enable Line-Out auto-muting on NID 0x%x\n", nid);
- snd_hda_jack_detect_enable_callback(codec, nid,
- HDA_GEN_FRONT_EVENT,
- spec->line_automute_hook ?
- spec->line_automute_hook :
- snd_hda_gen_line_automute);
- spec->detect_lo = 1;
- }
- spec->automute_lo_possible = spec->detect_hp;
- }
-
- spec->automute_speaker_possible = cfg->speaker_outs &&
- (spec->detect_hp || spec->detect_lo);
-
- spec->automute_lo = spec->automute_lo_possible;
- spec->automute_speaker = spec->automute_speaker_possible;
-
- if (spec->automute_speaker_possible || spec->automute_lo_possible) {
- /* create a control for automute mode */
- err = add_automute_mode_enum(codec);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/* check whether all auto-mic pins are valid; setup indices if OK */
-static bool auto_mic_check_imux(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- const struct hda_input_mux *imux;
- int i;
-
- imux = &spec->input_mux;
- for (i = 0; i < spec->am_num_entries; i++) {
- spec->am_entry[i].idx =
- find_idx_in_nid_list(spec->am_entry[i].pin,
- spec->imux_pins, imux->num_items);
- if (spec->am_entry[i].idx < 0)
- return false; /* no corresponding imux */
- }
-
- /* we don't need the jack detection for the first pin */
- for (i = 1; i < spec->am_num_entries; i++)
- snd_hda_jack_detect_enable_callback(codec,
- spec->am_entry[i].pin,
- HDA_GEN_MIC_EVENT,
- spec->mic_autoswitch_hook ?
- spec->mic_autoswitch_hook :
- snd_hda_gen_mic_autoswitch);
- return true;
-}
-
-static int compare_attr(const void *ap, const void *bp)
-{
- const struct automic_entry *a = ap;
- const struct automic_entry *b = bp;
- return (int)(a->attr - b->attr);
-}
-
-/*
- * Check the availability of auto-mic switch;
- * Set up if really supported
- */
-static int check_auto_mic_availability(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int types;
- int i, num_pins;
-
- if (spec->suppress_auto_mic)
- return 0;
-
- types = 0;
- num_pins = 0;
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- unsigned int attr;
- attr = snd_hda_codec_get_pincfg(codec, nid);
- attr = snd_hda_get_input_pin_attr(attr);
- if (types & (1 << attr))
- return 0; /* already occupied */
- switch (attr) {
- case INPUT_PIN_ATTR_INT:
- if (cfg->inputs[i].type != AUTO_PIN_MIC)
- return 0; /* invalid type */
- break;
- case INPUT_PIN_ATTR_UNUSED:
- return 0; /* invalid entry */
- default:
- if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
- return 0; /* invalid type */
- if (!spec->line_in_auto_switch &&
- cfg->inputs[i].type != AUTO_PIN_MIC)
- return 0; /* only mic is allowed */
- if (!is_jack_detectable(codec, nid))
- return 0; /* no unsol support */
- break;
- }
- if (num_pins >= MAX_AUTO_MIC_PINS)
- return 0;
- types |= (1 << attr);
- spec->am_entry[num_pins].pin = nid;
- spec->am_entry[num_pins].attr = attr;
- num_pins++;
- }
-
- if (num_pins < 2)
- return 0;
-
- spec->am_num_entries = num_pins;
- /* sort the am_entry in the order of attr so that the pin with a
- * higher attr will be selected when the jack is plugged.
- */
- sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]),
- compare_attr, NULL);
-
- if (!auto_mic_check_imux(codec))
- return 0;
-
- spec->auto_mic = 1;
- spec->num_adc_nids = 1;
- spec->cur_mux[0] = spec->am_entry[0].idx;
- snd_printdd("hda-codec: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
- spec->am_entry[0].pin,
- spec->am_entry[1].pin,
- spec->am_entry[2].pin);
-
- return 0;
-}
-
-/* power_filter hook; make inactive widgets into power down */
-static unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
- hda_nid_t nid,
- unsigned int power_state)
-{
- if (power_state != AC_PWRST_D0)
- return power_state;
- if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER)
- return power_state;
- if (is_active_nid(codec, nid, HDA_OUTPUT, 0))
- return power_state;
- return AC_PWRST_D3;
-}
-
-
-/*
- * Parse the given BIOS configuration and set up the hda_gen_spec
- *
- * return 1 if successful, 0 if the proper config is not found,
- * or a negative error code
- */
-int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
- struct auto_pin_cfg *cfg)
-{
- struct hda_gen_spec *spec = codec->spec;
- int err;
-
- parse_user_hints(codec);
-
- if (spec->mixer_nid && !spec->mixer_merge_nid)
- spec->mixer_merge_nid = spec->mixer_nid;
-
- if (cfg != &spec->autocfg) {
- spec->autocfg = *cfg;
- cfg = &spec->autocfg;
- }
-
- fill_all_dac_nids(codec);
-
- if (!cfg->line_outs) {
- if (cfg->dig_outs || cfg->dig_in_pin) {
- spec->multiout.max_channels = 2;
- spec->no_analog = 1;
- goto dig_only;
- }
- return 0; /* can't find valid BIOS pin config */
- }
-
- if (!spec->no_primary_hp &&
- cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
- cfg->line_outs <= cfg->hp_outs) {
- /* use HP as primary out */
- cfg->speaker_outs = cfg->line_outs;
- memcpy(cfg->speaker_pins, cfg->line_out_pins,
- sizeof(cfg->speaker_pins));
- cfg->line_outs = cfg->hp_outs;
- memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins));
- cfg->hp_outs = 0;
- memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
- cfg->line_out_type = AUTO_PIN_HP_OUT;
- }
-
- err = parse_output_paths(codec);
- if (err < 0)
- return err;
- err = create_multi_channel_mode(codec);
- if (err < 0)
- return err;
- err = create_multi_out_ctls(codec, cfg);
- if (err < 0)
- return err;
- err = create_hp_out_ctls(codec);
- if (err < 0)
- return err;
- err = create_speaker_out_ctls(codec);
- if (err < 0)
- return err;
- err = create_indep_hp_ctls(codec);
- if (err < 0)
- return err;
- err = create_loopback_mixing_ctl(codec);
- if (err < 0)
- return err;
- err = create_shared_input(codec);
- if (err < 0)
- return err;
- err = create_input_ctls(codec);
- if (err < 0)
- return err;
-
- spec->const_channel_count = spec->ext_channel_count;
- /* check the multiple speaker and headphone pins */
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
- spec->const_channel_count = max(spec->const_channel_count,
- cfg->speaker_outs * 2);
- if (cfg->line_out_type != AUTO_PIN_HP_OUT)
- spec->const_channel_count = max(spec->const_channel_count,
- cfg->hp_outs * 2);
- spec->multiout.max_channels = max(spec->ext_channel_count,
- spec->const_channel_count);
-
- err = check_auto_mute_availability(codec);
- if (err < 0)
- return err;
-
- err = check_dyn_adc_switch(codec);
- if (err < 0)
- return err;
-
- if (!spec->shared_mic_hp) {
- err = check_auto_mic_availability(codec);
- if (err < 0)
- return err;
- }
-
- err = create_capture_mixers(codec);
- if (err < 0)
- return err;
-
- err = parse_mic_boost(codec);
- if (err < 0)
- return err;
-
- if (spec->add_out_jack_modes) {
- if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
- err = create_out_jack_modes(codec, cfg->line_outs,
- cfg->line_out_pins);
- if (err < 0)
- return err;
- }
- if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
- err = create_out_jack_modes(codec, cfg->hp_outs,
- cfg->hp_pins);
- if (err < 0)
- return err;
- }
- }
-
- dig_only:
- parse_digital(codec);
-
- if (spec->power_down_unused)
- codec->power_filter = snd_hda_gen_path_power_filter;
-
- return 1;
-}
-EXPORT_SYMBOL_HDA(snd_hda_gen_parse_auto_config);
-
-
-/*
- * Build control elements
- */
-
-/* slave controls for virtual master */
-static const char * const slave_pfxs[] = {
- "Front", "Surround", "Center", "LFE", "Side",
- "Headphone", "Speaker", "Mono", "Line Out",
- "CLFE", "Bass Speaker", "PCM",
- "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side",
- "Headphone Front", "Headphone Surround", "Headphone CLFE",
- "Headphone Side",
- NULL,
-};
-
-int snd_hda_gen_build_controls(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- int err;
-
- if (spec->kctls.used) {
- err = snd_hda_add_new_ctls(codec, spec->kctls.list);
- if (err < 0)
- return err;
- }
-
- if (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) {
- err = snd_hda_create_spdif_share_sw(codec,
- &spec->multiout);
- if (err < 0)
- return err;
- spec->multiout.share_spdif = 1;
- }
- }
- if (spec->dig_in_nid) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
- if (err < 0)
- return err;
- }
-
- /* if we have no master control, let's create it */
- if (!spec->no_analog &&
- !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- spec->vmaster_tlv, slave_pfxs,
- "Playback Volume");
- if (err < 0)
- return err;
- }
- if (!spec->no_analog &&
- !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL, slave_pfxs,
- "Playback Switch",
- true, &spec->vmaster_mute.sw_kctl);
- if (err < 0)
- return err;
- if (spec->vmaster_mute.hook)
- snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute,
- spec->vmaster_mute_enum);
- }
-
- free_kctls(spec); /* no longer needed */
-
- if (spec->shared_mic_hp) {
- int err;
- int nid = spec->autocfg.inputs[1].pin;
- err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0);
- if (err < 0)
- return err;
- err = snd_hda_jack_detect_enable(codec, nid, 0);
- if (err < 0)
- return err;
- }
-
- err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_gen_build_controls);
-
-
-/*
- * PCM definitions
- */
-
-static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream,
- int action)
-{
- struct hda_gen_spec *spec = codec->spec;
- if (spec->pcm_playback_hook)
- spec->pcm_playback_hook(hinfo, codec, substream, action);
-}
-
-static void call_pcm_capture_hook(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream,
- int action)
-{
- struct hda_gen_spec *spec = codec->spec;
- if (spec->pcm_capture_hook)
- spec->pcm_capture_hook(hinfo, codec, substream, action);
-}
-
-/*
- * Analog playback callbacks
- */
-static int playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hda_gen_spec *spec = codec->spec;
- int err;
-
- mutex_lock(&spec->pcm_mutex);
- err = snd_hda_multi_out_analog_open(codec,
- &spec->multiout, substream,
- hinfo);
- if (!err) {
- spec->active_streams |= 1 << STREAM_MULTI_OUT;
- call_pcm_playback_hook(hinfo, codec, substream,
- HDA_GEN_PCM_ACT_OPEN);
+ if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER)
+ return create_output_mixers(codec, types_speaker);
+ else
+ return create_output_mixers(codec, types_line);
}
- mutex_unlock(&spec->pcm_mutex);
- return err;
+ return 0;
}
-static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+/* create capture volume/switch */
+static int build_input_controls(struct hda_codec *codec)
{
- struct hda_gen_spec *spec = codec->spec;
- int err;
+ struct hda_gspec *spec = codec->spec;
+ struct hda_gnode *adc_node = spec->adc_node;
+ int i, err;
+ static struct snd_kcontrol_new cap_sel = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = capture_source_info,
+ .get = capture_source_get,
+ .put = capture_source_put,
+ };
- err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
- if (!err)
- call_pcm_playback_hook(hinfo, codec, substream,
- HDA_GEN_PCM_ACT_PREPARE);
- return err;
-}
+ if (! adc_node || ! spec->input_mux.num_items)
+ return 0; /* not found */
-static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hda_gen_spec *spec = codec->spec;
- int err;
+ spec->cur_cap_src = 0;
+ select_input_connection(codec, adc_node,
+ spec->input_mux.items[0].index);
- err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
- if (!err)
- call_pcm_playback_hook(hinfo, codec, substream,
- HDA_GEN_PCM_ACT_CLEANUP);
- return err;
-}
+ /* create capture volume and switch controls if the ADC has an amp */
+ /* do we have only a single item? */
+ if (spec->input_mux.num_items == 1) {
+ err = create_mixer(codec, adc_node,
+ spec->input_mux.items[0].index,
+ NULL, "Capture", 0);
+ if (err < 0)
+ return err;
+ return 0;
+ }
-static int playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hda_gen_spec *spec = codec->spec;
- mutex_lock(&spec->pcm_mutex);
- spec->active_streams &= ~(1 << STREAM_MULTI_OUT);
- call_pcm_playback_hook(hinfo, codec, substream,
- HDA_GEN_PCM_ACT_CLOSE);
- mutex_unlock(&spec->pcm_mutex);
- return 0;
-}
+ /* create input MUX if multiple sources are available */
+ err = snd_hda_ctl_add(codec, spec->adc_node->nid,
+ snd_ctl_new1(&cap_sel, codec));
+ if (err < 0)
+ return err;
-static int capture_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_OPEN);
- return 0;
-}
+ /* no volume control? */
+ if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) ||
+ ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS))
+ return 0;
-static int capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
- call_pcm_capture_hook(hinfo, codec, substream,
- HDA_GEN_PCM_ACT_PREPARE);
- return 0;
-}
+ for (i = 0; i < spec->input_mux.num_items; i++) {
+ struct snd_kcontrol_new knew;
+ char name[32];
+ sprintf(name, "%s Capture Volume",
+ spec->input_mux.items[i].label);
+ knew = (struct snd_kcontrol_new)
+ HDA_CODEC_VOLUME(name, adc_node->nid,
+ spec->input_mux.items[i].index,
+ HDA_INPUT);
+ err = snd_hda_ctl_add(codec, adc_node->nid,
+ snd_ctl_new1(&knew, codec));
+ if (err < 0)
+ return err;
+ }
-static int capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_cleanup_stream(codec, hinfo->nid);
- call_pcm_capture_hook(hinfo, codec, substream,
- HDA_GEN_PCM_ACT_CLEANUP);
return 0;
}
-static int capture_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLOSE);
- return 0;
-}
-static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+/*
+ * parse the nodes recursively until reach to the output PIN.
+ *
+ * returns 0 - if not found,
+ * 1 - if found, but no mixer is created
+ * 2 - if found and mixer was already created, (just skip)
+ * a negative error code
+ */
+static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec,
+ struct hda_gnode *node, struct hda_gnode *dest_node,
+ const char *type)
{
- struct hda_gen_spec *spec = codec->spec;
- int err = 0;
-
- mutex_lock(&spec->pcm_mutex);
- if (!spec->indep_hp_enabled)
- err = -EBUSY;
- else
- spec->active_streams |= 1 << STREAM_INDEP_HP;
- call_pcm_playback_hook(hinfo, codec, substream,
- HDA_GEN_PCM_ACT_OPEN);
- mutex_unlock(&spec->pcm_mutex);
- return err;
-}
+ int i, err;
-static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hda_gen_spec *spec = codec->spec;
- mutex_lock(&spec->pcm_mutex);
- spec->active_streams &= ~(1 << STREAM_INDEP_HP);
- call_pcm_playback_hook(hinfo, codec, substream,
- HDA_GEN_PCM_ACT_CLOSE);
- mutex_unlock(&spec->pcm_mutex);
- return 0;
-}
+ if (node->checked)
+ return 0;
-static int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
- call_pcm_playback_hook(hinfo, codec, substream,
- HDA_GEN_PCM_ACT_PREPARE);
- return 0;
-}
+ node->checked = 1;
+ if (node == dest_node) {
+ /* loopback connection found */
+ return 1;
+ }
-static int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- snd_hda_codec_cleanup_stream(codec, hinfo->nid);
- call_pcm_playback_hook(hinfo, codec, substream,
- HDA_GEN_PCM_ACT_CLEANUP);
+ for (i = 0; i < node->nconns; i++) {
+ struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]);
+ if (! child)
+ continue;
+ err = parse_loopback_path(codec, spec, child, dest_node, type);
+ if (err < 0)
+ return err;
+ else if (err >= 1) {
+ if (err == 1) {
+ err = create_mixer(codec, node, i, type,
+ "Playback", 1);
+ if (err < 0)
+ return err;
+ if (err > 0)
+ return 2; /* ok, created */
+ /* not created, maybe in the lower path */
+ err = 1;
+ }
+ /* connect and unmute */
+ if (node->nconns > 1)
+ select_input_connection(codec, node, i);
+ unmute_input(codec, node, i);
+ unmute_output(codec, node);
+ return err;
+ }
+ }
return 0;
}
/*
- * Digital out
+ * parse the tree and build the loopback controls
*/
-static int dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hda_gen_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static int build_loopback_controls(struct hda_codec *codec)
{
- struct hda_gen_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
+ struct hda_gspec *spec = codec->spec;
+ struct hda_gnode *node;
+ int err;
+ const char *type;
-static int dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hda_gen_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
+ if (! spec->out_pin_node[0])
+ return 0;
-static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hda_gen_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+ list_for_each_entry(node, &spec->nid_list, list) {
+ if (node->type != AC_WID_PIN)
+ continue;
+ /* input capable? */
+ if (! (node->pin_caps & AC_PINCAP_IN))
+ return 0;
+ type = get_input_type(node, NULL);
+ if (type) {
+ if (check_existing_control(codec, type, "Playback"))
+ continue;
+ clear_check_flags(spec);
+ err = parse_loopback_path(codec, spec,
+ spec->out_pin_node[0],
+ node, type);
+ if (err < 0)
+ return err;
+ if (! err)
+ continue;
+ }
+ }
+ return 0;
}
/*
- * Analog capture
+ * build mixer controls
*/
-#define alt_capture_pcm_open capture_pcm_open
-#define alt_capture_pcm_close capture_pcm_close
-
-static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static int build_generic_controls(struct hda_codec *codec)
{
- struct hda_gen_spec *spec = codec->spec;
-
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
- stream_tag, 0, format);
- call_pcm_capture_hook(hinfo, codec, substream,
- HDA_GEN_PCM_ACT_PREPARE);
- return 0;
-}
+ int err;
-static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hda_gen_spec *spec = codec->spec;
+ if ((err = build_input_controls(codec)) < 0 ||
+ (err = build_output_controls(codec)) < 0 ||
+ (err = build_loopback_controls(codec)) < 0)
+ return err;
- snd_hda_codec_cleanup_stream(codec,
- spec->adc_nids[substream->number + 1]);
- call_pcm_capture_hook(hinfo, codec, substream,
- HDA_GEN_PCM_ACT_CLEANUP);
return 0;
}
/*
+ * PCM
*/
-static const struct hda_pcm_stream pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- /* NID is set in build_pcms */
- .ops = {
- .open = playback_pcm_open,
- .close = playback_pcm_close,
- .prepare = playback_pcm_prepare,
- .cleanup = playback_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in build_pcms */
- .ops = {
- .open = capture_pcm_open,
- .close = capture_pcm_close,
- .prepare = capture_pcm_prepare,
- .cleanup = capture_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream pcm_analog_alt_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in build_pcms */
- .ops = {
- .open = alt_playback_pcm_open,
- .close = alt_playback_pcm_close,
- .prepare = alt_playback_pcm_prepare,
- .cleanup = alt_playback_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream pcm_analog_alt_capture = {
- .substreams = 2, /* can be overridden */
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in build_pcms */
- .ops = {
- .open = alt_capture_pcm_open,
- .close = alt_capture_pcm_close,
- .prepare = alt_capture_pcm_prepare,
- .cleanup = alt_capture_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- /* NID is set in build_pcms */
- .ops = {
- .open = dig_playback_pcm_open,
- .close = dig_playback_pcm_close,
- .prepare = dig_playback_pcm_prepare,
- .cleanup = dig_playback_pcm_cleanup
- },
-};
-
-static const struct hda_pcm_stream pcm_digital_capture = {
+static struct hda_pcm_stream generic_pcm_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
- /* NID is set in build_pcms */
-};
-
-/* Used by build_pcms to flag that a PCM has no playback stream */
-static const struct hda_pcm_stream pcm_null_stream = {
- .substreams = 0,
- .channels_min = 0,
- .channels_max = 0,
};
-/*
- * dynamic changing ADC PCM streams
- */
-static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
+static int generic_pcm2_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
{
- struct hda_gen_spec *spec = codec->spec;
- hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]];
-
- if (spec->cur_adc && spec->cur_adc != new_adc) {
- /* stream is running, let's swap the current ADC */
- __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
- spec->cur_adc = new_adc;
- snd_hda_codec_setup_stream(codec, new_adc,
- spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
- return true;
- }
- return false;
-}
+ struct hda_gspec *spec = codec->spec;
-/* analog capture with dynamic dual-adc changes */
-static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct hda_gen_spec *spec = codec->spec;
- spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]];
- spec->cur_adc_stream_tag = stream_tag;
- spec->cur_adc_format = format;
- snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+ snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+ snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid,
+ stream_tag, 0, format);
return 0;
}
-static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
+static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
{
- struct hda_gen_spec *spec = codec->spec;
- snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
- spec->cur_adc = 0;
+ struct hda_gspec *spec = codec->spec;
+
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ snd_hda_codec_cleanup_stream(codec, spec->dac_node[1]->nid);
return 0;
}
-static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = 0, /* fill later */
- .ops = {
- .prepare = dyn_adc_capture_pcm_prepare,
- .cleanup = dyn_adc_capture_pcm_cleanup
- },
-};
-
-static void fill_pcm_stream_name(char *str, size_t len, const char *sfx,
- const char *chip_name)
+static int build_generic_pcms(struct hda_codec *codec)
{
- char *p;
-
- if (*str)
- return;
- strlcpy(str, chip_name, len);
+ struct hda_gspec *spec = codec->spec;
+ struct hda_pcm *info = &spec->pcm_rec;
- /* drop non-alnum chars after a space */
- for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) {
- if (!isalnum(p[1])) {
- *p = 0;
- break;
- }
+ if (! spec->dac_node[0] && ! spec->adc_node) {
+ snd_printd("hda_generic: no PCM found\n");
+ return 0;
}
- strlcat(str, sfx, len);
-}
-
-/* build PCM streams based on the parsed results */
-int snd_hda_gen_build_pcms(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
- const struct hda_pcm_stream *p;
- bool have_multi_adcs;
codec->num_pcms = 1;
codec->pcm_info = info;
- if (spec->no_analog)
- goto skip_analog;
-
- fill_pcm_stream_name(spec->stream_name_analog,
- sizeof(spec->stream_name_analog),
- " Analog", codec->chip_name);
- info->name = spec->stream_name_analog;
-
- if (spec->multiout.num_dacs > 0) {
- p = spec->stream_analog_playback;
- if (!p)
- p = &pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
- if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
- spec->autocfg.line_outs == 2)
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap =
- snd_pcm_2_1_chmaps;
- }
- if (spec->num_adc_nids) {
- p = spec->stream_analog_capture;
- if (!p) {
- if (spec->dyn_adc_switch)
- p = &dyn_adc_pcm_analog_capture;
- else
- p = &pcm_analog_capture;
- }
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
- }
-
- skip_analog:
- /* SPDIF for stream index #1 */
- if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
- fill_pcm_stream_name(spec->stream_name_digital,
- sizeof(spec->stream_name_digital),
- " Digital", codec->chip_name);
- codec->num_pcms = 2;
- codec->slave_dig_outs = spec->multiout.slave_dig_outs;
- info = spec->pcm_rec + 1;
- info->name = spec->stream_name_digital;
- if (spec->dig_out_type)
- info->pcm_type = spec->dig_out_type;
- else
- info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid) {
- p = spec->stream_digital_playback;
- if (!p)
- p = &pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- }
- if (spec->dig_in_nid) {
- p = spec->stream_digital_capture;
- if (!p)
- p = &pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
- }
- }
-
- if (spec->no_analog)
- return 0;
-
- /* If the use of more than one ADC is requested for the current
- * model, configure a second analog capture-only PCM.
- */
- have_multi_adcs = (spec->num_adc_nids > 1) &&
- !spec->dyn_adc_switch && !spec->auto_mic;
- /* Additional Analaog capture for index #2 */
- if (spec->alt_dac_nid || have_multi_adcs) {
- fill_pcm_stream_name(spec->stream_name_alt_analog,
- sizeof(spec->stream_name_alt_analog),
- " Alt Analog", codec->chip_name);
- codec->num_pcms = 3;
- info = spec->pcm_rec + 2;
- info->name = spec->stream_name_alt_analog;
- if (spec->alt_dac_nid) {
- p = spec->stream_analog_alt_playback;
- if (!p)
- p = &pcm_analog_alt_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->alt_dac_nid;
- } else {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- pcm_null_stream;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
- }
- if (have_multi_adcs) {
- p = spec->stream_analog_alt_capture;
- if (!p)
- p = &pcm_analog_alt_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->adc_nids[1];
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
- spec->num_adc_nids - 1;
- } else {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- pcm_null_stream;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
- }
- }
-
- return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms);
-
-
-/*
- * Standard auto-parser initializations
- */
-
-/* configure the given path as a proper output */
-static void set_output_and_unmute(struct hda_codec *codec, int path_idx)
-{
- struct nid_path *path;
- hda_nid_t pin;
-
- path = snd_hda_get_path_from_idx(codec, path_idx);
- if (!path || !path->depth)
- return;
- pin = path->path[path->depth - 1];
- restore_pin_ctl(codec, pin);
- snd_hda_activate_path(codec, path, path->active, true);
- set_pin_eapd(codec, pin, path->active);
-}
-
-/* initialize primary output paths */
-static void init_multi_out(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->autocfg.line_outs; i++)
- set_output_and_unmute(codec, spec->out_paths[i]);
-}
-
-
-static void __init_extra_out(struct hda_codec *codec, int num_outs, int *paths)
-{
- int i;
-
- for (i = 0; i < num_outs; i++)
- set_output_and_unmute(codec, paths[i]);
-}
-
-/* initialize hp and speaker paths */
-static void init_extra_out(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
-
- if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT)
- __init_extra_out(codec, spec->autocfg.hp_outs, spec->hp_paths);
- if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT)
- __init_extra_out(codec, spec->autocfg.speaker_outs,
- spec->speaker_paths);
-}
-
-/* initialize multi-io paths */
-static void init_multi_io(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->multi_ios; i++) {
- hda_nid_t pin = spec->multi_io[i].pin;
- struct nid_path *path;
- path = get_multiio_path(codec, i);
- if (!path)
- continue;
- if (!spec->multi_io[i].ctl_in)
- spec->multi_io[i].ctl_in =
- snd_hda_codec_get_pin_target(codec, pin);
- snd_hda_activate_path(codec, path, path->active, true);
- }
-}
-
-/* set up input pins and loopback paths */
-static void init_analog_input(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (is_input_pin(codec, nid))
- restore_pin_ctl(codec, nid);
-
- /* init loopback inputs */
- if (spec->mixer_nid) {
- resume_path_from_idx(codec, spec->loopback_paths[i]);
- resume_path_from_idx(codec, spec->loopback_merge_path);
- }
- }
-}
-
-/* initialize ADC paths */
-static void init_input_src(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->input_mux;
- struct nid_path *path;
- int i, c, nums;
-
- if (spec->dyn_adc_switch)
- nums = 1;
- else
- nums = spec->num_adc_nids;
-
- for (c = 0; c < nums; c++) {
- for (i = 0; i < imux->num_items; i++) {
- path = get_input_path(codec, c, i);
- if (path) {
- bool active = path->active;
- if (i == spec->cur_mux[c])
- active = true;
- snd_hda_activate_path(codec, path, active, false);
- }
+ info->name = "HDA Generic";
+ if (spec->dac_node[0]) {
+ info->stream[0] = generic_pcm_playback;
+ info->stream[0].nid = spec->dac_node[0]->nid;
+ if (spec->dac_node[1]) {
+ info->stream[0].ops.prepare = generic_pcm2_prepare;
+ info->stream[0].ops.cleanup = generic_pcm2_cleanup;
}
}
-
- if (spec->shared_mic_hp)
- update_shared_mic_hp(codec, spec->cur_mux[0]);
-
- if (spec->cap_sync_hook)
- spec->cap_sync_hook(codec, NULL);
-}
-
-/* set right pin controls for digital I/O */
-static void init_digital(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
- int i;
- hda_nid_t pin;
-
- for (i = 0; i < spec->autocfg.dig_outs; i++)
- set_output_and_unmute(codec, spec->digout_paths[i]);
- pin = spec->autocfg.dig_in_pin;
- if (pin) {
- restore_pin_ctl(codec, pin);
- resume_path_from_idx(codec, spec->digin_path);
- }
-}
-
-/* clear unsol-event tags on unused pins; Conexant codecs seem to leave
- * invalid unsol tags by some reason
- */
-static void clear_unsol_on_unused_pins(struct hda_codec *codec)
-{
- int i;
-
- for (i = 0; i < codec->init_pins.used; i++) {
- struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
- hda_nid_t nid = pin->nid;
- if (is_jack_detectable(codec, nid) &&
- !snd_hda_jack_tbl_get(codec, nid))
- snd_hda_codec_update_cache(codec, nid, 0,
- AC_VERB_SET_UNSOLICITED_ENABLE, 0);
+ if (spec->adc_node) {
+ info->stream[1] = generic_pcm_playback;
+ info->stream[1].nid = spec->adc_node->nid;
}
-}
-
-/*
- * initialize the generic spec;
- * this can be put as patch_ops.init function
- */
-int snd_hda_gen_init(struct hda_codec *codec)
-{
- struct hda_gen_spec *spec = codec->spec;
-
- if (spec->init_hook)
- spec->init_hook(codec);
-
- snd_hda_apply_verbs(codec);
-
- codec->cached_write = 1;
- init_multi_out(codec);
- init_extra_out(codec);
- init_multi_io(codec);
- init_analog_input(codec);
- init_input_src(codec);
- init_digital(codec);
-
- clear_unsol_on_unused_pins(codec);
-
- /* call init functions of standard auto-mute helpers */
- update_automute_all(codec);
-
- snd_hda_codec_flush_cache(codec);
-
- if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
- snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
-
- hda_call_check_power_status(codec, 0x01);
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_gen_init);
-
-/*
- * free the generic spec;
- * this can be put as patch_ops.free function
- */
-void snd_hda_gen_free(struct hda_codec *codec)
-{
- snd_hda_gen_spec_free(codec->spec);
- kfree(codec->spec);
- codec->spec = NULL;
-}
-EXPORT_SYMBOL_HDA(snd_hda_gen_free);
#ifdef CONFIG_PM
-/*
- * check the loopback power save state;
- * this can be put as patch_ops.check_power_status function
- */
-int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid)
{
- struct hda_gen_spec *spec = codec->spec;
+ struct hda_gspec *spec = codec->spec;
return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
}
-EXPORT_SYMBOL_HDA(snd_hda_gen_check_power_status);
#endif
/*
- * the generic codec support
*/
-
-static const struct hda_codec_ops generic_patch_ops = {
- .build_controls = snd_hda_gen_build_controls,
- .build_pcms = snd_hda_gen_build_pcms,
- .init = snd_hda_gen_init,
- .free = snd_hda_gen_free,
- .unsol_event = snd_hda_jack_unsol_event,
+static struct hda_codec_ops generic_patch_ops = {
+ .build_controls = build_generic_controls,
+ .build_pcms = build_generic_pcms,
+ .free = snd_hda_generic_free,
#ifdef CONFIG_PM
- .check_power_status = snd_hda_gen_check_power_status,
+ .check_power_status = generic_check_power_status,
#endif
};
+/*
+ * the generic parser
+ */
int snd_hda_parse_generic_codec(struct hda_codec *codec)
{
- struct hda_gen_spec *spec;
+ struct hda_gspec *spec;
int err;
+ if(!codec->afg)
+ return 0;
+
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
+ if (spec == NULL) {
+ printk(KERN_ERR "hda_generic: can't allocate spec\n");
return -ENOMEM;
- snd_hda_gen_spec_init(spec);
+ }
codec->spec = spec;
+ INIT_LIST_HEAD(&spec->nid_list);
- err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
- if (err < 0)
- return err;
+ if ((err = build_afg_tree(codec)) < 0)
+ goto error;
- err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg);
- if (err < 0)
+ if ((err = parse_input(codec)) < 0 ||
+ (err = parse_output(codec)) < 0)
goto error;
codec->patch_ops = generic_patch_ops;
+
return 0;
-error:
- snd_hda_gen_free(codec);
+ error:
+ snd_hda_generic_free(codec);
return err;
}
-EXPORT_SYMBOL_HDA(snd_hda_parse_generic_codec);
+EXPORT_SYMBOL(snd_hda_parse_generic_codec);
diff --git a/trunk/sound/pci/hda/hda_generic.h b/trunk/sound/pci/hda/hda_generic.h
deleted file mode 100644
index 009b57be96d3..000000000000
--- a/trunk/sound/pci/hda/hda_generic.h
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Generic BIOS auto-parser helper functions for HD-audio
- *
- * Copyright (c) 2012 Takashi Iwai
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __SOUND_HDA_GENERIC_H
-#define __SOUND_HDA_GENERIC_H
-
-/* unsol event tags */
-enum {
- HDA_GEN_HP_EVENT = 1, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT,
- HDA_GEN_LAST_EVENT = HDA_GEN_MIC_EVENT
-};
-
-/* table entry for multi-io paths */
-struct hda_multi_io {
- hda_nid_t pin; /* multi-io widget pin NID */
- hda_nid_t dac; /* DAC to be connected */
- unsigned int ctl_in; /* cached input-pin control value */
-};
-
-/* Widget connection path
- *
- * For output, stored in the order of DAC -> ... -> pin,
- * for input, pin -> ... -> ADC.
- *
- * idx[i] contains the source index number to select on of the widget path[i];
- * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget
- * multi[] indicates whether it's a selector widget with multi-connectors
- * (i.e. the connection selection is mandatory)
- * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
- */
-
-#define MAX_NID_PATH_DEPTH 10
-
-enum {
- NID_PATH_VOL_CTL,
- NID_PATH_MUTE_CTL,
- NID_PATH_BOOST_CTL,
- NID_PATH_NUM_CTLS
-};
-
-struct nid_path {
- int depth;
- hda_nid_t path[MAX_NID_PATH_DEPTH];
- unsigned char idx[MAX_NID_PATH_DEPTH];
- unsigned char multi[MAX_NID_PATH_DEPTH];
- unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */
- bool active;
-};
-
-/* mic/line-in auto switching entry */
-
-#define MAX_AUTO_MIC_PINS 3
-
-struct automic_entry {
- hda_nid_t pin; /* pin */
- int idx; /* imux index, -1 = invalid */
- unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */
-};
-
-/* active stream id */
-enum { STREAM_MULTI_OUT, STREAM_INDEP_HP };
-
-/* PCM hook action */
-enum {
- HDA_GEN_PCM_ACT_OPEN,
- HDA_GEN_PCM_ACT_PREPARE,
- HDA_GEN_PCM_ACT_CLEANUP,
- HDA_GEN_PCM_ACT_CLOSE,
-};
-
-struct hda_gen_spec {
- char stream_name_analog[32]; /* analog PCM stream */
- const struct hda_pcm_stream *stream_analog_playback;
- const struct hda_pcm_stream *stream_analog_capture;
-
- char stream_name_alt_analog[32]; /* alternative analog PCM stream */
- const struct hda_pcm_stream *stream_analog_alt_playback;
- const struct hda_pcm_stream *stream_analog_alt_capture;
-
- char stream_name_digital[32]; /* digital PCM stream */
- const struct hda_pcm_stream *stream_digital_playback;
- const struct hda_pcm_stream *stream_digital_capture;
-
- /* PCM */
- unsigned int active_streams;
- struct mutex pcm_mutex;
-
- /* playback */
- struct hda_multi_out multiout; /* playback set-up
- * max_channels, dacs must be set
- * dig_out_nid and hp_nid are optional
- */
- hda_nid_t alt_dac_nid;
- hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */
- int dig_out_type;
-
- /* capture */
- unsigned int num_adc_nids;
- hda_nid_t adc_nids[AUTO_CFG_MAX_INS];
- hda_nid_t dig_in_nid; /* digital-in NID; optional */
- hda_nid_t mixer_nid; /* analog-mixer NID */
- hda_nid_t mixer_merge_nid; /* aamix merge-point NID (optional) */
- const char *input_labels[HDA_MAX_NUM_INPUTS];
- int input_label_idxs[HDA_MAX_NUM_INPUTS];
-
- /* capture setup for dynamic dual-adc switch */
- hda_nid_t cur_adc;
- unsigned int cur_adc_stream_tag;
- unsigned int cur_adc_format;
-
- /* capture source */
- struct hda_input_mux input_mux;
- unsigned int cur_mux[3];
-
- /* channel model */
- /* min_channel_count contains the minimum channel count for primary
- * outputs. When multi_ios is set, the channels can be configured
- * between min_channel_count and (min_channel_count + multi_ios * 2).
- *
- * ext_channel_count contains the current channel count of the primary
- * out. This varies in the range above.
- *
- * Meanwhile, const_channel_count is the channel count for all outputs
- * including headphone and speakers. It's a constant value, and the
- * PCM is set up as max(ext_channel_count, const_channel_count).
- */
- int min_channel_count; /* min. channel count for primary out */
- int ext_channel_count; /* current channel count for primary */
- int const_channel_count; /* channel count for all */
-
- /* PCM information */
- struct hda_pcm pcm_rec[3]; /* used in build_pcms() */
-
- /* dynamic controls, init_verbs and input_mux */
- struct auto_pin_cfg autocfg;
- struct snd_array kctls;
- hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
- hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
- unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
- hda_nid_t shared_mic_vref_pin;
-
- /* DAC/ADC lists */
- int num_all_dacs;
- hda_nid_t all_dacs[16];
- int num_all_adcs;
- hda_nid_t all_adcs[AUTO_CFG_MAX_INS];
-
- /* path list */
- struct snd_array paths;
-
- /* path indices */
- int out_paths[AUTO_CFG_MAX_OUTS];
- int hp_paths[AUTO_CFG_MAX_OUTS];
- int speaker_paths[AUTO_CFG_MAX_OUTS];
- int aamix_out_paths[3];
- int digout_paths[AUTO_CFG_MAX_OUTS];
- int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_INS];
- int loopback_paths[HDA_MAX_NUM_INPUTS];
- int loopback_merge_path;
- int digin_path;
-
- /* auto-mic stuff */
- int am_num_entries;
- struct automic_entry am_entry[MAX_AUTO_MIC_PINS];
-
- /* for pin sensing */
- /* current status; set in hda_geneic.c */
- unsigned int hp_jack_present:1;
- unsigned int line_jack_present:1;
- unsigned int speaker_muted:1; /* current status of speaker mute */
- unsigned int line_out_muted:1; /* current status of LO mute */
-
- /* internal states of automute / autoswitch behavior */
- unsigned int auto_mic:1;
- unsigned int automute_speaker:1; /* automute speaker outputs */
- unsigned int automute_lo:1; /* automute LO outputs */
-
- /* capabilities detected by parser */
- unsigned int detect_hp:1; /* Headphone detection enabled */
- unsigned int detect_lo:1; /* Line-out detection enabled */
- unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */
- unsigned int automute_lo_possible:1; /* there are line outs and HP */
-
- /* additional parameters set by codec drivers */
- unsigned int master_mute:1; /* master mute over all */
- unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
- unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
-
- /* parser behavior flags; set before snd_hda_gen_parse_auto_config() */
- unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */
- unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */
-
- /* other parse behavior flags */
- unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */
- unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
- unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
- unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
- unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
- unsigned int own_eapd_ctl:1; /* set EAPD by own function */
- unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */
- unsigned int indep_hp:1; /* independent HP supported */
- unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */
- unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */
- unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */
- unsigned int add_in_jack_modes:1; /* add input jack mode enum ctls */
- unsigned int power_down_unused:1; /* power down unused widgets */
-
- /* other internal flags */
- unsigned int no_analog:1; /* digital I/O only */
- unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
- unsigned int indep_hp_enabled:1; /* independent HP enabled */
- unsigned int have_aamix_ctl:1;
-
- /* loopback mixing mode */
- bool aamix_mode;
-
- /* for virtual master */
- hda_nid_t vmaster_nid;
- unsigned int vmaster_tlv[4];
- struct hda_vmaster_mute_hook vmaster_mute;
-
- struct hda_loopback_check loopback;
- struct snd_array loopback_list;
-
- /* multi-io */
- int multi_ios;
- struct hda_multi_io multi_io[4];
-
- /* hooks */
- void (*init_hook)(struct hda_codec *codec);
- void (*automute_hook)(struct hda_codec *codec);
- void (*cap_sync_hook)(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol);
-
- /* PCM hooks */
- void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream,
- int action);
- void (*pcm_capture_hook)(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream,
- int action);
-
- /* automute / autoswitch hooks */
- void (*hp_automute_hook)(struct hda_codec *codec,
- struct hda_jack_tbl *tbl);
- void (*line_automute_hook)(struct hda_codec *codec,
- struct hda_jack_tbl *tbl);
- void (*mic_autoswitch_hook)(struct hda_codec *codec,
- struct hda_jack_tbl *tbl);
-};
-
-int snd_hda_gen_spec_init(struct hda_gen_spec *spec);
-void snd_hda_gen_spec_free(struct hda_gen_spec *spec);
-
-int snd_hda_gen_init(struct hda_codec *codec);
-void snd_hda_gen_free(struct hda_codec *codec);
-
-struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec,
- hda_nid_t from_nid, hda_nid_t to_nid);
-int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path);
-struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx);
-bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid,
- hda_nid_t to_nid, int anchor_nid,
- struct nid_path *path);
-struct nid_path *
-snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid,
- hda_nid_t to_nid, int anchor_nid);
-void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
- bool enable, bool add_aamix);
-
-struct snd_kcontrol_new *
-snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name,
- const struct snd_kcontrol_new *temp);
-
-int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
- struct auto_pin_cfg *cfg);
-int snd_hda_gen_build_controls(struct hda_codec *codec);
-int snd_hda_gen_build_pcms(struct hda_codec *codec);
-
-/* standard jack event callbacks */
-void snd_hda_gen_hp_automute(struct hda_codec *codec,
- struct hda_jack_tbl *jack);
-void snd_hda_gen_line_automute(struct hda_codec *codec,
- struct hda_jack_tbl *jack);
-void snd_hda_gen_mic_autoswitch(struct hda_codec *codec,
- struct hda_jack_tbl *jack);
-void snd_hda_gen_update_outputs(struct hda_codec *codec);
-
-#ifdef CONFIG_PM
-int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid);
-#endif
-
-#endif /* __SOUND_HDA_GENERIC_H */
diff --git a/trunk/sound/pci/hda/hda_hwdep.c b/trunk/sound/pci/hda/hda_hwdep.c
index ce67608734b5..a5c9411bb367 100644
--- a/trunk/sound/pci/hda/hda_hwdep.c
+++ b/trunk/sound/pci/hda/hda_hwdep.c
@@ -148,7 +148,6 @@ int snd_hda_create_hwdep(struct hda_codec *codec)
hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
#endif
- mutex_init(&codec->user_mutex);
snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
snd_array_init(&codec->hints, sizeof(struct hda_hint), 32);
snd_array_init(&codec->user_pins, sizeof(struct hda_pincfg), 16);
@@ -347,14 +346,12 @@ static ssize_t init_verbs_show(struct device *dev,
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
struct hda_codec *codec = hwdep->private_data;
int i, len = 0;
- mutex_lock(&codec->user_mutex);
for (i = 0; i < codec->init_verbs.used; i++) {
struct hda_verb *v = snd_array_elem(&codec->init_verbs, i);
len += snprintf(buf + len, PAGE_SIZE - len,
"0x%02x 0x%03x 0x%04x\n",
v->nid, v->verb, v->param);
}
- mutex_unlock(&codec->user_mutex);
return len;
}
@@ -367,16 +364,12 @@ static int parse_init_verbs(struct hda_codec *codec, const char *buf)
return -EINVAL;
if (!nid || !verb)
return -EINVAL;
- mutex_lock(&codec->user_mutex);
v = snd_array_new(&codec->init_verbs);
- if (!v) {
- mutex_unlock(&codec->user_mutex);
+ if (!v)
return -ENOMEM;
- }
v->nid = nid;
v->verb = verb;
v->param = param;
- mutex_unlock(&codec->user_mutex);
return 0;
}
@@ -399,13 +392,11 @@ static ssize_t hints_show(struct device *dev,
struct snd_hwdep *hwdep = dev_get_drvdata(dev);
struct hda_codec *codec = hwdep->private_data;
int i, len = 0;
- mutex_lock(&codec->user_mutex);
for (i = 0; i < codec->hints.used; i++) {
struct hda_hint *hint = snd_array_elem(&codec->hints, i);
len += snprintf(buf + len, PAGE_SIZE - len,
"%s = %s\n", hint->key, hint->val);
}
- mutex_unlock(&codec->user_mutex);
return len;
}
@@ -440,7 +431,6 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
{
char *key, *val;
struct hda_hint *hint;
- int err = 0;
buf = skip_spaces(buf);
if (!*buf || *buf == '#' || *buf == '\n')
@@ -460,31 +450,26 @@ static int parse_hints(struct hda_codec *codec, const char *buf)
val = skip_spaces(val);
remove_trail_spaces(key);
remove_trail_spaces(val);
- mutex_lock(&codec->user_mutex);
hint = get_hint(codec, key);
if (hint) {
/* replace */
kfree(hint->key);
hint->key = key;
hint->val = val;
- goto unlock;
+ return 0;
}
/* allocate a new hint entry */
if (codec->hints.used >= MAX_HINTS)
hint = NULL;
else
hint = snd_array_new(&codec->hints);
- if (hint) {
- hint->key = key;
- hint->val = val;
- } else {
- err = -ENOMEM;
- }
- unlock:
- mutex_unlock(&codec->user_mutex);
- if (err)
+ if (!hint) {
kfree(key);
- return err;
+ return -ENOMEM;
+ }
+ hint->key = key;
+ hint->val = val;
+ return 0;
}
static ssize_t hints_store(struct device *dev,
@@ -504,13 +489,11 @@ static ssize_t pin_configs_show(struct hda_codec *codec,
char *buf)
{
int i, len = 0;
- mutex_lock(&codec->user_mutex);
for (i = 0; i < list->used; i++) {
struct hda_pincfg *pin = snd_array_elem(list, i);
len += sprintf(buf + len, "0x%02x 0x%08x\n",
pin->nid, pin->cfg);
}
- mutex_unlock(&codec->user_mutex);
return len;
}
@@ -545,16 +528,13 @@ static ssize_t driver_pin_configs_show(struct device *dev,
static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
{
- int nid, cfg, err;
+ int nid, cfg;
if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
return -EINVAL;
if (!nid)
return -EINVAL;
- mutex_lock(&codec->user_mutex);
- err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
- mutex_unlock(&codec->user_mutex);
- return err;
+ return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
}
static ssize_t user_pin_configs_store(struct device *dev,
@@ -620,50 +600,19 @@ EXPORT_SYMBOL_HDA(snd_hda_get_hint);
int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
{
- const char *p;
- int ret;
-
- mutex_lock(&codec->user_mutex);
- p = snd_hda_get_hint(codec, key);
+ const char *p = snd_hda_get_hint(codec, key);
if (!p || !*p)
- ret = -ENOENT;
- else {
- switch (toupper(*p)) {
- case 'T': /* true */
- case 'Y': /* yes */
- case '1':
- ret = 1;
- break;
- default:
- ret = 0;
- break;
- }
+ return -ENOENT;
+ switch (toupper(*p)) {
+ case 'T': /* true */
+ case 'Y': /* yes */
+ case '1':
+ return 1;
}
- mutex_unlock(&codec->user_mutex);
- return ret;
+ return 0;
}
EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);
-int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
-{
- const char *p;
- unsigned long val;
- int ret;
-
- mutex_lock(&codec->user_mutex);
- p = snd_hda_get_hint(codec, key);
- if (!p)
- ret = -ENOENT;
- else if (strict_strtoul(p, 0, &val))
- ret = -EINVAL;
- else {
- *valp = val;
- ret = 0;
- }
- mutex_unlock(&codec->user_mutex);
- return ret;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_int_hint);
#endif /* CONFIG_SND_HDA_RECONFIG */
#ifdef CONFIG_SND_HDA_PATCH_LOADER
diff --git a/trunk/sound/pci/hda/hda_intel.c b/trunk/sound/pci/hda/hda_intel.c
index 4cea6bb6fade..c78286f6e5d8 100644
--- a/trunk/sound/pci/hda/hda_intel.c
+++ b/trunk/sound/pci/hda/hda_intel.c
@@ -134,8 +134,8 @@ MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
* this may give more power-saving, but will take longer time to
* wake up.
*/
-static int power_save_controller = -1;
-module_param(power_save_controller, bint, 0644);
+static bool power_save_controller = 1;
+module_param(power_save_controller, bool, 0644);
MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
#endif /* CONFIG_PM */
@@ -811,7 +811,7 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
{
struct azx *chip = bus->private_data;
unsigned int addr = azx_command_addr(val);
- unsigned int wp, rp;
+ unsigned int wp;
spin_lock_irq(&chip->reg_lock);
@@ -820,18 +820,11 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
if (wp == 0xffff) {
/* something wrong, controller likely turned to D3 */
spin_unlock_irq(&chip->reg_lock);
- return -EIO;
+ return -1;
}
wp++;
wp %= ICH6_MAX_CORB_ENTRIES;
- rp = azx_readw(chip, CORBRP);
- if (wp == rp) {
- /* oops, it's full */
- spin_unlock_irq(&chip->reg_lock);
- return -EAGAIN;
- }
-
chip->rirb.cmds[addr]++;
chip->corb.buf[wp] = cpu_to_le32(val);
azx_writel(chip, CORBWP, wp);
@@ -1085,15 +1078,6 @@ static unsigned int azx_get_response(struct hda_bus *bus,
static void azx_power_notify(struct hda_bus *bus, bool power_up);
#endif
-#ifdef CONFIG_SND_HDA_DSP_LOADER
-static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format,
- unsigned int byte_size,
- struct snd_dma_buffer *bufp);
-static void azx_load_dsp_trigger(struct hda_bus *bus, bool start);
-static void azx_load_dsp_cleanup(struct hda_bus *bus,
- struct snd_dma_buffer *dmab);
-#endif
-
/* reset codec link */
static int azx_reset(struct azx *chip, int full_reset)
{
@@ -1417,7 +1401,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
* set up a BDL entry
*/
static int setup_bdle(struct azx *chip,
- struct snd_dma_buffer *dmab,
+ struct snd_pcm_substream *substream,
struct azx_dev *azx_dev, u32 **bdlp,
int ofs, int size, int with_ioc)
{
@@ -1430,12 +1414,12 @@ static int setup_bdle(struct azx *chip,
if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
return -EINVAL;
- addr = snd_sgbuf_get_addr(dmab, ofs);
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs);
/* program the address field of the BDL entry */
bdl[0] = cpu_to_le32((u32)addr);
bdl[1] = cpu_to_le32(upper_32_bits(addr));
/* program the size field of the BDL entry */
- chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size);
+ chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
/* one BDLE cannot cross 4K boundary on CTHDA chips */
if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) {
u32 remain = 0x1000 - (ofs & 0xfff);
@@ -1494,8 +1478,7 @@ static int azx_setup_periods(struct azx *chip,
pci_name(chip->pci), bdl_pos_adj[chip->dev_index]);
pos_adj = 0;
} else {
- ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
- azx_dev,
+ ofs = setup_bdle(chip, substream, azx_dev,
&bdl, ofs, pos_adj, true);
if (ofs < 0)
goto error;
@@ -1504,12 +1487,10 @@ static int azx_setup_periods(struct azx *chip,
pos_adj = 0;
for (i = 0; i < periods; i++) {
if (i == periods - 1 && pos_adj)
- ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
- azx_dev, &bdl, ofs,
+ ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
period_bytes - pos_adj, 0);
else
- ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream),
- azx_dev, &bdl, ofs,
+ ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
period_bytes,
!azx_dev->no_period_wakeup);
if (ofs < 0)
@@ -1687,11 +1668,6 @@ static int azx_codec_create(struct azx *chip, const char *model)
bus_temp.power_save = &power_save;
bus_temp.ops.pm_notify = azx_power_notify;
#endif
-#ifdef CONFIG_SND_HDA_DSP_LOADER
- bus_temp.ops.load_dsp_prepare = azx_load_dsp_prepare;
- bus_temp.ops.load_dsp_trigger = azx_load_dsp_trigger;
- bus_temp.ops.load_dsp_cleanup = azx_load_dsp_cleanup;
-#endif
err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus);
if (err < 0)
@@ -2600,102 +2576,6 @@ static void azx_stop_chip(struct azx *chip)
chip->initialized = 0;
}
-#ifdef CONFIG_SND_HDA_DSP_LOADER
-/*
- * DSP loading code (e.g. for CA0132)
- */
-
-/* use the first stream for loading DSP */
-static struct azx_dev *
-azx_get_dsp_loader_dev(struct azx *chip)
-{
- return &chip->azx_dev[chip->playback_index_offset];
-}
-
-static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format,
- unsigned int byte_size,
- struct snd_dma_buffer *bufp)
-{
- u32 *bdl;
- struct azx *chip = bus->private_data;
- struct azx_dev *azx_dev;
- int err;
-
- if (snd_hda_lock_devices(bus))
- return -EBUSY;
-
- err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG,
- snd_dma_pci_data(chip->pci),
- byte_size, bufp);
- if (err < 0)
- goto unlock;
-
- mark_pages_wc(chip, bufp, true);
- azx_dev = azx_get_dsp_loader_dev(chip);
- azx_dev->bufsize = byte_size;
- azx_dev->period_bytes = byte_size;
- azx_dev->format_val = format;
-
- azx_stream_reset(chip, azx_dev);
-
- /* reset BDL address */
- azx_sd_writel(azx_dev, SD_BDLPL, 0);
- azx_sd_writel(azx_dev, SD_BDLPU, 0);
-
- azx_dev->frags = 0;
- bdl = (u32 *)azx_dev->bdl.area;
- err = setup_bdle(chip, bufp, azx_dev, &bdl, 0, byte_size, 0);
- if (err < 0)
- goto error;
-
- azx_setup_controller(chip, azx_dev);
- return azx_dev->stream_tag;
-
- error:
- mark_pages_wc(chip, bufp, false);
- snd_dma_free_pages(bufp);
-unlock:
- snd_hda_unlock_devices(bus);
- return err;
-}
-
-static void azx_load_dsp_trigger(struct hda_bus *bus, bool start)
-{
- struct azx *chip = bus->private_data;
- struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
-
- if (start)
- azx_stream_start(chip, azx_dev);
- else
- azx_stream_stop(chip, azx_dev);
- azx_dev->running = start;
-}
-
-static void azx_load_dsp_cleanup(struct hda_bus *bus,
- struct snd_dma_buffer *dmab)
-{
- struct azx *chip = bus->private_data;
- struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
-
- if (!dmab->area)
- return;
-
- /* reset BDL address */
- azx_sd_writel(azx_dev, SD_BDLPL, 0);
- azx_sd_writel(azx_dev, SD_BDLPU, 0);
- azx_sd_writel(azx_dev, SD_CTL, 0);
- azx_dev->bufsize = 0;
- azx_dev->period_bytes = 0;
- azx_dev->format_val = 0;
-
- mark_pages_wc(chip, dmab, false);
- snd_dma_free_pages(dmab);
- dmab->area = NULL;
-
- snd_hda_unlock_devices(bus);
-}
-#endif /* CONFIG_SND_HDA_DSP_LOADER */
-
#ifdef CONFIG_PM
/* power-up/down the controller */
static void azx_power_notify(struct hda_bus *bus, bool power_up)
@@ -2846,8 +2726,6 @@ static int azx_runtime_idle(struct device *dev)
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data;
- if (power_save_controller > 0)
- return 0;
if (!power_save_controller ||
!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME))
return -EBUSY;
@@ -3272,9 +3150,6 @@ static void azx_check_snoop_available(struct azx *chip)
/* new ATI HDMI requires non-snoop */
snoop = false;
break;
- case AZX_DRIVER_CTHDA:
- snoop = false;
- break;
}
if (snoop != chip->snoop) {
@@ -3736,11 +3611,6 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
/* Lynx Point */
{ PCI_DEVICE(0x8086, 0x8c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
- /* Wellsburg */
- { PCI_DEVICE(0x8086, 0x8d20),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
- { PCI_DEVICE(0x8086, 0x8d21),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
/* Lynx Point-LP */
{ PCI_DEVICE(0x8086, 0x9c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
@@ -3748,15 +3618,13 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
{ PCI_DEVICE(0x8086, 0x9c21),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
/* Haswell */
- { PCI_DEVICE(0x8086, 0x0a0c),
- .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
{ PCI_DEVICE(0x8086, 0x0c0c),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
{ PCI_DEVICE(0x8086, 0x0d0c),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
/* 5 Series/3400 */
{ PCI_DEVICE(0x8086, 0x3b56),
- .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
+ .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH },
/* Poulsbo */
{ PCI_DEVICE(0x8086, 0x811b),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM },
diff --git a/trunk/sound/pci/hda/hda_jack.c b/trunk/sound/pci/hda/hda_jack.c
index 1d035efeff4f..6e9f57bbe667 100644
--- a/trunk/sound/pci/hda/hda_jack.c
+++ b/trunk/sound/pci/hda/hda_jack.c
@@ -29,8 +29,7 @@ bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
AC_DEFCFG_MISC_NO_PRESENCE)
return false;
- if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) &&
- !codec->jackpoll_interval)
+ if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
return false;
return true;
}
@@ -40,7 +39,6 @@ EXPORT_SYMBOL_HDA(is_jack_detectable);
static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
{
u32 pincap;
- u32 val;
if (!codec->no_trigger_sense) {
pincap = snd_hda_query_pin_caps(codec, nid);
@@ -48,11 +46,8 @@ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid)
snd_hda_codec_read(codec, nid, 0,
AC_VERB_SET_PIN_SENSE, 0);
}
- val = snd_hda_codec_read(codec, nid, 0,
+ return snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PIN_SENSE, 0);
- if (codec->inv_jack_detect)
- val ^= AC_PINSENSE_PRESENCE;
- return val;
}
/**
diff --git a/trunk/sound/pci/hda/hda_local.h b/trunk/sound/pci/hda/hda_local.h
index 05f1d594d17b..4b40a5e7a8f5 100644
--- a/trunk/sound/pci/hda/hda_local.h
+++ b/trunk/sound/pci/hda/hda_local.h
@@ -133,11 +133,9 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int idx, int mask, int val);
int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
int dir, int idx, int mask, int val);
-int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
- int direction, int idx, int mask, int val);
-int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
- int dir, int idx, int mask, int val);
+#ifdef CONFIG_PM
void snd_hda_codec_resume_amp(struct hda_codec *codec);
+#endif
void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int *tlv);
@@ -385,61 +383,6 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
int snd_hda_add_new_ctls(struct hda_codec *codec,
const struct snd_kcontrol_new *knew);
-/*
- * Fix-up pin default configurations and add default verbs
- */
-
-struct hda_pintbl {
- hda_nid_t nid;
- u32 val;
-};
-
-struct hda_model_fixup {
- const int id;
- const char *name;
-};
-
-struct hda_fixup {
- int type;
- bool chained:1; /* call the chained fixup(s) after this */
- bool chained_before:1; /* call the chained fixup(s) before this */
- int chain_id;
- union {
- const struct hda_pintbl *pins;
- const struct hda_verb *verbs;
- void (*func)(struct hda_codec *codec,
- const struct hda_fixup *fix,
- int action);
- } v;
-};
-
-/* fixup types */
-enum {
- HDA_FIXUP_INVALID,
- HDA_FIXUP_PINS,
- HDA_FIXUP_VERBS,
- HDA_FIXUP_FUNC,
- HDA_FIXUP_PINCTLS,
-};
-
-/* fixup action definitions */
-enum {
- HDA_FIXUP_ACT_PRE_PROBE,
- HDA_FIXUP_ACT_PROBE,
- HDA_FIXUP_ACT_INIT,
- HDA_FIXUP_ACT_BUILD,
-};
-
-int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list);
-void snd_hda_apply_verbs(struct hda_codec *codec);
-void snd_hda_apply_pincfgs(struct hda_codec *codec,
- const struct hda_pintbl *cfg);
-void snd_hda_apply_fixup(struct hda_codec *codec, int action);
-void snd_hda_pick_fixup(struct hda_codec *codec,
- const struct hda_model_fixup *models,
- const struct snd_pci_quirk *quirk,
- const struct hda_fixup *fixlist);
-
/*
* unsolicited event handler
*/
@@ -488,8 +431,6 @@ struct hda_bus_unsolicited {
#define PIN_HP_AMP (AC_PINCTL_HP_EN)
unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin);
-unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec,
- hda_nid_t pin, unsigned int val);
int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
unsigned int val, bool cached);
@@ -529,10 +470,6 @@ snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin,
return _snd_hda_set_pin_ctl(codec, pin, val, true);
}
-int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid,
- unsigned int val);
-
/*
* get widget capabilities
*/
@@ -615,7 +552,6 @@ static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_RECONFIG
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key);
int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key);
-int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp);
#else
static inline
const char *snd_hda_get_hint(struct hda_codec *codec, const char *key)
@@ -628,12 +564,6 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
{
return -ENOENT;
}
-
-static inline
-int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
-{
- return -ENOENT;
-}
#endif
/*
@@ -657,19 +587,6 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
struct hda_loopback_check *check,
hda_nid_t nid);
-/* check whether the actual power state matches with the target state */
-static inline bool
-snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
- unsigned int target_state)
-{
- unsigned int state = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_POWER_STATE, 0);
- if (state & AC_PWRST_ERROR)
- return true;
- state = (state >> 4) & 0x0f;
- return (state != target_state);
-}
-
/*
* AMP control callbacks
*/
@@ -679,8 +596,7 @@ snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
#define get_amp_direction_(pv) (((pv) >> 18) & 0x1)
#define get_amp_direction(kc) get_amp_direction_((kc)->private_value)
-#define get_amp_index_(pv) (((pv) >> 19) & 0xf)
-#define get_amp_index(kc) get_amp_index_((kc)->private_value)
+#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f)
#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1)
diff --git a/trunk/sound/pci/hda/hda_proc.c b/trunk/sound/pci/hda/hda_proc.c
index 0fee8fae590a..045e5d32f5de 100644
--- a/trunk/sound/pci/hda/hda_proc.c
+++ b/trunk/sound/pci/hda/hda_proc.c
@@ -22,7 +22,6 @@
*/
#include
-#include
#include
#include "hda_codec.h"
#include "hda_local.h"
@@ -139,17 +138,16 @@ static void print_amp_vals(struct snd_info_buffer *buffer,
dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
for (i = 0; i < indices; i++) {
snd_iprintf(buffer, " [");
- val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_LEFT | dir | i);
- snd_iprintf(buffer, "0x%02x", val);
if (stereo) {
val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_AMP_GAIN_MUTE,
- AC_AMP_GET_RIGHT | dir | i);
- snd_iprintf(buffer, " 0x%02x", val);
+ AC_AMP_GET_LEFT | dir | i);
+ snd_iprintf(buffer, "0x%02x ", val);
}
- snd_iprintf(buffer, "]");
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_AMP_GAIN_MUTE,
+ AC_AMP_GET_RIGHT | dir | i);
+ snd_iprintf(buffer, "0x%02x]", val);
}
snd_iprintf(buffer, "\n");
}
@@ -605,8 +603,6 @@ static void print_codec_info(struct snd_info_entry *entry,
print_amp_caps(buffer, codec, codec->afg, HDA_INPUT);
snd_iprintf(buffer, "Default Amp-Out caps: ");
print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT);
- snd_iprintf(buffer, "State of AFG node 0x%02x:\n", codec->afg);
- print_power_state(buffer, codec, codec->afg);
nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
if (! nid || nodes < 0) {
@@ -624,7 +620,7 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP);
unsigned int wid_type = get_wcaps_type(wid_caps);
- hda_nid_t *conn = NULL;
+ hda_nid_t conn[HDA_MAX_CONNECTIONS];
int conn_len = 0;
snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
@@ -661,18 +657,9 @@ static void print_codec_info(struct snd_info_entry *entry,
if (wid_type == AC_WID_VOL_KNB)
wid_caps |= AC_WCAP_CONN_LIST;
- if (wid_caps & AC_WCAP_CONN_LIST) {
- conn_len = snd_hda_get_num_raw_conns(codec, nid);
- if (conn_len > 0) {
- conn = kmalloc(sizeof(hda_nid_t) * conn_len,
- GFP_KERNEL);
- if (!conn)
- return;
- if (snd_hda_get_raw_connections(codec, nid, conn,
- conn_len) < 0)
- conn_len = 0;
- }
- }
+ if (wid_caps & AC_WCAP_CONN_LIST)
+ conn_len = snd_hda_get_raw_connections(codec, nid, conn,
+ HDA_MAX_CONNECTIONS);
if (wid_caps & AC_WCAP_IN_AMP) {
snd_iprintf(buffer, " Amp-In caps: ");
@@ -745,8 +732,6 @@ static void print_codec_info(struct snd_info_entry *entry,
if (codec->proc_widget_hook)
codec->proc_widget_hook(buffer, codec, nid);
-
- kfree(conn);
}
snd_hda_power_down(codec);
}
diff --git a/trunk/sound/pci/hda/patch_analog.c b/trunk/sound/pci/hda/patch_analog.c
index df8014b27596..89fc5030ec79 100644
--- a/trunk/sound/pci/hda/patch_analog.c
+++ b/trunk/sound/pci/hda/patch_analog.c
@@ -20,6 +20,7 @@
*/
#include
+#include
#include
#include
#include
@@ -30,24 +31,11 @@
#include "hda_auto_parser.h"
#include "hda_beep.h"
#include "hda_jack.h"
-#include "hda_generic.h"
-
-#define ENABLE_AD_STATIC_QUIRKS
struct ad198x_spec {
- struct hda_gen_spec gen;
-
- /* for auto parser */
- int smux_paths[4];
- unsigned int cur_smux;
- hda_nid_t eapd_nid;
-
- unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
- hda_nid_t beep_dev_nid;
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
const struct snd_kcontrol_new *mixers[6];
int num_mixers;
+ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
const struct hda_verb *init_verbs[6]; /* initialization verbs
* don't forget NULL termination!
*/
@@ -61,6 +49,11 @@ struct ad198x_spec {
unsigned int cur_eapd;
unsigned int need_dac_fix;
+ const hda_nid_t *alt_dac_nid;
+ const struct hda_pcm_stream *stream_analog_alt_playback;
+ int independent_hp;
+ int num_active_streams;
+
/* capture */
unsigned int num_adc_nids;
const hda_nid_t *adc_nids;
@@ -80,8 +73,15 @@ struct ad198x_spec {
unsigned int spdif_route;
+ /* dynamic controls, init_verbs and input_mux */
+ struct auto_pin_cfg autocfg;
+ struct snd_array kctls;
+ struct hda_input_mux private_imux;
+ hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+
unsigned int jack_present: 1;
unsigned int inv_jack_detect: 1;/* inverted jack-detection */
+ unsigned int inv_eapd: 1; /* inverted EAPD implementation */
unsigned int analog_beep: 1; /* analog beep input present */
unsigned int avoid_init_slave_vol:1;
@@ -92,10 +92,8 @@ struct ad198x_spec {
hda_nid_t vmaster_nid;
const char * const *slave_vols;
const char * const *slave_sws;
-#endif /* ENABLE_AD_STATIC_QUIRKS */
};
-#ifdef ENABLE_AD_STATIC_QUIRKS
/*
* input MUX handling (common part)
*/
@@ -151,7 +149,8 @@ static const char * const ad1988_6stack_fp_slave_pfxs[] = {
"Front", "Surround", "Center", "LFE", "Side", "IEC958",
NULL
};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
+
+static void ad198x_free_kctls(struct hda_codec *codec);
#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* additional beep mixers; the actual parameters are overwritten at build */
@@ -173,34 +172,6 @@ static const struct snd_kcontrol_new ad_beep2_mixer[] = {
#define set_beep_amp(spec, nid, idx, dir) /* NOP */
#endif
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-static int create_beep_ctls(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- const struct snd_kcontrol_new *knew;
-
- if (!spec->beep_amp)
- return 0;
-
- knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
- for ( ; knew->name; knew++) {
- int err;
- struct snd_kcontrol *kctl;
- kctl = snd_ctl_new1(knew, codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- }
- return 0;
-}
-#else
-#define create_beep_ctls(codec) 0
-#endif
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
static int ad198x_build_controls(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
@@ -232,9 +203,22 @@ static int ad198x_build_controls(struct hda_codec *codec)
}
/* create beep controls if needed */
- err = create_beep_ctls(codec);
- if (err < 0)
- return err;
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+ if (spec->beep_amp) {
+ const struct snd_kcontrol_new *knew;
+ knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
+ for ( ; knew->name; knew++) {
+ struct snd_kcontrol *kctl;
+ kctl = snd_ctl_new1(knew, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = spec->beep_amp;
+ err = snd_hda_ctl_add(codec, 0, kctl);
+ if (err < 0)
+ return err;
+ }
+ }
+#endif
/* if we have no master control, let's create it */
if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
@@ -260,6 +244,8 @@ static int ad198x_build_controls(struct hda_codec *codec)
return err;
}
+ ad198x_free_kctls(codec); /* no longer needed */
+
/* assign Capture Source enums to NID */
kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
if (!kctl)
@@ -291,6 +277,72 @@ static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
}
#endif
+static void activate_ctl(struct hda_codec *codec, const char *name, int active)
+{
+ struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
+ if (ctl) {
+ ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ ctl->vd[0].access |= active ? 0 :
+ SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+ ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE;
+ ctl->vd[0].access |= active ?
+ SNDRV_CTL_ELEM_ACCESS_WRITE : 0;
+ snd_ctl_notify(codec->bus->card,
+ SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
+ }
+}
+
+static void set_stream_active(struct hda_codec *codec, bool active)
+{
+ struct ad198x_spec *spec = codec->spec;
+ if (active)
+ spec->num_active_streams++;
+ else
+ spec->num_active_streams--;
+ activate_ctl(codec, "Independent HP", spec->num_active_streams == 0);
+}
+
+static int ad1988_independent_hp_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const texts[] = { "OFF", "ON", NULL};
+ int index;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ index = uinfo->value.enumerated.item;
+ if (index >= 2)
+ index = 1;
+ strcpy(uinfo->value.enumerated.name, texts[index]);
+ return 0;
+}
+
+static int ad1988_independent_hp_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->independent_hp;
+ return 0;
+}
+
+static int ad1988_independent_hp_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ad198x_spec *spec = codec->spec;
+ unsigned int select = ucontrol->value.enumerated.item[0];
+ if (spec->independent_hp != select) {
+ spec->independent_hp = select;
+ if (spec->independent_hp)
+ spec->multiout.hp_nid = 0;
+ else
+ spec->multiout.hp_nid = spec->alt_dac_nid[0];
+ return 1;
+ }
+ return 0;
+}
+
/*
* Analog playback callbacks
*/
@@ -299,8 +351,15 @@ static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct ad198x_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ int err;
+ set_stream_active(codec, true);
+ err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
hinfo);
+ if (err < 0) {
+ set_stream_active(codec, false);
+ return err;
+ }
+ return 0;
}
static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -322,6 +381,43 @@ static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
}
+static int ad198x_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ set_stream_active(codec, false);
+ return 0;
+}
+
+static int ad1988_alt_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ad198x_spec *spec = codec->spec;
+ if (!spec->independent_hp)
+ return -EBUSY;
+ set_stream_active(codec, true);
+ return 0;
+}
+
+static int ad1988_alt_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ set_stream_active(codec, false);
+ return 0;
+}
+
+static const struct hda_pcm_stream ad198x_pcm_analog_alt_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = ad1988_alt_playback_pcm_open,
+ .close = ad1988_alt_playback_pcm_close
+ },
+};
+
/*
* Digital out
*/
@@ -395,6 +491,7 @@ static const struct hda_pcm_stream ad198x_pcm_analog_playback = {
.open = ad198x_playback_pcm_open,
.prepare = ad198x_playback_pcm_prepare,
.cleanup = ad198x_playback_pcm_cleanup,
+ .close = ad198x_playback_pcm_close
},
};
@@ -459,19 +556,43 @@ static int ad198x_build_pcms(struct hda_codec *codec)
}
}
+ if (spec->alt_dac_nid && spec->stream_analog_alt_playback) {
+ codec->num_pcms++;
+ info = spec->pcm_rec + 2;
+ info->name = "AD198x Headphone";
+ info->pcm_type = HDA_PCM_TYPE_AUDIO;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ *spec->stream_analog_alt_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->alt_dac_nid[0];
+ }
+
return 0;
}
-#endif /* ENABLE_AD_STATIC_QUIRKS */
+
+static void ad198x_free_kctls(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
+ }
+ snd_array_free(&spec->kctls);
+}
static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
hda_nid_t hp)
{
+ struct ad198x_spec *spec = codec->spec;
if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
- !codec->inv_eapd ? 0x00 : 0x02);
+ !spec->inv_eapd ? 0x00 : 0x02);
if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD)
snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
- !codec->inv_eapd ? 0x00 : 0x02);
+ !spec->inv_eapd ? 0x00 : 0x02);
}
static void ad198x_power_eapd(struct hda_codec *codec)
@@ -515,7 +636,7 @@ static void ad198x_free(struct hda_codec *codec)
if (!spec)
return;
- snd_hda_gen_spec_free(&spec->gen);
+ ad198x_free_kctls(codec);
kfree(spec);
snd_hda_detach_beep_device(codec);
}
@@ -528,7 +649,6 @@ static int ad198x_suspend(struct hda_codec *codec)
}
#endif
-#ifdef ENABLE_AD_STATIC_QUIRKS
static const struct hda_codec_ops ad198x_patch_ops = {
.build_controls = ad198x_build_controls,
.build_pcms = ad198x_build_pcms,
@@ -553,7 +673,7 @@ static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
- if (codec->inv_eapd)
+ if (spec->inv_eapd)
ucontrol->value.integer.value[0] = ! spec->cur_eapd;
else
ucontrol->value.integer.value[0] = spec->cur_eapd;
@@ -568,7 +688,7 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
hda_nid_t nid = kcontrol->private_value & 0xff;
unsigned int eapd;
eapd = !!ucontrol->value.integer.value[0];
- if (codec->inv_eapd)
+ if (spec->inv_eapd)
eapd = !eapd;
if (eapd == spec->cur_eapd)
return 0;
@@ -585,75 +705,12 @@ static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-/*
- * Automatic parse of I/O pins from the BIOS configuration
- */
-
-static int ad198x_auto_build_controls(struct hda_codec *codec)
-{
- int err;
-
- err = snd_hda_gen_build_controls(codec);
- if (err < 0)
- return err;
- err = create_beep_ctls(codec);
- if (err < 0)
- return err;
- return 0;
-}
-
-static const struct hda_codec_ops ad198x_auto_patch_ops = {
- .build_controls = ad198x_auto_build_controls,
- .build_pcms = snd_hda_gen_build_pcms,
- .init = snd_hda_gen_init,
- .free = ad198x_free,
- .unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
- .check_power_status = snd_hda_gen_check_power_status,
- .suspend = ad198x_suspend,
-#endif
- .reboot_notify = ad198x_shutup,
-};
-
-
-static int ad198x_parse_auto_config(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->gen.autocfg;
- int err;
-
- codec->spdif_status_reset = 1;
- codec->no_trigger_sense = 1;
- codec->no_sticky_stream = 1;
-
- spec->gen.indep_hp = 1;
-
- err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
- if (err < 0)
- return err;
- err = snd_hda_gen_parse_auto_config(codec, cfg);
- if (err < 0)
- return err;
-
- if (spec->beep_dev_nid) {
- err = snd_hda_attach_beep_device(codec, spec->beep_dev_nid);
- if (err < 0)
- return err;
- }
-
- codec->patch_ops = ad198x_auto_patch_ops;
-
- return 0;
-}
-
/*
* AD1986A specific
*/
-#ifdef ENABLE_AD_STATIC_QUIRKS
#define AD1986A_SPDIF_OUT 0x02
#define AD1986A_FRONT_DAC 0x03
#define AD1986A_SURR_DAC 0x04
@@ -938,7 +995,15 @@ static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ long *valp = ucontrol->value.integer.value;
+ int change;
+
+ change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE,
+ valp[0] ? 0 : HDA_AMP_MUTE);
+ change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE,
+ valp[1] ? 0 : HDA_AMP_MUTE);
if (change)
ad1986a_update_hp(codec);
return change;
@@ -1111,7 +1176,6 @@ static int ad1986a_samsung_p50_init(struct hda_codec *codec)
/* models */
enum {
- AD1986A_AUTO,
AD1986A_6STACK,
AD1986A_3STACK,
AD1986A_LAPTOP,
@@ -1124,7 +1188,6 @@ enum {
};
static const char * const ad1986a_models[AD1986A_MODELS] = {
- [AD1986A_AUTO] = "auto",
[AD1986A_6STACK] = "6stack",
[AD1986A_3STACK] = "3stack",
[AD1986A_LAPTOP] = "laptop",
@@ -1182,7 +1245,6 @@ static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
}
-#endif /* ENABLE_AD_STATIC_QUIRKS */
static int alloc_ad_spec(struct hda_codec *codec)
{
@@ -1192,97 +1254,15 @@ static int alloc_ad_spec(struct hda_codec *codec)
if (!spec)
return -ENOMEM;
codec->spec = spec;
- snd_hda_gen_spec_init(&spec->gen);
+ snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
return 0;
}
-/*
- * AD1986A fixup codes
- */
-
-/* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
-static void ad_fixup_inv_jack_detect(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
- codec->inv_jack_detect = 1;
-}
-
-enum {
- AD1986A_FIXUP_INV_JACK_DETECT,
-};
-
-static const struct hda_fixup ad1986a_fixups[] = {
- [AD1986A_FIXUP_INV_JACK_DETECT] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = ad_fixup_inv_jack_detect,
- },
-};
-
-static const struct snd_pci_quirk ad1986a_fixup_tbl[] = {
- SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT),
- {}
-};
-
-/*
- */
-static int ad1986a_parse_auto_config(struct hda_codec *codec)
-{
- int err;
- struct ad198x_spec *spec;
-
- err = alloc_ad_spec(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
- /* AD1986A has the inverted EAPD implementation */
- codec->inv_eapd = 1;
-
- spec->gen.mixer_nid = 0x07;
- spec->beep_dev_nid = 0x19;
- set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
-
- /* AD1986A has a hardware problem that it can't share a stream
- * with multiple output pins. The copy of front to surrounds
- * causes noisy or silent outputs at a certain timing, e.g.
- * changing the volume.
- * So, let's disable the shared stream.
- */
- spec->gen.multiout.no_share_stream = 1;
-
- snd_hda_pick_fixup(codec, NULL, ad1986a_fixup_tbl, ad1986a_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- err = ad198x_parse_auto_config(codec);
- if (err < 0) {
- ad198x_free(codec);
- return err;
- }
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-
- return 0;
-}
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
static int patch_ad1986a(struct hda_codec *codec)
{
struct ad198x_spec *spec;
int err, board_config;
- board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
- ad1986a_models,
- ad1986a_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1986A_AUTO;
- }
-
- if (board_config == AD1986A_AUTO)
- return ad1986a_parse_auto_config(codec);
-
err = alloc_ad_spec(codec);
if (err < 0)
return err;
@@ -1311,11 +1291,14 @@ static int patch_ad1986a(struct hda_codec *codec)
spec->loopback.amplist = ad1986a_loopbacks;
#endif
spec->vmaster_nid = 0x1b;
- codec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
+ spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
codec->patch_ops = ad198x_patch_ops;
/* override some parameters */
+ board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
+ ad1986a_models,
+ ad1986a_cfg_tbl);
switch (board_config) {
case AD1986A_3STACK:
spec->num_mixers = 2;
@@ -1426,15 +1409,11 @@ static int patch_ad1986a(struct hda_codec *codec)
return 0;
}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1986a ad1986a_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
/*
* AD1983 specific
*/
-#ifdef ENABLE_AD_STATIC_QUIRKS
#define AD1983_SPDIF_OUT 0x02
#define AD1983_DAC 0x03
#define AD1983_ADC 0x04
@@ -1575,137 +1554,11 @@ static const struct hda_amp_list ad1983_loopbacks[] = {
};
#endif
-/* models */
-enum {
- AD1983_AUTO,
- AD1983_BASIC,
- AD1983_MODELS
-};
-
-static const char * const ad1983_models[AD1983_MODELS] = {
- [AD1983_AUTO] = "auto",
- [AD1983_BASIC] = "basic",
-};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-
-/*
- * SPDIF mux control for AD1983 auto-parser
- */
-static int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- static const char * const texts2[] = { "PCM", "ADC" };
- static const char * const texts3[] = { "PCM", "ADC1", "ADC2" };
- hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
- int num_conns = snd_hda_get_num_conns(codec, dig_out);
-
- if (num_conns == 2)
- return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2);
- else if (num_conns == 3)
- return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
- else
- return -EINVAL;
-}
-
-static int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
-
- ucontrol->value.enumerated.item[0] = spec->cur_smux;
- return 0;
-}
-
-static int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
- unsigned int val = ucontrol->value.enumerated.item[0];
- hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
- int num_conns = snd_hda_get_num_conns(codec, dig_out);
-
- if (val >= num_conns)
- return -EINVAL;
- if (spec->cur_smux == val)
- return 0;
- spec->cur_smux = val;
- snd_hda_codec_write_cache(codec, dig_out, 0,
- AC_VERB_SET_CONNECT_SEL, val);
- return 1;
-}
-
-static struct snd_kcontrol_new ad1983_auto_smux_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "IEC958 Playback Source",
- .info = ad1983_auto_smux_enum_info,
- .get = ad1983_auto_smux_enum_get,
- .put = ad1983_auto_smux_enum_put,
-};
-
-static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)
-{
- struct ad198x_spec *spec = codec->spec;
- hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
- int num_conns;
-
- if (!dig_out)
- return 0;
- num_conns = snd_hda_get_num_conns(codec, dig_out);
- if (num_conns != 2 && num_conns != 3)
- return 0;
- if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1983_auto_smux_mixer))
- return -ENOMEM;
- return 0;
-}
-
-static int ad1983_parse_auto_config(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err;
-
- err = alloc_ad_spec(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
- spec->beep_dev_nid = 0x10;
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
- err = ad198x_parse_auto_config(codec);
- if (err < 0)
- goto error;
- err = ad1983_add_spdif_mux_ctl(codec);
- if (err < 0)
- goto error;
- return 0;
-
- error:
- ad198x_free(codec);
- return err;
-}
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
static int patch_ad1983(struct hda_codec *codec)
{
struct ad198x_spec *spec;
- int board_config;
int err;
- board_config = snd_hda_check_board_config(codec, AD1983_MODELS,
- ad1983_models, NULL);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1983_AUTO;
- }
-
- if (board_config == AD1983_AUTO)
- return ad1983_parse_auto_config(codec);
-
err = alloc_ad_spec(codec);
if (err < 0)
return err;
@@ -1743,16 +1596,12 @@ static int patch_ad1983(struct hda_codec *codec)
return 0;
}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1983 ad1983_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
/*
* AD1981 HD specific
*/
-#ifdef ENABLE_AD_STATIC_QUIRKS
#define AD1981_SPDIF_OUT 0x02
#define AD1981_DAC 0x03
#define AD1981_ADC 0x04
@@ -2083,7 +1932,6 @@ static const struct hda_input_mux ad1981_thinkpad_capture_source = {
/* models */
enum {
- AD1981_AUTO,
AD1981_BASIC,
AD1981_HP,
AD1981_THINKPAD,
@@ -2092,7 +1940,6 @@ enum {
};
static const char * const ad1981_models[AD1981_MODELS] = {
- [AD1981_AUTO] = "auto",
[AD1981_HP] = "hp",
[AD1981_THINKPAD] = "thinkpad",
[AD1981_BASIC] = "basic",
@@ -2111,122 +1958,12 @@ static const struct snd_pci_quirk ad1981_cfg_tbl[] = {
SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
{}
};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-/* follow EAPD via vmaster hook */
-static void ad_vmaster_eapd_hook(void *private_data, int enabled)
-{
- struct hda_codec *codec = private_data;
- struct ad198x_spec *spec = codec->spec;
- snd_hda_codec_update_cache(codec, spec->eapd_nid, 0,
- AC_VERB_SET_EAPD_BTLENABLE,
- enabled ? 0x02 : 0x00);
-}
-
-static void ad1981_fixup_hp_eapd(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct ad198x_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
- spec->eapd_nid = 0x05;
- }
-}
-
-/* set the upper-limit for mixer amp to 0dB for avoiding the possible
- * damage by overloading
- */
-static void ad1981_fixup_amp_override(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
- snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
-}
-
-enum {
- AD1981_FIXUP_AMP_OVERRIDE,
- AD1981_FIXUP_HP_EAPD,
-};
-
-static const struct hda_fixup ad1981_fixups[] = {
- [AD1981_FIXUP_AMP_OVERRIDE] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = ad1981_fixup_amp_override,
- },
- [AD1981_FIXUP_HP_EAPD] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = ad1981_fixup_hp_eapd,
- .chained = true,
- .chain_id = AD1981_FIXUP_AMP_OVERRIDE,
- },
-};
-
-static const struct snd_pci_quirk ad1981_fixup_tbl[] = {
- SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
- SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1981_FIXUP_HP_EAPD),
- SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
- /* HP nx6320 (reversed SSID, H/W bug) */
- SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_FIXUP_HP_EAPD),
- {}
-};
-
-static int ad1981_parse_auto_config(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err;
-
- err = alloc_ad_spec(codec);
- if (err < 0)
- return -ENOMEM;
- spec = codec->spec;
-
- spec->gen.mixer_nid = 0x0e;
- spec->beep_dev_nid = 0x10;
- set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
-
- snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- err = ad198x_parse_auto_config(codec);
- if (err < 0)
- goto error;
- err = ad1983_add_spdif_mux_ctl(codec);
- if (err < 0)
- goto error;
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-
- return 0;
-
- error:
- ad198x_free(codec);
- return err;
-}
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
static int patch_ad1981(struct hda_codec *codec)
{
struct ad198x_spec *spec;
int err, board_config;
- board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
- ad1981_models,
- ad1981_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1981_AUTO;
- }
-
- if (board_config == AD1981_AUTO)
- return ad1981_parse_auto_config(codec);
-
err = alloc_ad_spec(codec);
if (err < 0)
return -ENOMEM;
@@ -2260,6 +1997,9 @@ static int patch_ad1981(struct hda_codec *codec)
codec->patch_ops = ad198x_patch_ops;
/* override some parameters */
+ board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
+ ad1981_models,
+ ad1981_cfg_tbl);
switch (board_config) {
case AD1981_HP:
spec->mixers[0] = ad1981_hp_mixers;
@@ -2309,9 +2049,6 @@ static int patch_ad1981(struct hda_codec *codec)
return 0;
}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1981 ad1981_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
/*
@@ -2400,16 +2137,15 @@ static int patch_ad1981(struct hda_codec *codec)
*/
-#ifdef ENABLE_AD_STATIC_QUIRKS
/* models */
enum {
- AD1988_AUTO,
AD1988_6STACK,
AD1988_6STACK_DIG,
AD1988_3STACK,
AD1988_3STACK_DIG,
AD1988_LAPTOP,
AD1988_LAPTOP_DIG,
+ AD1988_AUTO,
AD1988_MODEL_LAST,
};
@@ -2514,6 +2250,17 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
return err;
}
+static const struct snd_kcontrol_new ad1988_hp_mixers[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Independent HP",
+ .info = ad1988_independent_hp_info,
+ .get = ad1988_independent_hp_get,
+ .put = ad1988_independent_hp_put,
+ },
+ { } /* end */
+};
+
/* 6-stack mode */
static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
@@ -3064,197 +2811,433 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
if (snd_hda_jack_detect(codec, 0x11))
snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
else
- snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
-}
+ snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
+}
+
+#ifdef CONFIG_PM
+static const struct hda_amp_list ad1988_loopbacks[] = {
+ { 0x20, HDA_INPUT, 0 }, /* Front Mic */
+ { 0x20, HDA_INPUT, 1 }, /* Line */
+ { 0x20, HDA_INPUT, 4 }, /* Mic */
+ { 0x20, HDA_INPUT, 6 }, /* CD */
+ { } /* end */
+};
+#endif
+
+/*
+ * Automatic parse of I/O pins from the BIOS configuration
+ */
+
+enum {
+ AD_CTL_WIDGET_VOL,
+ AD_CTL_WIDGET_MUTE,
+ AD_CTL_BIND_MUTE,
+};
+static const struct snd_kcontrol_new ad1988_control_templates[] = {
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+ HDA_CODEC_MUTE(NULL, 0, 0, 0),
+ HDA_BIND_MUTE(NULL, 0, 0, 0),
+};
+
+/* add dynamic controls */
+static int add_control(struct ad198x_spec *spec, int type, const char *name,
+ unsigned long val)
+{
+ struct snd_kcontrol_new *knew;
+
+ knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return -ENOMEM;
+ *knew = ad1988_control_templates[type];
+ knew->name = kstrdup(name, GFP_KERNEL);
+ if (! knew->name)
+ return -ENOMEM;
+ if (get_amp_nid_(val))
+ knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+ knew->private_value = val;
+ return 0;
+}
+
+#define AD1988_PIN_CD_NID 0x18
+#define AD1988_PIN_BEEP_NID 0x10
+
+static const hda_nid_t ad1988_mixer_nids[8] = {
+ /* A B C D E F G H */
+ 0x22, 0x2b, 0x2c, 0x29, 0x26, 0x2a, 0x27, 0x28
+};
+
+static inline hda_nid_t ad1988_idx_to_dac(struct hda_codec *codec, int idx)
+{
+ static const hda_nid_t idx_to_dac[8] = {
+ /* A B C D E F G H */
+ 0x03, 0x06, 0x05, 0x04, 0x0a, 0x06, 0x05, 0x0a
+ };
+ static const hda_nid_t idx_to_dac_rev2[8] = {
+ /* A B C D E F G H */
+ 0x03, 0x05, 0x0a, 0x04, 0x06, 0x05, 0x0a, 0x06
+ };
+ if (is_rev2(codec))
+ return idx_to_dac_rev2[idx];
+ else
+ return idx_to_dac[idx];
+}
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1988_loopbacks[] = {
- { 0x20, HDA_INPUT, 0 }, /* Front Mic */
- { 0x20, HDA_INPUT, 1 }, /* Line */
- { 0x20, HDA_INPUT, 4 }, /* Mic */
- { 0x20, HDA_INPUT, 6 }, /* CD */
- { } /* end */
+static const hda_nid_t ad1988_boost_nids[8] = {
+ 0x38, 0x39, 0x3a, 0x3d, 0x3c, 0x3b, 0, 0
};
-#endif
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int ad1988_pin_idx(hda_nid_t nid)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- static const char * const texts[] = {
- "PCM", "ADC1", "ADC2", "ADC3",
+ static const hda_nid_t ad1988_io_pins[8] = {
+ 0x11, 0x14, 0x15, 0x12, 0x17, 0x16, 0x24, 0x25
};
- int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
- if (num_conns > 4)
- num_conns = 4;
- return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts);
+ int i;
+ for (i = 0; i < ARRAY_SIZE(ad1988_io_pins); i++)
+ if (ad1988_io_pins[i] == nid)
+ return i;
+ return 0; /* should be -1 */
}
-static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int ad1988_pin_to_loopback_idx(hda_nid_t nid)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ad198x_spec *spec = codec->spec;
+ static const int loopback_idx[8] = {
+ 2, 0, 1, 3, 4, 5, 1, 4
+ };
+ switch (nid) {
+ case AD1988_PIN_CD_NID:
+ return 6;
+ default:
+ return loopback_idx[ad1988_pin_idx(nid)];
+ }
+}
- ucontrol->value.enumerated.item[0] = spec->cur_smux;
- return 0;
+static int ad1988_pin_to_adc_idx(hda_nid_t nid)
+{
+ static const int adc_idx[8] = {
+ 0, 1, 2, 8, 4, 3, 6, 7
+ };
+ switch (nid) {
+ case AD1988_PIN_CD_NID:
+ return 5;
+ default:
+ return adc_idx[ad1988_pin_idx(nid)];
+ }
}
-static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/* fill in the dac_nids table from the parsed pin configuration */
+static int ad1988_auto_fill_dac_nids(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
- unsigned int val = ucontrol->value.enumerated.item[0];
- struct nid_path *path;
- int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
+ int i, idx;
- if (val >= num_conns)
- return -EINVAL;
- if (spec->cur_smux == val)
- return 0;
+ spec->multiout.dac_nids = spec->private_dac_nids;
- mutex_lock(&codec->control_mutex);
- codec->cached_write = 1;
- path = snd_hda_get_path_from_idx(codec,
- spec->smux_paths[spec->cur_smux]);
- if (path)
- snd_hda_activate_path(codec, path, false, true);
- path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]);
- if (path)
- snd_hda_activate_path(codec, path, true, true);
- spec->cur_smux = val;
- codec->cached_write = 0;
- mutex_unlock(&codec->control_mutex);
- snd_hda_codec_flush_cache(codec); /* flush the updates */
- return 1;
+ /* check the pins hardwired to audio widget */
+ for (i = 0; i < cfg->line_outs; i++) {
+ idx = ad1988_pin_idx(cfg->line_out_pins[i]);
+ spec->private_dac_nids[i] = ad1988_idx_to_dac(codec, idx);
+ }
+ spec->multiout.num_dacs = cfg->line_outs;
+ return 0;
}
-static struct snd_kcontrol_new ad1988_auto_smux_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "IEC958 Playback Source",
- .info = ad1988_auto_smux_enum_info,
- .get = ad1988_auto_smux_enum_get,
- .put = ad1988_auto_smux_enum_put,
-};
+/* add playback controls from the parsed DAC table */
+static int ad1988_auto_create_multi_out_ctls(struct ad198x_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ char name[32];
+ static const char * const chname[4] = {
+ "Front", "Surround", NULL /*CLFE*/, "Side"
+ };
+ hda_nid_t nid;
+ int i, err;
-static int ad1988_auto_init(struct hda_codec *codec)
+ for (i = 0; i < cfg->line_outs; i++) {
+ hda_nid_t dac = spec->multiout.dac_nids[i];
+ if (! dac)
+ continue;
+ nid = ad1988_mixer_nids[ad1988_pin_idx(cfg->line_out_pins[i])];
+ if (i == 2) {
+ /* Center/LFE */
+ err = add_control(spec, AD_CTL_WIDGET_VOL,
+ "Center Playback Volume",
+ HDA_COMPOSE_AMP_VAL(dac, 1, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = add_control(spec, AD_CTL_WIDGET_VOL,
+ "LFE Playback Volume",
+ HDA_COMPOSE_AMP_VAL(dac, 2, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = add_control(spec, AD_CTL_BIND_MUTE,
+ "Center Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT));
+ if (err < 0)
+ return err;
+ err = add_control(spec, AD_CTL_BIND_MUTE,
+ "LFE Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT));
+ if (err < 0)
+ return err;
+ } else {
+ sprintf(name, "%s Playback Volume", chname[i]);
+ err = add_control(spec, AD_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ err = add_control(spec, AD_CTL_BIND_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+/* add playback controls for speaker and HP outputs */
+static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
+ const char *pfx)
{
struct ad198x_spec *spec = codec->spec;
- int i, err;
+ hda_nid_t nid;
+ int i, idx, err;
+ char name[32];
- err = snd_hda_gen_init(codec);
- if (err < 0)
- return err;
- if (!spec->gen.autocfg.dig_outs)
+ if (! pin)
return 0;
- for (i = 0; i < 4; i++) {
- struct nid_path *path;
- path = snd_hda_get_path_from_idx(codec, spec->smux_paths[i]);
- if (path)
- snd_hda_activate_path(codec, path, path->active, false);
+ idx = ad1988_pin_idx(pin);
+ nid = ad1988_idx_to_dac(codec, idx);
+ /* check whether the corresponding DAC was already taken */
+ for (i = 0; i < spec->autocfg.line_outs; i++) {
+ hda_nid_t pin = spec->autocfg.line_out_pins[i];
+ hda_nid_t dac = ad1988_idx_to_dac(codec, ad1988_pin_idx(pin));
+ if (dac == nid)
+ break;
+ }
+ if (i >= spec->autocfg.line_outs) {
+ /* specify the DAC as the extra output */
+ if (!spec->multiout.hp_nid)
+ spec->multiout.hp_nid = nid;
+ else
+ spec->multiout.extra_out_nid[0] = nid;
+ /* control HP volume/switch on the output mixer amp */
+ sprintf(name, "%s Playback Volume", pfx);
+ err = add_control(spec, AD_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
}
+ nid = ad1988_mixer_nids[idx];
+ sprintf(name, "%s Playback Switch", pfx);
+ if ((err = add_control(spec, AD_CTL_BIND_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
+ return err;
+ return 0;
+}
+
+/* create input playback/capture controls for the given pin */
+static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
+ const char *ctlname, int ctlidx, int boost)
+{
+ char name[32];
+ int err, idx;
+ sprintf(name, "%s Playback Volume", ctlname);
+ idx = ad1988_pin_to_loopback_idx(pin);
+ if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", ctlname);
+ if ((err = add_control(spec, AD_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(0x20, 3, idx, HDA_INPUT))) < 0)
+ return err;
+ if (boost) {
+ hda_nid_t bnid;
+ idx = ad1988_pin_idx(pin);
+ bnid = ad1988_boost_nids[idx];
+ if (bnid) {
+ sprintf(name, "%s Boost Volume", ctlname);
+ return add_control(spec, AD_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(bnid, 3, idx, HDA_OUTPUT));
+
+ }
+ }
return 0;
}
-static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
+/* create playback/capture controls for input pins */
+static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
{
struct ad198x_spec *spec = codec->spec;
- int i, num_conns;
- /* we create four static faked paths, since AD codecs have odd
- * widget connections regarding the SPDIF out source
- */
- static struct nid_path fake_paths[4] = {
- {
- .depth = 3,
- .path = { 0x02, 0x1d, 0x1b },
- .idx = { 0, 0, 0 },
- .multi = { 0, 0, 0 },
- },
- {
- .depth = 4,
- .path = { 0x08, 0x0b, 0x1d, 0x1b },
- .idx = { 0, 0, 1, 0 },
- .multi = { 0, 1, 0, 0 },
- },
- {
- .depth = 4,
- .path = { 0x09, 0x0b, 0x1d, 0x1b },
- .idx = { 0, 1, 1, 0 },
- .multi = { 0, 1, 0, 0 },
- },
- {
- .depth = 4,
- .path = { 0x0f, 0x0b, 0x1d, 0x1b },
- .idx = { 0, 2, 1, 0 },
- .multi = { 0, 1, 0, 0 },
- },
- };
+ struct hda_input_mux *imux = &spec->private_imux;
+ int i, err, type, type_idx;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ const char *label;
+ type = cfg->inputs[i].type;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ snd_hda_add_imux_item(imux, label,
+ ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
+ &type_idx);
+ err = new_analog_input(spec, cfg->inputs[i].pin,
+ label, type_idx,
+ type == AUTO_PIN_MIC);
+ if (err < 0)
+ return err;
+ }
+ snd_hda_add_imux_item(imux, "Mix", 9, NULL);
- /* SPDIF source mux appears to be present only on AD1988A */
- if (!spec->gen.autocfg.dig_outs ||
- get_wcaps_type(get_wcaps(codec, 0x1d)) != AC_WID_AUD_MIX)
- return 0;
+ if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
+ "Analog Mix Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
+ return err;
+ if ((err = add_control(spec, AD_CTL_WIDGET_MUTE,
+ "Analog Mix Playback Switch",
+ HDA_COMPOSE_AMP_VAL(0x21, 3, 0x0, HDA_OUTPUT))) < 0)
+ return err;
- num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
- if (num_conns != 3 && num_conns != 4)
- return 0;
+ return 0;
+}
- for (i = 0; i < num_conns; i++) {
- struct nid_path *path = snd_array_new(&spec->gen.paths);
- if (!path)
- return -ENOMEM;
- *path = fake_paths[i];
- if (!i)
- path->active = 1;
- spec->smux_paths[i] = snd_hda_get_path_idx(codec, path);
+static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
+ hda_nid_t nid, int pin_type,
+ int dac_idx)
+{
+ /* set as output */
+ snd_hda_set_pin_ctl(codec, nid, pin_type);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ switch (nid) {
+ case 0x11: /* port-A - DAC 03 */
+ snd_hda_codec_write(codec, 0x37, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
+ break;
+ case 0x14: /* port-B - DAC 06 */
+ snd_hda_codec_write(codec, 0x30, 0, AC_VERB_SET_CONNECT_SEL, 0x02);
+ break;
+ case 0x15: /* port-C - DAC 05 */
+ snd_hda_codec_write(codec, 0x31, 0, AC_VERB_SET_CONNECT_SEL, 0x00);
+ break;
+ case 0x17: /* port-E - DAC 0a */
+ snd_hda_codec_write(codec, 0x32, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
+ break;
+ case 0x13: /* mono - DAC 04 */
+ snd_hda_codec_write(codec, 0x36, 0, AC_VERB_SET_CONNECT_SEL, 0x01);
+ break;
}
+}
- if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer))
- return -ENOMEM;
+static void ad1988_auto_init_multi_out(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ int i;
- codec->patch_ops.init = ad1988_auto_init;
+ for (i = 0; i < spec->autocfg.line_outs; i++) {
+ hda_nid_t nid = spec->autocfg.line_out_pins[i];
+ ad1988_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
+ }
+}
- return 0;
+static void ad1988_auto_init_extra_out(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ hda_nid_t pin;
+
+ pin = spec->autocfg.speaker_pins[0];
+ if (pin) /* connect to front */
+ ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
+ pin = spec->autocfg.hp_pins[0];
+ if (pin) /* connect to front */
+ ad1988_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
}
-/*
- */
+static void ad1988_auto_init_analog_input(struct hda_codec *codec)
+{
+ struct ad198x_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, idx;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ int type = cfg->inputs[i].type;
+ int val;
+ switch (nid) {
+ case 0x15: /* port-C */
+ snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
+ break;
+ case 0x17: /* port-E */
+ snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
+ break;
+ }
+ val = PIN_IN;
+ if (type == AUTO_PIN_MIC)
+ val |= snd_hda_get_default_vref(codec, nid);
+ snd_hda_set_pin_ctl(codec, nid, val);
+ if (nid != AD1988_PIN_CD_NID)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE);
+ idx = ad1988_pin_idx(nid);
+ if (ad1988_boost_nids[idx])
+ snd_hda_codec_write(codec, ad1988_boost_nids[idx], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_ZERO);
+ }
+}
+/* parse the BIOS configuration and set up the alc_spec */
+/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
static int ad1988_parse_auto_config(struct hda_codec *codec)
{
- struct ad198x_spec *spec;
+ struct ad198x_spec *spec = codec->spec;
int err;
- err = alloc_ad_spec(codec);
- if (err < 0)
+ if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
+ return err;
+ if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
+ return err;
+ if (! spec->autocfg.line_outs)
+ return 0; /* can't find valid BIOS pin config */
+ if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
+ (err = ad1988_auto_create_extra_out(codec,
+ spec->autocfg.speaker_pins[0],
+ "Speaker")) < 0 ||
+ (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
+ "Headphone")) < 0 ||
+ (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
return err;
- spec = codec->spec;
- spec->gen.mixer_nid = 0x20;
- spec->gen.mixer_merge_nid = 0x21;
- spec->beep_dev_nid = 0x10;
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
- err = ad198x_parse_auto_config(codec);
- if (err < 0)
- goto error;
- err = ad1988_add_spdif_mux_ctl(codec);
- if (err < 0)
- goto error;
- return 0;
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- error:
- ad198x_free(codec);
- return err;
+ if (spec->autocfg.dig_outs)
+ spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
+ if (spec->autocfg.dig_in_pin)
+ spec->dig_in_nid = AD1988_SPDIF_IN;
+
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
+
+ spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
+
+ spec->input_mux = &spec->private_imux;
+
+ return 1;
+}
+
+/* init callback for auto-configuration model -- overriding the default init */
+static int ad1988_auto_init(struct hda_codec *codec)
+{
+ ad198x_init(codec);
+ ad1988_auto_init_multi_out(codec);
+ ad1988_auto_init_extra_out(codec);
+ ad1988_auto_init_analog_input(codec);
+ return 0;
}
/*
*/
-#ifdef ENABLE_AD_STATIC_QUIRKS
static const char * const ad1988_models[AD1988_MODEL_LAST] = {
[AD1988_6STACK] = "6stack",
[AD1988_6STACK_DIG] = "6stack-dig",
@@ -3279,6 +3262,14 @@ static int patch_ad1988(struct hda_codec *codec)
struct ad198x_spec *spec;
int err, board_config;
+ err = alloc_ad_spec(codec);
+ if (err < 0)
+ return err;
+ spec = codec->spec;
+
+ if (is_rev2(codec))
+ snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
+
board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
ad1988_models, ad1988_cfg_tbl);
if (board_config < 0) {
@@ -3287,16 +3278,17 @@ static int patch_ad1988(struct hda_codec *codec)
board_config = AD1988_AUTO;
}
- if (board_config == AD1988_AUTO)
- return ad1988_parse_auto_config(codec);
-
- err = alloc_ad_spec(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
- if (is_rev2(codec))
- snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
+ if (board_config == AD1988_AUTO) {
+ /* automatic parse from the BIOS config */
+ err = ad1988_parse_auto_config(codec);
+ if (err < 0) {
+ ad198x_free(codec);
+ return err;
+ } else if (! err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 6-stack mode...\n");
+ board_config = AD1988_6STACK;
+ }
+ }
err = snd_hda_attach_beep_device(codec, 0x10);
if (err < 0) {
@@ -3360,7 +3352,7 @@ static int patch_ad1988(struct hda_codec *codec)
spec->input_mux = &ad1988_laptop_capture_source;
spec->num_mixers = 1;
spec->mixers[0] = ad1988_laptop_mixers;
- codec->inv_eapd = 1; /* inverted EAPD */
+ spec->inv_eapd = 1; /* inverted EAPD */
spec->num_init_verbs = 1;
spec->init_verbs[0] = ad1988_laptop_init_verbs;
if (board_config == AD1988_LAPTOP_DIG)
@@ -3368,6 +3360,15 @@ static int patch_ad1988(struct hda_codec *codec)
break;
}
+ if (spec->autocfg.hp_pins[0]) {
+ spec->mixers[spec->num_mixers++] = ad1988_hp_mixers;
+ spec->slave_vols = ad1988_6stack_fp_slave_pfxs;
+ spec->slave_sws = ad1988_6stack_fp_slave_pfxs;
+ spec->alt_dac_nid = ad1988_alt_dac_nid;
+ spec->stream_analog_alt_playback =
+ &ad198x_pcm_analog_alt_playback;
+ }
+
spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
spec->adc_nids = ad1988_adc_nids;
spec->capsrc_nids = ad1988_capsrc_nids;
@@ -3395,6 +3396,9 @@ static int patch_ad1988(struct hda_codec *codec)
codec->patch_ops = ad198x_patch_ops;
switch (board_config) {
+ case AD1988_AUTO:
+ codec->patch_ops.init = ad1988_auto_init;
+ break;
case AD1988_LAPTOP:
case AD1988_LAPTOP_DIG:
codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
@@ -3410,9 +3414,6 @@ static int patch_ad1988(struct hda_codec *codec)
return 0;
}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1988 ad1988_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
/*
@@ -3433,7 +3434,6 @@ static int patch_ad1988(struct hda_codec *codec)
* but no build-up framework is given, so far.
*/
-#ifdef ENABLE_AD_STATIC_QUIRKS
static const hda_nid_t ad1884_dac_nids[1] = {
0x04,
};
@@ -3576,107 +3576,7 @@ static const char * const ad1884_slave_vols[] = {
NULL
};
-enum {
- AD1884_AUTO,
- AD1884_BASIC,
- AD1884_MODELS
-};
-
-static const char * const ad1884_models[AD1884_MODELS] = {
- [AD1884_AUTO] = "auto",
- [AD1884_BASIC] = "basic",
-};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-
-/* set the upper-limit for mixer amp to 0dB for avoiding the possible
- * damage by overloading
- */
-static void ad1884_fixup_amp_override(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
- snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
- (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
- (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (1 << AC_AMPCAP_MUTE_SHIFT));
-}
-
-static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct ad198x_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
- spec->eapd_nid = spec->gen.autocfg.line_out_pins[0];
- else
- spec->eapd_nid = spec->gen.autocfg.speaker_pins[0];
- if (spec->eapd_nid)
- spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
- }
-}
-
-enum {
- AD1884_FIXUP_AMP_OVERRIDE,
- AD1884_FIXUP_HP_EAPD,
-};
-
-static const struct hda_fixup ad1884_fixups[] = {
- [AD1884_FIXUP_AMP_OVERRIDE] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = ad1884_fixup_amp_override,
- },
- [AD1884_FIXUP_HP_EAPD] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = ad1884_fixup_hp_eapd,
- .chained = true,
- .chain_id = AD1884_FIXUP_AMP_OVERRIDE,
- },
-};
-
-static const struct snd_pci_quirk ad1884_fixup_tbl[] = {
- SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD),
- {}
-};
-
-
-static int ad1884_parse_auto_config(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err;
-
- err = alloc_ad_spec(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
- spec->gen.mixer_nid = 0x20;
- spec->beep_dev_nid = 0x10;
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
- snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- err = ad198x_parse_auto_config(codec);
- if (err < 0)
- goto error;
- err = ad1983_add_spdif_mux_ctl(codec);
- if (err < 0)
- goto error;
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-
- return 0;
-
- error:
- ad198x_free(codec);
- return err;
-}
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static int patch_ad1884_basic(struct hda_codec *codec)
+static int patch_ad1884(struct hda_codec *codec)
{
struct ad198x_spec *spec;
int err;
@@ -3723,29 +3623,6 @@ static int patch_ad1884_basic(struct hda_codec *codec)
return 0;
}
-static int patch_ad1884(struct hda_codec *codec)
-{
- int board_config;
-
- board_config = snd_hda_check_board_config(codec, AD1884_MODELS,
- ad1884_models, NULL);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1884_AUTO;
- }
-
- if (board_config == AD1884_AUTO)
- return ad1884_parse_auto_config(codec);
- else
- return patch_ad1884_basic(codec);
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1884 ad1884_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
/*
* Lenovo Thinkpad T61/X61
*/
@@ -3918,7 +3795,6 @@ static int ad1984_build_pcms(struct hda_codec *codec)
/* models */
enum {
- AD1984_AUTO,
AD1984_BASIC,
AD1984_THINKPAD,
AD1984_DELL_DESKTOP,
@@ -3926,7 +3802,6 @@ enum {
};
static const char * const ad1984_models[AD1984_MODELS] = {
- [AD1984_AUTO] = "auto",
[AD1984_BASIC] = "basic",
[AD1984_THINKPAD] = "thinkpad",
[AD1984_DELL_DESKTOP] = "dell_desktop",
@@ -3945,22 +3820,12 @@ static int patch_ad1984(struct hda_codec *codec)
struct ad198x_spec *spec;
int board_config, err;
- board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
- ad1984_models, ad1984_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1984_AUTO;
- }
-
- if (board_config == AD1984_AUTO)
- return ad1884_parse_auto_config(codec);
-
- err = patch_ad1884_basic(codec);
+ err = patch_ad1884(codec);
if (err < 0)
return err;
spec = codec->spec;
-
+ board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
+ ad1984_models, ad1984_cfg_tbl);
switch (board_config) {
case AD1984_BASIC:
/* additional digital mics */
@@ -3987,9 +3852,6 @@ static int patch_ad1984(struct hda_codec *codec)
}
return 0;
}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1984 ad1884_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
/*
@@ -4010,7 +3872,6 @@ static int patch_ad1984(struct hda_codec *codec)
* We share the single DAC for both HP and line-outs (see AD1884/1984).
*/
-#ifdef ENABLE_AD_STATIC_QUIRKS
static const hda_nid_t ad1884a_dac_nids[1] = {
0x03,
};
@@ -4681,7 +4542,6 @@ static int ad1984a_touchsmart_init(struct hda_codec *codec)
*/
enum {
- AD1884A_AUTO,
AD1884A_DESKTOP,
AD1884A_LAPTOP,
AD1884A_MOBILE,
@@ -4692,7 +4552,6 @@ enum {
};
static const char * const ad1884a_models[AD1884A_MODELS] = {
- [AD1884A_AUTO] = "auto",
[AD1884A_DESKTOP] = "desktop",
[AD1884A_LAPTOP] = "laptop",
[AD1884A_MOBILE] = "mobile",
@@ -4721,18 +4580,6 @@ static int patch_ad1884a(struct hda_codec *codec)
struct ad198x_spec *spec;
int err, board_config;
- board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
- ad1884a_models,
- ad1884a_cfg_tbl);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1884A_AUTO;
- }
-
- if (board_config == AD1884A_AUTO)
- return ad1884_parse_auto_config(codec);
-
err = alloc_ad_spec(codec);
if (err < 0)
return err;
@@ -4764,6 +4611,9 @@ static int patch_ad1884a(struct hda_codec *codec)
codec->patch_ops = ad198x_patch_ops;
/* override some parameters */
+ board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
+ ad1884a_models,
+ ad1884a_cfg_tbl);
switch (board_config) {
case AD1884A_LAPTOP:
spec->mixers[0] = ad1884a_laptop_mixers;
@@ -4834,9 +4684,6 @@ static int patch_ad1884a(struct hda_codec *codec)
return 0;
}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1884a ad1884_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
/*
@@ -4851,7 +4698,6 @@ static int patch_ad1884a(struct hda_codec *codec)
* port-G - rear clfe-out (6stack)
*/
-#ifdef ENABLE_AD_STATIC_QUIRKS
static const hda_nid_t ad1882_dac_nids[3] = {
0x04, 0x03, 0x05
};
@@ -5128,7 +4974,6 @@ static const struct hda_amp_list ad1882_loopbacks[] = {
/* models */
enum {
- AD1882_AUTO,
AD1882_3STACK,
AD1882_6STACK,
AD1882_3STACK_AUTOMUTE,
@@ -5136,57 +4981,17 @@ enum {
};
static const char * const ad1882_models[AD1986A_MODELS] = {
- [AD1882_AUTO] = "auto",
[AD1882_3STACK] = "3stack",
[AD1882_6STACK] = "6stack",
[AD1882_3STACK_AUTOMUTE] = "3stack-automute",
};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-static int ad1882_parse_auto_config(struct hda_codec *codec)
-{
- struct ad198x_spec *spec;
- int err;
-
- err = alloc_ad_spec(codec);
- if (err < 0)
- return err;
- spec = codec->spec;
-
- spec->gen.mixer_nid = 0x20;
- spec->gen.mixer_merge_nid = 0x21;
- spec->beep_dev_nid = 0x10;
- set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
- err = ad198x_parse_auto_config(codec);
- if (err < 0)
- goto error;
- err = ad1988_add_spdif_mux_ctl(codec);
- if (err < 0)
- goto error;
- return 0;
- error:
- ad198x_free(codec);
- return err;
-}
-#ifdef ENABLE_AD_STATIC_QUIRKS
static int patch_ad1882(struct hda_codec *codec)
{
struct ad198x_spec *spec;
int err, board_config;
- board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
- ad1882_models, NULL);
- if (board_config < 0) {
- printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
- codec->chip_name);
- board_config = AD1882_AUTO;
- }
-
- if (board_config == AD1882_AUTO)
- return ad1882_parse_auto_config(codec);
-
err = alloc_ad_spec(codec);
if (err < 0)
return err;
@@ -5227,6 +5032,8 @@ static int patch_ad1882(struct hda_codec *codec)
codec->patch_ops = ad198x_patch_ops;
/* override some parameters */
+ board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
+ ad1882_models, NULL);
switch (board_config) {
default:
case AD1882_3STACK:
@@ -5256,9 +5063,6 @@ static int patch_ad1882(struct hda_codec *codec)
return 0;
}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1882 ad1882_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
/*
diff --git a/trunk/sound/pci/hda/patch_ca0110.c b/trunk/sound/pci/hda/patch_ca0110.c
index 30b3a4bc06ee..19ae14f739cb 100644
--- a/trunk/sound/pci/hda/patch_ca0110.c
+++ b/trunk/sound/pci/hda/patch_ca0110.c
@@ -19,6 +19,7 @@
*/
#include
+#include
#include
#include
#include
@@ -26,46 +27,502 @@
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_auto_parser.h"
-#include "hda_jack.h"
-#include "hda_generic.h"
+/*
+ */
+
+struct ca0110_spec {
+ struct auto_pin_cfg autocfg;
+ struct hda_multi_out multiout;
+ hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
+ hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
+ hda_nid_t hp_dac;
+ hda_nid_t input_pins[AUTO_PIN_LAST];
+ hda_nid_t adcs[AUTO_PIN_LAST];
+ hda_nid_t dig_out;
+ hda_nid_t dig_in;
+ unsigned int num_inputs;
+ char input_labels[AUTO_PIN_LAST][32];
+ struct hda_pcm pcm_rec[2]; /* PCM information */
+};
+
+/*
+ * PCM callbacks
+ */
+static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0110_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
+}
+
+static int ca0110_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0110_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0110_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0110_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0110_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int ca0110_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0110_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+ format, substream);
+}
+
+/*
+ * Analog capture
+ */
+static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0110_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->adcs[substream->number],
+ stream_tag, 0, format);
+ return 0;
+}
+
+static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0110_spec *spec = codec->spec;
+
+ snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]);
+ return 0;
+}
+
+/*
+ */
+
+static const char * const dirstr[2] = { "Playback", "Capture" };
+
+static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+ int chan, int dir)
+{
+ char namestr[44];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
+ sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+ int chan, int dir)
+{
+ char namestr[44];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
+ sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
+#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0)
+#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1)
+#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1)
+#define add_mono_switch(codec, nid, pfx, chan) \
+ _add_switch(codec, nid, pfx, chan, 0)
+#define add_mono_volume(codec, nid, pfx, chan) \
+ _add_volume(codec, nid, pfx, chan, 0)
+
+static int ca0110_build_controls(struct hda_codec *codec)
+{
+ struct ca0110_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ static const char * const prefix[AUTO_CFG_MAX_OUTS] = {
+ "Front", "Surround", NULL, "Side", "Multi"
+ };
+ hda_nid_t mutenid;
+ int i, err;
+
+ for (i = 0; i < spec->multiout.num_dacs; i++) {
+ if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP)
+ mutenid = spec->out_pins[i];
+ else
+ mutenid = spec->multiout.dac_nids[i];
+ if (!prefix[i]) {
+ err = add_mono_switch(codec, mutenid,
+ "Center", 1);
+ if (err < 0)
+ return err;
+ err = add_mono_switch(codec, mutenid,
+ "LFE", 1);
+ if (err < 0)
+ return err;
+ err = add_mono_volume(codec, spec->multiout.dac_nids[i],
+ "Center", 1);
+ if (err < 0)
+ return err;
+ err = add_mono_volume(codec, spec->multiout.dac_nids[i],
+ "LFE", 1);
+ if (err < 0)
+ return err;
+ } else {
+ err = add_out_switch(codec, mutenid,
+ prefix[i]);
+ if (err < 0)
+ return err;
+ err = add_out_volume(codec, spec->multiout.dac_nids[i],
+ prefix[i]);
+ if (err < 0)
+ return err;
+ }
+ }
+ if (cfg->hp_outs) {
+ if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP)
+ mutenid = cfg->hp_pins[0];
+ else
+ mutenid = spec->multiout.dac_nids[i];
+
+ err = add_out_switch(codec, mutenid, "Headphone");
+ if (err < 0)
+ return err;
+ if (spec->hp_dac) {
+ err = add_out_volume(codec, spec->hp_dac, "Headphone");
+ if (err < 0)
+ return err;
+ }
+ }
+ for (i = 0; i < spec->num_inputs; i++) {
+ const char *label = spec->input_labels[i];
+ if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP)
+ mutenid = spec->input_pins[i];
+ else
+ mutenid = spec->adcs[i];
+ err = add_in_switch(codec, mutenid, label);
+ if (err < 0)
+ return err;
+ err = add_in_volume(codec, spec->adcs[i], label);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->dig_out) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
+ spec->dig_out);
+ if (err < 0)
+ return err;
+ err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
+ }
+ if (spec->dig_in) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+ if (err < 0)
+ return err;
+ err = add_in_volume(codec, spec->dig_in, "IEC958");
+ }
+ return 0;
+}
+
+/*
+ */
+static const struct hda_pcm_stream ca0110_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .ops = {
+ .open = ca0110_playback_pcm_open,
+ .prepare = ca0110_playback_pcm_prepare,
+ .cleanup = ca0110_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream ca0110_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .prepare = ca0110_capture_pcm_prepare,
+ .cleanup = ca0110_capture_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream ca0110_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = ca0110_dig_playback_pcm_open,
+ .close = ca0110_dig_playback_pcm_close,
+ .prepare = ca0110_dig_playback_pcm_prepare
+ },
+};
+
+static const struct hda_pcm_stream ca0110_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+static int ca0110_build_pcms(struct hda_codec *codec)
+{
+ struct ca0110_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+
+ codec->pcm_info = info;
+ codec->num_pcms = 0;
+
+ info->name = "CA0110 Analog";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
+ codec->num_pcms++;
+
+ if (!spec->dig_out && !spec->dig_in)
+ return 0;
+
+ info++;
+ info->name = "CA0110 Digital";
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->dig_out) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ ca0110_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
+ }
+ if (spec->dig_in) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ ca0110_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+ }
+ codec->num_pcms++;
+
+ return 0;
+}
+
+static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
+{
+ if (pin) {
+ snd_hda_set_pin_ctl(codec, pin, PIN_HP);
+ if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ }
+ if (dac)
+ snd_hda_codec_write(codec, dac, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
+}
+
+static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
+{
+ if (pin) {
+ snd_hda_set_pin_ctl(codec, pin, PIN_IN |
+ snd_hda_get_default_vref(codec, pin));
+ if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+ }
+ if (adc)
+ snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+}
+
+static int ca0110_init(struct hda_codec *codec)
+{
+ struct ca0110_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < spec->multiout.num_dacs; i++)
+ init_output(codec, spec->out_pins[i],
+ spec->multiout.dac_nids[i]);
+ init_output(codec, cfg->hp_pins[0], spec->hp_dac);
+ init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
+
+ for (i = 0; i < spec->num_inputs; i++)
+ init_input(codec, spec->input_pins[i], spec->adcs[i]);
+ init_input(codec, cfg->dig_in_pin, spec->dig_in);
+ return 0;
+}
+
+static void ca0110_free(struct hda_codec *codec)
+{
+ kfree(codec->spec);
+}
static const struct hda_codec_ops ca0110_patch_ops = {
- .build_controls = snd_hda_gen_build_controls,
- .build_pcms = snd_hda_gen_build_pcms,
- .init = snd_hda_gen_init,
- .free = snd_hda_gen_free,
- .unsol_event = snd_hda_jack_unsol_event,
+ .build_controls = ca0110_build_controls,
+ .build_pcms = ca0110_build_pcms,
+ .init = ca0110_init,
+ .free = ca0110_free,
};
+
+static void parse_line_outs(struct hda_codec *codec)
+{
+ struct ca0110_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, n;
+ unsigned int def_conf;
+ hda_nid_t nid;
+
+ n = 0;
+ for (i = 0; i < cfg->line_outs; i++) {
+ nid = cfg->line_out_pins[i];
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ if (!def_conf)
+ continue; /* invalid pin */
+ if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1)
+ continue;
+ spec->out_pins[n++] = nid;
+ }
+ spec->multiout.dac_nids = spec->dacs;
+ spec->multiout.num_dacs = n;
+ spec->multiout.max_channels = n * 2;
+}
+
+static void parse_hp_out(struct hda_codec *codec)
+{
+ struct ca0110_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+ unsigned int def_conf;
+ hda_nid_t nid, dac;
+
+ if (!cfg->hp_outs)
+ return;
+ nid = cfg->hp_pins[0];
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ if (!def_conf) {
+ cfg->hp_outs = 0;
+ return;
+ }
+ if (snd_hda_get_connections(codec, nid, &dac, 1) != 1)
+ return;
+
+ for (i = 0; i < cfg->line_outs; i++)
+ if (dac == spec->dacs[i])
+ break;
+ if (i >= cfg->line_outs) {
+ spec->hp_dac = dac;
+ spec->multiout.hp_nid = dac;
+ }
+}
+
+static void parse_input(struct hda_codec *codec)
+{
+ struct ca0110_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid, pin;
+ int n, i, j;
+
+ n = 0;
+ nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int type = get_wcaps_type(wcaps);
+ if (type != AC_WID_AUD_IN)
+ continue;
+ if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)
+ continue;
+ if (pin == cfg->dig_in_pin) {
+ spec->dig_in = nid;
+ continue;
+ }
+ for (j = 0; j < cfg->num_inputs; j++)
+ if (cfg->inputs[j].pin == pin)
+ break;
+ if (j >= cfg->num_inputs)
+ continue;
+ spec->input_pins[n] = pin;
+ snd_hda_get_pin_label(codec, pin, cfg,
+ spec->input_labels[n],
+ sizeof(spec->input_labels[n]), NULL);
+ spec->adcs[n] = nid;
+ n++;
+ }
+ spec->num_inputs = n;
+}
+
+static void parse_digital(struct hda_codec *codec)
+{
+ struct ca0110_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ if (cfg->dig_outs &&
+ snd_hda_get_connections(codec, cfg->dig_out_pins[0],
+ &spec->dig_out, 1) == 1)
+ spec->multiout.dig_out_nid = spec->dig_out;
+}
+
static int ca0110_parse_auto_config(struct hda_codec *codec)
{
- struct hda_gen_spec *spec = codec->spec;
+ struct ca0110_spec *spec = codec->spec;
int err;
- err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0);
- if (err < 0)
- return err;
- err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg);
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
if (err < 0)
return err;
+ parse_line_outs(codec);
+ parse_hp_out(codec);
+ parse_digital(codec);
+ parse_input(codec);
return 0;
}
static int patch_ca0110(struct hda_codec *codec)
{
- struct hda_gen_spec *spec;
+ struct ca0110_spec *spec;
int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
- snd_hda_gen_spec_init(spec);
codec->spec = spec;
- spec->multi_cap_vol = 1;
codec->bus->needs_damn_long_delay = 1;
err = ca0110_parse_auto_config(codec);
@@ -77,7 +534,8 @@ static int patch_ca0110(struct hda_codec *codec)
return 0;
error:
- snd_hda_gen_free(codec);
+ kfree(codec->spec);
+ codec->spec = NULL;
return err;
}
diff --git a/trunk/sound/pci/hda/patch_ca0132.c b/trunk/sound/pci/hda/patch_ca0132.c
index db02c1e96b08..49750a96d649 100644
--- a/trunk/sound/pci/hda/patch_ca0132.c
+++ b/trunk/sound/pci/hda/patch_ca0132.c
@@ -27,445 +27,16 @@
#include
#include
#include
-#include
#include
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_auto_parser.h"
-#include "hda_jack.h"
-
-#include "ca0132_regs.h"
-
-/* Enable this to see controls for tuning purpose. */
-/*#define ENABLE_TUNING_CONTROLS*/
-
-#define FLOAT_ZERO 0x00000000
-#define FLOAT_ONE 0x3f800000
-#define FLOAT_TWO 0x40000000
-#define FLOAT_MINUS_5 0xc0a00000
-
-#define UNSOL_TAG_HP 0x10
-#define UNSOL_TAG_AMIC1 0x12
-#define UNSOL_TAG_DSP 0x16
-
-#define DSP_DMA_WRITE_BUFLEN_INIT (1UL<<18)
-#define DSP_DMA_WRITE_BUFLEN_OVLY (1UL<<15)
-
-#define DMA_TRANSFER_FRAME_SIZE_NWORDS 8
-#define DMA_TRANSFER_MAX_FRAME_SIZE_NWORDS 32
-#define DMA_OVERLAY_FRAME_SIZE_NWORDS 2
-
-#define MASTERCONTROL 0x80
-#define MASTERCONTROL_ALLOC_DMA_CHAN 10
-#define MASTERCONTROL_QUERY_SPEAKER_EQ_ADDRESS 60
#define WIDGET_CHIP_CTRL 0x15
#define WIDGET_DSP_CTRL 0x16
-#define MEM_CONNID_MICIN1 3
-#define MEM_CONNID_MICIN2 5
-#define MEM_CONNID_MICOUT1 12
-#define MEM_CONNID_MICOUT2 14
-#define MEM_CONNID_WUH 10
-#define MEM_CONNID_DSP 16
-#define MEM_CONNID_DMIC 100
-
-#define SCP_SET 0
-#define SCP_GET 1
-
-#define EFX_FILE "ctefx.bin"
-
-#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP
-MODULE_FIRMWARE(EFX_FILE);
-#endif
-
-static char *dirstr[2] = { "Playback", "Capture" };
-
-enum {
- SPEAKER_OUT,
- HEADPHONE_OUT
-};
-
-enum {
- DIGITAL_MIC,
- LINE_MIC_IN
-};
-
-enum {
-#define VNODE_START_NID 0x80
- VNID_SPK = VNODE_START_NID, /* Speaker vnid */
- VNID_MIC,
- VNID_HP_SEL,
- VNID_AMIC1_SEL,
- VNID_HP_ASEL,
- VNID_AMIC1_ASEL,
- VNODE_END_NID,
-#define VNODES_COUNT (VNODE_END_NID - VNODE_START_NID)
-
-#define EFFECT_START_NID 0x90
-#define OUT_EFFECT_START_NID EFFECT_START_NID
- SURROUND = OUT_EFFECT_START_NID,
- CRYSTALIZER,
- DIALOG_PLUS,
- SMART_VOLUME,
- X_BASS,
- EQUALIZER,
- OUT_EFFECT_END_NID,
-#define OUT_EFFECTS_COUNT (OUT_EFFECT_END_NID - OUT_EFFECT_START_NID)
-
-#define IN_EFFECT_START_NID OUT_EFFECT_END_NID
- ECHO_CANCELLATION = IN_EFFECT_START_NID,
- VOICE_FOCUS,
- MIC_SVM,
- NOISE_REDUCTION,
- IN_EFFECT_END_NID,
-#define IN_EFFECTS_COUNT (IN_EFFECT_END_NID - IN_EFFECT_START_NID)
-
- VOICEFX = IN_EFFECT_END_NID,
- PLAY_ENHANCEMENT,
- CRYSTAL_VOICE,
- EFFECT_END_NID
-#define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID)
-};
-
-/* Effects values size*/
-#define EFFECT_VALS_MAX_COUNT 12
-
-struct ct_effect {
- char name[44];
- hda_nid_t nid;
- int mid; /*effect module ID*/
- int reqs[EFFECT_VALS_MAX_COUNT]; /*effect module request*/
- int direct; /* 0:output; 1:input*/
- int params; /* number of default non-on/off params */
- /*effect default values, 1st is on/off. */
- unsigned int def_vals[EFFECT_VALS_MAX_COUNT];
-};
-
-#define EFX_DIR_OUT 0
-#define EFX_DIR_IN 1
-
-static struct ct_effect ca0132_effects[EFFECTS_COUNT] = {
- { .name = "Surround",
- .nid = SURROUND,
- .mid = 0x96,
- .reqs = {0, 1},
- .direct = EFX_DIR_OUT,
- .params = 1,
- .def_vals = {0x3F800000, 0x3F2B851F}
- },
- { .name = "Crystalizer",
- .nid = CRYSTALIZER,
- .mid = 0x96,
- .reqs = {7, 8},
- .direct = EFX_DIR_OUT,
- .params = 1,
- .def_vals = {0x3F800000, 0x3F266666}
- },
- { .name = "Dialog Plus",
- .nid = DIALOG_PLUS,
- .mid = 0x96,
- .reqs = {2, 3},
- .direct = EFX_DIR_OUT,
- .params = 1,
- .def_vals = {0x00000000, 0x3F000000}
- },
- { .name = "Smart Volume",
- .nid = SMART_VOLUME,
- .mid = 0x96,
- .reqs = {4, 5, 6},
- .direct = EFX_DIR_OUT,
- .params = 2,
- .def_vals = {0x3F800000, 0x3F3D70A4, 0x00000000}
- },
- { .name = "X-Bass",
- .nid = X_BASS,
- .mid = 0x96,
- .reqs = {24, 23, 25},
- .direct = EFX_DIR_OUT,
- .params = 2,
- .def_vals = {0x3F800000, 0x42A00000, 0x3F000000}
- },
- { .name = "Equalizer",
- .nid = EQUALIZER,
- .mid = 0x96,
- .reqs = {9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20},
- .direct = EFX_DIR_OUT,
- .params = 11,
- .def_vals = {0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000}
- },
- { .name = "Echo Cancellation",
- .nid = ECHO_CANCELLATION,
- .mid = 0x95,
- .reqs = {0, 1, 2, 3},
- .direct = EFX_DIR_IN,
- .params = 3,
- .def_vals = {0x00000000, 0x3F3A9692, 0x00000000, 0x00000000}
- },
- { .name = "Voice Focus",
- .nid = VOICE_FOCUS,
- .mid = 0x95,
- .reqs = {6, 7, 8, 9},
- .direct = EFX_DIR_IN,
- .params = 3,
- .def_vals = {0x3F800000, 0x3D7DF3B6, 0x41F00000, 0x41F00000}
- },
- { .name = "Mic SVM",
- .nid = MIC_SVM,
- .mid = 0x95,
- .reqs = {44, 45},
- .direct = EFX_DIR_IN,
- .params = 1,
- .def_vals = {0x00000000, 0x3F3D70A4}
- },
- { .name = "Noise Reduction",
- .nid = NOISE_REDUCTION,
- .mid = 0x95,
- .reqs = {4, 5},
- .direct = EFX_DIR_IN,
- .params = 1,
- .def_vals = {0x3F800000, 0x3F000000}
- },
- { .name = "VoiceFX",
- .nid = VOICEFX,
- .mid = 0x95,
- .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18},
- .direct = EFX_DIR_IN,
- .params = 8,
- .def_vals = {0x00000000, 0x43C80000, 0x44AF0000, 0x44FA0000,
- 0x3F800000, 0x3F800000, 0x3F800000, 0x00000000,
- 0x00000000}
- }
-};
-
-/* Tuning controls */
-#ifdef ENABLE_TUNING_CONTROLS
-
-enum {
-#define TUNING_CTL_START_NID 0xC0
- WEDGE_ANGLE = TUNING_CTL_START_NID,
- SVM_LEVEL,
- EQUALIZER_BAND_0,
- EQUALIZER_BAND_1,
- EQUALIZER_BAND_2,
- EQUALIZER_BAND_3,
- EQUALIZER_BAND_4,
- EQUALIZER_BAND_5,
- EQUALIZER_BAND_6,
- EQUALIZER_BAND_7,
- EQUALIZER_BAND_8,
- EQUALIZER_BAND_9,
- TUNING_CTL_END_NID
-#define TUNING_CTLS_COUNT (TUNING_CTL_END_NID - TUNING_CTL_START_NID)
-};
-
-struct ct_tuning_ctl {
- char name[44];
- hda_nid_t parent_nid;
- hda_nid_t nid;
- int mid; /*effect module ID*/
- int req; /*effect module request*/
- int direct; /* 0:output; 1:input*/
- unsigned int def_val;/*effect default values*/
-};
-
-static struct ct_tuning_ctl ca0132_tuning_ctls[] = {
- { .name = "Wedge Angle",
- .parent_nid = VOICE_FOCUS,
- .nid = WEDGE_ANGLE,
- .mid = 0x95,
- .req = 8,
- .direct = EFX_DIR_IN,
- .def_val = 0x41F00000
- },
- { .name = "SVM Level",
- .parent_nid = MIC_SVM,
- .nid = SVM_LEVEL,
- .mid = 0x95,
- .req = 45,
- .direct = EFX_DIR_IN,
- .def_val = 0x3F3D70A4
- },
- { .name = "EQ Band0",
- .parent_nid = EQUALIZER,
- .nid = EQUALIZER_BAND_0,
- .mid = 0x96,
- .req = 11,
- .direct = EFX_DIR_OUT,
- .def_val = 0x00000000
- },
- { .name = "EQ Band1",
- .parent_nid = EQUALIZER,
- .nid = EQUALIZER_BAND_1,
- .mid = 0x96,
- .req = 12,
- .direct = EFX_DIR_OUT,
- .def_val = 0x00000000
- },
- { .name = "EQ Band2",
- .parent_nid = EQUALIZER,
- .nid = EQUALIZER_BAND_2,
- .mid = 0x96,
- .req = 13,
- .direct = EFX_DIR_OUT,
- .def_val = 0x00000000
- },
- { .name = "EQ Band3",
- .parent_nid = EQUALIZER,
- .nid = EQUALIZER_BAND_3,
- .mid = 0x96,
- .req = 14,
- .direct = EFX_DIR_OUT,
- .def_val = 0x00000000
- },
- { .name = "EQ Band4",
- .parent_nid = EQUALIZER,
- .nid = EQUALIZER_BAND_4,
- .mid = 0x96,
- .req = 15,
- .direct = EFX_DIR_OUT,
- .def_val = 0x00000000
- },
- { .name = "EQ Band5",
- .parent_nid = EQUALIZER,
- .nid = EQUALIZER_BAND_5,
- .mid = 0x96,
- .req = 16,
- .direct = EFX_DIR_OUT,
- .def_val = 0x00000000
- },
- { .name = "EQ Band6",
- .parent_nid = EQUALIZER,
- .nid = EQUALIZER_BAND_6,
- .mid = 0x96,
- .req = 17,
- .direct = EFX_DIR_OUT,
- .def_val = 0x00000000
- },
- { .name = "EQ Band7",
- .parent_nid = EQUALIZER,
- .nid = EQUALIZER_BAND_7,
- .mid = 0x96,
- .req = 18,
- .direct = EFX_DIR_OUT,
- .def_val = 0x00000000
- },
- { .name = "EQ Band8",
- .parent_nid = EQUALIZER,
- .nid = EQUALIZER_BAND_8,
- .mid = 0x96,
- .req = 19,
- .direct = EFX_DIR_OUT,
- .def_val = 0x00000000
- },
- { .name = "EQ Band9",
- .parent_nid = EQUALIZER,
- .nid = EQUALIZER_BAND_9,
- .mid = 0x96,
- .req = 20,
- .direct = EFX_DIR_OUT,
- .def_val = 0x00000000
- }
-};
-#endif
-
-/* Voice FX Presets */
-#define VOICEFX_MAX_PARAM_COUNT 9
-
-struct ct_voicefx {
- char *name;
- hda_nid_t nid;
- int mid;
- int reqs[VOICEFX_MAX_PARAM_COUNT]; /*effect module request*/
-};
-
-struct ct_voicefx_preset {
- char *name; /*preset name*/
- unsigned int vals[VOICEFX_MAX_PARAM_COUNT];
-};
-
-static struct ct_voicefx ca0132_voicefx = {
- .name = "VoiceFX Capture Switch",
- .nid = VOICEFX,
- .mid = 0x95,
- .reqs = {10, 11, 12, 13, 14, 15, 16, 17, 18}
-};
-
-static struct ct_voicefx_preset ca0132_voicefx_presets[] = {
- { .name = "Neutral",
- .vals = { 0x00000000, 0x43C80000, 0x44AF0000,
- 0x44FA0000, 0x3F800000, 0x3F800000,
- 0x3F800000, 0x00000000, 0x00000000 }
- },
- { .name = "Female2Male",
- .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
- 0x44FA0000, 0x3F19999A, 0x3F866666,
- 0x3F800000, 0x00000000, 0x00000000 }
- },
- { .name = "Male2Female",
- .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
- 0x450AC000, 0x4017AE14, 0x3F6B851F,
- 0x3F800000, 0x00000000, 0x00000000 }
- },
- { .name = "ScrappyKid",
- .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
- 0x44FA0000, 0x40400000, 0x3F28F5C3,
- 0x3F800000, 0x00000000, 0x00000000 }
- },
- { .name = "Elderly",
- .vals = { 0x3F800000, 0x44324000, 0x44BB8000,
- 0x44E10000, 0x3FB33333, 0x3FB9999A,
- 0x3F800000, 0x3E3A2E43, 0x00000000 }
- },
- { .name = "Orc",
- .vals = { 0x3F800000, 0x43EA0000, 0x44A52000,
- 0x45098000, 0x3F266666, 0x3FC00000,
- 0x3F800000, 0x00000000, 0x00000000 }
- },
- { .name = "Elf",
- .vals = { 0x3F800000, 0x43C70000, 0x44AE6000,
- 0x45193000, 0x3F8E147B, 0x3F75C28F,
- 0x3F800000, 0x00000000, 0x00000000 }
- },
- { .name = "Dwarf",
- .vals = { 0x3F800000, 0x43930000, 0x44BEE000,
- 0x45007000, 0x3F451EB8, 0x3F7851EC,
- 0x3F800000, 0x00000000, 0x00000000 }
- },
- { .name = "AlienBrute",
- .vals = { 0x3F800000, 0x43BFC5AC, 0x44B28FDF,
- 0x451F6000, 0x3F266666, 0x3FA7D945,
- 0x3F800000, 0x3CF5C28F, 0x00000000 }
- },
- { .name = "Robot",
- .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
- 0x44FA0000, 0x3FB2718B, 0x3F800000,
- 0xBC07010E, 0x00000000, 0x00000000 }
- },
- { .name = "Marine",
- .vals = { 0x3F800000, 0x43C20000, 0x44906000,
- 0x44E70000, 0x3F4CCCCD, 0x3F8A3D71,
- 0x3F0A3D71, 0x00000000, 0x00000000 }
- },
- { .name = "Emo",
- .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
- 0x44FA0000, 0x3F800000, 0x3F800000,
- 0x3E4CCCCD, 0x00000000, 0x00000000 }
- },
- { .name = "DeepVoice",
- .vals = { 0x3F800000, 0x43A9C5AC, 0x44AA4FDF,
- 0x44FFC000, 0x3EDBB56F, 0x3F99C4CA,
- 0x3F800000, 0x00000000, 0x00000000 }
- },
- { .name = "Munchkin",
- .vals = { 0x3F800000, 0x43C80000, 0x44AF0000,
- 0x44FA0000, 0x3F800000, 0x3F1A043C,
- 0x3F800000, 0x00000000, 0x00000000 }
- }
-};
+#define WUH_MEM_CONNID 10
+#define DSP_MEM_CONNID 16
enum hda_cmd_vendor_io {
/* for DspIO node */
@@ -491,11 +62,7 @@ enum hda_cmd_vendor_io {
VENDOR_CHIPIO_HIC_POST_READ = 0x702,
VENDOR_CHIPIO_HIC_READ_DATA = 0xF03,
- VENDOR_CHIPIO_8051_DATA_WRITE = 0x707,
- VENDOR_CHIPIO_8051_DATA_READ = 0xF07,
-
VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE = 0x70A,
- VENDOR_CHIPIO_CT_EXTENSIONS_GET = 0xF0A,
VENDOR_CHIPIO_PLL_PMU_WRITE = 0x70C,
VENDOR_CHIPIO_PLL_PMU_READ = 0xF0C,
@@ -503,27 +70,18 @@ enum hda_cmd_vendor_io {
VENDOR_CHIPIO_8051_ADDRESS_HIGH = 0x70E,
VENDOR_CHIPIO_FLAG_SET = 0x70F,
VENDOR_CHIPIO_FLAGS_GET = 0xF0F,
- VENDOR_CHIPIO_PARAM_SET = 0x710,
- VENDOR_CHIPIO_PARAM_GET = 0xF10,
+ VENDOR_CHIPIO_PARAMETER_SET = 0x710,
+ VENDOR_CHIPIO_PARAMETER_GET = 0xF10,
VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET = 0x711,
VENDOR_CHIPIO_PORT_ALLOC_SET = 0x712,
VENDOR_CHIPIO_PORT_ALLOC_GET = 0xF12,
VENDOR_CHIPIO_PORT_FREE_SET = 0x713,
- VENDOR_CHIPIO_PARAM_EX_ID_GET = 0xF17,
- VENDOR_CHIPIO_PARAM_EX_ID_SET = 0x717,
- VENDOR_CHIPIO_PARAM_EX_VALUE_GET = 0xF18,
- VENDOR_CHIPIO_PARAM_EX_VALUE_SET = 0x718,
-
- VENDOR_CHIPIO_DMIC_CTL_SET = 0x788,
- VENDOR_CHIPIO_DMIC_CTL_GET = 0xF88,
- VENDOR_CHIPIO_DMIC_PIN_SET = 0x789,
- VENDOR_CHIPIO_DMIC_PIN_GET = 0xF89,
- VENDOR_CHIPIO_DMIC_MCLK_SET = 0x78A,
- VENDOR_CHIPIO_DMIC_MCLK_GET = 0xF8A,
-
- VENDOR_CHIPIO_EAPD_SEL_SET = 0x78D
+ VENDOR_CHIPIO_PARAMETER_EX_ID_GET = 0xF17,
+ VENDOR_CHIPIO_PARAMETER_EX_ID_SET = 0x717,
+ VENDOR_CHIPIO_PARAMETER_EX_VALUE_GET = 0xF18,
+ VENDOR_CHIPIO_PARAMETER_EX_VALUE_SET = 0x718
};
/*
@@ -573,7 +131,7 @@ enum control_flag_id {
/* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */
CONTROL_FLAG_PORT_A_10KOHM_LOAD = 20,
/* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */
- CONTROL_FLAG_PORT_D_10KOHM_LOAD = 21,
+ CONTROL_FLAG_PORT_D_10K0HM_LOAD = 21,
/* ASI rate is 48kHz/96kHz */
CONTROL_FLAG_ASI_96KHZ = 22,
/* DAC power settings able to control attached ports no/yes */
@@ -587,17 +145,9 @@ enum control_flag_id {
/*
* Control parameter IDs
*/
-enum control_param_id {
- /* 0: None, 1: Mic1In*/
- CONTROL_PARAM_VIP_SOURCE = 1,
+enum control_parameter_id {
/* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */
CONTROL_PARAM_SPDIF1_SOURCE = 2,
- /* Port A output stage gain setting to use when 16 Ohm output
- * impedance is selected*/
- CONTROL_PARAM_PORTA_160OHM_GAIN = 8,
- /* Port D output stage gain setting to use when 16 Ohm output
- * impedance is selected*/
- CONTROL_PARAM_PORTD_160OHM_GAIN = 10,
/* Stream Control */
@@ -675,115 +225,123 @@ enum ca0132_sample_rate {
SR_RATE_UNKNOWN = 0x1F
};
-enum dsp_download_state {
- DSP_DOWNLOAD_FAILED = -1,
- DSP_DOWNLOAD_INIT = 0,
- DSP_DOWNLOADING = 1,
- DSP_DOWNLOADED = 2
+/*
+ * Scp Helper function
+ */
+enum get_set {
+ IS_SET = 0,
+ IS_GET = 1,
};
-/* retrieve parameters from hda format */
-#define get_hdafmt_chs(fmt) (fmt & 0xf)
-#define get_hdafmt_bits(fmt) ((fmt >> 4) & 0x7)
-#define get_hdafmt_rate(fmt) ((fmt >> 8) & 0x7f)
-#define get_hdafmt_type(fmt) ((fmt >> 15) & 0x1)
+/*
+ * Duplicated from ca0110 codec
+ */
+
+static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
+{
+ if (pin) {
+ snd_hda_set_pin_ctl(codec, pin, PIN_HP);
+ if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ }
+ if (dac && (get_wcaps(codec, dac) & AC_WCAP_OUT_AMP))
+ snd_hda_codec_write(codec, dac, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
+}
+
+static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
+{
+ if (pin) {
+ snd_hda_set_pin_ctl(codec, pin, PIN_IN |
+ snd_hda_get_default_vref(codec, pin));
+ if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+ }
+ if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP))
+ snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+}
+
+static char *dirstr[2] = { "Playback", "Capture" };
+
+static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+ int chan, int dir)
+{
+ char namestr[44];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
+ if ((query_amp_caps(codec, nid, type) & AC_AMPCAP_MUTE) == 0) {
+ snd_printdd("Skipping '%s %s Switch' (no mute on node 0x%x)\n", pfx, dirstr[dir], nid);
+ return 0;
+ }
+ sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+ int chan, int dir)
+{
+ char namestr[44];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
+ if ((query_amp_caps(codec, nid, type) & AC_AMPCAP_NUM_STEPS) == 0) {
+ snd_printdd("Skipping '%s %s Volume' (no amp on node 0x%x)\n", pfx, dirstr[dir], nid);
+ return 0;
+ }
+ sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
+#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0)
+#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1)
+#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1)
+#define add_mono_switch(codec, nid, pfx, chan) \
+ _add_switch(codec, nid, pfx, chan, 0)
+#define add_mono_volume(codec, nid, pfx, chan) \
+ _add_volume(codec, nid, pfx, chan, 0)
+#define add_in_mono_switch(codec, nid, pfx, chan) \
+ _add_switch(codec, nid, pfx, chan, 1)
+#define add_in_mono_volume(codec, nid, pfx, chan) \
+ _add_volume(codec, nid, pfx, chan, 1)
+
/*
* CA0132 specific
*/
struct ca0132_spec {
- struct snd_kcontrol_new *mixers[5];
- unsigned int num_mixers;
- const struct hda_verb *base_init_verbs;
- const struct hda_verb *base_exit_verbs;
- const struct hda_verb *init_verbs[5];
- unsigned int num_init_verbs; /* exclude base init verbs */
struct auto_pin_cfg autocfg;
-
- /* Nodes configurations */
struct hda_multi_out multiout;
hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
- unsigned int num_outputs;
+ hda_nid_t hp_dac;
hda_nid_t input_pins[AUTO_PIN_LAST];
hda_nid_t adcs[AUTO_PIN_LAST];
hda_nid_t dig_out;
hda_nid_t dig_in;
unsigned int num_inputs;
- hda_nid_t shared_mic_nid;
- hda_nid_t shared_out_nid;
- struct hda_pcm pcm_rec[5]; /* PCM information */
-
- /* chip access */
- struct mutex chipio_mutex; /* chip access mutex */
- u32 curr_chip_addx;
-
- /* DSP download related */
- enum dsp_download_state dsp_state;
- unsigned int dsp_stream_id;
- unsigned int wait_scp;
- unsigned int wait_scp_header;
- unsigned int wait_num_data;
- unsigned int scp_resp_header;
- unsigned int scp_resp_data[4];
- unsigned int scp_resp_count;
-
- /* mixer and effects related */
- unsigned char dmic_ctl;
- int cur_out_type;
- int cur_mic_type;
- long vnode_lvol[VNODES_COUNT];
- long vnode_rvol[VNODES_COUNT];
- long vnode_lswitch[VNODES_COUNT];
- long vnode_rswitch[VNODES_COUNT];
- long effects_switch[EFFECTS_COUNT];
- long voicefx_val;
- long cur_mic_boost;
-
-#ifdef ENABLE_TUNING_CONTROLS
- long cur_ctl_vals[TUNING_CTLS_COUNT];
-#endif
+ long curr_hp_switch;
+ long curr_hp_volume[2];
+ long curr_speaker_switch;
+ struct mutex chipio_mutex;
+ const char *input_labels[AUTO_PIN_LAST];
+ struct hda_pcm pcm_rec[2]; /* PCM information */
};
-/*
- * CA0132 codec access
- */
-unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid,
- unsigned int verb, unsigned int parm, unsigned int *res)
-{
- unsigned int response;
- response = snd_hda_codec_read(codec, nid, 0, verb, parm);
- *res = response;
-
- return ((response == -1) ? -1 : 0);
-}
-
-static int codec_set_converter_format(struct hda_codec *codec, hda_nid_t nid,
- unsigned short converter_format, unsigned int *res)
-{
- return codec_send_command(codec, nid, VENDOR_CHIPIO_STREAM_FORMAT,
- converter_format & 0xffff, res);
-}
-
-static int codec_set_converter_stream_channel(struct hda_codec *codec,
- hda_nid_t nid, unsigned char stream,
- unsigned char channel, unsigned int *res)
-{
- unsigned char converter_stream_channel = 0;
-
- converter_stream_channel = (stream << 4) | (channel & 0x0f);
- return codec_send_command(codec, nid, AC_VERB_SET_CHANNEL_STREAMID,
- converter_stream_channel, res);
-}
-
/* Chip access helper function */
static int chipio_send(struct hda_codec *codec,
unsigned int reg,
unsigned int data)
{
unsigned int res;
- unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ int retry = 50;
/* send bits of data specified by reg */
do {
@@ -791,9 +349,7 @@ static int chipio_send(struct hda_codec *codec,
reg, data);
if (res == VENDOR_STATUS_CHIPIO_OK)
return 0;
- msleep(20);
- } while (time_before(jiffies, timeout));
-
+ } while (--retry);
return -EIO;
}
@@ -803,12 +359,8 @@ static int chipio_send(struct hda_codec *codec,
static int chipio_write_address(struct hda_codec *codec,
unsigned int chip_addx)
{
- struct ca0132_spec *spec = codec->spec;
int res;
- if (spec->curr_chip_addx == chip_addx)
- return 0;
-
/* send low 16 bits of the address */
res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
chip_addx & 0xffff);
@@ -819,17 +371,15 @@ static int chipio_write_address(struct hda_codec *codec,
chip_addx >> 16);
}
- spec->curr_chip_addx = (res < 0) ? ~0UL : chip_addx;
-
return res;
}
/*
* Write data through the vendor widget -- NOT protected by the Mutex!
*/
+
static int chipio_write_data(struct hda_codec *codec, unsigned int data)
{
- struct ca0132_spec *spec = codec->spec;
int res;
/* send low 16 bits of the data */
@@ -841,40 +391,14 @@ static int chipio_write_data(struct hda_codec *codec, unsigned int data)
data >> 16);
}
- /*If no error encountered, automatically increment the address
- as per chip behaviour*/
- spec->curr_chip_addx = (res != -EIO) ?
- (spec->curr_chip_addx + 4) : ~0UL;
return res;
}
-/*
- * Write multiple data through the vendor widget -- NOT protected by the Mutex!
- */
-static int chipio_write_data_multiple(struct hda_codec *codec,
- const u32 *data,
- unsigned int count)
-{
- int status = 0;
-
- if (data == NULL) {
- snd_printdd(KERN_ERR "chipio_write_data null ptr\n");
- return -EINVAL;
- }
-
- while ((count-- != 0) && (status == 0))
- status = chipio_write_data(codec, *data++);
-
- return status;
-}
-
-
/*
* Read data through the vendor widget -- NOT protected by the Mutex!
*/
static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
{
- struct ca0132_spec *spec = codec->spec;
int res;
/* post read */
@@ -892,10 +416,6 @@ static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
0);
}
- /*If no error encountered, automatically increment the address
- as per chip behaviour*/
- spec->curr_chip_addx = (res != -EIO) ?
- (spec->curr_chip_addx + 4) : ~0UL;
return res;
}
@@ -925,30 +445,6 @@ static int chipio_write(struct hda_codec *codec,
return err;
}
-/*
- * Write multiple values to the given address through the chip I/O widget.
- * protected by the Mutex
- */
-static int chipio_write_multiple(struct hda_codec *codec,
- u32 chip_addx,
- const u32 *data,
- unsigned int count)
-{
- struct ca0132_spec *spec = codec->spec;
- int status;
-
- mutex_lock(&spec->chipio_mutex);
- status = chipio_write_address(codec, chip_addx);
- if (status < 0)
- goto error;
-
- status = chipio_write_data_multiple(codec, data, count);
-error:
- mutex_unlock(&spec->chipio_mutex);
-
- return status;
-}
-
/*
* Read the given address through the chip I/O widget
* protected by the Mutex
@@ -976,3029 +472,91 @@ static int chipio_read(struct hda_codec *codec,
}
/*
- * Set chip control flags through the chip I/O widget.
+ * PCM callbacks
*/
-static void chipio_set_control_flag(struct hda_codec *codec,
- enum control_flag_id flag_id,
- bool flag_state)
+static int ca0132_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
{
- unsigned int val;
- unsigned int flag_bit;
-
- flag_bit = (flag_state ? 1 : 0);
- val = (flag_bit << 7) | (flag_id);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_FLAG_SET, val);
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
}
-/*
- * Set chip parameters through the chip I/O widget.
- */
-static void chipio_set_control_param(struct hda_codec *codec,
- enum control_param_id param_id, int param_val)
+static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
- int val;
-
- if ((param_id < 32) && (param_val < 8)) {
- val = (param_val << 5) | (param_id);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_PARAM_SET, val);
- } else {
- mutex_lock(&spec->chipio_mutex);
- if (chipio_send(codec, VENDOR_CHIPIO_STATUS, 0) == 0) {
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_PARAM_EX_ID_SET,
- param_id);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_PARAM_EX_VALUE_SET,
- param_val);
- }
- mutex_unlock(&spec->chipio_mutex);
- }
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
}
-/*
- * Set sampling rate of the connection point.
- */
-static void chipio_set_conn_rate(struct hda_codec *codec,
- int connid, enum ca0132_sample_rate rate)
+static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
{
- chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_ID, connid);
- chipio_set_control_param(codec, CONTROL_PARAM_CONN_POINT_SAMPLE_RATE,
- rate);
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
}
/*
- * Enable clocks.
+ * Digital out
*/
-static void chipio_enable_clocks(struct hda_codec *codec)
+static int ca0132_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
-
- mutex_lock(&spec->chipio_mutex);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_LOW, 0);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_LOW, 5);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_PLL_PMU_WRITE, 0x0b);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_LOW, 6);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff);
- mutex_unlock(&spec->chipio_mutex);
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
}
-/*
- * CA0132 DSP IO stuffs
- */
-static int dspio_send(struct hda_codec *codec, unsigned int reg,
- unsigned int data)
+static int ca0132_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
{
- int res;
- unsigned long timeout = jiffies + msecs_to_jiffies(1000);
-
- /* send bits of data specified by reg to dsp */
- do {
- res = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0, reg, data);
- if ((res >= 0) && (res != VENDOR_STATUS_DSPIO_BUSY))
- return res;
- msleep(20);
- } while (time_before(jiffies, timeout));
-
- return -EIO;
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
}
-/*
- * Wait for DSP to be ready for commands
- */
-static void dspio_write_wait(struct hda_codec *codec)
+static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
{
- int status;
- unsigned long timeout = jiffies + msecs_to_jiffies(1000);
-
- do {
- status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
- VENDOR_DSPIO_STATUS, 0);
- if ((status == VENDOR_STATUS_DSPIO_OK) ||
- (status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY))
- break;
- msleep(1);
- } while (time_before(jiffies, timeout));
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
}
-/*
- * Write SCP data to DSP
- */
-static int dspio_write(struct hda_codec *codec, unsigned int scp_data)
+static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
- int status;
-
- dspio_write_wait(codec);
-
- mutex_lock(&spec->chipio_mutex);
- status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_LOW,
- scp_data & 0xffff);
- if (status < 0)
- goto error;
-
- status = dspio_send(codec, VENDOR_DSPIO_SCP_WRITE_DATA_HIGH,
- scp_data >> 16);
- if (status < 0)
- goto error;
-
- /* OK, now check if the write itself has executed*/
- status = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
- VENDOR_DSPIO_STATUS, 0);
-error:
- mutex_unlock(&spec->chipio_mutex);
-
- return (status == VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL) ?
- -EIO : 0;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
/*
- * Write multiple SCP data to DSP
*/
-static int dspio_write_multiple(struct hda_codec *codec,
- unsigned int *buffer, unsigned int size)
-{
- int status = 0;
- unsigned int count;
-
- if ((buffer == NULL))
- return -EINVAL;
-
- count = 0;
- while (count < size) {
- status = dspio_write(codec, *buffer++);
- if (status != 0)
- break;
- count++;
- }
-
- return status;
-}
-
-static int dspio_read(struct hda_codec *codec, unsigned int *data)
-{
- int status;
-
- status = dspio_send(codec, VENDOR_DSPIO_SCP_POST_READ_DATA, 0);
- if (status == -EIO)
- return status;
-
- status = dspio_send(codec, VENDOR_DSPIO_STATUS, 0);
- if (status == -EIO ||
- status == VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY)
- return -EIO;
-
- *data = snd_hda_codec_read(codec, WIDGET_DSP_CTRL, 0,
- VENDOR_DSPIO_SCP_READ_DATA, 0);
-
- return 0;
-}
-
-static int dspio_read_multiple(struct hda_codec *codec, unsigned int *buffer,
- unsigned int *buf_size, unsigned int size_count)
-{
- int status = 0;
- unsigned int size = *buf_size;
- unsigned int count;
- unsigned int skip_count;
- unsigned int dummy;
-
- if ((buffer == NULL))
- return -1;
-
- count = 0;
- while (count < size && count < size_count) {
- status = dspio_read(codec, buffer++);
- if (status != 0)
- break;
- count++;
- }
-
- skip_count = count;
- if (status == 0) {
- while (skip_count < size) {
- status = dspio_read(codec, &dummy);
- if (status != 0)
- break;
- skip_count++;
- }
- }
- *buf_size = count;
-
- return status;
-}
-
-/*
- * Construct the SCP header using corresponding fields
- */
-static inline unsigned int
-make_scp_header(unsigned int target_id, unsigned int source_id,
- unsigned int get_flag, unsigned int req,
- unsigned int device_flag, unsigned int resp_flag,
- unsigned int error_flag, unsigned int data_size)
-{
- unsigned int header = 0;
-
- header = (data_size & 0x1f) << 27;
- header |= (error_flag & 0x01) << 26;
- header |= (resp_flag & 0x01) << 25;
- header |= (device_flag & 0x01) << 24;
- header |= (req & 0x7f) << 17;
- header |= (get_flag & 0x01) << 16;
- header |= (source_id & 0xff) << 8;
- header |= target_id & 0xff;
-
- return header;
-}
-
-/*
- * Extract corresponding fields from SCP header
- */
-static inline void
-extract_scp_header(unsigned int header,
- unsigned int *target_id, unsigned int *source_id,
- unsigned int *get_flag, unsigned int *req,
- unsigned int *device_flag, unsigned int *resp_flag,
- unsigned int *error_flag, unsigned int *data_size)
-{
- if (data_size)
- *data_size = (header >> 27) & 0x1f;
- if (error_flag)
- *error_flag = (header >> 26) & 0x01;
- if (resp_flag)
- *resp_flag = (header >> 25) & 0x01;
- if (device_flag)
- *device_flag = (header >> 24) & 0x01;
- if (req)
- *req = (header >> 17) & 0x7f;
- if (get_flag)
- *get_flag = (header >> 16) & 0x01;
- if (source_id)
- *source_id = (header >> 8) & 0xff;
- if (target_id)
- *target_id = header & 0xff;
-}
-
-#define SCP_MAX_DATA_WORDS (16)
-
-/* Structure to contain any SCP message */
-struct scp_msg {
- unsigned int hdr;
- unsigned int data[SCP_MAX_DATA_WORDS];
-};
-
-static void dspio_clear_response_queue(struct hda_codec *codec)
-{
- unsigned int dummy = 0;
- int status = -1;
-
- /* clear all from the response queue */
- do {
- status = dspio_read(codec, &dummy);
- } while (status == 0);
-}
-
-static int dspio_get_response_data(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- unsigned int data = 0;
- unsigned int count;
-
- if (dspio_read(codec, &data) < 0)
- return -EIO;
-
- if ((data & 0x00ffffff) == spec->wait_scp_header) {
- spec->scp_resp_header = data;
- spec->scp_resp_count = data >> 27;
- count = spec->wait_num_data;
- dspio_read_multiple(codec, spec->scp_resp_data,
- &spec->scp_resp_count, count);
- return 0;
- }
-
- return -EIO;
-}
-
-/*
- * Send SCP message to DSP
- */
-static int dspio_send_scp_message(struct hda_codec *codec,
- unsigned char *send_buf,
- unsigned int send_buf_size,
- unsigned char *return_buf,
- unsigned int return_buf_size,
- unsigned int *bytes_returned)
-{
- struct ca0132_spec *spec = codec->spec;
- int status = -1;
- unsigned int scp_send_size = 0;
- unsigned int total_size;
- bool waiting_for_resp = false;
- unsigned int header;
- struct scp_msg *ret_msg;
- unsigned int resp_src_id, resp_target_id;
- unsigned int data_size, src_id, target_id, get_flag, device_flag;
-
- if (bytes_returned)
- *bytes_returned = 0;
-
- /* get scp header from buffer */
- header = *((unsigned int *)send_buf);
- extract_scp_header(header, &target_id, &src_id, &get_flag, NULL,
- &device_flag, NULL, NULL, &data_size);
- scp_send_size = data_size + 1;
- total_size = (scp_send_size * 4);
-
- if (send_buf_size < total_size)
- return -EINVAL;
-
- if (get_flag || device_flag) {
- if (!return_buf || return_buf_size < 4 || !bytes_returned)
- return -EINVAL;
-
- spec->wait_scp_header = *((unsigned int *)send_buf);
-
- /* swap source id with target id */
- resp_target_id = src_id;
- resp_src_id = target_id;
- spec->wait_scp_header &= 0xffff0000;
- spec->wait_scp_header |= (resp_src_id << 8) | (resp_target_id);
- spec->wait_num_data = return_buf_size/sizeof(unsigned int) - 1;
- spec->wait_scp = 1;
- waiting_for_resp = true;
- }
-
- status = dspio_write_multiple(codec, (unsigned int *)send_buf,
- scp_send_size);
- if (status < 0) {
- spec->wait_scp = 0;
- return status;
- }
-
- if (waiting_for_resp) {
- unsigned long timeout = jiffies + msecs_to_jiffies(1000);
- memset(return_buf, 0, return_buf_size);
- do {
- msleep(20);
- } while (spec->wait_scp && time_before(jiffies, timeout));
- waiting_for_resp = false;
- if (!spec->wait_scp) {
- ret_msg = (struct scp_msg *)return_buf;
- memcpy(&ret_msg->hdr, &spec->scp_resp_header, 4);
- memcpy(&ret_msg->data, spec->scp_resp_data,
- spec->wait_num_data);
- *bytes_returned = (spec->scp_resp_count + 1) * 4;
- status = 0;
- } else {
- status = -EIO;
- }
- spec->wait_scp = 0;
- }
-
- return status;
-}
-
-/**
- * Prepare and send the SCP message to DSP
- * @codec: the HDA codec
- * @mod_id: ID of the DSP module to send the command
- * @req: ID of request to send to the DSP module
- * @dir: SET or GET
- * @data: pointer to the data to send with the request, request specific
- * @len: length of the data, in bytes
- * @reply: point to the buffer to hold data returned for a reply
- * @reply_len: length of the reply buffer returned from GET
- *
- * Returns zero or a negative error code.
- */
-static int dspio_scp(struct hda_codec *codec,
- int mod_id, int req, int dir, void *data, unsigned int len,
- void *reply, unsigned int *reply_len)
-{
- int status = 0;
- struct scp_msg scp_send, scp_reply;
- unsigned int ret_bytes, send_size, ret_size;
- unsigned int send_get_flag, reply_resp_flag, reply_error_flag;
- unsigned int reply_data_size;
-
- memset(&scp_send, 0, sizeof(scp_send));
- memset(&scp_reply, 0, sizeof(scp_reply));
-
- if ((len != 0 && data == NULL) || (len > SCP_MAX_DATA_WORDS))
- return -EINVAL;
-
- if (dir == SCP_GET && reply == NULL) {
- snd_printdd(KERN_ERR "dspio_scp get but has no buffer\n");
- return -EINVAL;
- }
-
- if (reply != NULL && (reply_len == NULL || (*reply_len == 0))) {
- snd_printdd(KERN_ERR "dspio_scp bad resp buf len parms\n");
- return -EINVAL;
- }
-
- scp_send.hdr = make_scp_header(mod_id, 0x20, (dir == SCP_GET), req,
- 0, 0, 0, len/sizeof(unsigned int));
- if (data != NULL && len > 0) {
- len = min((unsigned int)(sizeof(scp_send.data)), len);
- memcpy(scp_send.data, data, len);
- }
-
- ret_bytes = 0;
- send_size = sizeof(unsigned int) + len;
- status = dspio_send_scp_message(codec, (unsigned char *)&scp_send,
- send_size, (unsigned char *)&scp_reply,
- sizeof(scp_reply), &ret_bytes);
-
- if (status < 0) {
- snd_printdd(KERN_ERR "dspio_scp: send scp msg failed\n");
- return status;
- }
-
- /* extract send and reply headers members */
- extract_scp_header(scp_send.hdr, NULL, NULL, &send_get_flag,
- NULL, NULL, NULL, NULL, NULL);
- extract_scp_header(scp_reply.hdr, NULL, NULL, NULL, NULL, NULL,
- &reply_resp_flag, &reply_error_flag,
- &reply_data_size);
-
- if (!send_get_flag)
- return 0;
-
- if (reply_resp_flag && !reply_error_flag) {
- ret_size = (ret_bytes - sizeof(scp_reply.hdr))
- / sizeof(unsigned int);
-
- if (*reply_len < ret_size*sizeof(unsigned int)) {
- snd_printdd(KERN_ERR "reply too long for buf\n");
- return -EINVAL;
- } else if (ret_size != reply_data_size) {
- snd_printdd(KERN_ERR "RetLen and HdrLen .NE.\n");
- return -EINVAL;
- } else {
- *reply_len = ret_size*sizeof(unsigned int);
- memcpy(reply, scp_reply.data, *reply_len);
- }
- } else {
- snd_printdd(KERN_ERR "reply ill-formed or errflag set\n");
- return -EIO;
- }
-
- return status;
-}
-
-/*
- * Set DSP parameters
- */
-static int dspio_set_param(struct hda_codec *codec, int mod_id,
- int req, void *data, unsigned int len)
-{
- return dspio_scp(codec, mod_id, req, SCP_SET, data, len, NULL, NULL);
-}
-
-static int dspio_set_uint_param(struct hda_codec *codec, int mod_id,
- int req, unsigned int data)
-{
- return dspio_set_param(codec, mod_id, req, &data, sizeof(unsigned int));
-}
-
-/*
- * Allocate a DSP DMA channel via an SCP message
- */
-static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan)
-{
- int status = 0;
- unsigned int size = sizeof(dma_chan);
-
- snd_printdd(KERN_INFO " dspio_alloc_dma_chan() -- begin\n");
- status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN,
- SCP_GET, NULL, 0, dma_chan, &size);
-
- if (status < 0) {
- snd_printdd(KERN_INFO "dspio_alloc_dma_chan: SCP Failed\n");
- return status;
- }
-
- if ((*dma_chan + 1) == 0) {
- snd_printdd(KERN_INFO "no free dma channels to allocate\n");
- return -EBUSY;
- }
-
- snd_printdd("dspio_alloc_dma_chan: chan=%d\n", *dma_chan);
- snd_printdd(KERN_INFO " dspio_alloc_dma_chan() -- complete\n");
-
- return status;
-}
-
-/*
- * Free a DSP DMA via an SCP message
- */
-static int dspio_free_dma_chan(struct hda_codec *codec, unsigned int dma_chan)
-{
- int status = 0;
- unsigned int dummy = 0;
-
- snd_printdd(KERN_INFO " dspio_free_dma_chan() -- begin\n");
- snd_printdd("dspio_free_dma_chan: chan=%d\n", dma_chan);
-
- status = dspio_scp(codec, MASTERCONTROL, MASTERCONTROL_ALLOC_DMA_CHAN,
- SCP_SET, &dma_chan, sizeof(dma_chan), NULL, &dummy);
-
- if (status < 0) {
- snd_printdd(KERN_INFO "dspio_free_dma_chan: SCP Failed\n");
- return status;
- }
-
- snd_printdd(KERN_INFO " dspio_free_dma_chan() -- complete\n");
-
- return status;
-}
-
-/*
- * (Re)start the DSP
- */
-static int dsp_set_run_state(struct hda_codec *codec)
-{
- unsigned int dbg_ctrl_reg;
- unsigned int halt_state;
- int err;
-
- err = chipio_read(codec, DSP_DBGCNTL_INST_OFFSET, &dbg_ctrl_reg);
- if (err < 0)
- return err;
-
- halt_state = (dbg_ctrl_reg & DSP_DBGCNTL_STATE_MASK) >>
- DSP_DBGCNTL_STATE_LOBIT;
-
- if (halt_state != 0) {
- dbg_ctrl_reg &= ~((halt_state << DSP_DBGCNTL_SS_LOBIT) &
- DSP_DBGCNTL_SS_MASK);
- err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET,
- dbg_ctrl_reg);
- if (err < 0)
- return err;
-
- dbg_ctrl_reg |= (halt_state << DSP_DBGCNTL_EXEC_LOBIT) &
- DSP_DBGCNTL_EXEC_MASK;
- err = chipio_write(codec, DSP_DBGCNTL_INST_OFFSET,
- dbg_ctrl_reg);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-/*
- * Reset the DSP
- */
-static int dsp_reset(struct hda_codec *codec)
-{
- unsigned int res;
- int retry = 20;
-
- snd_printdd("dsp_reset\n");
- do {
- res = dspio_send(codec, VENDOR_DSPIO_DSP_INIT, 0);
- retry--;
- } while (res == -EIO && retry);
-
- if (!retry) {
- snd_printdd("dsp_reset timeout\n");
- return -EIO;
- }
-
- return 0;
-}
-
-/*
- * Convert chip address to DSP address
- */
-static unsigned int dsp_chip_to_dsp_addx(unsigned int chip_addx,
- bool *code, bool *yram)
-{
- *code = *yram = false;
-
- if (UC_RANGE(chip_addx, 1)) {
- *code = true;
- return UC_OFF(chip_addx);
- } else if (X_RANGE_ALL(chip_addx, 1)) {
- return X_OFF(chip_addx);
- } else if (Y_RANGE_ALL(chip_addx, 1)) {
- *yram = true;
- return Y_OFF(chip_addx);
- }
-
- return INVALID_CHIP_ADDRESS;
-}
-
-/*
- * Check if the DSP DMA is active
- */
-static bool dsp_is_dma_active(struct hda_codec *codec, unsigned int dma_chan)
-{
- unsigned int dma_chnlstart_reg;
-
- chipio_read(codec, DSPDMAC_CHNLSTART_INST_OFFSET, &dma_chnlstart_reg);
-
- return ((dma_chnlstart_reg & (1 <<
- (DSPDMAC_CHNLSTART_EN_LOBIT + dma_chan))) != 0);
-}
-
-static int dsp_dma_setup_common(struct hda_codec *codec,
- unsigned int chip_addx,
- unsigned int dma_chan,
- unsigned int port_map_mask,
- bool ovly)
-{
- int status = 0;
- unsigned int chnl_prop;
- unsigned int dsp_addx;
- unsigned int active;
- bool code, yram;
-
- snd_printdd(KERN_INFO "-- dsp_dma_setup_common() -- Begin ---------\n");
-
- if (dma_chan >= DSPDMAC_DMA_CFG_CHANNEL_COUNT) {
- snd_printdd(KERN_ERR "dma chan num invalid\n");
- return -EINVAL;
- }
-
- if (dsp_is_dma_active(codec, dma_chan)) {
- snd_printdd(KERN_ERR "dma already active\n");
- return -EBUSY;
- }
-
- dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram);
-
- if (dsp_addx == INVALID_CHIP_ADDRESS) {
- snd_printdd(KERN_ERR "invalid chip addr\n");
- return -ENXIO;
- }
-
- chnl_prop = DSPDMAC_CHNLPROP_AC_MASK;
- active = 0;
-
- snd_printdd(KERN_INFO " dsp_dma_setup_common() start reg pgm\n");
-
- if (ovly) {
- status = chipio_read(codec, DSPDMAC_CHNLPROP_INST_OFFSET,
- &chnl_prop);
-
- if (status < 0) {
- snd_printdd(KERN_ERR "read CHNLPROP Reg fail\n");
- return status;
- }
- snd_printdd(KERN_INFO "dsp_dma_setup_common() Read CHNLPROP\n");
- }
-
- if (!code)
- chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan));
- else
- chnl_prop |= (1 << (DSPDMAC_CHNLPROP_MSPCE_LOBIT + dma_chan));
-
- chnl_prop &= ~(1 << (DSPDMAC_CHNLPROP_DCON_LOBIT + dma_chan));
-
- status = chipio_write(codec, DSPDMAC_CHNLPROP_INST_OFFSET, chnl_prop);
- if (status < 0) {
- snd_printdd(KERN_ERR "write CHNLPROP Reg fail\n");
- return status;
- }
- snd_printdd(KERN_INFO " dsp_dma_setup_common() Write CHNLPROP\n");
-
- if (ovly) {
- status = chipio_read(codec, DSPDMAC_ACTIVE_INST_OFFSET,
- &active);
-
- if (status < 0) {
- snd_printdd(KERN_ERR "read ACTIVE Reg fail\n");
- return status;
- }
- snd_printdd(KERN_INFO "dsp_dma_setup_common() Read ACTIVE\n");
- }
-
- active &= (~(1 << (DSPDMAC_ACTIVE_AAR_LOBIT + dma_chan))) &
- DSPDMAC_ACTIVE_AAR_MASK;
-
- status = chipio_write(codec, DSPDMAC_ACTIVE_INST_OFFSET, active);
- if (status < 0) {
- snd_printdd(KERN_ERR "write ACTIVE Reg fail\n");
- return status;
- }
-
- snd_printdd(KERN_INFO " dsp_dma_setup_common() Write ACTIVE\n");
-
- status = chipio_write(codec, DSPDMAC_AUDCHSEL_INST_OFFSET(dma_chan),
- port_map_mask);
- if (status < 0) {
- snd_printdd(KERN_ERR "write AUDCHSEL Reg fail\n");
- return status;
- }
- snd_printdd(KERN_INFO " dsp_dma_setup_common() Write AUDCHSEL\n");
-
- status = chipio_write(codec, DSPDMAC_IRQCNT_INST_OFFSET(dma_chan),
- DSPDMAC_IRQCNT_BICNT_MASK | DSPDMAC_IRQCNT_CICNT_MASK);
- if (status < 0) {
- snd_printdd(KERN_ERR "write IRQCNT Reg fail\n");
- return status;
- }
- snd_printdd(KERN_INFO " dsp_dma_setup_common() Write IRQCNT\n");
-
- snd_printdd(
- "ChipA=0x%x,DspA=0x%x,dmaCh=%u, "
- "CHSEL=0x%x,CHPROP=0x%x,Active=0x%x\n",
- chip_addx, dsp_addx, dma_chan,
- port_map_mask, chnl_prop, active);
-
- snd_printdd(KERN_INFO "-- dsp_dma_setup_common() -- Complete ------\n");
-
- return 0;
-}
-
-/*
- * Setup the DSP DMA per-transfer-specific registers
- */
-static int dsp_dma_setup(struct hda_codec *codec,
- unsigned int chip_addx,
- unsigned int count,
- unsigned int dma_chan)
-{
- int status = 0;
- bool code, yram;
- unsigned int dsp_addx;
- unsigned int addr_field;
- unsigned int incr_field;
- unsigned int base_cnt;
- unsigned int cur_cnt;
- unsigned int dma_cfg = 0;
- unsigned int adr_ofs = 0;
- unsigned int xfr_cnt = 0;
- const unsigned int max_dma_count = 1 << (DSPDMAC_XFRCNT_BCNT_HIBIT -
- DSPDMAC_XFRCNT_BCNT_LOBIT + 1);
-
- snd_printdd(KERN_INFO "-- dsp_dma_setup() -- Begin ---------\n");
-
- if (count > max_dma_count) {
- snd_printdd(KERN_ERR "count too big\n");
- return -EINVAL;
- }
-
- dsp_addx = dsp_chip_to_dsp_addx(chip_addx, &code, &yram);
- if (dsp_addx == INVALID_CHIP_ADDRESS) {
- snd_printdd(KERN_ERR "invalid chip addr\n");
- return -ENXIO;
- }
-
- snd_printdd(KERN_INFO " dsp_dma_setup() start reg pgm\n");
-
- addr_field = dsp_addx << DSPDMAC_DMACFG_DBADR_LOBIT;
- incr_field = 0;
-
- if (!code) {
- addr_field <<= 1;
- if (yram)
- addr_field |= (1 << DSPDMAC_DMACFG_DBADR_LOBIT);
-
- incr_field = (1 << DSPDMAC_DMACFG_AINCR_LOBIT);
- }
-
- dma_cfg = addr_field + incr_field;
- status = chipio_write(codec, DSPDMAC_DMACFG_INST_OFFSET(dma_chan),
- dma_cfg);
- if (status < 0) {
- snd_printdd(KERN_ERR "write DMACFG Reg fail\n");
- return status;
- }
- snd_printdd(KERN_INFO " dsp_dma_setup() Write DMACFG\n");
-
- adr_ofs = (count - 1) << (DSPDMAC_DSPADROFS_BOFS_LOBIT +
- (code ? 0 : 1));
-
- status = chipio_write(codec, DSPDMAC_DSPADROFS_INST_OFFSET(dma_chan),
- adr_ofs);
- if (status < 0) {
- snd_printdd(KERN_ERR "write DSPADROFS Reg fail\n");
- return status;
- }
- snd_printdd(KERN_INFO " dsp_dma_setup() Write DSPADROFS\n");
-
- base_cnt = (count - 1) << DSPDMAC_XFRCNT_BCNT_LOBIT;
-
- cur_cnt = (count - 1) << DSPDMAC_XFRCNT_CCNT_LOBIT;
-
- xfr_cnt = base_cnt | cur_cnt;
-
- status = chipio_write(codec,
- DSPDMAC_XFRCNT_INST_OFFSET(dma_chan), xfr_cnt);
- if (status < 0) {
- snd_printdd(KERN_ERR "write XFRCNT Reg fail\n");
- return status;
- }
- snd_printdd(KERN_INFO " dsp_dma_setup() Write XFRCNT\n");
-
- snd_printdd(
- "ChipA=0x%x, cnt=0x%x, DMACFG=0x%x, "
- "ADROFS=0x%x, XFRCNT=0x%x\n",
- chip_addx, count, dma_cfg, adr_ofs, xfr_cnt);
-
- snd_printdd(KERN_INFO "-- dsp_dma_setup() -- Complete ---------\n");
-
- return 0;
-}
-
-/*
- * Start the DSP DMA
- */
-static int dsp_dma_start(struct hda_codec *codec,
- unsigned int dma_chan, bool ovly)
-{
- unsigned int reg = 0;
- int status = 0;
-
- snd_printdd(KERN_INFO "-- dsp_dma_start() -- Begin ---------\n");
-
- if (ovly) {
- status = chipio_read(codec,
- DSPDMAC_CHNLSTART_INST_OFFSET, ®);
-
- if (status < 0) {
- snd_printdd(KERN_ERR "read CHNLSTART reg fail\n");
- return status;
- }
- snd_printdd(KERN_INFO "-- dsp_dma_start() Read CHNLSTART\n");
-
- reg &= ~(DSPDMAC_CHNLSTART_EN_MASK |
- DSPDMAC_CHNLSTART_DIS_MASK);
- }
-
- status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET,
- reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_EN_LOBIT)));
- if (status < 0) {
- snd_printdd(KERN_ERR "write CHNLSTART reg fail\n");
- return status;
- }
- snd_printdd(KERN_INFO "-- dsp_dma_start() -- Complete ---------\n");
-
- return status;
-}
-
-/*
- * Stop the DSP DMA
- */
-static int dsp_dma_stop(struct hda_codec *codec,
- unsigned int dma_chan, bool ovly)
-{
- unsigned int reg = 0;
- int status = 0;
-
- snd_printdd(KERN_INFO "-- dsp_dma_stop() -- Begin ---------\n");
-
- if (ovly) {
- status = chipio_read(codec,
- DSPDMAC_CHNLSTART_INST_OFFSET, ®);
-
- if (status < 0) {
- snd_printdd(KERN_ERR "read CHNLSTART reg fail\n");
- return status;
- }
- snd_printdd(KERN_INFO "-- dsp_dma_stop() Read CHNLSTART\n");
- reg &= ~(DSPDMAC_CHNLSTART_EN_MASK |
- DSPDMAC_CHNLSTART_DIS_MASK);
- }
-
- status = chipio_write(codec, DSPDMAC_CHNLSTART_INST_OFFSET,
- reg | (1 << (dma_chan + DSPDMAC_CHNLSTART_DIS_LOBIT)));
- if (status < 0) {
- snd_printdd(KERN_ERR "write CHNLSTART reg fail\n");
- return status;
- }
- snd_printdd(KERN_INFO "-- dsp_dma_stop() -- Complete ---------\n");
-
- return status;
-}
-
-/**
- * Allocate router ports
- *
- * @codec: the HDA codec
- * @num_chans: number of channels in the stream
- * @ports_per_channel: number of ports per channel
- * @start_device: start device
- * @port_map: pointer to the port list to hold the allocated ports
- *
- * Returns zero or a negative error code.
- */
-static int dsp_allocate_router_ports(struct hda_codec *codec,
- unsigned int num_chans,
- unsigned int ports_per_channel,
- unsigned int start_device,
- unsigned int *port_map)
-{
- int status = 0;
- int res;
- u8 val;
-
- status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
- if (status < 0)
- return status;
-
- val = start_device << 6;
- val |= (ports_per_channel - 1) << 4;
- val |= num_chans - 1;
-
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET,
- val);
-
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_PORT_ALLOC_SET,
- MEM_CONNID_DSP);
-
- status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
- if (status < 0)
- return status;
-
- res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_PORT_ALLOC_GET, 0);
-
- *port_map = res;
-
- return (res < 0) ? res : 0;
-}
-
-/*
- * Free router ports
- */
-static int dsp_free_router_ports(struct hda_codec *codec)
-{
- int status = 0;
-
- status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
- if (status < 0)
- return status;
-
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_PORT_FREE_SET,
- MEM_CONNID_DSP);
-
- status = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
-
- return status;
-}
-
-/*
- * Allocate DSP ports for the download stream
- */
-static int dsp_allocate_ports(struct hda_codec *codec,
- unsigned int num_chans,
- unsigned int rate_multi, unsigned int *port_map)
-{
- int status;
-
- snd_printdd(KERN_INFO " dsp_allocate_ports() -- begin\n");
-
- if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) {
- snd_printdd(KERN_ERR "bad rate multiple\n");
- return -EINVAL;
- }
-
- status = dsp_allocate_router_ports(codec, num_chans,
- rate_multi, 0, port_map);
-
- snd_printdd(KERN_INFO " dsp_allocate_ports() -- complete\n");
-
- return status;
-}
-
-static int dsp_allocate_ports_format(struct hda_codec *codec,
- const unsigned short fmt,
- unsigned int *port_map)
-{
- int status;
- unsigned int num_chans;
-
- unsigned int sample_rate_div = ((get_hdafmt_rate(fmt) >> 0) & 3) + 1;
- unsigned int sample_rate_mul = ((get_hdafmt_rate(fmt) >> 3) & 3) + 1;
- unsigned int rate_multi = sample_rate_mul / sample_rate_div;
-
- if ((rate_multi != 1) && (rate_multi != 2) && (rate_multi != 4)) {
- snd_printdd(KERN_ERR "bad rate multiple\n");
- return -EINVAL;
- }
-
- num_chans = get_hdafmt_chs(fmt) + 1;
-
- status = dsp_allocate_ports(codec, num_chans, rate_multi, port_map);
-
- return status;
-}
-
-/*
- * free DSP ports
- */
-static int dsp_free_ports(struct hda_codec *codec)
-{
- int status;
-
- snd_printdd(KERN_INFO " dsp_free_ports() -- begin\n");
-
- status = dsp_free_router_ports(codec);
- if (status < 0) {
- snd_printdd(KERN_ERR "free router ports fail\n");
- return status;
- }
- snd_printdd(KERN_INFO " dsp_free_ports() -- complete\n");
-
- return status;
-}
-
-/*
- * HDA DMA engine stuffs for DSP code download
- */
-struct dma_engine {
- struct hda_codec *codec;
- unsigned short m_converter_format;
- struct snd_dma_buffer *dmab;
- unsigned int buf_size;
-};
-
-
-enum dma_state {
- DMA_STATE_STOP = 0,
- DMA_STATE_RUN = 1
-};
-
-static int dma_convert_to_hda_format(
- unsigned int sample_rate,
- unsigned short channels,
- unsigned short *hda_format)
-{
- unsigned int format_val;
-
- format_val = snd_hda_calc_stream_format(
- sample_rate,
- channels,
- SNDRV_PCM_FORMAT_S32_LE,
- 32, 0);
-
- if (hda_format)
- *hda_format = (unsigned short)format_val;
-
- return 0;
-}
-
-/*
- * Reset DMA for DSP download
- */
-static int dma_reset(struct dma_engine *dma)
-{
- struct hda_codec *codec = dma->codec;
- struct ca0132_spec *spec = codec->spec;
- int status;
-
- if (dma->dmab->area)
- snd_hda_codec_load_dsp_cleanup(codec, dma->dmab);
-
- status = snd_hda_codec_load_dsp_prepare(codec,
- dma->m_converter_format,
- dma->buf_size,
- dma->dmab);
- if (status < 0)
- return status;
- spec->dsp_stream_id = status;
- return 0;
-}
-
-static int dma_set_state(struct dma_engine *dma, enum dma_state state)
-{
- bool cmd;
-
- snd_printdd("dma_set_state state=%d\n", state);
-
- switch (state) {
- case DMA_STATE_STOP:
- cmd = false;
- break;
- case DMA_STATE_RUN:
- cmd = true;
- break;
- default:
- return 0;
- }
-
- snd_hda_codec_load_dsp_trigger(dma->codec, cmd);
- return 0;
-}
-
-static unsigned int dma_get_buffer_size(struct dma_engine *dma)
-{
- return dma->dmab->bytes;
-}
-
-static unsigned char *dma_get_buffer_addr(struct dma_engine *dma)
-{
- return dma->dmab->area;
-}
-
-static int dma_xfer(struct dma_engine *dma,
- const unsigned int *data,
- unsigned int count)
-{
- memcpy(dma->dmab->area, data, count);
- return 0;
-}
-
-static void dma_get_converter_format(
- struct dma_engine *dma,
- unsigned short *format)
-{
- if (format)
- *format = dma->m_converter_format;
-}
-
-static unsigned int dma_get_stream_id(struct dma_engine *dma)
-{
- struct ca0132_spec *spec = dma->codec->spec;
-
- return spec->dsp_stream_id;
-}
-
-struct dsp_image_seg {
- u32 magic;
- u32 chip_addr;
- u32 count;
- u32 data[0];
-};
-
-static const u32 g_magic_value = 0x4c46584d;
-static const u32 g_chip_addr_magic_value = 0xFFFFFF01;
-
-static bool is_valid(const struct dsp_image_seg *p)
-{
- return p->magic == g_magic_value;
-}
-
-static bool is_hci_prog_list_seg(const struct dsp_image_seg *p)
-{
- return g_chip_addr_magic_value == p->chip_addr;
-}
-
-static bool is_last(const struct dsp_image_seg *p)
-{
- return p->count == 0;
-}
-
-static size_t dsp_sizeof(const struct dsp_image_seg *p)
-{
- return sizeof(*p) + p->count*sizeof(u32);
-}
-
-static const struct dsp_image_seg *get_next_seg_ptr(
- const struct dsp_image_seg *p)
-{
- return (struct dsp_image_seg *)((unsigned char *)(p) + dsp_sizeof(p));
-}
-
-/*
- * CA0132 chip DSP transfer stuffs. For DSP download.
- */
-#define INVALID_DMA_CHANNEL (~0U)
-
-/*
- * Program a list of address/data pairs via the ChipIO widget.
- * The segment data is in the format of successive pairs of words.
- * These are repeated as indicated by the segment's count field.
- */
-static int dspxfr_hci_write(struct hda_codec *codec,
- const struct dsp_image_seg *fls)
-{
- int status;
- const u32 *data;
- unsigned int count;
-
- if (fls == NULL || fls->chip_addr != g_chip_addr_magic_value) {
- snd_printdd(KERN_ERR "hci_write invalid params\n");
- return -EINVAL;
- }
-
- count = fls->count;
- data = (u32 *)(fls->data);
- while (count >= 2) {
- status = chipio_write(codec, data[0], data[1]);
- if (status < 0) {
- snd_printdd(KERN_ERR "hci_write chipio failed\n");
- return status;
- }
- count -= 2;
- data += 2;
- }
- return 0;
-}
-
-/**
- * Write a block of data into DSP code or data RAM using pre-allocated
- * DMA engine.
- *
- * @codec: the HDA codec
- * @fls: pointer to a fast load image
- * @reloc: Relocation address for loading single-segment overlays, or 0 for
- * no relocation
- * @dma_engine: pointer to DMA engine to be used for DSP download
- * @dma_chan: The number of DMA channels used for DSP download
- * @port_map_mask: port mapping
- * @ovly: TRUE if overlay format is required
- *
- * Returns zero or a negative error code.
- */
-static int dspxfr_one_seg(struct hda_codec *codec,
- const struct dsp_image_seg *fls,
- unsigned int reloc,
- struct dma_engine *dma_engine,
- unsigned int dma_chan,
- unsigned int port_map_mask,
- bool ovly)
-{
- int status = 0;
- bool comm_dma_setup_done = false;
- const unsigned int *data;
- unsigned int chip_addx;
- unsigned int words_to_write;
- unsigned int buffer_size_words;
- unsigned char *buffer_addx;
- unsigned short hda_format;
- unsigned int sample_rate_div;
- unsigned int sample_rate_mul;
- unsigned int num_chans;
- unsigned int hda_frame_size_words;
- unsigned int remainder_words;
- const u32 *data_remainder;
- u32 chip_addx_remainder;
- unsigned int run_size_words;
- const struct dsp_image_seg *hci_write = NULL;
- unsigned long timeout;
- bool dma_active;
-
- if (fls == NULL)
- return -EINVAL;
- if (is_hci_prog_list_seg(fls)) {
- hci_write = fls;
- fls = get_next_seg_ptr(fls);
- }
-
- if (hci_write && (!fls || is_last(fls))) {
- snd_printdd("hci_write\n");
- return dspxfr_hci_write(codec, hci_write);
- }
-
- if (fls == NULL || dma_engine == NULL || port_map_mask == 0) {
- snd_printdd("Invalid Params\n");
- return -EINVAL;
- }
-
- data = fls->data;
- chip_addx = fls->chip_addr,
- words_to_write = fls->count;
-
- if (!words_to_write)
- return hci_write ? dspxfr_hci_write(codec, hci_write) : 0;
- if (reloc)
- chip_addx = (chip_addx & (0xFFFF0000 << 2)) + (reloc << 2);
-
- if (!UC_RANGE(chip_addx, words_to_write) &&
- !X_RANGE_ALL(chip_addx, words_to_write) &&
- !Y_RANGE_ALL(chip_addx, words_to_write)) {
- snd_printdd("Invalid chip_addx Params\n");
- return -EINVAL;
- }
-
- buffer_size_words = (unsigned int)dma_get_buffer_size(dma_engine) /
- sizeof(u32);
-
- buffer_addx = dma_get_buffer_addr(dma_engine);
-
- if (buffer_addx == NULL) {
- snd_printdd(KERN_ERR "dma_engine buffer NULL\n");
- return -EINVAL;
- }
-
- dma_get_converter_format(dma_engine, &hda_format);
- sample_rate_div = ((get_hdafmt_rate(hda_format) >> 0) & 3) + 1;
- sample_rate_mul = ((get_hdafmt_rate(hda_format) >> 3) & 3) + 1;
- num_chans = get_hdafmt_chs(hda_format) + 1;
-
- hda_frame_size_words = ((sample_rate_div == 0) ? 0 :
- (num_chans * sample_rate_mul / sample_rate_div));
-
- buffer_size_words = min(buffer_size_words,
- (unsigned int)(UC_RANGE(chip_addx, 1) ?
- 65536 : 32768));
- buffer_size_words -= buffer_size_words % hda_frame_size_words;
- snd_printdd(
- "chpadr=0x%08x frmsz=%u nchan=%u "
- "rate_mul=%u div=%u bufsz=%u\n",
- chip_addx, hda_frame_size_words, num_chans,
- sample_rate_mul, sample_rate_div, buffer_size_words);
-
- if ((buffer_addx == NULL) || (hda_frame_size_words == 0) ||
- (buffer_size_words < hda_frame_size_words)) {
- snd_printdd(KERN_ERR "dspxfr_one_seg:failed\n");
- return -EINVAL;
- }
-
- remainder_words = words_to_write % hda_frame_size_words;
- data_remainder = data;
- chip_addx_remainder = chip_addx;
-
- data += remainder_words;
- chip_addx += remainder_words*sizeof(u32);
- words_to_write -= remainder_words;
-
- while (words_to_write != 0) {
- run_size_words = min(buffer_size_words, words_to_write);
- snd_printdd("dspxfr (seg loop)cnt=%u rs=%u remainder=%u\n",
- words_to_write, run_size_words, remainder_words);
- dma_xfer(dma_engine, data, run_size_words*sizeof(u32));
- if (!comm_dma_setup_done) {
- status = dsp_dma_stop(codec, dma_chan, ovly);
- if (status < 0)
- return status;
- status = dsp_dma_setup_common(codec, chip_addx,
- dma_chan, port_map_mask, ovly);
- if (status < 0)
- return status;
- comm_dma_setup_done = true;
- }
-
- status = dsp_dma_setup(codec, chip_addx,
- run_size_words, dma_chan);
- if (status < 0)
- return status;
- status = dsp_dma_start(codec, dma_chan, ovly);
- if (status < 0)
- return status;
- if (!dsp_is_dma_active(codec, dma_chan)) {
- snd_printdd(KERN_ERR "dspxfr:DMA did not start\n");
- return -EIO;
- }
- status = dma_set_state(dma_engine, DMA_STATE_RUN);
- if (status < 0)
- return status;
- if (remainder_words != 0) {
- status = chipio_write_multiple(codec,
- chip_addx_remainder,
- data_remainder,
- remainder_words);
- if (status < 0)
- return status;
- remainder_words = 0;
- }
- if (hci_write) {
- status = dspxfr_hci_write(codec, hci_write);
- if (status < 0)
- return status;
- hci_write = NULL;
- }
-
- timeout = jiffies + msecs_to_jiffies(2000);
- do {
- dma_active = dsp_is_dma_active(codec, dma_chan);
- if (!dma_active)
- break;
- msleep(20);
- } while (time_before(jiffies, timeout));
- if (dma_active)
- break;
-
- snd_printdd(KERN_INFO "+++++ DMA complete\n");
- dma_set_state(dma_engine, DMA_STATE_STOP);
- status = dma_reset(dma_engine);
-
- if (status < 0)
- return status;
-
- data += run_size_words;
- chip_addx += run_size_words*sizeof(u32);
- words_to_write -= run_size_words;
- }
-
- if (remainder_words != 0) {
- status = chipio_write_multiple(codec, chip_addx_remainder,
- data_remainder, remainder_words);
- }
-
- return status;
-}
-
-/**
- * Write the entire DSP image of a DSP code/data overlay to DSP memories
- *
- * @codec: the HDA codec
- * @fls_data: pointer to a fast load image
- * @reloc: Relocation address for loading single-segment overlays, or 0 for
- * no relocation
- * @sample_rate: sampling rate of the stream used for DSP download
- * @number_channels: channels of the stream used for DSP download
- * @ovly: TRUE if overlay format is required
- *
- * Returns zero or a negative error code.
- */
-static int dspxfr_image(struct hda_codec *codec,
- const struct dsp_image_seg *fls_data,
- unsigned int reloc,
- unsigned int sample_rate,
- unsigned short channels,
- bool ovly)
-{
- struct ca0132_spec *spec = codec->spec;
- int status;
- unsigned short hda_format = 0;
- unsigned int response;
- unsigned char stream_id = 0;
- struct dma_engine *dma_engine;
- unsigned int dma_chan;
- unsigned int port_map_mask;
-
- if (fls_data == NULL)
- return -EINVAL;
-
- dma_engine = kzalloc(sizeof(*dma_engine), GFP_KERNEL);
- if (!dma_engine)
- return -ENOMEM;
-
- dma_engine->dmab = kzalloc(sizeof(*dma_engine->dmab), GFP_KERNEL);
- if (!dma_engine->dmab) {
- kfree(dma_engine);
- return -ENOMEM;
- }
-
- dma_engine->codec = codec;
- dma_convert_to_hda_format(sample_rate, channels, &hda_format);
- dma_engine->m_converter_format = hda_format;
- dma_engine->buf_size = (ovly ? DSP_DMA_WRITE_BUFLEN_OVLY :
- DSP_DMA_WRITE_BUFLEN_INIT) * 2;
-
- dma_chan = ovly ? INVALID_DMA_CHANNEL : 0;
-
- status = codec_set_converter_format(codec, WIDGET_CHIP_CTRL,
- hda_format, &response);
-
- if (status < 0) {
- snd_printdd(KERN_ERR "set converter format fail\n");
- goto exit;
- }
-
- status = snd_hda_codec_load_dsp_prepare(codec,
- dma_engine->m_converter_format,
- dma_engine->buf_size,
- dma_engine->dmab);
- if (status < 0)
- goto exit;
- spec->dsp_stream_id = status;
-
- if (ovly) {
- status = dspio_alloc_dma_chan(codec, &dma_chan);
- if (status < 0) {
- snd_printdd(KERN_ERR "alloc dmachan fail\n");
- dma_chan = INVALID_DMA_CHANNEL;
- goto exit;
- }
- }
-
- port_map_mask = 0;
- status = dsp_allocate_ports_format(codec, hda_format,
- &port_map_mask);
- if (status < 0) {
- snd_printdd(KERN_ERR "alloc ports fail\n");
- goto exit;
- }
-
- stream_id = dma_get_stream_id(dma_engine);
- status = codec_set_converter_stream_channel(codec,
- WIDGET_CHIP_CTRL, stream_id, 0, &response);
- if (status < 0) {
- snd_printdd(KERN_ERR "set stream chan fail\n");
- goto exit;
- }
-
- while ((fls_data != NULL) && !is_last(fls_data)) {
- if (!is_valid(fls_data)) {
- snd_printdd(KERN_ERR "FLS check fail\n");
- status = -EINVAL;
- goto exit;
- }
- status = dspxfr_one_seg(codec, fls_data, reloc,
- dma_engine, dma_chan,
- port_map_mask, ovly);
- if (status < 0)
- break;
-
- if (is_hci_prog_list_seg(fls_data))
- fls_data = get_next_seg_ptr(fls_data);
-
- if ((fls_data != NULL) && !is_last(fls_data))
- fls_data = get_next_seg_ptr(fls_data);
- }
-
- if (port_map_mask != 0)
- status = dsp_free_ports(codec);
-
- if (status < 0)
- goto exit;
-
- status = codec_set_converter_stream_channel(codec,
- WIDGET_CHIP_CTRL, 0, 0, &response);
-
-exit:
- if (ovly && (dma_chan != INVALID_DMA_CHANNEL))
- dspio_free_dma_chan(codec, dma_chan);
-
- if (dma_engine->dmab->area)
- snd_hda_codec_load_dsp_cleanup(codec, dma_engine->dmab);
- kfree(dma_engine->dmab);
- kfree(dma_engine);
-
- return status;
-}
-
-/*
- * CA0132 DSP download stuffs.
- */
-static void dspload_post_setup(struct hda_codec *codec)
-{
- snd_printdd(KERN_INFO "---- dspload_post_setup ------\n");
-
- /*set DSP speaker to 2.0 configuration*/
- chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x18), 0x08080080);
- chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x19), 0x3f800000);
-
- /*update write pointer*/
- chipio_write(codec, XRAM_XRAM_INST_OFFSET(0x29), 0x00000002);
-}
-
-/**
- * Download DSP from a DSP Image Fast Load structure. This structure is a
- * linear, non-constant sized element array of structures, each of which
- * contain the count of the data to be loaded, the data itself, and the
- * corresponding starting chip address of the starting data location.
- *
- * @codec: the HDA codec
- * @fls: pointer to a fast load image
- * @ovly: TRUE if overlay format is required
- * @reloc: Relocation address for loading single-segment overlays, or 0 for
- * no relocation
- * @autostart: TRUE if DSP starts after loading; ignored if ovly is TRUE
- * @router_chans: number of audio router channels to be allocated (0 means use
- * internal defaults; max is 32)
- *
- * Returns zero or a negative error code.
- */
-static int dspload_image(struct hda_codec *codec,
- const struct dsp_image_seg *fls,
- bool ovly,
- unsigned int reloc,
- bool autostart,
- int router_chans)
-{
- int status = 0;
- unsigned int sample_rate;
- unsigned short channels;
-
- snd_printdd(KERN_INFO "---- dspload_image begin ------\n");
- if (router_chans == 0) {
- if (!ovly)
- router_chans = DMA_TRANSFER_FRAME_SIZE_NWORDS;
- else
- router_chans = DMA_OVERLAY_FRAME_SIZE_NWORDS;
- }
-
- sample_rate = 48000;
- channels = (unsigned short)router_chans;
-
- while (channels > 16) {
- sample_rate *= 2;
- channels /= 2;
- }
-
- do {
- snd_printdd(KERN_INFO "Ready to program DMA\n");
- if (!ovly)
- status = dsp_reset(codec);
-
- if (status < 0)
- break;
-
- snd_printdd(KERN_INFO "dsp_reset() complete\n");
- status = dspxfr_image(codec, fls, reloc, sample_rate, channels,
- ovly);
-
- if (status < 0)
- break;
-
- snd_printdd(KERN_INFO "dspxfr_image() complete\n");
- if (autostart && !ovly) {
- dspload_post_setup(codec);
- status = dsp_set_run_state(codec);
- }
-
- snd_printdd(KERN_INFO "LOAD FINISHED\n");
- } while (0);
-
- return status;
-}
-
-#ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP
-static bool dspload_is_loaded(struct hda_codec *codec)
-{
- unsigned int data = 0;
- int status = 0;
-
- status = chipio_read(codec, 0x40004, &data);
- if ((status < 0) || (data != 1))
- return false;
-
- return true;
-}
-#else
-#define dspload_is_loaded(codec) false
-#endif
-
-static bool dspload_wait_loaded(struct hda_codec *codec)
-{
- unsigned long timeout = jiffies + msecs_to_jiffies(2000);
-
- do {
- if (dspload_is_loaded(codec)) {
- pr_info("ca0132 DOWNLOAD OK :-) DSP IS RUNNING.\n");
- return true;
- }
- msleep(20);
- } while (time_before(jiffies, timeout));
-
- pr_err("ca0132 DOWNLOAD FAILED!!! DSP IS NOT RUNNING.\n");
- return false;
-}
-
-/*
- * PCM stuffs
- */
-static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag,
- int channel_id, int format)
-{
- unsigned int oldval, newval;
-
- if (!nid)
- return;
-
- snd_printdd(
- "ca0132_setup_stream: NID=0x%x, stream=0x%x, "
- "channel=%d, format=0x%x\n",
- nid, stream_tag, channel_id, format);
-
- /* update the format-id if changed */
- oldval = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_STREAM_FORMAT,
- 0);
- if (oldval != format) {
- msleep(20);
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_STREAM_FORMAT,
- format);
- }
-
- oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
- newval = (stream_tag << 4) | channel_id;
- if (oldval != newval) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- newval);
- }
-}
-
-static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
-{
- unsigned int val;
-
- if (!nid)
- return;
-
- snd_printdd(KERN_INFO "ca0132_cleanup_stream: NID=0x%x\n", nid);
-
- val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
- if (!val)
- return;
-
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
-}
-
-/*
- * PCM callbacks
- */
-static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
-
- ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
-
- return 0;
-}
-
-static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
-
- if (spec->dsp_state == DSP_DOWNLOADING)
- return 0;
-
- /*If Playback effects are on, allow stream some time to flush
- *effects tail*/
- if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
- msleep(50);
-
- ca0132_cleanup_stream(codec, spec->dacs[0]);
-
- return 0;
-}
-
-/*
- * Digital out
- */
-static int ca0132_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int ca0132_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-/*
- * Analog capture
- */
-static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
-
- ca0132_setup_stream(codec, spec->adcs[substream->number],
- stream_tag, 0, format);
-
- return 0;
-}
-
-static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
-
- if (spec->dsp_state == DSP_DOWNLOADING)
- return 0;
-
- ca0132_cleanup_stream(codec, hinfo->nid);
- return 0;
-}
-
-/*
- * Controls stuffs.
- */
-
-/*
- * Mixer controls helpers.
- */
-#define CA0132_CODEC_VOL_MONO(xname, nid, channel, dir) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .subdevice = HDA_SUBDEV_AMP_FLAG, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
- SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
- .info = ca0132_volume_info, \
- .get = ca0132_volume_get, \
- .put = ca0132_volume_put, \
- .tlv = { .c = ca0132_volume_tlv }, \
- .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
-
-#define CA0132_CODEC_MUTE_MONO(xname, nid, channel, dir) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .subdevice = HDA_SUBDEV_AMP_FLAG, \
- .info = snd_hda_mixer_amp_switch_info, \
- .get = ca0132_switch_get, \
- .put = ca0132_switch_put, \
- .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, 0, dir) }
-
-/* stereo */
-#define CA0132_CODEC_VOL(xname, nid, dir) \
- CA0132_CODEC_VOL_MONO(xname, nid, 3, dir)
-#define CA0132_CODEC_MUTE(xname, nid, dir) \
- CA0132_CODEC_MUTE_MONO(xname, nid, 3, dir)
-
-/* The followings are for tuning of products */
-#ifdef ENABLE_TUNING_CONTROLS
-
-static unsigned int voice_focus_vals_lookup[] = {
-0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000, 0x41C00000, 0x41C80000,
-0x41D00000, 0x41D80000, 0x41E00000, 0x41E80000, 0x41F00000, 0x41F80000,
-0x42000000, 0x42040000, 0x42080000, 0x420C0000, 0x42100000, 0x42140000,
-0x42180000, 0x421C0000, 0x42200000, 0x42240000, 0x42280000, 0x422C0000,
-0x42300000, 0x42340000, 0x42380000, 0x423C0000, 0x42400000, 0x42440000,
-0x42480000, 0x424C0000, 0x42500000, 0x42540000, 0x42580000, 0x425C0000,
-0x42600000, 0x42640000, 0x42680000, 0x426C0000, 0x42700000, 0x42740000,
-0x42780000, 0x427C0000, 0x42800000, 0x42820000, 0x42840000, 0x42860000,
-0x42880000, 0x428A0000, 0x428C0000, 0x428E0000, 0x42900000, 0x42920000,
-0x42940000, 0x42960000, 0x42980000, 0x429A0000, 0x429C0000, 0x429E0000,
-0x42A00000, 0x42A20000, 0x42A40000, 0x42A60000, 0x42A80000, 0x42AA0000,
-0x42AC0000, 0x42AE0000, 0x42B00000, 0x42B20000, 0x42B40000, 0x42B60000,
-0x42B80000, 0x42BA0000, 0x42BC0000, 0x42BE0000, 0x42C00000, 0x42C20000,
-0x42C40000, 0x42C60000, 0x42C80000, 0x42CA0000, 0x42CC0000, 0x42CE0000,
-0x42D00000, 0x42D20000, 0x42D40000, 0x42D60000, 0x42D80000, 0x42DA0000,
-0x42DC0000, 0x42DE0000, 0x42E00000, 0x42E20000, 0x42E40000, 0x42E60000,
-0x42E80000, 0x42EA0000, 0x42EC0000, 0x42EE0000, 0x42F00000, 0x42F20000,
-0x42F40000, 0x42F60000, 0x42F80000, 0x42FA0000, 0x42FC0000, 0x42FE0000,
-0x43000000, 0x43010000, 0x43020000, 0x43030000, 0x43040000, 0x43050000,
-0x43060000, 0x43070000, 0x43080000, 0x43090000, 0x430A0000, 0x430B0000,
-0x430C0000, 0x430D0000, 0x430E0000, 0x430F0000, 0x43100000, 0x43110000,
-0x43120000, 0x43130000, 0x43140000, 0x43150000, 0x43160000, 0x43170000,
-0x43180000, 0x43190000, 0x431A0000, 0x431B0000, 0x431C0000, 0x431D0000,
-0x431E0000, 0x431F0000, 0x43200000, 0x43210000, 0x43220000, 0x43230000,
-0x43240000, 0x43250000, 0x43260000, 0x43270000, 0x43280000, 0x43290000,
-0x432A0000, 0x432B0000, 0x432C0000, 0x432D0000, 0x432E0000, 0x432F0000,
-0x43300000, 0x43310000, 0x43320000, 0x43330000, 0x43340000
-};
-
-static unsigned int mic_svm_vals_lookup[] = {
-0x00000000, 0x3C23D70A, 0x3CA3D70A, 0x3CF5C28F, 0x3D23D70A, 0x3D4CCCCD,
-0x3D75C28F, 0x3D8F5C29, 0x3DA3D70A, 0x3DB851EC, 0x3DCCCCCD, 0x3DE147AE,
-0x3DF5C28F, 0x3E051EB8, 0x3E0F5C29, 0x3E19999A, 0x3E23D70A, 0x3E2E147B,
-0x3E3851EC, 0x3E428F5C, 0x3E4CCCCD, 0x3E570A3D, 0x3E6147AE, 0x3E6B851F,
-0x3E75C28F, 0x3E800000, 0x3E851EB8, 0x3E8A3D71, 0x3E8F5C29, 0x3E947AE1,
-0x3E99999A, 0x3E9EB852, 0x3EA3D70A, 0x3EA8F5C3, 0x3EAE147B, 0x3EB33333,
-0x3EB851EC, 0x3EBD70A4, 0x3EC28F5C, 0x3EC7AE14, 0x3ECCCCCD, 0x3ED1EB85,
-0x3ED70A3D, 0x3EDC28F6, 0x3EE147AE, 0x3EE66666, 0x3EEB851F, 0x3EF0A3D7,
-0x3EF5C28F, 0x3EFAE148, 0x3F000000, 0x3F028F5C, 0x3F051EB8, 0x3F07AE14,
-0x3F0A3D71, 0x3F0CCCCD, 0x3F0F5C29, 0x3F11EB85, 0x3F147AE1, 0x3F170A3D,
-0x3F19999A, 0x3F1C28F6, 0x3F1EB852, 0x3F2147AE, 0x3F23D70A, 0x3F266666,
-0x3F28F5C3, 0x3F2B851F, 0x3F2E147B, 0x3F30A3D7, 0x3F333333, 0x3F35C28F,
-0x3F3851EC, 0x3F3AE148, 0x3F3D70A4, 0x3F400000, 0x3F428F5C, 0x3F451EB8,
-0x3F47AE14, 0x3F4A3D71, 0x3F4CCCCD, 0x3F4F5C29, 0x3F51EB85, 0x3F547AE1,
-0x3F570A3D, 0x3F59999A, 0x3F5C28F6, 0x3F5EB852, 0x3F6147AE, 0x3F63D70A,
-0x3F666666, 0x3F68F5C3, 0x3F6B851F, 0x3F6E147B, 0x3F70A3D7, 0x3F733333,
-0x3F75C28F, 0x3F7851EC, 0x3F7AE148, 0x3F7D70A4, 0x3F800000
-};
-
-static unsigned int equalizer_vals_lookup[] = {
-0xC1C00000, 0xC1B80000, 0xC1B00000, 0xC1A80000, 0xC1A00000, 0xC1980000,
-0xC1900000, 0xC1880000, 0xC1800000, 0xC1700000, 0xC1600000, 0xC1500000,
-0xC1400000, 0xC1300000, 0xC1200000, 0xC1100000, 0xC1000000, 0xC0E00000,
-0xC0C00000, 0xC0A00000, 0xC0800000, 0xC0400000, 0xC0000000, 0xBF800000,
-0x00000000, 0x3F800000, 0x40000000, 0x40400000, 0x40800000, 0x40A00000,
-0x40C00000, 0x40E00000, 0x41000000, 0x41100000, 0x41200000, 0x41300000,
-0x41400000, 0x41500000, 0x41600000, 0x41700000, 0x41800000, 0x41880000,
-0x41900000, 0x41980000, 0x41A00000, 0x41A80000, 0x41B00000, 0x41B80000,
-0x41C00000
-};
-
-static int tuning_ctl_set(struct hda_codec *codec, hda_nid_t nid,
- unsigned int *lookup, int idx)
-{
- int i = 0;
-
- for (i = 0; i < TUNING_CTLS_COUNT; i++)
- if (nid == ca0132_tuning_ctls[i].nid)
- break;
-
- snd_hda_power_up(codec);
- dspio_set_param(codec, ca0132_tuning_ctls[i].mid,
- ca0132_tuning_ctls[i].req,
- &(lookup[idx]), sizeof(unsigned int));
- snd_hda_power_down(codec);
-
- return 1;
-}
-
-static int tuning_ctl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int idx = nid - TUNING_CTL_START_NID;
-
- *valp = spec->cur_ctl_vals[idx];
- return 0;
-}
-
-static int voice_focus_ctl_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- int chs = get_amp_channels(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = chs == 3 ? 2 : 1;
- uinfo->value.integer.min = 20;
- uinfo->value.integer.max = 180;
- uinfo->value.integer.step = 1;
-
- return 0;
-}
-
-static int voice_focus_ctl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int idx;
-
- idx = nid - TUNING_CTL_START_NID;
- /* any change? */
- if (spec->cur_ctl_vals[idx] == *valp)
- return 0;
-
- spec->cur_ctl_vals[idx] = *valp;
-
- idx = *valp - 20;
- tuning_ctl_set(codec, nid, voice_focus_vals_lookup, idx);
-
- return 1;
-}
-
-static int mic_svm_ctl_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- int chs = get_amp_channels(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = chs == 3 ? 2 : 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 100;
- uinfo->value.integer.step = 1;
-
- return 0;
-}
-
-static int mic_svm_ctl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int idx;
-
- idx = nid - TUNING_CTL_START_NID;
- /* any change? */
- if (spec->cur_ctl_vals[idx] == *valp)
- return 0;
-
- spec->cur_ctl_vals[idx] = *valp;
-
- idx = *valp;
- tuning_ctl_set(codec, nid, mic_svm_vals_lookup, idx);
-
- return 0;
-}
-
-static int equalizer_ctl_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- int chs = get_amp_channels(kcontrol);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = chs == 3 ? 2 : 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 48;
- uinfo->value.integer.step = 1;
-
- return 0;
-}
-
-static int equalizer_ctl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int idx;
-
- idx = nid - TUNING_CTL_START_NID;
- /* any change? */
- if (spec->cur_ctl_vals[idx] == *valp)
- return 0;
-
- spec->cur_ctl_vals[idx] = *valp;
-
- idx = *valp;
- tuning_ctl_set(codec, nid, equalizer_vals_lookup, idx);
-
- return 1;
-}
-
-static const DECLARE_TLV_DB_SCALE(voice_focus_db_scale, 2000, 100, 0);
-static const DECLARE_TLV_DB_SCALE(eq_db_scale, -2400, 100, 0);
-
-static int add_tuning_control(struct hda_codec *codec,
- hda_nid_t pnid, hda_nid_t nid,
- const char *name, int dir)
-{
- char namestr[44];
- int type = dir ? HDA_INPUT : HDA_OUTPUT;
- struct snd_kcontrol_new knew =
- HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type);
-
- knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- knew.tlv.c = 0;
- knew.tlv.p = 0;
- switch (pnid) {
- case VOICE_FOCUS:
- knew.info = voice_focus_ctl_info;
- knew.get = tuning_ctl_get;
- knew.put = voice_focus_ctl_put;
- knew.tlv.p = voice_focus_db_scale;
- break;
- case MIC_SVM:
- knew.info = mic_svm_ctl_info;
- knew.get = tuning_ctl_get;
- knew.put = mic_svm_ctl_put;
- break;
- case EQUALIZER:
- knew.info = equalizer_ctl_info;
- knew.get = tuning_ctl_get;
- knew.put = equalizer_ctl_put;
- knew.tlv.p = eq_db_scale;
- break;
- default:
- return 0;
- }
- knew.private_value =
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, type);
- sprintf(namestr, "%s %s Volume", name, dirstr[dir]);
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
-}
-
-static int add_tuning_ctls(struct hda_codec *codec)
-{
- int i;
- int err;
-
- for (i = 0; i < TUNING_CTLS_COUNT; i++) {
- err = add_tuning_control(codec,
- ca0132_tuning_ctls[i].parent_nid,
- ca0132_tuning_ctls[i].nid,
- ca0132_tuning_ctls[i].name,
- ca0132_tuning_ctls[i].direct);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static void ca0132_init_tuning_defaults(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- int i;
-
- /* Wedge Angle defaults to 30. 10 below is 30 - 20. 20 is min. */
- spec->cur_ctl_vals[WEDGE_ANGLE - TUNING_CTL_START_NID] = 10;
- /* SVM level defaults to 0.74. */
- spec->cur_ctl_vals[SVM_LEVEL - TUNING_CTL_START_NID] = 74;
-
- /* EQ defaults to 0dB. */
- for (i = 2; i < TUNING_CTLS_COUNT; i++)
- spec->cur_ctl_vals[i] = 24;
-}
-#endif /*ENABLE_TUNING_CONTROLS*/
-
-/*
- * Select the active output.
- * If autodetect is enabled, output will be selected based on jack detection.
- * If jack inserted, headphone will be selected, else built-in speakers
- * If autodetect is disabled, output will be selected based on selection.
- */
-static int ca0132_select_out(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- unsigned int pin_ctl;
- int jack_present;
- int auto_jack;
- unsigned int tmp;
- int err;
-
- snd_printdd(KERN_INFO "ca0132_select_out\n");
-
- snd_hda_power_up(codec);
-
- auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
-
- if (auto_jack)
- jack_present = snd_hda_jack_detect(codec, spec->out_pins[1]);
- else
- jack_present =
- spec->vnode_lswitch[VNID_HP_SEL - VNODE_START_NID];
-
- if (jack_present)
- spec->cur_out_type = HEADPHONE_OUT;
- else
- spec->cur_out_type = SPEAKER_OUT;
-
- if (spec->cur_out_type == SPEAKER_OUT) {
- snd_printdd(KERN_INFO "ca0132_select_out speaker\n");
- /*speaker out config*/
- tmp = FLOAT_ONE;
- err = dspio_set_uint_param(codec, 0x80, 0x04, tmp);
- if (err < 0)
- goto exit;
- /*enable speaker EQ*/
- tmp = FLOAT_ONE;
- err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp);
- if (err < 0)
- goto exit;
-
- /* Setup EAPD */
- snd_hda_codec_write(codec, spec->out_pins[1], 0,
- VENDOR_CHIPIO_EAPD_SEL_SET, 0x02);
- snd_hda_codec_write(codec, spec->out_pins[0], 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x00);
- snd_hda_codec_write(codec, spec->out_pins[0], 0,
- VENDOR_CHIPIO_EAPD_SEL_SET, 0x00);
- snd_hda_codec_write(codec, spec->out_pins[0], 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x02);
-
- /* disable headphone node */
- pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- snd_hda_set_pin_ctl(codec, spec->out_pins[1],
- pin_ctl & ~PIN_HP);
- /* enable speaker node */
- pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- snd_hda_set_pin_ctl(codec, spec->out_pins[0],
- pin_ctl | PIN_OUT);
- } else {
- snd_printdd(KERN_INFO "ca0132_select_out hp\n");
- /*headphone out config*/
- tmp = FLOAT_ZERO;
- err = dspio_set_uint_param(codec, 0x80, 0x04, tmp);
- if (err < 0)
- goto exit;
- /*disable speaker EQ*/
- tmp = FLOAT_ZERO;
- err = dspio_set_uint_param(codec, 0x8f, 0x00, tmp);
- if (err < 0)
- goto exit;
-
- /* Setup EAPD */
- snd_hda_codec_write(codec, spec->out_pins[0], 0,
- VENDOR_CHIPIO_EAPD_SEL_SET, 0x00);
- snd_hda_codec_write(codec, spec->out_pins[0], 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x00);
- snd_hda_codec_write(codec, spec->out_pins[1], 0,
- VENDOR_CHIPIO_EAPD_SEL_SET, 0x02);
- snd_hda_codec_write(codec, spec->out_pins[0], 0,
- AC_VERB_SET_EAPD_BTLENABLE, 0x02);
-
- /* disable speaker*/
- pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- snd_hda_set_pin_ctl(codec, spec->out_pins[0],
- pin_ctl & ~PIN_HP);
- /* enable headphone*/
- pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- snd_hda_set_pin_ctl(codec, spec->out_pins[1],
- pin_ctl | PIN_HP);
- }
-
-exit:
- snd_hda_power_down(codec);
-
- return err < 0 ? err : 0;
-}
-
-static void ca0132_set_dmic(struct hda_codec *codec, int enable);
-static int ca0132_mic_boost_set(struct hda_codec *codec, long val);
-static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val);
-
-/*
- * Select the active VIP source
- */
-static int ca0132_set_vipsource(struct hda_codec *codec, int val)
-{
- struct ca0132_spec *spec = codec->spec;
- unsigned int tmp;
-
- if (!dspload_is_loaded(codec))
- return 0;
-
- /* if CrystalVoice if off, vipsource should be 0 */
- if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ||
- (val == 0)) {
- chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, 0);
- chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
- chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
- if (spec->cur_mic_type == DIGITAL_MIC)
- tmp = FLOAT_TWO;
- else
- tmp = FLOAT_ONE;
- dspio_set_uint_param(codec, 0x80, 0x00, tmp);
- tmp = FLOAT_ZERO;
- dspio_set_uint_param(codec, 0x80, 0x05, tmp);
- } else {
- chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_16_000);
- chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_16_000);
- if (spec->cur_mic_type == DIGITAL_MIC)
- tmp = FLOAT_TWO;
- else
- tmp = FLOAT_ONE;
- dspio_set_uint_param(codec, 0x80, 0x00, tmp);
- tmp = FLOAT_ONE;
- dspio_set_uint_param(codec, 0x80, 0x05, tmp);
- msleep(20);
- chipio_set_control_param(codec, CONTROL_PARAM_VIP_SOURCE, val);
- }
-
- return 1;
-}
-
-/*
- * Select the active microphone.
- * If autodetect is enabled, mic will be selected based on jack detection.
- * If jack inserted, ext.mic will be selected, else built-in mic
- * If autodetect is disabled, mic will be selected based on selection.
- */
-static int ca0132_select_mic(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- int jack_present;
- int auto_jack;
-
- snd_printdd(KERN_INFO "ca0132_select_mic\n");
-
- snd_hda_power_up(codec);
-
- auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID];
-
- if (auto_jack)
- jack_present = snd_hda_jack_detect(codec, spec->input_pins[0]);
- else
- jack_present =
- spec->vnode_lswitch[VNID_AMIC1_SEL - VNODE_START_NID];
-
- if (jack_present)
- spec->cur_mic_type = LINE_MIC_IN;
- else
- spec->cur_mic_type = DIGITAL_MIC;
-
- if (spec->cur_mic_type == DIGITAL_MIC) {
- /* enable digital Mic */
- chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_32_000);
- ca0132_set_dmic(codec, 1);
- ca0132_mic_boost_set(codec, 0);
- /* set voice focus */
- ca0132_effects_set(codec, VOICE_FOCUS,
- spec->effects_switch
- [VOICE_FOCUS - EFFECT_START_NID]);
- } else {
- /* disable digital Mic */
- chipio_set_conn_rate(codec, MEM_CONNID_DMIC, SR_96_000);
- ca0132_set_dmic(codec, 0);
- ca0132_mic_boost_set(codec, spec->cur_mic_boost);
- /* disable voice focus */
- ca0132_effects_set(codec, VOICE_FOCUS, 0);
- }
-
- snd_hda_power_down(codec);
-
- return 0;
-}
-
-/*
- * Check if VNODE settings take effect immediately.
- */
-static bool ca0132_is_vnode_effective(struct hda_codec *codec,
- hda_nid_t vnid,
- hda_nid_t *shared_nid)
-{
- struct ca0132_spec *spec = codec->spec;
- hda_nid_t nid;
-
- switch (vnid) {
- case VNID_SPK:
- nid = spec->shared_out_nid;
- break;
- case VNID_MIC:
- nid = spec->shared_mic_nid;
- break;
- default:
- return false;
- }
-
- if (shared_nid)
- *shared_nid = nid;
-
- return true;
-}
-
-/*
-* The following functions are control change helpers.
-* They return 0 if no changed. Return 1 if changed.
-*/
-static int ca0132_voicefx_set(struct hda_codec *codec, int enable)
-{
- struct ca0132_spec *spec = codec->spec;
- unsigned int tmp;
-
- /* based on CrystalVoice state to enable VoiceFX. */
- if (enable) {
- tmp = spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] ?
- FLOAT_ONE : FLOAT_ZERO;
- } else {
- tmp = FLOAT_ZERO;
- }
-
- dspio_set_uint_param(codec, ca0132_voicefx.mid,
- ca0132_voicefx.reqs[0], tmp);
-
- return 1;
-}
-
-/*
- * Set the effects parameters
- */
-static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val)
-{
- struct ca0132_spec *spec = codec->spec;
- unsigned int on;
- int num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
- int err = 0;
- int idx = nid - EFFECT_START_NID;
-
- if ((idx < 0) || (idx >= num_fx))
- return 0; /* no changed */
-
- /* for out effect, qualify with PE */
- if ((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) {
- /* if PE if off, turn off out effects. */
- if (!spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID])
- val = 0;
- }
-
- /* for in effect, qualify with CrystalVoice */
- if ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID)) {
- /* if CrystalVoice if off, turn off in effects. */
- if (!spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID])
- val = 0;
-
- /* Voice Focus applies to 2-ch Mic, Digital Mic */
- if ((nid == VOICE_FOCUS) && (spec->cur_mic_type != DIGITAL_MIC))
- val = 0;
- }
-
- snd_printdd(KERN_INFO "ca0132_effect_set: nid=0x%x, val=%ld\n",
- nid, val);
-
- on = (val == 0) ? FLOAT_ZERO : FLOAT_ONE;
- err = dspio_set_uint_param(codec, ca0132_effects[idx].mid,
- ca0132_effects[idx].reqs[0], on);
-
- if (err < 0)
- return 0; /* no changed */
-
- return 1;
-}
-
-/*
- * Turn on/off Playback Enhancements
- */
-static int ca0132_pe_switch_set(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- hda_nid_t nid;
- int i, ret = 0;
-
- snd_printdd(KERN_INFO "ca0132_pe_switch_set: val=%ld\n",
- spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]);
-
- i = OUT_EFFECT_START_NID - EFFECT_START_NID;
- nid = OUT_EFFECT_START_NID;
- /* PE affects all out effects */
- for (; nid < OUT_EFFECT_END_NID; nid++, i++)
- ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]);
-
- return ret;
-}
-
-/* Check if Mic1 is streaming, if so, stop streaming */
-static int stop_mic1(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- unsigned int oldval = snd_hda_codec_read(codec, spec->adcs[0], 0,
- AC_VERB_GET_CONV, 0);
- if (oldval != 0)
- snd_hda_codec_write(codec, spec->adcs[0], 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- 0);
- return oldval;
-}
-
-/* Resume Mic1 streaming if it was stopped. */
-static void resume_mic1(struct hda_codec *codec, unsigned int oldval)
-{
- struct ca0132_spec *spec = codec->spec;
- /* Restore the previous stream and channel */
- if (oldval != 0)
- snd_hda_codec_write(codec, spec->adcs[0], 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- oldval);
-}
-
-/*
- * Turn on/off CrystalVoice
- */
-static int ca0132_cvoice_switch_set(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- hda_nid_t nid;
- int i, ret = 0;
- unsigned int oldval;
-
- snd_printdd(KERN_INFO "ca0132_cvoice_switch_set: val=%ld\n",
- spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]);
-
- i = IN_EFFECT_START_NID - EFFECT_START_NID;
- nid = IN_EFFECT_START_NID;
- /* CrystalVoice affects all in effects */
- for (; nid < IN_EFFECT_END_NID; nid++, i++)
- ret |= ca0132_effects_set(codec, nid, spec->effects_switch[i]);
-
- /* including VoiceFX */
- ret |= ca0132_voicefx_set(codec, (spec->voicefx_val ? 1 : 0));
-
- /* set correct vipsource */
- oldval = stop_mic1(codec);
- ret |= ca0132_set_vipsource(codec, 1);
- resume_mic1(codec, oldval);
- return ret;
-}
-
-static int ca0132_mic_boost_set(struct hda_codec *codec, long val)
-{
- struct ca0132_spec *spec = codec->spec;
- int ret = 0;
-
- if (val) /* on */
- ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
- HDA_INPUT, 0, HDA_AMP_VOLMASK, 3);
- else /* off */
- ret = snd_hda_codec_amp_update(codec, spec->input_pins[0], 0,
- HDA_INPUT, 0, HDA_AMP_VOLMASK, 0);
-
- return ret;
-}
-
-static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = get_amp_nid(kcontrol);
- hda_nid_t shared_nid = 0;
- bool effective;
- int ret = 0;
- struct ca0132_spec *spec = codec->spec;
- int auto_jack;
-
- if (nid == VNID_HP_SEL) {
- auto_jack =
- spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID];
- if (!auto_jack)
- ca0132_select_out(codec);
- return 1;
- }
-
- if (nid == VNID_AMIC1_SEL) {
- auto_jack =
- spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID];
- if (!auto_jack)
- ca0132_select_mic(codec);
- return 1;
- }
-
- if (nid == VNID_HP_ASEL) {
- ca0132_select_out(codec);
- return 1;
- }
-
- if (nid == VNID_AMIC1_ASEL) {
- ca0132_select_mic(codec);
- return 1;
- }
-
- /* if effective conditions, then update hw immediately. */
- effective = ca0132_is_vnode_effective(codec, nid, &shared_nid);
- if (effective) {
- int dir = get_amp_direction(kcontrol);
- int ch = get_amp_channels(kcontrol);
- unsigned long pval;
-
- mutex_lock(&codec->control_mutex);
- pval = kcontrol->private_value;
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch,
- 0, dir);
- ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
- kcontrol->private_value = pval;
- mutex_unlock(&codec->control_mutex);
- }
-
- return ret;
-}
-/* End of control change helpers. */
-
-static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- unsigned int items = sizeof(ca0132_voicefx_presets)
- / sizeof(struct ct_voicefx_preset);
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = items;
- if (uinfo->value.enumerated.item >= items)
- uinfo->value.enumerated.item = items - 1;
- strcpy(uinfo->value.enumerated.name,
- ca0132_voicefx_presets[uinfo->value.enumerated.item].name);
- return 0;
-}
-
-static int ca0132_voicefx_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
-
- ucontrol->value.enumerated.item[0] = spec->voicefx_val;
- return 0;
-}
-
-static int ca0132_voicefx_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
- int i, err = 0;
- int sel = ucontrol->value.enumerated.item[0];
- unsigned int items = sizeof(ca0132_voicefx_presets)
- / sizeof(struct ct_voicefx_preset);
-
- if (sel >= items)
- return 0;
-
- snd_printdd(KERN_INFO "ca0132_voicefx_put: sel=%d, preset=%s\n",
- sel, ca0132_voicefx_presets[sel].name);
-
- /*
- * Idx 0 is default.
- * Default needs to qualify with CrystalVoice state.
- */
- for (i = 0; i < VOICEFX_MAX_PARAM_COUNT; i++) {
- err = dspio_set_uint_param(codec, ca0132_voicefx.mid,
- ca0132_voicefx.reqs[i],
- ca0132_voicefx_presets[sel].vals[i]);
- if (err < 0)
- break;
- }
-
- if (err >= 0) {
- spec->voicefx_val = sel;
- /* enable voice fx */
- ca0132_voicefx_set(codec, (sel ? 1 : 0));
- }
-
- return 1;
-}
-
-static int ca0132_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kcontrol);
- int ch = get_amp_channels(kcontrol);
- long *valp = ucontrol->value.integer.value;
-
- /* vnode */
- if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) {
- if (ch & 1) {
- *valp = spec->vnode_lswitch[nid - VNODE_START_NID];
- valp++;
- }
- if (ch & 2) {
- *valp = spec->vnode_rswitch[nid - VNODE_START_NID];
- valp++;
- }
- return 0;
- }
-
- /* effects, include PE and CrystalVoice */
- if ((nid >= EFFECT_START_NID) && (nid < EFFECT_END_NID)) {
- *valp = spec->effects_switch[nid - EFFECT_START_NID];
- return 0;
- }
-
- /* mic boost */
- if (nid == spec->input_pins[0]) {
- *valp = spec->cur_mic_boost;
- return 0;
- }
-
- return 0;
-}
-
-static int ca0132_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kcontrol);
- int ch = get_amp_channels(kcontrol);
- long *valp = ucontrol->value.integer.value;
- int changed = 1;
-
- snd_printdd(KERN_INFO "ca0132_switch_put: nid=0x%x, val=%ld\n",
- nid, *valp);
-
- snd_hda_power_up(codec);
- /* vnode */
- if ((nid >= VNODE_START_NID) && (nid < VNODE_END_NID)) {
- if (ch & 1) {
- spec->vnode_lswitch[nid - VNODE_START_NID] = *valp;
- valp++;
- }
- if (ch & 2) {
- spec->vnode_rswitch[nid - VNODE_START_NID] = *valp;
- valp++;
- }
- changed = ca0132_vnode_switch_set(kcontrol, ucontrol);
- goto exit;
- }
-
- /* PE */
- if (nid == PLAY_ENHANCEMENT) {
- spec->effects_switch[nid - EFFECT_START_NID] = *valp;
- changed = ca0132_pe_switch_set(codec);
- goto exit;
- }
-
- /* CrystalVoice */
- if (nid == CRYSTAL_VOICE) {
- spec->effects_switch[nid - EFFECT_START_NID] = *valp;
- changed = ca0132_cvoice_switch_set(codec);
- goto exit;
- }
-
- /* out and in effects */
- if (((nid >= OUT_EFFECT_START_NID) && (nid < OUT_EFFECT_END_NID)) ||
- ((nid >= IN_EFFECT_START_NID) && (nid < IN_EFFECT_END_NID))) {
- spec->effects_switch[nid - EFFECT_START_NID] = *valp;
- changed = ca0132_effects_set(codec, nid, *valp);
- goto exit;
- }
-
- /* mic boost */
- if (nid == spec->input_pins[0]) {
- spec->cur_mic_boost = *valp;
-
- /* Mic boost does not apply to Digital Mic */
- if (spec->cur_mic_type != DIGITAL_MIC)
- changed = ca0132_mic_boost_set(codec, *valp);
- goto exit;
- }
-
-exit:
- snd_hda_power_down(codec);
- return changed;
-}
-
-/*
- * Volume related
- */
-static int ca0132_volume_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kcontrol);
- int ch = get_amp_channels(kcontrol);
- int dir = get_amp_direction(kcontrol);
- unsigned long pval;
- int err;
-
- switch (nid) {
- case VNID_SPK:
- /* follow shared_out info */
- nid = spec->shared_out_nid;
- mutex_lock(&codec->control_mutex);
- pval = kcontrol->private_value;
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
- err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
- kcontrol->private_value = pval;
- mutex_unlock(&codec->control_mutex);
- break;
- case VNID_MIC:
- /* follow shared_mic info */
- nid = spec->shared_mic_nid;
- mutex_lock(&codec->control_mutex);
- pval = kcontrol->private_value;
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
- err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
- kcontrol->private_value = pval;
- mutex_unlock(&codec->control_mutex);
- break;
- default:
- err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
- }
- return err;
-}
-
-static int ca0132_volume_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kcontrol);
- int ch = get_amp_channels(kcontrol);
- long *valp = ucontrol->value.integer.value;
-
- /* store the left and right volume */
- if (ch & 1) {
- *valp = spec->vnode_lvol[nid - VNODE_START_NID];
- valp++;
- }
- if (ch & 2) {
- *valp = spec->vnode_rvol[nid - VNODE_START_NID];
- valp++;
- }
- return 0;
-}
-
-static int ca0132_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kcontrol);
- int ch = get_amp_channels(kcontrol);
- long *valp = ucontrol->value.integer.value;
- hda_nid_t shared_nid = 0;
- bool effective;
- int changed = 1;
-
- /* store the left and right volume */
- if (ch & 1) {
- spec->vnode_lvol[nid - VNODE_START_NID] = *valp;
- valp++;
- }
- if (ch & 2) {
- spec->vnode_rvol[nid - VNODE_START_NID] = *valp;
- valp++;
- }
-
- /* if effective conditions, then update hw immediately. */
- effective = ca0132_is_vnode_effective(codec, nid, &shared_nid);
- if (effective) {
- int dir = get_amp_direction(kcontrol);
- unsigned long pval;
-
- snd_hda_power_up(codec);
- mutex_lock(&codec->control_mutex);
- pval = kcontrol->private_value;
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(shared_nid, ch,
- 0, dir);
- changed = snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
- kcontrol->private_value = pval;
- mutex_unlock(&codec->control_mutex);
- snd_hda_power_down(codec);
- }
-
- return changed;
-}
-
-static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct ca0132_spec *spec = codec->spec;
- hda_nid_t nid = get_amp_nid(kcontrol);
- int ch = get_amp_channels(kcontrol);
- int dir = get_amp_direction(kcontrol);
- unsigned long pval;
- int err;
-
- switch (nid) {
- case VNID_SPK:
- /* follow shared_out tlv */
- nid = spec->shared_out_nid;
- mutex_lock(&codec->control_mutex);
- pval = kcontrol->private_value;
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
- err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
- kcontrol->private_value = pval;
- mutex_unlock(&codec->control_mutex);
- break;
- case VNID_MIC:
- /* follow shared_mic tlv */
- nid = spec->shared_mic_nid;
- mutex_lock(&codec->control_mutex);
- pval = kcontrol->private_value;
- kcontrol->private_value = HDA_COMPOSE_AMP_VAL(nid, ch, 0, dir);
- err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
- kcontrol->private_value = pval;
- mutex_unlock(&codec->control_mutex);
- break;
- default:
- err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
- }
- return err;
-}
-
-static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid,
- const char *pfx, int dir)
-{
- char namestr[44];
- int type = dir ? HDA_INPUT : HDA_OUTPUT;
- struct snd_kcontrol_new knew =
- CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type);
- sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
- return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
-}
-
-static int add_voicefx(struct hda_codec *codec)
-{
- struct snd_kcontrol_new knew =
- HDA_CODEC_MUTE_MONO(ca0132_voicefx.name,
- VOICEFX, 1, 0, HDA_INPUT);
- knew.info = ca0132_voicefx_info;
- knew.get = ca0132_voicefx_get;
- knew.put = ca0132_voicefx_put;
- return snd_hda_ctl_add(codec, VOICEFX, snd_ctl_new1(&knew, codec));
-}
-
-/*
- * When changing Node IDs for Mixer Controls below, make sure to update
- * Node IDs in ca0132_config() as well.
- */
-static struct snd_kcontrol_new ca0132_mixer[] = {
- CA0132_CODEC_VOL("Master Playback Volume", VNID_SPK, HDA_OUTPUT),
- CA0132_CODEC_MUTE("Master Playback Switch", VNID_SPK, HDA_OUTPUT),
- CA0132_CODEC_VOL("Capture Volume", VNID_MIC, HDA_INPUT),
- CA0132_CODEC_MUTE("Capture Switch", VNID_MIC, HDA_INPUT),
- HDA_CODEC_VOLUME("Analog-Mic2 Capture Volume", 0x08, 0, HDA_INPUT),
- HDA_CODEC_MUTE("Analog-Mic2 Capture Switch", 0x08, 0, HDA_INPUT),
- HDA_CODEC_VOLUME("What U Hear Capture Volume", 0x0a, 0, HDA_INPUT),
- HDA_CODEC_MUTE("What U Hear Capture Switch", 0x0a, 0, HDA_INPUT),
- CA0132_CODEC_MUTE_MONO("Mic1-Boost (30dB) Capture Switch",
- 0x12, 1, HDA_INPUT),
- CA0132_CODEC_MUTE_MONO("HP/Speaker Playback Switch",
- VNID_HP_SEL, 1, HDA_OUTPUT),
- CA0132_CODEC_MUTE_MONO("AMic1/DMic Capture Switch",
- VNID_AMIC1_SEL, 1, HDA_INPUT),
- CA0132_CODEC_MUTE_MONO("HP/Speaker Auto Detect Playback Switch",
- VNID_HP_ASEL, 1, HDA_OUTPUT),
- CA0132_CODEC_MUTE_MONO("AMic1/DMic Auto Detect Capture Switch",
- VNID_AMIC1_ASEL, 1, HDA_INPUT),
- { } /* end */
-};
-
-static int ca0132_build_controls(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- int i, num_fx;
- int err = 0;
-
- /* Add Mixer controls */
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
- }
-
- /* Add in and out effects controls.
- * VoiceFX, PE and CrystalVoice are added separately.
- */
- num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
- for (i = 0; i < num_fx; i++) {
- err = add_fx_switch(codec, ca0132_effects[i].nid,
- ca0132_effects[i].name,
- ca0132_effects[i].direct);
- if (err < 0)
- return err;
- }
-
- err = add_fx_switch(codec, PLAY_ENHANCEMENT, "PlayEnhancement", 0);
- if (err < 0)
- return err;
-
- err = add_fx_switch(codec, CRYSTAL_VOICE, "CrystalVoice", 1);
- if (err < 0)
- return err;
-
- add_voicefx(codec);
-
-#ifdef ENABLE_TUNING_CONTROLS
- add_tuning_ctls(codec);
-#endif
-
- err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
- if (err < 0)
- return err;
-
- if (spec->dig_out) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
- spec->dig_out);
- if (err < 0)
- return err;
- err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
- if (err < 0)
- return err;
- /* spec->multiout.share_spdif = 1; */
- }
-
- if (spec->dig_in) {
- err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
- if (err < 0)
- return err;
- }
- return 0;
-}
-
-/*
- * PCM
- */
-static struct hda_pcm_stream ca0132_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 6,
- .ops = {
- .prepare = ca0132_playback_pcm_prepare,
- .cleanup = ca0132_playback_pcm_cleanup
- },
-};
+static struct hda_pcm_stream ca0132_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = ca0132_playback_pcm_open,
+ .prepare = ca0132_playback_pcm_prepare,
+ .cleanup = ca0132_playback_pcm_cleanup
+ },
+};
static struct hda_pcm_stream ca0132_pcm_analog_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
- .ops = {
- .prepare = ca0132_capture_pcm_prepare,
- .cleanup = ca0132_capture_pcm_cleanup
- },
};
static struct hda_pcm_stream ca0132_pcm_digital_playback = {
@@ -4028,27 +586,13 @@ static int ca0132_build_pcms(struct hda_codec *codec)
codec->num_pcms = 0;
info->name = "CA0132 Analog";
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
- spec->multiout.max_channels;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
- codec->num_pcms++;
-
- info++;
- info->name = "CA0132 Analog Mic-In2";
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1];
- codec->num_pcms++;
-
- info++;
- info->name = "CA0132 What U Hear";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2];
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
codec->num_pcms++;
if (!spec->dig_out && !spec->dig_in)
@@ -4072,465 +616,330 @@ static int ca0132_build_pcms(struct hda_codec *codec)
return 0;
}
-static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
-{
- if (pin) {
- snd_hda_set_pin_ctl(codec, pin, PIN_HP);
- if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- }
- if (dac && (get_wcaps(codec, dac) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, dac, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
-}
+#define REG_CODEC_MUTE 0x18b014
+#define REG_CODEC_HP_VOL_L 0x18b070
+#define REG_CODEC_HP_VOL_R 0x18b074
-static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
+static int ca0132_hp_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- if (pin) {
- snd_hda_set_pin_ctl(codec, pin, PIN_VREF80);
- if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
- }
- if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP)) {
- snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(0));
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ long *valp = ucontrol->value.integer.value;
- /* init to 0 dB and unmute. */
- snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0,
- HDA_AMP_VOLMASK, 0x5a);
- snd_hda_codec_amp_stereo(codec, adc, HDA_INPUT, 0,
- HDA_AMP_MUTE, 0);
- }
+ *valp = spec->curr_hp_switch;
+ return 0;
}
-static void ca0132_init_unsol(struct hda_codec *codec)
+static int ca0132_hp_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- snd_hda_jack_detect_enable(codec, UNSOL_TAG_HP, UNSOL_TAG_HP);
- snd_hda_jack_detect_enable(codec, UNSOL_TAG_AMIC1, UNSOL_TAG_AMIC1);
-}
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ long *valp = ucontrol->value.integer.value;
+ unsigned int data;
+ int err;
-static void refresh_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir)
-{
- unsigned int caps;
+ /* any change? */
+ if (spec->curr_hp_switch == *valp)
+ return 0;
+
+ snd_hda_power_up(codec);
+
+ err = chipio_read(codec, REG_CODEC_MUTE, &data);
+ if (err < 0)
+ goto exit;
- caps = snd_hda_param_read(codec, nid, dir == HDA_OUTPUT ?
- AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
- snd_hda_override_amp_caps(codec, nid, dir, caps);
+ /* *valp 0 is mute, 1 is unmute */
+ data = (data & 0x7f) | (*valp ? 0 : 0x80);
+ err = chipio_write(codec, REG_CODEC_MUTE, data);
+ if (err < 0)
+ goto exit;
+
+ spec->curr_hp_switch = *valp;
+
+ exit:
+ snd_hda_power_down(codec);
+ return err < 0 ? err : 1;
}
-/*
- * Switch between Digital built-in mic and analog mic.
- */
-static void ca0132_set_dmic(struct hda_codec *codec, int enable)
+static int ca0132_speaker_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ca0132_spec *spec = codec->spec;
- unsigned int tmp;
- u8 val;
- unsigned int oldval;
-
- snd_printdd(KERN_INFO "ca0132_set_dmic: enable=%d\n", enable);
-
- oldval = stop_mic1(codec);
- ca0132_set_vipsource(codec, 0);
- if (enable) {
- /* set DMic input as 2-ch */
- tmp = FLOAT_TWO;
- dspio_set_uint_param(codec, 0x80, 0x00, tmp);
-
- val = spec->dmic_ctl;
- val |= 0x80;
- snd_hda_codec_write(codec, spec->input_pins[0], 0,
- VENDOR_CHIPIO_DMIC_CTL_SET, val);
-
- if (!(spec->dmic_ctl & 0x20))
- chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 1);
- } else {
- /* set AMic input as mono */
- tmp = FLOAT_ONE;
- dspio_set_uint_param(codec, 0x80, 0x00, tmp);
-
- val = spec->dmic_ctl;
- /* clear bit7 and bit5 to disable dmic */
- val &= 0x5f;
- snd_hda_codec_write(codec, spec->input_pins[0], 0,
- VENDOR_CHIPIO_DMIC_CTL_SET, val);
-
- if (!(spec->dmic_ctl & 0x20))
- chipio_set_control_flag(codec, CONTROL_FLAG_DMIC, 0);
- }
- ca0132_set_vipsource(codec, 1);
- resume_mic1(codec, oldval);
+ long *valp = ucontrol->value.integer.value;
+
+ *valp = spec->curr_speaker_switch;
+ return 0;
}
-/*
- * Initialization for Digital Mic.
- */
-static void ca0132_init_dmic(struct hda_codec *codec)
+static int ca0132_speaker_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ca0132_spec *spec = codec->spec;
- u8 val;
-
- /* Setup Digital Mic here, but don't enable.
- * Enable based on jack detect.
- */
-
- /* MCLK uses MPIO1, set to enable.
- * Bit 2-0: MPIO select
- * Bit 3: set to disable
- * Bit 7-4: reserved
- */
- val = 0x01;
- snd_hda_codec_write(codec, spec->input_pins[0], 0,
- VENDOR_CHIPIO_DMIC_MCLK_SET, val);
-
- /* Data1 uses MPIO3. Data2 not use
- * Bit 2-0: Data1 MPIO select
- * Bit 3: set disable Data1
- * Bit 6-4: Data2 MPIO select
- * Bit 7: set disable Data2
- */
- val = 0x83;
- snd_hda_codec_write(codec, spec->input_pins[0], 0,
- VENDOR_CHIPIO_DMIC_PIN_SET, val);
-
- /* Use Ch-0 and Ch-1. Rate is 48K, mode 1. Disable DMic first.
- * Bit 3-0: Channel mask
- * Bit 4: set for 48KHz, clear for 32KHz
- * Bit 5: mode
- * Bit 6: set to select Data2, clear for Data1
- * Bit 7: set to enable DMic, clear for AMic
- */
- val = 0x23;
- /* keep a copy of dmic ctl val for enable/disable dmic purpuse */
- spec->dmic_ctl = val;
- snd_hda_codec_write(codec, spec->input_pins[0], 0,
- VENDOR_CHIPIO_DMIC_CTL_SET, val);
+ long *valp = ucontrol->value.integer.value;
+ unsigned int data;
+ int err;
+
+ /* any change? */
+ if (spec->curr_speaker_switch == *valp)
+ return 0;
+
+ snd_hda_power_up(codec);
+
+ err = chipio_read(codec, REG_CODEC_MUTE, &data);
+ if (err < 0)
+ goto exit;
+
+ /* *valp 0 is mute, 1 is unmute */
+ data = (data & 0xef) | (*valp ? 0 : 0x10);
+ err = chipio_write(codec, REG_CODEC_MUTE, data);
+ if (err < 0)
+ goto exit;
+
+ spec->curr_speaker_switch = *valp;
+
+ exit:
+ snd_hda_power_down(codec);
+ return err < 0 ? err : 1;
}
-/*
- * Initialization for Analog Mic 2
- */
-static void ca0132_init_analog_mic2(struct hda_codec *codec)
+static int ca0132_hp_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ca0132_spec *spec = codec->spec;
+ long *valp = ucontrol->value.integer.value;
- mutex_lock(&spec->chipio_mutex);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x20);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_DATA_WRITE, 0x00);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x2D);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19);
- snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
- VENDOR_CHIPIO_8051_DATA_WRITE, 0x00);
- mutex_unlock(&spec->chipio_mutex);
+ *valp++ = spec->curr_hp_volume[0];
+ *valp = spec->curr_hp_volume[1];
+ return 0;
}
-static void ca0132_refresh_widget_caps(struct hda_codec *codec)
+static int ca0132_hp_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ca0132_spec *spec = codec->spec;
- int i;
- hda_nid_t nid;
-
- snd_printdd(KERN_INFO "ca0132_refresh_widget_caps.\n");
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++)
- codec->wcaps[i] = snd_hda_param_read(codec, nid,
- AC_PAR_AUDIO_WIDGET_CAP);
-
- for (i = 0; i < spec->multiout.num_dacs; i++)
- refresh_amp_caps(codec, spec->dacs[i], HDA_OUTPUT);
+ long *valp = ucontrol->value.integer.value;
+ long left_vol, right_vol;
+ unsigned int data;
+ int val;
+ int err;
- for (i = 0; i < spec->num_outputs; i++)
- refresh_amp_caps(codec, spec->out_pins[i], HDA_OUTPUT);
+ left_vol = *valp++;
+ right_vol = *valp;
- for (i = 0; i < spec->num_inputs; i++) {
- refresh_amp_caps(codec, spec->adcs[i], HDA_INPUT);
- refresh_amp_caps(codec, spec->input_pins[i], HDA_INPUT);
- }
-}
+ /* any change? */
+ if ((spec->curr_hp_volume[0] == left_vol) &&
+ (spec->curr_hp_volume[1] == right_vol))
+ return 0;
-/*
- * Setup default parameters for DSP
- */
-static void ca0132_setup_defaults(struct hda_codec *codec)
-{
- unsigned int tmp;
- int num_fx;
- int idx, i;
-
- if (!dspload_is_loaded(codec))
- return;
-
- /* out, in effects + voicefx */
- num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1;
- for (idx = 0; idx < num_fx; idx++) {
- for (i = 0; i <= ca0132_effects[idx].params; i++) {
- dspio_set_uint_param(codec, ca0132_effects[idx].mid,
- ca0132_effects[idx].reqs[i],
- ca0132_effects[idx].def_vals[i]);
- }
- }
+ snd_hda_power_up(codec);
- /*remove DSP headroom*/
- tmp = FLOAT_ZERO;
- dspio_set_uint_param(codec, 0x96, 0x3C, tmp);
+ err = chipio_read(codec, REG_CODEC_HP_VOL_L, &data);
+ if (err < 0)
+ goto exit;
- /*set speaker EQ bypass attenuation*/
- dspio_set_uint_param(codec, 0x8f, 0x01, tmp);
+ val = 31 - left_vol;
+ data = (data & 0xe0) | val;
+ err = chipio_write(codec, REG_CODEC_HP_VOL_L, data);
+ if (err < 0)
+ goto exit;
- /* set AMic1 and AMic2 as mono mic */
- tmp = FLOAT_ONE;
- dspio_set_uint_param(codec, 0x80, 0x00, tmp);
- dspio_set_uint_param(codec, 0x80, 0x01, tmp);
+ val = 31 - right_vol;
+ data = (data & 0xe0) | val;
+ err = chipio_write(codec, REG_CODEC_HP_VOL_R, data);
+ if (err < 0)
+ goto exit;
- /* set AMic1 as CrystalVoice input */
- tmp = FLOAT_ONE;
- dspio_set_uint_param(codec, 0x80, 0x05, tmp);
+ spec->curr_hp_volume[0] = left_vol;
+ spec->curr_hp_volume[1] = right_vol;
- /* set WUH source */
- tmp = FLOAT_TWO;
- dspio_set_uint_param(codec, 0x31, 0x00, tmp);
+ exit:
+ snd_hda_power_down(codec);
+ return err < 0 ? err : 1;
}
-/*
- * Initialization of flags in chip
- */
-static void ca0132_init_flags(struct hda_codec *codec)
+static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid)
{
- chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0);
- chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_COMMON_MODE, 0);
- chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_COMMON_MODE, 0);
- chipio_set_control_flag(codec, CONTROL_FLAG_PORT_A_10KOHM_LOAD, 0);
- chipio_set_control_flag(codec, CONTROL_FLAG_PORT_D_10KOHM_LOAD, 0);
- chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_HIGH_PASS, 1);
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Headphone Playback Switch",
+ nid, 1, 0, HDA_OUTPUT);
+ knew.get = ca0132_hp_switch_get;
+ knew.put = ca0132_hp_switch_put;
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
}
-/*
- * Initialization of parameters in chip
- */
-static void ca0132_init_params(struct hda_codec *codec)
+static int add_hp_volume(struct hda_codec *codec, hda_nid_t nid)
{
- chipio_set_control_param(codec, CONTROL_PARAM_PORTA_160OHM_GAIN, 6);
- chipio_set_control_param(codec, CONTROL_PARAM_PORTD_160OHM_GAIN, 6);
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO("Headphone Playback Volume",
+ nid, 3, 0, HDA_OUTPUT);
+ knew.get = ca0132_hp_volume_get;
+ knew.put = ca0132_hp_volume_put;
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
}
-static void ca0132_set_dsp_msr(struct hda_codec *codec, bool is96k)
+static int add_speaker_switch(struct hda_codec *codec, hda_nid_t nid)
{
- chipio_set_control_flag(codec, CONTROL_FLAG_DSP_96KHZ, is96k);
- chipio_set_control_flag(codec, CONTROL_FLAG_DAC_96KHZ, is96k);
- chipio_set_control_flag(codec, CONTROL_FLAG_SRC_RATE_96KHZ, is96k);
- chipio_set_control_flag(codec, CONTROL_FLAG_SRC_CLOCK_196MHZ, is96k);
- chipio_set_control_flag(codec, CONTROL_FLAG_ADC_B_96KHZ, is96k);
- chipio_set_control_flag(codec, CONTROL_FLAG_ADC_C_96KHZ, is96k);
-
- chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000);
- chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000);
- chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000);
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Speaker Playback Switch",
+ nid, 1, 0, HDA_OUTPUT);
+ knew.get = ca0132_speaker_switch_get;
+ knew.put = ca0132_speaker_switch_put;
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
}
-static bool ca0132_download_dsp_images(struct hda_codec *codec)
+static void ca0132_fix_hp_caps(struct hda_codec *codec)
{
- bool dsp_loaded = false;
- const struct dsp_image_seg *dsp_os_image;
- const struct firmware *fw_entry;
-
- if (request_firmware(&fw_entry, EFX_FILE, codec->bus->card->dev) != 0)
- return false;
-
- dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
- dspload_image(codec, dsp_os_image, 0, 0, true, 0);
- dsp_loaded = dspload_wait_loaded(codec);
-
- release_firmware(fw_entry);
-
+ struct ca0132_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int caps;
- return dsp_loaded;
+ /* set mute-capable, 1db step, 32 steps, ofs 6 */
+ caps = 0x80031f06;
+ snd_hda_override_amp_caps(codec, cfg->hp_pins[0], HDA_OUTPUT, caps);
}
-static void ca0132_download_dsp(struct hda_codec *codec)
+static int ca0132_build_controls(struct hda_codec *codec)
{
struct ca0132_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, err;
-#ifndef CONFIG_SND_HDA_CODEC_CA0132_DSP
- return; /* NOP */
-#endif
- spec->dsp_state = DSP_DOWNLOAD_INIT;
-
- if (spec->dsp_state == DSP_DOWNLOAD_INIT) {
- chipio_enable_clocks(codec);
- spec->dsp_state = DSP_DOWNLOADING;
- if (!ca0132_download_dsp_images(codec))
- spec->dsp_state = DSP_DOWNLOAD_FAILED;
- else
- spec->dsp_state = DSP_DOWNLOADED;
+ if (spec->multiout.num_dacs) {
+ err = add_speaker_switch(codec, spec->out_pins[0]);
+ if (err < 0)
+ return err;
}
- if (spec->dsp_state == DSP_DOWNLOADED)
- ca0132_set_dsp_msr(codec, true);
-}
+ if (cfg->hp_outs) {
+ ca0132_fix_hp_caps(codec);
+ err = add_hp_switch(codec, cfg->hp_pins[0]);
+ if (err < 0)
+ return err;
+ err = add_hp_volume(codec, cfg->hp_pins[0]);
+ if (err < 0)
+ return err;
+ }
-static void ca0132_process_dsp_response(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
+ for (i = 0; i < spec->num_inputs; i++) {
+ const char *label = spec->input_labels[i];
+
+ err = add_in_switch(codec, spec->adcs[i], label);
+ if (err < 0)
+ return err;
+ err = add_in_volume(codec, spec->adcs[i], label);
+ if (err < 0)
+ return err;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC) {
+ /* add Mic-Boost */
+ err = add_in_mono_volume(codec, spec->input_pins[i],
+ "Mic Boost", 1);
+ if (err < 0)
+ return err;
+ }
+ }
- snd_printdd(KERN_INFO "ca0132_process_dsp_response\n");
- if (spec->wait_scp) {
- if (dspio_get_response_data(codec) >= 0)
- spec->wait_scp = 0;
+ if (spec->dig_out) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
+ spec->dig_out);
+ if (err < 0)
+ return err;
+ err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
+ if (err < 0)
+ return err;
+ /* spec->multiout.share_spdif = 1; */
}
- dspio_clear_response_queue(codec);
+ if (spec->dig_in) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+ if (err < 0)
+ return err;
+ }
+ return 0;
}
-static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res)
+
+static void ca0132_set_ct_ext(struct hda_codec *codec, int enable)
{
- snd_printdd(KERN_INFO "ca0132_unsol_event: 0x%x\n", res);
-
-
- if (((res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f) == UNSOL_TAG_DSP) {
- ca0132_process_dsp_response(codec);
- } else {
- res = snd_hda_jack_get_action(codec,
- (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f);
-
- snd_printdd(KERN_INFO "snd_hda_jack_get_action: 0x%x\n", res);
-
- switch (res) {
- case UNSOL_TAG_HP:
- ca0132_select_out(codec);
- snd_hda_jack_report_sync(codec);
- break;
- case UNSOL_TAG_AMIC1:
- ca0132_select_mic(codec);
- snd_hda_jack_report_sync(codec);
- break;
- default:
- break;
- }
- }
+ /* Set Creative extension */
+ snd_printdd("SET CREATIVE EXTENSION\n");
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE,
+ enable);
+ msleep(20);
}
-/*
- * Verbs tables.
- */
-/* Sends before DSP download. */
-static struct hda_verb ca0132_base_init_verbs[] = {
- /*enable ct extension*/
- {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0x1},
- /*enable DSP node unsol, needed for DSP download*/
- {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_DSP},
- {}
-};
+static void ca0132_config(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
-/* Send at exit. */
-static struct hda_verb ca0132_base_exit_verbs[] = {
- /*set afg to D3*/
- {0x01, AC_VERB_SET_POWER_STATE, 0x03},
- /*disable ct extension*/
- {0x15, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE, 0},
- {}
-};
+ codec->pcm_format_first = 1;
+ codec->no_sticky_stream = 1;
-/* Other verbs tables. Sends after DSP download. */
-static struct hda_verb ca0132_init_verbs0[] = {
- /* chip init verbs */
- {0x15, 0x70D, 0xF0},
- {0x15, 0x70E, 0xFE},
- {0x15, 0x707, 0x75},
- {0x15, 0x707, 0xD3},
- {0x15, 0x707, 0x09},
- {0x15, 0x707, 0x53},
- {0x15, 0x707, 0xD4},
- {0x15, 0x707, 0xEF},
- {0x15, 0x707, 0x75},
- {0x15, 0x707, 0xD3},
- {0x15, 0x707, 0x09},
- {0x15, 0x707, 0x02},
- {0x15, 0x707, 0x37},
- {0x15, 0x707, 0x78},
- {0x15, 0x53C, 0xCE},
- {0x15, 0x575, 0xC9},
- {0x15, 0x53D, 0xCE},
- {0x15, 0x5B7, 0xC9},
- {0x15, 0x70D, 0xE8},
- {0x15, 0x70E, 0xFE},
- {0x15, 0x707, 0x02},
- {0x15, 0x707, 0x68},
- {0x15, 0x707, 0x62},
- {0x15, 0x53A, 0xCE},
- {0x15, 0x546, 0xC9},
- {0x15, 0x53B, 0xCE},
- {0x15, 0x5E8, 0xC9},
- {0x15, 0x717, 0x0D},
- {0x15, 0x718, 0x20},
- {}
-};
+ /* line-outs */
+ cfg->line_outs = 1;
+ cfg->line_out_pins[0] = 0x0b; /* front */
+ cfg->line_out_type = AUTO_PIN_LINE_OUT;
-static struct hda_verb ca0132_init_verbs1[] = {
- {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_HP},
- {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | UNSOL_TAG_AMIC1},
- /* config EAPD */
- {0x0b, 0x78D, 0x00},
- /*{0x0b, AC_VERB_SET_EAPD_BTLENABLE, 0x02},*/
- /*{0x10, 0x78D, 0x02},*/
- /*{0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x02},*/
- {}
-};
+ spec->dacs[0] = 0x02;
+ spec->out_pins[0] = 0x0b;
+ spec->multiout.dac_nids = spec->dacs;
+ spec->multiout.num_dacs = 1;
+ spec->multiout.max_channels = 2;
-static void ca0132_init_chip(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- int num_fx;
- int i;
- unsigned int on;
+ /* headphone */
+ cfg->hp_outs = 1;
+ cfg->hp_pins[0] = 0x0f;
- mutex_init(&spec->chipio_mutex);
+ spec->hp_dac = 0;
+ spec->multiout.hp_nid = 0;
- spec->cur_out_type = SPEAKER_OUT;
- spec->cur_mic_type = DIGITAL_MIC;
- spec->cur_mic_boost = 0;
+ /* inputs */
+ cfg->num_inputs = 2; /* Mic-in and line-in */
+ cfg->inputs[0].pin = 0x12;
+ cfg->inputs[0].type = AUTO_PIN_MIC;
+ cfg->inputs[1].pin = 0x11;
+ cfg->inputs[1].type = AUTO_PIN_LINE_IN;
- for (i = 0; i < VNODES_COUNT; i++) {
- spec->vnode_lvol[i] = 0x5a;
- spec->vnode_rvol[i] = 0x5a;
- spec->vnode_lswitch[i] = 0;
- spec->vnode_rswitch[i] = 0;
- }
+ /* Mic-in */
+ spec->input_pins[0] = 0x12;
+ spec->input_labels[0] = "Mic";
+ spec->adcs[0] = 0x07;
- /*
- * Default states for effects are in ca0132_effects[].
- */
- num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT;
- for (i = 0; i < num_fx; i++) {
- on = (unsigned int)ca0132_effects[i].reqs[0];
- spec->effects_switch[i] = on ? 1 : 0;
- }
+ /* Line-In */
+ spec->input_pins[1] = 0x11;
+ spec->input_labels[1] = "Line";
+ spec->adcs[1] = 0x08;
+ spec->num_inputs = 2;
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ cfg->dig_out_pins[0] = 0x0c;
+ cfg->dig_outs = 1;
+ cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF;
+ spec->dig_in = 0x09;
+ cfg->dig_in_pin = 0x0e;
+ cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+}
- spec->voicefx_val = 0;
- spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1;
- spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] = 0;
+static void ca0132_init_chip(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
-#ifdef ENABLE_TUNING_CONTROLS
- ca0132_init_tuning_defaults(codec);
-#endif
+ mutex_init(&spec->chipio_mutex);
}
static void ca0132_exit_chip(struct hda_codec *codec)
{
/* put any chip cleanup stuffs here. */
-
- if (dspload_is_loaded(codec))
- dsp_reset(codec);
}
static int ca0132_init(struct hda_codec *codec)
@@ -4539,23 +948,11 @@ static int ca0132_init(struct hda_codec *codec)
struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- spec->dsp_state = DSP_DOWNLOAD_INIT;
- spec->curr_chip_addx = INVALID_CHIP_ADDRESS;
-
- snd_hda_power_up(codec);
-
- ca0132_init_params(codec);
- ca0132_init_flags(codec);
- snd_hda_sequence_write(codec, spec->base_init_verbs);
- ca0132_download_dsp(codec);
- ca0132_refresh_widget_caps(codec);
- ca0132_setup_defaults(codec);
- ca0132_init_analog_mic2(codec);
- ca0132_init_dmic(codec);
-
- for (i = 0; i < spec->num_outputs; i++)
- init_output(codec, spec->out_pins[i], spec->dacs[0]);
-
+ for (i = 0; i < spec->multiout.num_dacs; i++) {
+ init_output(codec, spec->out_pins[i],
+ spec->multiout.dac_nids[i]);
+ }
+ init_output(codec, cfg->hp_pins[0], spec->hp_dac);
init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
for (i = 0; i < spec->num_inputs; i++)
@@ -4563,29 +960,16 @@ static int ca0132_init(struct hda_codec *codec)
init_input(codec, cfg->dig_in_pin, spec->dig_in);
- for (i = 0; i < spec->num_init_verbs; i++)
- snd_hda_sequence_write(codec, spec->init_verbs[i]);
-
- ca0132_init_unsol(codec);
-
- ca0132_select_out(codec);
- ca0132_select_mic(codec);
-
- snd_hda_jack_report_sync(codec);
-
- snd_hda_power_down(codec);
+ ca0132_set_ct_ext(codec, 1);
return 0;
}
+
static void ca0132_free(struct hda_codec *codec)
{
- struct ca0132_spec *spec = codec->spec;
-
- snd_hda_power_up(codec);
- snd_hda_sequence_write(codec, spec->base_exit_verbs);
+ ca0132_set_ct_ext(codec, 0);
ca0132_exit_chip(codec);
- snd_hda_power_down(codec);
kfree(codec->spec);
}
@@ -4594,52 +978,13 @@ static struct hda_codec_ops ca0132_patch_ops = {
.build_pcms = ca0132_build_pcms,
.init = ca0132_init,
.free = ca0132_free,
- .unsol_event = ca0132_unsol_event,
};
-static void ca0132_config(struct hda_codec *codec)
-{
- struct ca0132_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
-
- spec->dacs[0] = 0x2;
- spec->dacs[1] = 0x3;
- spec->dacs[2] = 0x4;
-
- spec->multiout.dac_nids = spec->dacs;
- spec->multiout.num_dacs = 3;
- spec->multiout.max_channels = 2;
-
- spec->num_outputs = 2;
- spec->out_pins[0] = 0x0b; /* speaker out */
- spec->out_pins[1] = 0x10; /* headphone out */
- spec->shared_out_nid = 0x2;
-
- spec->num_inputs = 3;
- spec->adcs[0] = 0x7; /* digital mic / analog mic1 */
- spec->adcs[1] = 0x8; /* analog mic2 */
- spec->adcs[2] = 0xa; /* what u hear */
- spec->shared_mic_nid = 0x7;
-
- spec->input_pins[0] = 0x12;
- spec->input_pins[1] = 0x11;
- spec->input_pins[2] = 0x13;
- /* SPDIF I/O */
- spec->dig_out = 0x05;
- spec->multiout.dig_out_nid = spec->dig_out;
- cfg->dig_out_pins[0] = 0x0c;
- cfg->dig_outs = 1;
- cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF;
- spec->dig_in = 0x09;
- cfg->dig_in_pin = 0x0e;
- cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
-}
static int patch_ca0132(struct hda_codec *codec)
{
struct ca0132_spec *spec;
- int err;
snd_printdd("patch_ca0132\n");
@@ -4648,23 +993,10 @@ static int patch_ca0132(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
- spec->num_mixers = 1;
- spec->mixers[0] = ca0132_mixer;
-
- spec->base_init_verbs = ca0132_base_init_verbs;
- spec->base_exit_verbs = ca0132_base_exit_verbs;
- spec->init_verbs[0] = ca0132_init_verbs0;
- spec->init_verbs[1] = ca0132_init_verbs1;
- spec->num_init_verbs = 2;
-
ca0132_init_chip(codec);
ca0132_config(codec);
- err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
- if (err < 0)
- return err;
-
codec->patch_ops = ca0132_patch_ops;
return 0;
@@ -4681,7 +1013,7 @@ static struct hda_codec_preset snd_hda_preset_ca0132[] = {
MODULE_ALIAS("snd-hda-codec-id:11020011");
MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Creative Sound Core3D codec");
+MODULE_DESCRIPTION("Creative CA0132, CA0132 HD-audio codec");
static struct hda_codec_preset_list ca0132_list = {
.preset = snd_hda_preset_ca0132,
diff --git a/trunk/sound/pci/hda/patch_cirrus.c b/trunk/sound/pci/hda/patch_cirrus.c
index 72ebb8a36b13..a2537b2f8724 100644
--- a/trunk/sound/pci/hda/patch_cirrus.c
+++ b/trunk/sound/pci/hda/patch_cirrus.c
@@ -19,16 +19,16 @@
*/
#include
+#include
#include
#include
#include
#include
-#include
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_auto_parser.h"
#include "hda_jack.h"
-#include "hda_generic.h"
+#include
/*
*/
@@ -36,17 +36,45 @@
struct cs_spec {
struct hda_gen_spec gen;
+ struct auto_pin_cfg autocfg;
+ struct hda_multi_out multiout;
+ struct snd_kcontrol *vmaster_sw;
+ struct snd_kcontrol *vmaster_vol;
+
+ hda_nid_t dac_nid[AUTO_CFG_MAX_OUTS];
+ hda_nid_t slave_dig_outs[2];
+
+ unsigned int input_idx[AUTO_PIN_LAST];
+ unsigned int capsrc_idx[AUTO_PIN_LAST];
+ hda_nid_t adc_nid[AUTO_PIN_LAST];
+ unsigned int adc_idx[AUTO_PIN_LAST];
+ unsigned int num_inputs;
+ unsigned int cur_input;
+ unsigned int automic_idx;
+ hda_nid_t cur_adc;
+ unsigned int cur_adc_stream_tag;
+ unsigned int cur_adc_format;
+ hda_nid_t dig_in;
+
+ const struct hda_bind_ctls *capture_bind[2];
+
unsigned int gpio_mask;
unsigned int gpio_dir;
unsigned int gpio_data;
unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */
unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */
+ struct hda_pcm pcm_rec[2]; /* PCM information */
+
+ unsigned int hp_detect:1;
+ unsigned int mic_detect:1;
+ unsigned int speaker_2_1:1;
/* CS421x */
unsigned int spdif_detect:1;
- unsigned int spdif_present:1;
unsigned int sense_b:1;
hda_nid_t vendor_nid;
+ struct hda_input_mux input_mux;
+ unsigned int last_input;
};
/* available models with CS420x */
@@ -122,34 +150,756 @@ enum {
#define CS421X_DMIC_PIN_NID 0x09 /* Port E */
#define CS421X_SPDIF_PIN_NID 0x0A /* Port H */
-#define CS421X_IDX_DEV_CFG 0x01
-#define CS421X_IDX_ADC_CFG 0x02
-#define CS421X_IDX_DAC_CFG 0x03
-#define CS421X_IDX_SPK_CTL 0x04
+#define CS421X_IDX_DEV_CFG 0x01
+#define CS421X_IDX_ADC_CFG 0x02
+#define CS421X_IDX_DAC_CFG 0x03
+#define CS421X_IDX_SPK_CTL 0x04
+
+#define SPDIF_EVENT 0x04
+
+/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */
+#define CS4213_VENDOR_NID 0x09
+
+
+static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
+{
+ struct cs_spec *spec = codec->spec;
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
+ AC_VERB_SET_COEF_INDEX, idx);
+ return snd_hda_codec_read(codec, spec->vendor_nid, 0,
+ AC_VERB_GET_PROC_COEF, 0);
+}
+
+static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
+ unsigned int coef)
+{
+ struct cs_spec *spec = codec->spec;
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
+ AC_VERB_SET_COEF_INDEX, idx);
+ snd_hda_codec_write(codec, spec->vendor_nid, 0,
+ AC_VERB_SET_PROC_COEF, coef);
+}
+
+
+#define HP_EVENT 1
+#define MIC_EVENT 2
+
+/*
+ * PCM callbacks
+ */
+static int cs_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
+}
+
+static int cs_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int cs_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int cs_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int cs_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int cs_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+ format, substream);
+}
+
+static int cs_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
+static void cs_update_input_select(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ if (spec->cur_adc)
+ snd_hda_codec_write(codec, spec->cur_adc, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ spec->adc_idx[spec->cur_input]);
+}
+
+/*
+ * Analog capture
+ */
+static int cs_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ spec->cur_adc = spec->adc_nid[spec->cur_input];
+ spec->cur_adc_stream_tag = stream_tag;
+ spec->cur_adc_format = format;
+ cs_update_input_select(codec);
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+ return 0;
+}
+
+static int cs_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct cs_spec *spec = codec->spec;
+ snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+ spec->cur_adc = 0;
+ return 0;
+}
+
+/*
+ */
+static const struct hda_pcm_stream cs_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = cs_playback_pcm_open,
+ .prepare = cs_playback_pcm_prepare,
+ .cleanup = cs_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream cs_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .prepare = cs_capture_pcm_prepare,
+ .cleanup = cs_capture_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream cs_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .open = cs_dig_playback_pcm_open,
+ .close = cs_dig_playback_pcm_close,
+ .prepare = cs_dig_playback_pcm_prepare,
+ .cleanup = cs_dig_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream cs_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+static int cs_build_pcms(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+
+ codec->pcm_info = info;
+ codec->num_pcms = 0;
+
+ info->name = "Cirrus Analog";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cs_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dac_nid[0];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+ if (spec->speaker_2_1)
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap =
+ snd_pcm_2_1_chmaps;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = cs_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+ spec->adc_nid[spec->cur_input];
+ codec->num_pcms++;
+
+ if (!spec->multiout.dig_out_nid && !spec->dig_in)
+ return 0;
+
+ info++;
+ info->name = "Cirrus Digital";
+ info->pcm_type = spec->autocfg.dig_out_type[0];
+ if (!info->pcm_type)
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->multiout.dig_out_nid) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ cs_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->multiout.dig_out_nid;
+ }
+ if (spec->dig_in) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ cs_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+ }
+ codec->num_pcms++;
+
+ return 0;
+}
+
+/*
+ * parse codec topology
+ */
+
+static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin)
+{
+ hda_nid_t dac;
+ if (!pin)
+ return 0;
+ if (snd_hda_get_connections(codec, pin, &dac, 1) != 1)
+ return 0;
+ return dac;
+}
+
+static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t pin = cfg->inputs[idx].pin;
+ unsigned int val;
+ if (!is_jack_detectable(codec, pin))
+ return 0;
+ val = snd_hda_codec_get_pincfg(codec, pin);
+ return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT);
+}
+
+static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
+ unsigned int *idxp)
+{
+ int i, idx;
+ hda_nid_t nid;
+
+ nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ unsigned int type;
+ type = get_wcaps_type(get_wcaps(codec, nid));
+ if (type != AC_WID_AUD_IN)
+ continue;
+ idx = snd_hda_get_conn_index(codec, nid, pin, false);
+ if (idx >= 0) {
+ *idxp = idx;
+ return nid;
+ }
+ }
+ return 0;
+}
+
+static int is_active_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int val;
+ val = snd_hda_codec_get_pincfg(codec, nid);
+ return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
+}
+
+static int parse_output(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, extra_nids;
+ hda_nid_t dac;
+
+ for (i = 0; i < cfg->line_outs; i++) {
+ dac = get_dac(codec, cfg->line_out_pins[i]);
+ if (!dac)
+ break;
+ spec->dac_nid[i] = dac;
+ }
+ spec->multiout.num_dacs = i;
+ spec->multiout.dac_nids = spec->dac_nid;
+ spec->multiout.max_channels = i * 2;
+
+ if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && i == 2)
+ spec->speaker_2_1 = 1; /* assume 2.1 speakers */
+
+ /* add HP and speakers */
+ extra_nids = 0;
+ for (i = 0; i < cfg->hp_outs; i++) {
+ dac = get_dac(codec, cfg->hp_pins[i]);
+ if (!dac)
+ break;
+ if (!i)
+ spec->multiout.hp_nid = dac;
+ else
+ spec->multiout.extra_out_nid[extra_nids++] = dac;
+ }
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ dac = get_dac(codec, cfg->speaker_pins[i]);
+ if (!dac)
+ break;
+ spec->multiout.extra_out_nid[extra_nids++] = dac;
+ }
+
+ if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ cfg->speaker_outs = cfg->line_outs;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->line_outs = 0;
+ memset(cfg->line_out_pins, 0, sizeof(cfg->line_out_pins));
+ }
+
+ return 0;
+}
+
+static int parse_input(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin = cfg->inputs[i].pin;
+ spec->input_idx[spec->num_inputs] = i;
+ spec->capsrc_idx[i] = spec->num_inputs++;
+ spec->cur_input = i;
+ spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]);
+ }
+ if (!spec->num_inputs)
+ return 0;
+
+ /* check whether the automatic mic switch is available */
+ if (spec->num_inputs == 2 &&
+ cfg->inputs[0].type == AUTO_PIN_MIC &&
+ cfg->inputs[1].type == AUTO_PIN_MIC) {
+ if (is_ext_mic(codec, cfg->inputs[0].pin)) {
+ if (!is_ext_mic(codec, cfg->inputs[1].pin)) {
+ spec->mic_detect = 1;
+ spec->automic_idx = 0;
+ }
+ } else {
+ if (is_ext_mic(codec, cfg->inputs[1].pin)) {
+ spec->mic_detect = 1;
+ spec->automic_idx = 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+static int parse_digital_output(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
+
+ if (!cfg->dig_outs)
+ return 0;
+ if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1)
+ return 0;
+ spec->multiout.dig_out_nid = nid;
+ spec->multiout.share_spdif = 1;
+ if (cfg->dig_outs > 1 &&
+ snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) {
+ spec->slave_dig_outs[0] = nid;
+ codec->slave_dig_outs = spec->slave_dig_outs;
+ }
+ return 0;
+}
+
+static int parse_digital_input(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int idx;
+
+ if (cfg->dig_in_pin)
+ spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx);
+ return 0;
+}
+
+/*
+ * create mixer controls
+ */
+
+static const char * const dir_sfx[2] = { "Playback", "Capture" };
+
+static int add_mute(struct hda_codec *codec, const char *name, int index,
+ unsigned int pval, int dir, struct snd_kcontrol **kctlp)
+{
+ char tmp[44];
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_IDX(tmp, index, 0, 0, HDA_OUTPUT);
+ knew.private_value = pval;
+ snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]);
+ *kctlp = snd_ctl_new1(&knew, codec);
+ (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG;
+ return snd_hda_ctl_add(codec, 0, *kctlp);
+}
+
+static int add_volume(struct hda_codec *codec, const char *name,
+ int index, unsigned int pval, int dir,
+ struct snd_kcontrol **kctlp)
+{
+ char tmp[44];
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_IDX(tmp, index, 0, 0, HDA_OUTPUT);
+ knew.private_value = pval;
+ snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]);
+ *kctlp = snd_ctl_new1(&knew, codec);
+ (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG;
+ return snd_hda_ctl_add(codec, 0, *kctlp);
+}
+
+static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
+{
+ unsigned int caps;
+
+ /* set the upper-limit for mixer amp to 0dB */
+ caps = query_amp_caps(codec, dac, HDA_OUTPUT);
+ caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT);
+ caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f)
+ << AC_AMPCAP_NUM_STEPS_SHIFT;
+ snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps);
+}
+
+static int add_vmaster(struct hda_codec *codec, hda_nid_t dac)
+{
+ struct cs_spec *spec = codec->spec;
+ unsigned int tlv[4];
+ int err;
+
+ spec->vmaster_sw =
+ snd_ctl_make_virtual_master("Master Playback Switch", NULL);
+ err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw);
+ if (err < 0)
+ return err;
+
+ snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv);
+ spec->vmaster_vol =
+ snd_ctl_make_virtual_master("Master Playback Volume", tlv);
+ err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx,
+ int num_ctls, int type)
+{
+ struct cs_spec *spec = codec->spec;
+ const char *name;
+ int err, index;
+ struct snd_kcontrol *kctl;
+ static const char * const speakers[] = {
+ "Front Speaker", "Surround Speaker", "Bass Speaker"
+ };
+ static const char * const line_outs[] = {
+ "Front Line Out", "Surround Line Out", "Bass Line Out"
+ };
+
+ fix_volume_caps(codec, dac);
+ if (!spec->vmaster_sw) {
+ err = add_vmaster(codec, dac);
+ if (err < 0)
+ return err;
+ }
+
+ index = 0;
+ switch (type) {
+ case AUTO_PIN_HP_OUT:
+ name = "Headphone";
+ index = idx;
+ break;
+ case AUTO_PIN_SPEAKER_OUT:
+ if (spec->speaker_2_1)
+ name = idx ? "Bass Speaker" : "Speaker";
+ else if (num_ctls > 1)
+ name = speakers[idx];
+ else
+ name = "Speaker";
+ break;
+ default:
+ if (num_ctls > 1)
+ name = line_outs[idx];
+ else
+ name = "Line Out";
+ break;
+ }
+
+ err = add_mute(codec, name, index,
+ HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
+ if (err < 0)
+ return err;
+ err = snd_ctl_add_slave(spec->vmaster_sw, kctl);
+ if (err < 0)
+ return err;
+
+ err = add_volume(codec, name, index,
+ HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
+ if (err < 0)
+ return err;
+ err = snd_ctl_add_slave(spec->vmaster_vol, kctl);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int build_output(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, err;
+
+ for (i = 0; i < cfg->line_outs; i++) {
+ err = add_output(codec, get_dac(codec, cfg->line_out_pins[i]),
+ i, cfg->line_outs, cfg->line_out_type);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < cfg->hp_outs; i++) {
+ err = add_output(codec, get_dac(codec, cfg->hp_pins[i]),
+ i, cfg->hp_outs, AUTO_PIN_HP_OUT);
+ if (err < 0)
+ return err;
+ }
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ err = add_output(codec, get_dac(codec, cfg->speaker_pins[i]),
+ i, cfg->speaker_outs, AUTO_PIN_SPEAKER_OUT);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/*
+ */
+
+static const struct snd_kcontrol_new cs_capture_ctls[] = {
+ HDA_BIND_SW("Capture Switch", 0),
+ HDA_BIND_VOL("Capture Volume", 0),
+};
+
+static int change_cur_input(struct hda_codec *codec, unsigned int idx,
+ int force)
+{
+ struct cs_spec *spec = codec->spec;
+
+ if (spec->cur_input == idx && !force)
+ return 0;
+ if (spec->cur_adc && spec->cur_adc != spec->adc_nid[idx]) {
+ /* stream is running, let's swap the current ADC */
+ __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+ spec->cur_adc = spec->adc_nid[idx];
+ snd_hda_codec_setup_stream(codec, spec->cur_adc,
+ spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+ }
+ spec->cur_input = idx;
+ cs_update_input_select(codec);
+ return 1;
+}
+
+static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int idx;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = spec->num_inputs;
+ if (uinfo->value.enumerated.item >= spec->num_inputs)
+ uinfo->value.enumerated.item = spec->num_inputs - 1;
+ idx = spec->input_idx[uinfo->value.enumerated.item];
+ snd_hda_get_pin_label(codec, cfg->inputs[idx].pin, cfg,
+ uinfo->value.enumerated.name,
+ sizeof(uinfo->value.enumerated.name), NULL);
+ return 0;
+}
+
+static int cs_capture_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->capsrc_idx[spec->cur_input];
+ return 0;
+}
+
+static int cs_capture_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs_spec *spec = codec->spec;
+ unsigned int idx = ucontrol->value.enumerated.item[0];
+
+ if (idx >= spec->num_inputs)
+ return -EINVAL;
+ idx = spec->input_idx[idx];
+ return change_cur_input(codec, idx, 0);
+}
+
+static const struct snd_kcontrol_new cs_capture_source = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = cs_capture_source_info,
+ .get = cs_capture_source_get,
+ .put = cs_capture_source_put,
+};
+
+static const struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
+ struct hda_ctl_ops *ops)
+{
+ struct cs_spec *spec = codec->spec;
+ struct hda_bind_ctls *bind;
+ int i, n;
+
+ bind = kzalloc(sizeof(*bind) + sizeof(long) * (spec->num_inputs + 1),
+ GFP_KERNEL);
+ if (!bind)
+ return NULL;
+ bind->ops = ops;
+ n = 0;
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (!spec->adc_nid[i])
+ continue;
+ bind->values[n++] =
+ HDA_COMPOSE_AMP_VAL(spec->adc_nid[i], 3,
+ spec->adc_idx[i], HDA_INPUT);
+ }
+ return bind;
+}
+
+/* add a (input-boost) volume control to the given input pin */
+static int add_input_volume_control(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg,
+ int item)
+{
+ hda_nid_t pin = cfg->inputs[item].pin;
+ u32 caps;
+ const char *label;
+ struct snd_kcontrol *kctl;
+
+ if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP))
+ return 0;
+ caps = query_amp_caps(codec, pin, HDA_INPUT);
+ caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ if (caps <= 1)
+ return 0;
+ label = hda_get_autocfg_input_label(codec, cfg, item);
+ return add_volume(codec, label, 0,
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl);
+}
+
+static int build_input(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ int i, err;
+
+ if (!spec->num_inputs)
+ return 0;
+
+ /* make bind-capture */
+ spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw);
+ spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol);
+ for (i = 0; i < 2; i++) {
+ struct snd_kcontrol *kctl;
+ int n;
+ if (!spec->capture_bind[i])
+ return -ENOMEM;
+ kctl = snd_ctl_new1(&cs_capture_ctls[i], codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = (long)spec->capture_bind[i];
+ err = snd_hda_ctl_add(codec, 0, kctl);
+ if (err < 0)
+ return err;
+ for (n = 0; n < AUTO_PIN_LAST; n++) {
+ if (!spec->adc_nid[n])
+ continue;
+ err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ if (spec->num_inputs > 1 && !spec->mic_detect) {
+ err = snd_hda_ctl_add(codec, 0,
+ snd_ctl_new1(&cs_capture_source, codec));
+ if (err < 0)
+ return err;
+ }
-#define SPDIF_EVENT 0x04
+ for (i = 0; i < spec->num_inputs; i++) {
+ err = add_input_volume_control(codec, &spec->autocfg, i);
+ if (err < 0)
+ return err;
+ }
-/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */
-#define CS4213_VENDOR_NID 0x09
+ return 0;
+}
+/*
+ */
-static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
+static int build_digital_output(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- snd_hda_codec_write(codec, spec->vendor_nid, 0,
- AC_VERB_SET_COEF_INDEX, idx);
- return snd_hda_codec_read(codec, spec->vendor_nid, 0,
- AC_VERB_GET_PROC_COEF, 0);
+ int err;
+
+ if (!spec->multiout.dig_out_nid)
+ return 0;
+
+ 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);
+ if (err < 0)
+ return err;
+ return 0;
}
-static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
- unsigned int coef)
+static int build_digital_input(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- snd_hda_codec_write(codec, spec->vendor_nid, 0,
- AC_VERB_SET_COEF_INDEX, idx);
- snd_hda_codec_write(codec, spec->vendor_nid, 0,
- AC_VERB_SET_PROC_COEF, coef);
+ if (spec->dig_in)
+ return snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+ return 0;
}
/*
@@ -158,37 +908,187 @@ static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
* HP/SPK/SPDIF
*/
-static void cs_automute(struct hda_codec *codec)
+static void cs_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
{
struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int hp_present;
+ unsigned int spdif_present;
+ hda_nid_t nid;
+ int i;
- /* mute HPs if spdif jack (SENSE_B) is present */
- spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b);
+ spdif_present = 0;
+ if (cfg->dig_outs) {
+ nid = cfg->dig_out_pins[0];
+ if (is_jack_detectable(codec, nid)) {
+ /*
+ TODO: SPDIF output redirect when SENSE_B is enabled.
+ Shared (SENSE_A) jack (e.g HP/mini-TOSLINK)
+ assumed.
+ */
+ if (snd_hda_jack_detect(codec, nid)
+ /* && spec->sense_b */)
+ spdif_present = 1;
+ }
+ }
+
+ hp_present = 0;
+ for (i = 0; i < cfg->hp_outs; i++) {
+ nid = cfg->hp_pins[i];
+ if (!is_jack_detectable(codec, nid))
+ continue;
+ hp_present = snd_hda_jack_detect(codec, nid);
+ if (hp_present)
+ break;
+ }
- snd_hda_gen_update_outputs(codec);
+ /* mute speakers if spdif or hp jack is plugged in */
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ int pin_ctl = hp_present ? 0 : PIN_OUT;
+ /* detect on spdif is specific to CS4210 */
+ if (spdif_present && (spec->vendor_nid == CS4210_VENDOR_NID))
+ pin_ctl = 0;
+ nid = cfg->speaker_pins[i];
+ snd_hda_set_pin_ctl(codec, nid, pin_ctl);
+ }
if (spec->gpio_eapd_hp) {
- unsigned int gpio = spec->gen.hp_jack_present ?
+ unsigned int gpio = hp_present ?
spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
snd_hda_codec_write(codec, 0x01, 0,
AC_VERB_SET_GPIO_DATA, gpio);
}
+
+ /* specific to CS4210 */
+ if (spec->vendor_nid == CS4210_VENDOR_NID) {
+ /* mute HPs if spdif jack (SENSE_B) is present */
+ for (i = 0; i < cfg->hp_outs; i++) {
+ nid = cfg->hp_pins[i];
+ snd_hda_set_pin_ctl(codec, nid,
+ (spdif_present && spec->sense_b) ? 0 : PIN_HP);
+ }
+
+ /* SPDIF TX on/off */
+ if (cfg->dig_outs) {
+ nid = cfg->dig_out_pins[0];
+ snd_hda_set_pin_ctl(codec, nid,
+ spdif_present ? PIN_OUT : 0);
+
+ }
+ /* Update board GPIOs if neccessary ... */
+ }
}
-static bool is_active_pin(struct hda_codec *codec, hda_nid_t nid)
+/*
+ * Auto-input redirect for CS421x
+ * Switch max 3 inputs of a single ADC (nid 3)
+*/
+
+static void cs_automic(struct hda_codec *codec, struct hda_jack_tbl *tbl)
{
- unsigned int val;
- val = snd_hda_codec_get_pincfg(codec, nid);
- return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
+ unsigned int present;
+
+ nid = cfg->inputs[spec->automic_idx].pin;
+ present = snd_hda_jack_detect(codec, nid);
+
+ /* specific to CS421x, single ADC */
+ if (spec->vendor_nid == CS420X_VENDOR_NID) {
+ if (present)
+ change_cur_input(codec, spec->automic_idx, 0);
+ else
+ change_cur_input(codec, !spec->automic_idx, 0);
+ } else {
+ if (present) {
+ if (spec->cur_input != spec->automic_idx) {
+ spec->last_input = spec->cur_input;
+ spec->cur_input = spec->automic_idx;
+ }
+ } else {
+ spec->cur_input = spec->last_input;
+ }
+ cs_update_input_select(codec);
+ }
+}
+
+/*
+ */
+
+static void init_output(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ /* mute first */
+ for (i = 0; i < spec->multiout.num_dacs; i++)
+ snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ if (spec->multiout.hp_nid)
+ snd_hda_codec_write(codec, spec->multiout.hp_nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
+ if (!spec->multiout.extra_out_nid[i])
+ break;
+ snd_hda_codec_write(codec, spec->multiout.extra_out_nid[i], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+ }
+
+ /* set appropriate pin controls */
+ for (i = 0; i < cfg->line_outs; i++)
+ snd_hda_set_pin_ctl(codec, cfg->line_out_pins[i], PIN_OUT);
+ /* HP */
+ for (i = 0; i < cfg->hp_outs; i++) {
+ hda_nid_t nid = cfg->hp_pins[i];
+ snd_hda_set_pin_ctl(codec, nid, PIN_HP);
+ if (!cfg->speaker_outs)
+ continue;
+ if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
+ snd_hda_jack_detect_enable_callback(codec, nid, HP_EVENT, cs_automute);
+ spec->hp_detect = 1;
+ }
+ }
+
+ /* Speaker */
+ for (i = 0; i < cfg->speaker_outs; i++)
+ snd_hda_set_pin_ctl(codec, cfg->speaker_pins[i], PIN_OUT);
+
+ /* SPDIF is enabled on presence detect for CS421x */
+ if (spec->hp_detect || spec->spdif_detect)
+ cs_automute(codec, NULL);
}
-static void init_input_coef(struct hda_codec *codec)
+static void init_input(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int coef;
+ int i;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ unsigned int ctl;
+ hda_nid_t pin = cfg->inputs[i].pin;
+ if (!spec->adc_nid[i])
+ continue;
+ /* set appropriate pin control and mute first */
+ ctl = PIN_IN;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC)
+ ctl |= snd_hda_get_default_vref(codec, pin);
+ snd_hda_set_pin_ctl(codec, pin, ctl);
+ snd_hda_codec_write(codec, spec->adc_nid[i], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_MUTE(spec->adc_idx[i]));
+ if (spec->mic_detect && spec->automic_idx == i)
+ snd_hda_jack_detect_enable_callback(codec, pin, MIC_EVENT, cs_automic);
+ }
/* CS420x has multiple ADC, CS421x has single ADC */
if (spec->vendor_nid == CS420X_VENDOR_NID) {
+ change_cur_input(codec, spec->cur_input, 1);
+ if (spec->mic_detect)
+ cs_automic(codec, NULL);
+
coef = cs_vendor_coef_get(codec, IDX_BEEP_CFG);
if (is_active_pin(codec, CS_DMIC2_PIN_NID))
coef |= 1 << 4; /* DMIC2 2 chan on, GPIO1 off */
@@ -199,6 +1099,13 @@ static void init_input_coef(struct hda_codec *codec)
*/
cs_vendor_coef_set(codec, IDX_BEEP_CFG, coef);
+ } else {
+ if (spec->mic_detect)
+ cs_automic(codec, NULL);
+ else {
+ spec->cur_adc = spec->adc_nid[spec->cur_input];
+ cs_update_input_select(codec);
+ }
}
}
@@ -271,7 +1178,7 @@ static const struct hda_verb cs_errata_init_verbs[] = {
};
/* SPDIF setup */
-static void init_digital_coef(struct hda_codec *codec)
+static void init_digital(struct hda_codec *codec)
{
unsigned int coef;
@@ -294,7 +1201,7 @@ static int cs_init(struct hda_codec *codec)
snd_hda_sequence_write(codec, cs_coef_init_verbs);
- snd_hda_gen_init(codec);
+ snd_hda_gen_apply_verbs(codec);
if (spec->gpio_mask) {
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
@@ -305,17 +1212,53 @@ static int cs_init(struct hda_codec *codec)
spec->gpio_data);
}
- init_input_coef(codec);
- init_digital_coef(codec);
+ init_output(codec);
+ init_input(codec);
+ init_digital(codec);
+
+ return 0;
+}
+
+static int cs_build_controls(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ int err;
+
+ err = build_output(codec);
+ if (err < 0)
+ return err;
+ err = build_input(codec);
+ if (err < 0)
+ return err;
+ err = build_digital_output(codec);
+ if (err < 0)
+ return err;
+ err = build_digital_input(codec);
+ if (err < 0)
+ return err;
+ err = cs_init(codec);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
return 0;
}
-#define cs_free snd_hda_gen_free
+static void cs_free(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ kfree(spec->capture_bind[0]);
+ kfree(spec->capture_bind[1]);
+ snd_hda_gen_free(&spec->gen);
+ kfree(codec->spec);
+}
static const struct hda_codec_ops cs_patch_ops = {
- .build_controls = snd_hda_gen_build_controls,
- .build_pcms = snd_hda_gen_build_pcms,
+ .build_controls = cs_build_controls,
+ .build_pcms = cs_build_pcms,
.init = cs_init,
.free = cs_free,
.unsol_event = snd_hda_jack_unsol_event,
@@ -326,14 +1269,22 @@ static int cs_parse_auto_config(struct hda_codec *codec)
struct cs_spec *spec = codec->spec;
int err;
- err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
if (err < 0)
return err;
- err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ err = parse_output(codec);
+ if (err < 0)
+ return err;
+ err = parse_input(codec);
+ if (err < 0)
+ return err;
+ err = parse_digital_output(codec);
+ if (err < 0)
+ return err;
+ err = parse_digital_input(codec);
if (err < 0)
return err;
-
return 0;
}
@@ -483,28 +1434,18 @@ static const struct hda_fixup cs420x_fixups[] = {
},
};
-static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
-{
- struct cs_spec *spec;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (!spec)
- return NULL;
- codec->spec = spec;
- spec->vendor_nid = vendor_nid;
- snd_hda_gen_spec_init(&spec->gen);
-
- return spec;
-}
-
static int patch_cs420x(struct hda_codec *codec)
{
struct cs_spec *spec;
int err;
- spec = cs_alloc_spec(codec, CS420X_VENDOR_NID);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
+ codec->spec = spec;
+ snd_hda_gen_init(&spec->gen);
+
+ spec->vendor_nid = CS420X_VENDOR_NID;
snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl,
cs420x_fixups);
@@ -522,6 +1463,7 @@ static int patch_cs420x(struct hda_codec *codec)
error:
cs_free(codec);
+ codec->spec = NULL;
return err;
}
@@ -680,7 +1622,7 @@ static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol,
}
}
-static const struct snd_kcontrol_new cs421x_speaker_boost_ctl = {
+static const struct snd_kcontrol_new cs421x_speaker_bost_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -725,44 +1667,20 @@ static void cs4210_pinmux_init(struct hda_codec *codec)
}
}
-static void cs4210_spdif_automute(struct hda_codec *codec,
- struct hda_jack_tbl *tbl)
-{
- struct cs_spec *spec = codec->spec;
- bool spdif_present = false;
- hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0];
-
- /* detect on spdif is specific to CS4210 */
- if (!spec->spdif_detect ||
- spec->vendor_nid != CS4210_VENDOR_NID)
- return;
-
- spdif_present = snd_hda_jack_detect(codec, spdif_pin);
- if (spdif_present == spec->spdif_present)
- return;
-
- spec->spdif_present = spdif_present;
- /* SPDIF TX on/off */
- if (spdif_present)
- snd_hda_set_pin_ctl(codec, spdif_pin,
- spdif_present ? PIN_OUT : 0);
-
- cs_automute(codec);
-}
-
-static void parse_cs421x_digital(struct hda_codec *codec)
+static void init_cs421x_digital(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
+
for (i = 0; i < cfg->dig_outs; i++) {
hda_nid_t nid = cfg->dig_out_pins[i];
+ if (!cfg->speaker_outs)
+ continue;
if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
+ snd_hda_jack_detect_enable_callback(codec, nid, SPDIF_EVENT, cs_automute);
spec->spdif_detect = 1;
- snd_hda_jack_detect_enable_callback(codec, nid,
- SPDIF_EVENT,
- cs4210_spdif_automute);
}
}
}
@@ -777,8 +1695,6 @@ static int cs421x_init(struct hda_codec *codec)
cs4210_pinmux_init(codec);
}
- snd_hda_gen_init(codec);
-
if (spec->gpio_mask) {
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
spec->gpio_mask);
@@ -788,61 +1704,233 @@ static int cs421x_init(struct hda_codec *codec)
spec->gpio_data);
}
- init_input_coef(codec);
+ init_output(codec);
+ init_input(codec);
+ init_cs421x_digital(codec);
+
+ return 0;
+}
+
+/*
+ * CS4210 Input MUX (1 ADC)
+ */
+static int cs421x_mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs_spec *spec = codec->spec;
+
+ return snd_hda_input_mux_info(&spec->input_mux, uinfo);
+}
- cs4210_spdif_automute(codec, NULL);
+static int cs421x_mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->cur_input;
return 0;
}
-static int cs421x_build_controls(struct hda_codec *codec)
+static int cs421x_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct cs_spec *spec = codec->spec;
+
+ return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol,
+ spec->adc_nid[0], &spec->cur_input);
+
+}
+
+static const struct snd_kcontrol_new cs421x_capture_source = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = cs421x_mux_enum_info,
+ .get = cs421x_mux_enum_get,
+ .put = cs421x_mux_enum_put,
+};
+
+static int cs421x_add_input_volume_control(struct hda_codec *codec, int item)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ const struct hda_input_mux *imux = &spec->input_mux;
+ hda_nid_t pin = cfg->inputs[item].pin;
+ struct snd_kcontrol *kctl;
+ u32 caps;
+
+ if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP))
+ return 0;
+
+ caps = query_amp_caps(codec, pin, HDA_INPUT);
+ caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ if (caps <= 1)
+ return 0;
+
+ return add_volume(codec, imux->items[item].label, 0,
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl);
+}
+
+/* add a (input-boost) volume control to the given input pin */
+static int build_cs421x_input(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct hda_input_mux *imux = &spec->input_mux;
+ int i, err, type_idx;
+ const char *label;
+
+ if (!spec->num_inputs)
+ return 0;
+
+ /* make bind-capture */
+ spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw);
+ spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol);
+ for (i = 0; i < 2; i++) {
+ struct snd_kcontrol *kctl;
+ int n;
+ if (!spec->capture_bind[i])
+ return -ENOMEM;
+ kctl = snd_ctl_new1(&cs_capture_ctls[i], codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = (long)spec->capture_bind[i];
+ err = snd_hda_ctl_add(codec, 0, kctl);
+ if (err < 0)
+ return err;
+ for (n = 0; n < AUTO_PIN_LAST; n++) {
+ if (!spec->adc_nid[n])
+ continue;
+ err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[n]);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ /* Add Input MUX Items + Capture Volume/Switch */
+ for (i = 0; i < spec->num_inputs; i++) {
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ snd_hda_add_imux_item(imux, label, spec->adc_idx[i], &type_idx);
+
+ err = cs421x_add_input_volume_control(codec, i);
+ if (err < 0)
+ return err;
+ }
+
+ /*
+ Add 'Capture Source' Switch if
+ * 2 inputs and no mic detec
+ * 3 inputs
+ */
+ if ((spec->num_inputs == 2 && !spec->mic_detect) ||
+ (spec->num_inputs == 3)) {
+
+ err = snd_hda_ctl_add(codec, spec->adc_nid[0],
+ snd_ctl_new1(&cs421x_capture_source, codec));
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Single DAC (Mute/Gain) */
+static int build_cs421x_output(struct hda_codec *codec)
{
+ hda_nid_t dac = CS4210_DAC_NID;
struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct snd_kcontrol *kctl;
int err;
+ char *name = "Master";
+
+ fix_volume_caps(codec, dac);
- err = snd_hda_gen_build_controls(codec);
+ err = add_mute(codec, name, 0,
+ HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
if (err < 0)
return err;
- if (spec->gen.autocfg.speaker_outs &&
- spec->vendor_nid == CS4210_VENDOR_NID) {
+ err = add_volume(codec, name, 0,
+ HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
+ if (err < 0)
+ return err;
+
+ if (cfg->speaker_outs && (spec->vendor_nid == CS4210_VENDOR_NID)) {
err = snd_hda_ctl_add(codec, 0,
- snd_ctl_new1(&cs421x_speaker_boost_ctl, codec));
+ snd_ctl_new1(&cs421x_speaker_bost_ctl, codec));
if (err < 0)
return err;
}
+ return err;
+}
+
+static int cs421x_build_controls(struct hda_codec *codec)
+{
+ struct cs_spec *spec = codec->spec;
+ int err;
+
+ err = build_cs421x_output(codec);
+ if (err < 0)
+ return err;
+ err = build_cs421x_input(codec);
+ if (err < 0)
+ return err;
+ err = build_digital_output(codec);
+ if (err < 0)
+ return err;
+ err = cs421x_init(codec);
+ if (err < 0)
+ return err;
+
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
return 0;
}
-static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
+static int parse_cs421x_input(struct hda_codec *codec)
{
- unsigned int caps;
+ struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
- /* set the upper-limit for mixer amp to 0dB */
- caps = query_amp_caps(codec, dac, HDA_OUTPUT);
- caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT);
- caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f)
- << AC_AMPCAP_NUM_STEPS_SHIFT;
- snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps);
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin = cfg->inputs[i].pin;
+ spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]);
+ spec->cur_input = spec->last_input = i;
+ spec->num_inputs++;
+
+ /* check whether the automatic mic switch is available */
+ if (is_ext_mic(codec, i) && cfg->num_inputs >= 2) {
+ spec->mic_detect = 1;
+ spec->automic_idx = i;
+ }
+ }
+ return 0;
}
static int cs421x_parse_auto_config(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
- hda_nid_t dac = CS4210_DAC_NID;
int err;
- fix_volume_caps(codec, dac);
-
- err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
if (err < 0)
return err;
-
- err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
+ err = parse_output(codec);
+ if (err < 0)
+ return err;
+ err = parse_cs421x_input(codec);
+ if (err < 0)
+ return err;
+ err = parse_digital_output(codec);
if (err < 0)
return err;
-
- parse_cs421x_digital(codec);
return 0;
}
@@ -875,7 +1963,7 @@ static int cs421x_suspend(struct hda_codec *codec)
static const struct hda_codec_ops cs421x_patch_ops = {
.build_controls = cs421x_build_controls,
- .build_pcms = snd_hda_gen_build_pcms,
+ .build_pcms = cs_build_pcms,
.init = cs421x_init,
.free = cs_free,
.unsol_event = snd_hda_jack_unsol_event,
@@ -889,9 +1977,13 @@ static int patch_cs4210(struct hda_codec *codec)
struct cs_spec *spec;
int err;
- spec = cs_alloc_spec(codec, CS4210_VENDOR_NID);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
+ codec->spec = spec;
+ snd_hda_gen_init(&spec->gen);
+
+ spec->vendor_nid = CS4210_VENDOR_NID;
snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl,
cs421x_fixups);
@@ -916,6 +2008,7 @@ static int patch_cs4210(struct hda_codec *codec)
error:
cs_free(codec);
+ codec->spec = NULL;
return err;
}
@@ -924,9 +2017,13 @@ static int patch_cs4213(struct hda_codec *codec)
struct cs_spec *spec;
int err;
- spec = cs_alloc_spec(codec, CS4213_VENDOR_NID);
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
+ codec->spec = spec;
+ snd_hda_gen_init(&spec->gen);
+
+ spec->vendor_nid = CS4213_VENDOR_NID;
err = cs421x_parse_auto_config(codec);
if (err < 0)
@@ -937,6 +2034,7 @@ static int patch_cs4213(struct hda_codec *codec)
error:
cs_free(codec);
+ codec->spec = NULL;
return err;
}
diff --git a/trunk/sound/pci/hda/patch_cmedia.c b/trunk/sound/pci/hda/patch_cmedia.c
index 9c6ce73b03c5..c8fdaaefe702 100644
--- a/trunk/sound/pci/hda/patch_cmedia.c
+++ b/trunk/sound/pci/hda/patch_cmedia.c
@@ -22,6 +22,7 @@
*/
#include
+#include
#include
#include
#include
@@ -29,9 +30,6 @@
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_auto_parser.h"
-#include "hda_jack.h"
-#include "hda_generic.h"
-
#define NUM_PINS 11
@@ -47,10 +45,6 @@ enum {
};
struct cmi_spec {
- struct hda_gen_spec gen;
-
- /* below are only for static models */
-
int board_config;
unsigned int no_line_in: 1; /* no line-in (5-jack) */
unsigned int front_panel: 1; /* has front-panel 2-jack */
@@ -362,6 +356,77 @@ static int cmi9880_build_controls(struct hda_codec *codec)
return 0;
}
+/* fill in the multi_dac_nids table, which will decide
+ which audio widget to use for each channel */
+static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
+{
+ struct cmi_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int assigned[4];
+ int i, j;
+
+ /* clear the table, only one c-media dac assumed here */
+ memset(spec->dac_nids, 0, sizeof(spec->dac_nids));
+ memset(assigned, 0, sizeof(assigned));
+ /* check the pins we found */
+ for (i = 0; i < cfg->line_outs; i++) {
+ nid = cfg->line_out_pins[i];
+ /* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */
+ if (nid >= 0x0b && nid <= 0x0e) {
+ spec->dac_nids[i] = (nid - 0x0b) + 0x03;
+ assigned[nid - 0x0b] = 1;
+ }
+ }
+ /* left pin can be connect to any audio widget */
+ for (i = 0; i < cfg->line_outs; i++) {
+ nid = cfg->line_out_pins[i];
+ if (nid <= 0x0e)
+ continue;
+ /* search for an empty channel */
+ for (j = 0; j < cfg->line_outs; j++) {
+ if (! assigned[j]) {
+ spec->dac_nids[i] = j + 0x03;
+ assigned[j] = 1;
+ break;
+ }
+ }
+ }
+ spec->num_dacs = cfg->line_outs;
+ return 0;
+}
+
+/* create multi_init table, which is used for multichannel initialization */
+static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
+{
+ struct cmi_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int i, j, k;
+
+ /* clear the table, only one c-media dac assumed here */
+ memset(spec->multi_init, 0, sizeof(spec->multi_init));
+ for (j = 0, i = 0; i < cfg->line_outs; i++) {
+ nid = cfg->line_out_pins[i];
+ /* set as output */
+ spec->multi_init[j].nid = nid;
+ spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL;
+ spec->multi_init[j].param = PIN_OUT;
+ j++;
+ if (nid > 0x0e) {
+ /* set connection */
+ spec->multi_init[j].nid = nid;
+ spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL;
+ spec->multi_init[j].param = 0;
+ /* find the index in connect list */
+ k = snd_hda_get_conn_index(codec, nid,
+ spec->dac_nids[i], 0);
+ if (k >= 0)
+ spec->multi_init[j].param = k;
+ j++;
+ }
+ }
+ return 0;
+}
+
static int cmi9880_init(struct hda_codec *codec)
{
struct cmi_spec *spec = codec->spec;
@@ -567,36 +632,6 @@ static const struct hda_codec_ops cmi9880_patch_ops = {
.free = cmi9880_free,
};
-/*
- * stuff for auto-parser
- */
-static const struct hda_codec_ops cmi_auto_patch_ops = {
- .build_controls = snd_hda_gen_build_controls,
- .build_pcms = snd_hda_gen_build_pcms,
- .init = snd_hda_gen_init,
- .free = snd_hda_gen_free,
- .unsol_event = snd_hda_jack_unsol_event,
-};
-
-static int cmi_parse_auto_config(struct hda_codec *codec)
-{
- struct cmi_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->gen.autocfg;
- int err;
-
- snd_hda_gen_spec_init(&spec->gen);
-
- err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
- if (err < 0)
- return err;
- err = snd_hda_gen_parse_auto_config(codec, cfg);
- if (err < 0)
- return err;
-
- codec->patch_ops = cmi_auto_patch_ops;
- return 0;
-}
-
static int patch_cmi9880(struct hda_codec *codec)
{
struct cmi_spec *spec;
@@ -615,15 +650,6 @@ static int patch_cmi9880(struct hda_codec *codec)
spec->board_config = CMI_AUTO; /* try everything */
}
- if (spec->board_config == CMI_AUTO) {
- int err = cmi_parse_auto_config(codec);
- if (err < 0) {
- snd_hda_gen_free(codec);
- return err;
- }
- return 0;
- }
-
/* copy default DAC NIDs */
memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids));
spec->num_dacs = 4;
@@ -652,13 +678,59 @@ static int patch_cmi9880(struct hda_codec *codec)
}
break;
case CMI_ALLOUT:
- default:
spec->front_panel = 1;
spec->multiout.max_channels = 8;
spec->no_line_in = 1;
spec->input_mux = &cmi9880_no_line_mux;
spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
break;
+ case CMI_AUTO:
+ {
+ unsigned int port_e, port_f, port_g, port_h;
+ unsigned int port_spdifi, port_spdifo;
+ struct auto_pin_cfg cfg;
+
+ /* collect pin default configuration */
+ port_e = snd_hda_codec_get_pincfg(codec, 0x0f);
+ port_f = snd_hda_codec_get_pincfg(codec, 0x10);
+ spec->front_panel = 1;
+ if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE ||
+ get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) {
+ port_g = snd_hda_codec_get_pincfg(codec, 0x1f);
+ port_h = snd_hda_codec_get_pincfg(codec, 0x20);
+ spec->channel_modes = cmi9880_channel_modes;
+ /* no front panel */
+ if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE ||
+ get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) {
+ /* no optional rear panel */
+ spec->board_config = CMI_MINIMAL;
+ spec->front_panel = 0;
+ spec->num_channel_modes = 2;
+ } else {
+ spec->board_config = CMI_MIN_FP;
+ spec->num_channel_modes = 3;
+ }
+ spec->input_mux = &cmi9880_basic_mux;
+ spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
+ } else {
+ spec->input_mux = &cmi9880_basic_mux;
+ port_spdifi = snd_hda_codec_get_pincfg(codec, 0x13);
+ port_spdifo = snd_hda_codec_get_pincfg(codec, 0x12);
+ if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE)
+ spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
+ if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE)
+ spec->dig_in_nid = CMI_DIG_IN_NID;
+ spec->multiout.max_channels = 8;
+ }
+ snd_hda_parse_pin_def_config(codec, &cfg, NULL);
+ if (cfg.line_outs) {
+ spec->multiout.max_channels = cfg.line_outs * 2;
+ cmi9880_fill_multi_dac_nids(codec, &cfg);
+ cmi9880_fill_multi_init(codec, &cfg);
+ } else
+ snd_printd("patch_cmedia: cannot detect association in defcfg\n");
+ break;
+ }
}
spec->multiout.num_dacs = spec->num_dacs;
diff --git a/trunk/sound/pci/hda/patch_conexant.c b/trunk/sound/pci/hda/patch_conexant.c
index 941bf6c766ec..009b77a693cf 100644
--- a/trunk/sound/pci/hda/patch_conexant.c
+++ b/trunk/sound/pci/hda/patch_conexant.c
@@ -33,9 +33,6 @@
#include "hda_auto_parser.h"
#include "hda_beep.h"
#include "hda_jack.h"
-#include "hda_generic.h"
-
-#define ENABLE_CXT_STATIC_QUIRKS
#define CXT_PIN_DIR_IN 0x00
#define CXT_PIN_DIR_OUT 0x01
@@ -56,19 +53,27 @@
#define AUTO_MIC_PORTB (1 << 1)
#define AUTO_MIC_PORTC (1 << 2)
-struct conexant_spec {
- struct hda_gen_spec gen;
+struct pin_dac_pair {
+ hda_nid_t pin;
+ hda_nid_t dac;
+ int type;
+};
- unsigned int beep_amp;
+struct imux_info {
+ hda_nid_t pin; /* input pin NID */
+ hda_nid_t adc; /* connected ADC NID */
+ hda_nid_t boost; /* optional boost volume NID */
+ int index; /* corresponding to autocfg.input */
+};
- /* extra EAPD pins */
- unsigned int num_eapds;
- hda_nid_t eapds[4];
+struct conexant_spec {
+ struct hda_gen_spec gen;
-#ifdef ENABLE_CXT_STATIC_QUIRKS
const struct snd_kcontrol_new *mixers[5];
int num_mixers;
hda_nid_t vmaster_nid;
+ struct hda_vmaster_mute_hook vmaster_mute;
+ bool vmaster_mute_led;
const struct hda_verb *init_verbs[5]; /* initialization verbs
* don't forget NULL
@@ -85,6 +90,11 @@ struct conexant_spec {
unsigned int hp_present;
unsigned int line_present;
unsigned int auto_mic;
+ int auto_mic_ext; /* imux_pins[] index for ext mic */
+ int auto_mic_dock; /* imux_pins[] index for dock mic */
+ int auto_mic_int; /* imux_pins[] index for int mic */
+ unsigned int need_dac_fix;
+ hda_nid_t slave_dig_outs[2];
/* capture */
unsigned int num_adc_nids;
@@ -112,13 +122,30 @@ struct conexant_spec {
unsigned int spdif_route;
+ /* dynamic controls, init_verbs and input_mux */
+ struct auto_pin_cfg autocfg;
+ struct hda_input_mux private_imux;
+ struct imux_info imux_info[HDA_MAX_NUM_INPUTS];
+ hda_nid_t private_adc_nids[HDA_MAX_NUM_INPUTS];
+ hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+ struct pin_dac_pair dac_info[8];
+ int dac_info_filled;
+
unsigned int port_d_mode;
+ unsigned int auto_mute:1; /* used in auto-parser */
+ unsigned int detect_line:1; /* Line-out detection enabled */
+ unsigned int automute_lines:1; /* automute line-out as well */
+ unsigned int automute_hp_lo:1; /* both HP and LO available */
unsigned int dell_automute:1;
unsigned int dell_vostro:1;
unsigned int ideapad:1;
unsigned int thinkpad:1;
unsigned int hp_laptop:1;
unsigned int asus:1;
+ unsigned int pin_eapd_ctrls:1;
+ unsigned int fixup_stereo_dmic:1;
+
+ unsigned int adc_switching:1;
unsigned int ext_mic_present;
unsigned int recording;
@@ -134,48 +161,14 @@ struct conexant_spec {
unsigned int dc_enable;
unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
-#endif /* ENABLE_CXT_STATIC_QUIRKS */
-};
+ unsigned int beep_amp;
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-#define set_beep_amp(spec, nid, idx, dir) \
- ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir))
-/* additional beep mixers; the actual parameters are overwritten at build */
-static const struct snd_kcontrol_new cxt_beep_mixer[] = {
- HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
- HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
- { } /* end */
+ /* extra EAPD pins */
+ unsigned int num_eapds;
+ hda_nid_t eapds[4];
};
-/* create beep controls if needed */
-static int add_beep_ctls(struct hda_codec *codec)
-{
- struct conexant_spec *spec = codec->spec;
- int err;
-
- if (spec->beep_amp) {
- const struct snd_kcontrol_new *knew;
- for (knew = cxt_beep_mixer; knew->name; knew++) {
- struct snd_kcontrol *kctl;
- kctl = snd_ctl_new1(knew, codec);
- if (!kctl)
- return -ENOMEM;
- kctl->private_value = spec->beep_amp;
- err = snd_hda_ctl_add(codec, 0, kctl);
- if (err < 0)
- return err;
- }
- }
- return 0;
-}
-#else
-#define set_beep_amp(spec, nid, idx, dir) /* NOP */
-#define add_beep_ctls(codec) 0
-#endif
-
-
-#ifdef ENABLE_CXT_STATIC_QUIRKS
static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
@@ -344,6 +337,8 @@ static const struct hda_pcm_stream cx5051_pcm_analog_capture = {
},
};
+static bool is_2_1_speaker(struct conexant_spec *spec);
+
static int conexant_build_pcms(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
@@ -358,6 +353,9 @@ static int conexant_build_pcms(struct hda_codec *codec)
spec->multiout.max_channels;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
spec->multiout.dac_nids[0];
+ if (is_2_1_speaker(spec))
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap =
+ snd_pcm_2_1_chmaps;
if (spec->capture_stream)
info->stream[SNDRV_PCM_STREAM_CAPTURE] = *spec->capture_stream;
else {
@@ -388,6 +386,8 @@ static int conexant_build_pcms(struct hda_codec *codec)
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
spec->dig_in_nid;
}
+ if (spec->slave_dig_outs[0])
+ codec->slave_dig_outs = spec->slave_dig_outs;
}
return 0;
@@ -435,7 +435,7 @@ static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg,
/* partial workaround for "azx_get_response timeout" */
if (power_state == AC_PWRST_D0)
msleep(10);
- snd_hda_codec_set_power_to_all(codec, fg, power_state);
+ snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
}
static int conexant_init(struct hda_codec *codec)
@@ -451,6 +451,7 @@ static int conexant_init(struct hda_codec *codec)
static void conexant_free(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
+ snd_hda_gen_free(&spec->gen);
snd_hda_detach_beep_device(codec);
kfree(spec);
}
@@ -466,6 +467,15 @@ static const struct snd_kcontrol_new cxt_capture_mixers[] = {
{}
};
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* additional beep mixers; the actual parameters are overwritten at build */
+static const struct snd_kcontrol_new cxt_beep_mixer[] = {
+ HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
+ { } /* end */
+};
+#endif
+
static const char * const slave_pfxs[] = {
"Headphone", "Speaker", "Bass Speaker", "Front", "Surround", "CLFE",
NULL
@@ -514,9 +524,10 @@ static int conexant_build_controls(struct hda_codec *codec)
}
if (spec->vmaster_nid &&
!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL, slave_pfxs,
- "Playback Switch");
+ err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, slave_pfxs,
+ "Playback Switch", true,
+ &spec->vmaster_mute.sw_kctl);
if (err < 0)
return err;
}
@@ -527,9 +538,22 @@ static int conexant_build_controls(struct hda_codec *codec)
return err;
}
- err = add_beep_ctls(codec);
- if (err < 0)
- return err;
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+ /* create beep controls if needed */
+ if (spec->beep_amp) {
+ const struct snd_kcontrol_new *knew;
+ for (knew = cxt_beep_mixer; knew->name; knew++) {
+ struct snd_kcontrol *kctl;
+ kctl = snd_ctl_new1(knew, codec);
+ if (!kctl)
+ return -ENOMEM;
+ kctl->private_value = spec->beep_amp;
+ err = snd_hda_ctl_add(codec, 0, kctl);
+ if (err < 0)
+ return err;
+ }
+ }
+#endif
return 0;
}
@@ -542,6 +566,13 @@ static const struct hda_codec_ops conexant_patch_ops = {
.set_power_state = conexant_set_power,
};
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+#define set_beep_amp(spec, nid, idx, dir) \
+ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir))
+#else
+#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#endif
+
static int patch_conexant_auto(struct hda_codec *codec);
/*
* EAPD control
@@ -625,6 +656,8 @@ static int conexant_ch_mode_put(struct snd_kcontrol *kcontrol,
int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
spec->num_channel_mode,
&spec->multiout.max_channels);
+ if (err >= 0 && spec->need_dac_fix)
+ spec->multiout.num_dacs = spec->multiout.max_channels / 2;
return err;
}
@@ -2463,6 +2496,10 @@ static void conexant_check_dig_outs(struct hda_codec *codec,
continue;
if (snd_hda_get_connections(codec, *dig_pins, nid_loc, 1) != 1)
continue;
+ if (spec->slave_dig_outs[0])
+ nid_loc++;
+ else
+ nid_loc = spec->slave_dig_outs;
}
}
@@ -3104,152 +3141,1308 @@ static int patch_cxt5066(struct hda_codec *codec)
return 0;
}
-#endif /* ENABLE_CXT_STATIC_QUIRKS */
-
-
/*
* Automatic parser for CX20641 & co
*/
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-static void cx_auto_parse_beep(struct hda_codec *codec)
+static int cx_auto_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
{
struct conexant_spec *spec = codec->spec;
- hda_nid_t nid, end_nid;
+ hda_nid_t adc = spec->imux_info[spec->cur_mux[0]].adc;
+ if (spec->adc_switching) {
+ spec->cur_adc = adc;
+ spec->cur_adc_stream_tag = stream_tag;
+ spec->cur_adc_format = format;
+ }
+ snd_hda_codec_setup_stream(codec, adc, stream_tag, 0, format);
+ return 0;
+}
- end_nid = codec->start_nid + codec->num_nodes;
- for (nid = codec->start_nid; nid < end_nid; nid++)
- if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) {
- set_beep_amp(spec, nid, 0, HDA_OUTPUT);
+static int cx_auto_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct conexant_spec *spec = codec->spec;
+ snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+ spec->cur_adc = 0;
+ return 0;
+}
+
+static const struct hda_pcm_stream cx_auto_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0, /* fill later */
+ .ops = {
+ .prepare = cx_auto_capture_pcm_prepare,
+ .cleanup = cx_auto_capture_pcm_cleanup
+ },
+};
+
+static const hda_nid_t cx_auto_adc_nids[] = { 0x14 };
+
+#define get_connection_index(codec, mux, nid)\
+ snd_hda_get_conn_index(codec, mux, nid, 0)
+
+/* get an unassigned DAC from the given list.
+ * Return the nid if found and reduce the DAC list, or return zero if
+ * not found
+ */
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin,
+ hda_nid_t *dacs, int *num_dacs)
+{
+ int i, nums = *num_dacs;
+ hda_nid_t ret = 0;
+
+ for (i = 0; i < nums; i++) {
+ if (get_connection_index(codec, pin, dacs[i]) >= 0) {
+ ret = dacs[i];
break;
}
+ }
+ if (!ret)
+ return 0;
+ if (--nums > 0)
+ memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t));
+ *num_dacs = nums;
+ return ret;
}
-#else
-#define cx_auto_parse_beep(codec)
-#endif
-/* parse EAPDs */
-static void cx_auto_parse_eapd(struct hda_codec *codec)
+#define MAX_AUTO_DACS 5
+
+#define DAC_SLAVE_FLAG 0x8000 /* filled dac is a slave */
+
+/* fill analog DAC list from the widget tree */
+static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs)
{
- struct conexant_spec *spec = codec->spec;
hda_nid_t nid, end_nid;
+ int nums = 0;
end_nid = codec->start_nid + codec->num_nodes;
for (nid = codec->start_nid; nid < end_nid; nid++) {
- if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int type = get_wcaps_type(wcaps);
+ if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) {
+ dacs[nums++] = nid;
+ if (nums >= MAX_AUTO_DACS)
+ break;
+ }
+ }
+ return nums;
+}
+
+/* fill pin_dac_pair list from the pin and dac list */
+static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins,
+ int num_pins, hda_nid_t *dacs, int *rest,
+ struct pin_dac_pair *filled, int nums,
+ int type)
+{
+ int i, start = nums;
+
+ for (i = 0; i < num_pins; i++, nums++) {
+ filled[nums].pin = pins[i];
+ filled[nums].type = type;
+ filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest);
+ if (filled[nums].dac)
continue;
- if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD))
+ if (filled[start].dac && get_connection_index(codec, pins[i], filled[start].dac) >= 0) {
+ filled[nums].dac = filled[start].dac | DAC_SLAVE_FLAG;
continue;
- spec->eapds[spec->num_eapds++] = nid;
- if (spec->num_eapds >= ARRAY_SIZE(spec->eapds))
+ }
+ if (filled[0].dac && get_connection_index(codec, pins[i], filled[0].dac) >= 0) {
+ filled[nums].dac = filled[0].dac | DAC_SLAVE_FLAG;
+ continue;
+ }
+ snd_printdd("Failed to find a DAC for pin 0x%x", pins[i]);
+ }
+ return nums;
+}
+
+/* parse analog output paths */
+static void cx_auto_parse_output(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t dacs[MAX_AUTO_DACS];
+ int i, j, nums, rest;
+
+ rest = fill_cx_auto_dacs(codec, dacs);
+ /* parse all analog output pins */
+ nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs,
+ dacs, &rest, spec->dac_info, 0,
+ AUTO_PIN_LINE_OUT);
+ nums = fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs,
+ dacs, &rest, spec->dac_info, nums,
+ AUTO_PIN_HP_OUT);
+ nums = fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs,
+ dacs, &rest, spec->dac_info, nums,
+ AUTO_PIN_SPEAKER_OUT);
+ spec->dac_info_filled = nums;
+ /* fill multiout struct */
+ for (i = 0; i < nums; i++) {
+ hda_nid_t dac = spec->dac_info[i].dac;
+ if (!dac || (dac & DAC_SLAVE_FLAG))
+ continue;
+ switch (spec->dac_info[i].type) {
+ case AUTO_PIN_LINE_OUT:
+ spec->private_dac_nids[spec->multiout.num_dacs] = dac;
+ spec->multiout.num_dacs++;
+ break;
+ case AUTO_PIN_HP_OUT:
+ case AUTO_PIN_SPEAKER_OUT:
+ if (!spec->multiout.hp_nid) {
+ spec->multiout.hp_nid = dac;
+ break;
+ }
+ for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++)
+ if (!spec->multiout.extra_out_nid[j]) {
+ spec->multiout.extra_out_nid[j] = dac;
+ break;
+ }
break;
+ }
}
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
- /* NOTE: below is a wild guess; if we have more than two EAPDs,
- * it's a new chip, where EAPDs are supposed to be associated to
- * pins, and we can control EAPD per pin.
- * OTOH, if only one or two EAPDs are found, it's an old chip,
- * thus it might control over all pins.
- */
- if (spec->num_eapds > 2)
- spec->gen.own_eapd_ctl = 1;
+ for (i = 0; i < cfg->hp_outs; i++) {
+ if (is_jack_detectable(codec, cfg->hp_pins[i])) {
+ spec->auto_mute = 1;
+ break;
+ }
+ }
+ if (spec->auto_mute &&
+ cfg->line_out_pins[0] &&
+ cfg->line_out_type != AUTO_PIN_SPEAKER_OUT &&
+ cfg->line_out_pins[0] != cfg->hp_pins[0] &&
+ cfg->line_out_pins[0] != cfg->speaker_pins[0]) {
+ for (i = 0; i < cfg->line_outs; i++) {
+ if (is_jack_detectable(codec, cfg->line_out_pins[i])) {
+ spec->detect_line = 1;
+ break;
+ }
+ }
+ spec->automute_lines = spec->detect_line;
+ }
+
+ spec->vmaster_nid = spec->private_dac_nids[0];
}
static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins,
- hda_nid_t *pins, bool on)
+ hda_nid_t *pins, bool on);
+
+static void do_automute(struct hda_codec *codec, int num_pins,
+ hda_nid_t *pins, bool on)
{
+ struct conexant_spec *spec = codec->spec;
int i;
+ for (i = 0; i < num_pins; i++)
+ snd_hda_set_pin_ctl(codec, pins[i], on ? PIN_OUT : 0);
+ if (spec->pin_eapd_ctrls)
+ cx_auto_turn_eapd(codec, num_pins, pins, on);
+}
+
+static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
+{
+ int i, present = 0;
+
for (i = 0; i < num_pins; i++) {
- if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
- snd_hda_codec_write(codec, pins[i], 0,
- AC_VERB_SET_EAPD_BTLENABLE,
- on ? 0x02 : 0);
+ hda_nid_t nid = pins[i];
+ if (!nid || !is_jack_detectable(codec, nid))
+ break;
+ present |= snd_hda_jack_detect(codec, nid);
}
+ return present;
}
-/* turn on/off EAPD according to Master switch */
-static void cx_auto_vmaster_hook(void *private_data, int enabled)
+/* auto-mute/unmute speaker and line outs according to headphone jack */
+static void cx_auto_update_speakers(struct hda_codec *codec)
{
- struct hda_codec *codec = private_data;
struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int on = 1;
+
+ /* turn on HP EAPD when HP jacks are present */
+ if (spec->pin_eapd_ctrls) {
+ if (spec->auto_mute)
+ on = spec->hp_present;
+ cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, on);
+ }
- cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
+ /* mute speakers in auto-mode if HP or LO jacks are plugged */
+ if (spec->auto_mute)
+ on = !(spec->hp_present ||
+ (spec->detect_line && spec->line_present));
+ do_automute(codec, cfg->speaker_outs, cfg->speaker_pins, on);
+
+ /* toggle line-out mutes if needed, too */
+ /* if LO is a copy of either HP or Speaker, don't need to handle it */
+ if (cfg->line_out_pins[0] == cfg->hp_pins[0] ||
+ cfg->line_out_pins[0] == cfg->speaker_pins[0])
+ return;
+ if (spec->auto_mute) {
+ /* mute LO in auto-mode when HP jack is present */
+ if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ||
+ spec->automute_lines)
+ on = !spec->hp_present;
+ else
+ on = 1;
+ }
+ do_automute(codec, cfg->line_outs, cfg->line_out_pins, on);
}
-static int cx_auto_build_controls(struct hda_codec *codec)
+static void cx_auto_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
- int err;
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
- err = snd_hda_gen_build_controls(codec);
- if (err < 0)
- return err;
+ if (!spec->auto_mute)
+ return;
+ spec->hp_present = detect_jacks(codec, cfg->hp_outs, cfg->hp_pins);
+ cx_auto_update_speakers(codec);
+}
- err = add_beep_ctls(codec);
- if (err < 0)
- return err;
+static void cx_auto_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
- return 0;
+ if (!spec->auto_mute || !spec->detect_line)
+ return;
+ spec->line_present = detect_jacks(codec, cfg->line_outs,
+ cfg->line_out_pins);
+ cx_auto_update_speakers(codec);
}
-static const struct hda_codec_ops cx_auto_patch_ops = {
- .build_controls = cx_auto_build_controls,
- .build_pcms = snd_hda_gen_build_pcms,
- .init = snd_hda_gen_init,
- .free = snd_hda_gen_free,
- .unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
- .check_power_status = snd_hda_gen_check_power_status,
-#endif
-};
+static int cx_automute_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+ static const char * const texts3[] = {
+ "Disabled", "Speaker Only", "Line Out+Speaker"
+ };
-/*
- * pin fix-up
- */
-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,
-};
+ if (spec->automute_hp_lo)
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
+ return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
+}
-static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+static int cx_automute_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct conexant_spec *spec = codec->spec;
- spec->gen.inv_dmic_split = 1;
+ unsigned int val;
+ if (!spec->auto_mute)
+ val = 0;
+ else if (!spec->automute_lines)
+ val = 1;
+ else
+ val = 2;
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
}
-static void cxt5066_increase_mic_boost(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+static int cx_automute_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
- 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));
+ switch (ucontrol->value.enumerated.item[0]) {
+ case 0:
+ if (!spec->auto_mute)
+ return 0;
+ spec->auto_mute = 0;
+ break;
+ case 1:
+ if (spec->auto_mute && !spec->automute_lines)
+ return 0;
+ spec->auto_mute = 1;
+ spec->automute_lines = 0;
+ break;
+ case 2:
+ if (!spec->automute_hp_lo)
+ return -EINVAL;
+ if (spec->auto_mute && spec->automute_lines)
+ return 0;
+ spec->auto_mute = 1;
+ spec->automute_lines = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ cx_auto_update_speakers(codec);
+ return 1;
}
-/* ThinkPad X200 & co with cxt5051 */
-static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
- { 0x16, 0x042140ff }, /* HP (seq# overridden) */
- { 0x17, 0x21a11000 }, /* dock-mic */
- { 0x19, 0x2121103f }, /* dock-HP */
- { 0x1c, 0x21440100 }, /* dock SPDIF out */
- {}
+static const struct snd_kcontrol_new cx_automute_mode_enum[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Auto-Mute Mode",
+ .info = cx_automute_mode_info,
+ .get = cx_automute_mode_get,
+ .put = cx_automute_mode_put,
+ },
+ { }
};
-/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */
-static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = {
- { 0x19, 0x042110ff }, /* HP (seq# overridden) */
- { 0x1a, 0x21a190f0 }, /* dock-mic */
- { 0x1c, 0x212140ff }, /* dock-HP */
- {}
-};
+static int cx_auto_mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+
+ return snd_hda_input_mux_info(&spec->private_imux, uinfo);
+}
+
+static int cx_auto_mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+
+ ucontrol->value.enumerated.item[0] = spec->cur_mux[0];
+ return 0;
+}
+
+/* look for the route the given pin from mux and return the index;
+ * if do_select is set, actually select the route.
+ */
+static int __select_input_connection(struct hda_codec *codec, hda_nid_t mux,
+ hda_nid_t pin, hda_nid_t *srcp,
+ bool do_select, int depth)
+{
+ struct conexant_spec *spec = codec->spec;
+ hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+ int startidx, i, nums;
+
+ switch (get_wcaps_type(get_wcaps(codec, mux))) {
+ case AC_WID_AUD_IN:
+ case AC_WID_AUD_SEL:
+ case AC_WID_AUD_MIX:
+ break;
+ default:
+ return -1;
+ }
+
+ nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+ for (i = 0; i < nums; i++)
+ if (conn[i] == pin) {
+ if (do_select)
+ snd_hda_codec_write(codec, mux, 0,
+ AC_VERB_SET_CONNECT_SEL, i);
+ if (srcp)
+ *srcp = mux;
+ return i;
+ }
+ depth++;
+ if (depth == 2)
+ return -1;
+
+ /* Try to rotate around connections to avoid one boost controlling
+ another input path as well */
+ startidx = 0;
+ for (i = 0; i < spec->private_imux.num_items; i++)
+ if (spec->imux_info[i].pin == pin) {
+ startidx = i;
+ break;
+ }
+
+ for (i = 0; i < nums; i++) {
+ int j = (i + startidx) % nums;
+ int ret = __select_input_connection(codec, conn[j], pin, srcp,
+ do_select, depth);
+ if (ret >= 0) {
+ if (do_select)
+ snd_hda_codec_write(codec, mux, 0,
+ AC_VERB_SET_CONNECT_SEL, j);
+ return j;
+ }
+ }
+ return -1;
+}
+
+static void select_input_connection(struct hda_codec *codec, hda_nid_t mux,
+ hda_nid_t pin)
+{
+ __select_input_connection(codec, mux, pin, NULL, true, 0);
+}
+
+static int get_input_connection(struct hda_codec *codec, hda_nid_t mux,
+ hda_nid_t pin)
+{
+ return __select_input_connection(codec, mux, pin, NULL, false, 0);
+}
+
+static int cx_auto_mux_enum_update(struct hda_codec *codec,
+ const struct hda_input_mux *imux,
+ unsigned int idx)
+{
+ struct conexant_spec *spec = codec->spec;
+ hda_nid_t adc;
+ int changed = 1;
+
+ if (!imux->num_items)
+ return 0;
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (spec->cur_mux[0] == idx)
+ changed = 0;
+ adc = spec->imux_info[idx].adc;
+ select_input_connection(codec, spec->imux_info[idx].adc,
+ spec->imux_info[idx].pin);
+ if (spec->cur_adc && spec->cur_adc != adc) {
+ /* stream is running, let's swap the current ADC */
+ __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+ spec->cur_adc = adc;
+ snd_hda_codec_setup_stream(codec, adc,
+ spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+ }
+ spec->cur_mux[0] = idx;
+ return changed;
+}
+
+static int cx_auto_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct conexant_spec *spec = codec->spec;
+
+ return cx_auto_mux_enum_update(codec, &spec->private_imux,
+ ucontrol->value.enumerated.item[0]);
+}
+
+static const struct snd_kcontrol_new cx_auto_capture_mixers[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Capture Source",
+ .info = cx_auto_mux_enum_info,
+ .get = cx_auto_mux_enum_get,
+ .put = cx_auto_mux_enum_put
+ },
+ {}
+};
+
+static bool select_automic(struct hda_codec *codec, int idx, bool detect)
+{
+ struct conexant_spec *spec = codec->spec;
+ if (idx < 0)
+ return false;
+ if (detect && !snd_hda_jack_detect(codec, spec->imux_info[idx].pin))
+ return false;
+ cx_auto_mux_enum_update(codec, &spec->private_imux, idx);
+ return true;
+}
+
+/* automatic switch internal and external mic */
+static void cx_auto_automic(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ if (!spec->auto_mic)
+ return;
+ if (!select_automic(codec, spec->auto_mic_ext, true))
+ if (!select_automic(codec, spec->auto_mic_dock, true))
+ select_automic(codec, spec->auto_mic_int, false);
+}
+
+/* check whether the pin config is suitable for auto-mic switching;
+ * auto-mic is enabled only when one int-mic and one ext- and/or
+ * one dock-mic exist
+ */
+static void cx_auto_check_auto_mic(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ int pset[INPUT_PIN_ATTR_NORMAL + 1];
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pset); i++)
+ pset[i] = -1;
+ for (i = 0; i < spec->private_imux.num_items; i++) {
+ hda_nid_t pin = spec->imux_info[i].pin;
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
+ int type, attr;
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (attr == INPUT_PIN_ATTR_UNUSED)
+ return; /* invalid entry */
+ if (attr > INPUT_PIN_ATTR_NORMAL)
+ attr = INPUT_PIN_ATTR_NORMAL;
+ if (attr != INPUT_PIN_ATTR_INT &&
+ !is_jack_detectable(codec, pin))
+ return; /* non-detectable pin */
+ type = get_defcfg_device(def_conf);
+ if (type != AC_JACK_MIC_IN &&
+ (attr != INPUT_PIN_ATTR_DOCK || type != AC_JACK_LINE_IN))
+ return; /* no valid input type */
+ if (pset[attr] >= 0)
+ return; /* already occupied */
+ pset[attr] = i;
+ }
+ if (pset[INPUT_PIN_ATTR_INT] < 0 ||
+ (pset[INPUT_PIN_ATTR_NORMAL] < 0 && pset[INPUT_PIN_ATTR_DOCK]))
+ return; /* no input to switch*/
+ spec->auto_mic = 1;
+ spec->auto_mic_ext = pset[INPUT_PIN_ATTR_NORMAL];
+ spec->auto_mic_dock = pset[INPUT_PIN_ATTR_DOCK];
+ spec->auto_mic_int = pset[INPUT_PIN_ATTR_INT];
+}
+
+static void cx_auto_parse_input(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct hda_input_mux *imux;
+ int i, j;
+
+ imux = &spec->private_imux;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ for (j = 0; j < spec->num_adc_nids; j++) {
+ hda_nid_t adc = spec->adc_nids[j];
+ int idx = get_input_connection(codec, adc,
+ cfg->inputs[i].pin);
+ if (idx >= 0) {
+ const char *label;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ spec->imux_info[imux->num_items].index = i;
+ spec->imux_info[imux->num_items].boost = 0;
+ spec->imux_info[imux->num_items].adc = adc;
+ spec->imux_info[imux->num_items].pin =
+ cfg->inputs[i].pin;
+ snd_hda_add_imux_item(imux, label, idx, NULL);
+ break;
+ }
+ }
+ }
+ if (imux->num_items >= 2 && cfg->num_inputs == imux->num_items)
+ cx_auto_check_auto_mic(codec);
+ if (imux->num_items > 1) {
+ for (i = 1; i < imux->num_items; i++) {
+ if (spec->imux_info[i].adc != spec->imux_info[0].adc) {
+ spec->adc_switching = 1;
+ break;
+ }
+ }
+ }
+}
+
+/* get digital-input audio widget corresponding to the given pin */
+static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin)
+{
+ hda_nid_t nid, end_nid;
+
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int type = get_wcaps_type(wcaps);
+ if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) {
+ if (get_connection_index(codec, nid, pin) >= 0)
+ return nid;
+ }
+ }
+ return 0;
+}
+
+static void cx_auto_parse_digital(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
+
+ if (cfg->dig_outs &&
+ snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1)
+ spec->multiout.dig_out_nid = nid;
+ if (cfg->dig_in_pin)
+ spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin);
+}
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static void cx_auto_parse_beep(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ hda_nid_t nid, end_nid;
+
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++)
+ if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) {
+ set_beep_amp(spec, nid, 0, HDA_OUTPUT);
+ break;
+ }
+}
+#else
+#define cx_auto_parse_beep(codec)
+#endif
+
+/* parse EAPDs */
+static void cx_auto_parse_eapd(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ hda_nid_t nid, end_nid;
+
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++) {
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ continue;
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD))
+ continue;
+ spec->eapds[spec->num_eapds++] = nid;
+ if (spec->num_eapds >= ARRAY_SIZE(spec->eapds))
+ break;
+ }
+
+ /* NOTE: below is a wild guess; if we have more than two EAPDs,
+ * it's a new chip, where EAPDs are supposed to be associated to
+ * pins, and we can control EAPD per pin.
+ * OTOH, if only one or two EAPDs are found, it's an old chip,
+ * thus it might control over all pins.
+ */
+ spec->pin_eapd_ctrls = spec->num_eapds > 2;
+}
+
+static int cx_auto_parse_auto_config(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ if (err < 0)
+ return err;
+
+ cx_auto_parse_output(codec);
+ cx_auto_parse_input(codec);
+ cx_auto_parse_digital(codec);
+ cx_auto_parse_beep(codec);
+ cx_auto_parse_eapd(codec);
+ return 0;
+}
+
+static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins,
+ hda_nid_t *pins, bool on)
+{
+ int i;
+ for (i = 0; i < num_pins; i++) {
+ if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ on ? 0x02 : 0);
+ }
+}
+
+static void select_connection(struct hda_codec *codec, hda_nid_t pin,
+ hda_nid_t src)
+{
+ int idx = get_connection_index(codec, pin, src);
+ if (idx >= 0)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_CONNECT_SEL, idx);
+}
+
+static void mute_outputs(struct hda_codec *codec, int num_nids,
+ const hda_nid_t *nids)
+{
+ int i, val;
+
+ for (i = 0; i < num_nids; i++) {
+ hda_nid_t nid = nids[i];
+ if (!(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
+ continue;
+ if (query_amp_caps(codec, nid, HDA_OUTPUT) & AC_AMPCAP_MUTE)
+ val = AMP_OUT_MUTE;
+ else
+ val = AMP_OUT_ZERO;
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
+ }
+}
+
+static void enable_unsol_pins(struct hda_codec *codec, int num_pins,
+ hda_nid_t *pins, unsigned int action,
+ hda_jack_callback cb)
+{
+ int i;
+ for (i = 0; i < num_pins; i++)
+ snd_hda_jack_detect_enable_callback(codec, pins[i], action, cb);
+}
+
+static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ int i;
+ for (i = 0; i < nums; i++)
+ if (list[i] == nid)
+ return true;
+ return false;
+}
+
+/* is the given NID found in any of autocfg items? */
+static bool found_in_autocfg(struct auto_pin_cfg *cfg, hda_nid_t nid)
+{
+ int i;
+
+ if (found_in_nid_list(nid, cfg->line_out_pins, cfg->line_outs) ||
+ found_in_nid_list(nid, cfg->hp_pins, cfg->hp_outs) ||
+ found_in_nid_list(nid, cfg->speaker_pins, cfg->speaker_outs) ||
+ found_in_nid_list(nid, cfg->dig_out_pins, cfg->dig_outs))
+ return true;
+ for (i = 0; i < cfg->num_inputs; i++)
+ if (cfg->inputs[i].pin == nid)
+ return true;
+ if (cfg->dig_in_pin == nid)
+ return true;
+ return false;
+}
+
+/* clear unsol-event tags on unused pins; Conexant codecs seem to leave
+ * invalid unsol tags by some reason
+ */
+static void clear_unsol_on_unused_pins(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < codec->init_pins.used; i++) {
+ struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ if (!found_in_autocfg(cfg, pin->nid))
+ snd_hda_codec_write(codec, pin->nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE, 0);
+ }
+}
+
+/* turn on/off EAPD according to Master switch */
+static void cx_auto_vmaster_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct conexant_spec *spec = codec->spec;
+
+ if (enabled && spec->pin_eapd_ctrls) {
+ cx_auto_update_speakers(codec);
+ return;
+ }
+ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
+}
+
+static void cx_auto_init_output(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
+ int i;
+
+ mute_outputs(codec, spec->multiout.num_dacs, spec->multiout.dac_nids);
+ for (i = 0; i < cfg->hp_outs; i++) {
+ unsigned int val = PIN_OUT;
+ if (snd_hda_query_pin_caps(codec, cfg->hp_pins[i]) &
+ AC_PINCAP_HP_DRV)
+ val |= AC_PINCTL_HP_EN;
+ snd_hda_set_pin_ctl(codec, cfg->hp_pins[i], val);
+ }
+ mute_outputs(codec, cfg->hp_outs, cfg->hp_pins);
+ mute_outputs(codec, cfg->line_outs, cfg->line_out_pins);
+ mute_outputs(codec, cfg->speaker_outs, cfg->speaker_pins);
+ for (i = 0; i < spec->dac_info_filled; i++) {
+ nid = spec->dac_info[i].dac;
+ if (!nid)
+ nid = spec->multiout.dac_nids[0];
+ else if (nid & DAC_SLAVE_FLAG)
+ nid &= ~DAC_SLAVE_FLAG;
+ select_connection(codec, spec->dac_info[i].pin, nid);
+ }
+ if (spec->auto_mute) {
+ enable_unsol_pins(codec, cfg->hp_outs, cfg->hp_pins,
+ CONEXANT_HP_EVENT, cx_auto_hp_automute);
+ spec->hp_present = detect_jacks(codec, cfg->hp_outs,
+ cfg->hp_pins);
+ if (spec->detect_line) {
+ enable_unsol_pins(codec, cfg->line_outs,
+ cfg->line_out_pins,
+ CONEXANT_LINE_EVENT,
+ cx_auto_line_automute);
+ spec->line_present =
+ detect_jacks(codec, cfg->line_outs,
+ cfg->line_out_pins);
+ }
+ }
+ cx_auto_update_speakers(codec);
+ /* turn on all EAPDs if no individual EAPD control is available */
+ if (!spec->pin_eapd_ctrls)
+ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
+ clear_unsol_on_unused_pins(codec);
+}
+
+static void cx_auto_init_input(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, val;
+
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ hda_nid_t nid = spec->adc_nids[i];
+ if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
+ continue;
+ if (query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE)
+ val = AMP_IN_MUTE(0);
+ else
+ val = AMP_IN_UNMUTE(0);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ val);
+ }
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin = cfg->inputs[i].pin;
+ unsigned int type = PIN_IN;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC)
+ type |= snd_hda_get_default_vref(codec, pin);
+ snd_hda_set_pin_ctl(codec, pin, type);
+ }
+
+ if (spec->auto_mic) {
+ if (spec->auto_mic_ext >= 0) {
+ snd_hda_jack_detect_enable_callback(codec,
+ cfg->inputs[spec->auto_mic_ext].pin,
+ CONEXANT_MIC_EVENT, cx_auto_automic);
+ }
+ if (spec->auto_mic_dock >= 0) {
+ snd_hda_jack_detect_enable_callback(codec,
+ cfg->inputs[spec->auto_mic_dock].pin,
+ CONEXANT_MIC_EVENT, cx_auto_automic);
+ }
+ cx_auto_automic(codec, NULL);
+ } else {
+ select_input_connection(codec, spec->imux_info[0].adc,
+ spec->imux_info[0].pin);
+ }
+}
+
+static void cx_auto_init_digital(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ if (spec->multiout.dig_out_nid)
+ snd_hda_set_pin_ctl(codec, cfg->dig_out_pins[0], PIN_OUT);
+ if (spec->dig_in_nid)
+ snd_hda_set_pin_ctl(codec, cfg->dig_in_pin, PIN_IN);
+}
+
+static int cx_auto_init(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ snd_hda_gen_apply_verbs(codec);
+ cx_auto_init_output(codec);
+ cx_auto_init_input(codec);
+ cx_auto_init_digital(codec);
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+ return 0;
+}
+
+static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
+ const char *dir, int cidx,
+ hda_nid_t nid, int hda_dir, int amp_idx, int chs)
+{
+ static char name[44];
+ static struct snd_kcontrol_new knew[] = {
+ HDA_CODEC_VOLUME(name, 0, 0, 0),
+ HDA_CODEC_MUTE(name, 0, 0, 0),
+ };
+ static const char * const sfx[2] = { "Volume", "Switch" };
+ int i, err;
+
+ for (i = 0; i < 2; i++) {
+ struct snd_kcontrol *kctl;
+ knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, chs, amp_idx,
+ hda_dir);
+ knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
+ knew[i].index = cidx;
+ snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]);
+ kctl = snd_ctl_new1(&knew[i], codec);
+ if (!kctl)
+ return -ENOMEM;
+ err = snd_hda_ctl_add(codec, nid, kctl);
+ if (err < 0)
+ return err;
+ if (!(query_amp_caps(codec, nid, hda_dir) &
+ (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)))
+ break;
+ }
+ return 0;
+}
+
+#define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \
+ cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0, 3)
+
+#define cx_auto_add_pb_volume(codec, nid, str, idx) \
+ cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
+
+static int try_add_pb_volume(struct hda_codec *codec, hda_nid_t dac,
+ hda_nid_t pin, const char *name, int idx)
+{
+ unsigned int caps;
+ if (dac && !(dac & DAC_SLAVE_FLAG)) {
+ caps = query_amp_caps(codec, dac, HDA_OUTPUT);
+ if (caps & AC_AMPCAP_NUM_STEPS)
+ return cx_auto_add_pb_volume(codec, dac, name, idx);
+ }
+ caps = query_amp_caps(codec, pin, HDA_OUTPUT);
+ if (caps & AC_AMPCAP_NUM_STEPS)
+ return cx_auto_add_pb_volume(codec, pin, name, idx);
+ return 0;
+}
+
+static bool is_2_1_speaker(struct conexant_spec *spec)
+{
+ int i, type, num_spk = 0;
+
+ for (i = 0; i < spec->dac_info_filled; i++) {
+ type = spec->dac_info[i].type;
+ if (type == AUTO_PIN_LINE_OUT)
+ type = spec->autocfg.line_out_type;
+ if (type == AUTO_PIN_SPEAKER_OUT)
+ num_spk++;
+ }
+ return (num_spk == 2 && spec->autocfg.line_out_type != AUTO_PIN_LINE_OUT);
+}
+
+static int cx_auto_build_output_controls(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ int i, err;
+ int num_line = 0, num_hp = 0, num_spk = 0;
+ bool speaker_2_1;
+ static const char * const texts[3] = { "Front", "Surround", "CLFE" };
+
+ if (spec->dac_info_filled == 1)
+ return try_add_pb_volume(codec, spec->dac_info[0].dac,
+ spec->dac_info[0].pin,
+ "Master", 0);
+
+ speaker_2_1 = is_2_1_speaker(spec);
+
+ for (i = 0; i < spec->dac_info_filled; i++) {
+ const char *label;
+ int idx, type;
+ hda_nid_t dac = spec->dac_info[i].dac;
+ type = spec->dac_info[i].type;
+ if (type == AUTO_PIN_LINE_OUT)
+ type = spec->autocfg.line_out_type;
+ switch (type) {
+ case AUTO_PIN_LINE_OUT:
+ default:
+ label = texts[num_line++];
+ idx = 0;
+ break;
+ case AUTO_PIN_HP_OUT:
+ label = "Headphone";
+ idx = num_hp++;
+ break;
+ case AUTO_PIN_SPEAKER_OUT:
+ if (speaker_2_1) {
+ label = num_spk++ ? "Bass Speaker" : "Speaker";
+ idx = 0;
+ } else {
+ label = "Speaker";
+ idx = num_spk++;
+ }
+ break;
+ }
+ err = try_add_pb_volume(codec, dac,
+ spec->dac_info[i].pin,
+ label, idx);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->auto_mute) {
+ err = snd_hda_add_new_ctls(codec, cx_automute_mode_enum);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Returns zero if this is a normal stereo channel, and non-zero if it should
+ be split in two independent channels.
+ dest_label must be at least 44 characters. */
+static int cx_auto_get_rightch_label(struct hda_codec *codec, const char *label,
+ char *dest_label, int nid)
+{
+ struct conexant_spec *spec = codec->spec;
+ int i;
+
+ if (!spec->fixup_stereo_dmic)
+ return 0;
+
+ for (i = 0; i < AUTO_CFG_MAX_INS; i++) {
+ int def_conf;
+ if (spec->autocfg.inputs[i].pin != nid)
+ continue;
+
+ if (spec->autocfg.inputs[i].type != AUTO_PIN_MIC)
+ return 0;
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT)
+ return 0;
+
+ /* Finally found the inverted internal mic! */
+ snprintf(dest_label, 44, "Inverted %s", label);
+ return 1;
+ }
+ return 0;
+}
+
+static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
+ const char *label, const char *pfx,
+ int cidx)
+{
+ struct conexant_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ char rightch_label[44];
+ hda_nid_t adc_nid = spec->adc_nids[i];
+ int idx = get_input_connection(codec, adc_nid, nid);
+ if (idx < 0)
+ continue;
+ if (codec->single_adc_amp)
+ idx = 0;
+
+ if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
+ /* Make two independent kcontrols for left and right */
+ int err = cx_auto_add_volume_idx(codec, label, pfx,
+ cidx, adc_nid, HDA_INPUT, idx, 1);
+ if (err < 0)
+ return err;
+ return cx_auto_add_volume_idx(codec, rightch_label, pfx,
+ cidx, adc_nid, HDA_INPUT, idx, 2);
+ }
+ return cx_auto_add_volume_idx(codec, label, pfx,
+ cidx, adc_nid, HDA_INPUT, idx, 3);
+ }
+ return 0;
+}
+
+static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx,
+ const char *label, int cidx)
+{
+ struct conexant_spec *spec = codec->spec;
+ hda_nid_t mux, nid;
+ int i, con;
+
+ nid = spec->imux_info[idx].pin;
+ if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
+ char rightch_label[44];
+ if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
+ int err = cx_auto_add_volume_idx(codec, label, " Boost",
+ cidx, nid, HDA_INPUT, 0, 1);
+ if (err < 0)
+ return err;
+ return cx_auto_add_volume_idx(codec, rightch_label, " Boost",
+ cidx, nid, HDA_INPUT, 0, 2);
+ }
+ return cx_auto_add_volume(codec, label, " Boost", cidx,
+ nid, HDA_INPUT);
+ }
+ con = __select_input_connection(codec, spec->imux_info[idx].adc, nid,
+ &mux, false, 0);
+ if (con < 0)
+ return 0;
+ for (i = 0; i < idx; i++) {
+ if (spec->imux_info[i].boost == mux)
+ return 0; /* already present */
+ }
+
+ if (get_wcaps(codec, mux) & AC_WCAP_OUT_AMP) {
+ spec->imux_info[idx].boost = mux;
+ return cx_auto_add_volume(codec, label, " Boost", cidx,
+ mux, HDA_OUTPUT);
+ }
+ return 0;
+}
+
+static int cx_auto_build_input_controls(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->private_imux;
+ const char *prev_label;
+ int input_conn[HDA_MAX_NUM_INPUTS];
+ int i, j, err, cidx;
+ int multi_connection;
+
+ if (!imux->num_items)
+ return 0;
+
+ multi_connection = 0;
+ for (i = 0; i < imux->num_items; i++) {
+ cidx = get_input_connection(codec, spec->imux_info[i].adc,
+ spec->imux_info[i].pin);
+ if (cidx < 0)
+ continue;
+ input_conn[i] = spec->imux_info[i].adc;
+ if (!codec->single_adc_amp)
+ input_conn[i] |= cidx << 8;
+ if (i > 0 && input_conn[i] != input_conn[0])
+ multi_connection = 1;
+ }
+
+ prev_label = NULL;
+ cidx = 0;
+ for (i = 0; i < imux->num_items; i++) {
+ hda_nid_t nid = spec->imux_info[i].pin;
+ const char *label;
+
+ label = hda_get_autocfg_input_label(codec, &spec->autocfg,
+ spec->imux_info[i].index);
+ if (label == prev_label)
+ cidx++;
+ else
+ cidx = 0;
+ prev_label = label;
+
+ err = cx_auto_add_boost_volume(codec, i, label, cidx);
+ if (err < 0)
+ return err;
+
+ if (!multi_connection) {
+ if (i > 0)
+ continue;
+ err = cx_auto_add_capture_volume(codec, nid,
+ "Capture", "", cidx);
+ } else {
+ bool dup_found = false;
+ for (j = 0; j < i; j++) {
+ if (input_conn[j] == input_conn[i]) {
+ dup_found = true;
+ break;
+ }
+ }
+ if (dup_found)
+ continue;
+ err = cx_auto_add_capture_volume(codec, nid,
+ label, " Capture", cidx);
+ }
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->private_imux.num_items > 1 && !spec->auto_mic) {
+ err = snd_hda_add_new_ctls(codec, cx_auto_capture_mixers);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int cx_auto_build_controls(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ int err;
+
+ err = cx_auto_build_output_controls(codec);
+ if (err < 0)
+ return err;
+ err = cx_auto_build_input_controls(codec);
+ if (err < 0)
+ return err;
+ err = conexant_build_controls(codec);
+ if (err < 0)
+ return err;
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ if (spec->vmaster_mute.sw_kctl) {
+ spec->vmaster_mute.hook = cx_auto_vmaster_hook;
+ err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute,
+ spec->vmaster_mute_led);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int cx_auto_search_adcs(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ hda_nid_t nid, end_nid;
+
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++) {
+ unsigned int caps = get_wcaps(codec, nid);
+ if (get_wcaps_type(caps) != AC_WID_AUD_IN)
+ continue;
+ if (caps & AC_WCAP_DIGITAL)
+ continue;
+ if (snd_BUG_ON(spec->num_adc_nids >=
+ ARRAY_SIZE(spec->private_adc_nids)))
+ break;
+ spec->private_adc_nids[spec->num_adc_nids++] = nid;
+ }
+ spec->adc_nids = spec->private_adc_nids;
+ return 0;
+}
+
+static const struct hda_codec_ops cx_auto_patch_ops = {
+ .build_controls = cx_auto_build_controls,
+ .build_pcms = conexant_build_pcms,
+ .init = cx_auto_init,
+ .free = conexant_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+};
+
+/*
+ * pin fix-up
+ */
+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,
+ const struct hda_fixup *fix, int action)
+{
+ struct conexant_spec *spec = codec->spec;
+ 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) */
+ { 0x17, 0x21a11000 }, /* dock-mic */
+ { 0x19, 0x2121103f }, /* dock-HP */
+ { 0x1c, 0x21440100 }, /* dock SPDIF out */
+ {}
+};
+
+/* ThinkPad 410/420/510/520, X201 & co with cxt5066 */
+static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = {
+ { 0x19, 0x042110ff }, /* HP (seq# overridden) */
+ { 0x1a, 0x21a190f0 }, /* dock-mic */
+ { 0x1c, 0x212140ff }, /* dock-HP */
+ {}
+};
/* Lemote A1004/A1205 with cxt5066 */
static const struct hda_pintbl cxt_pincfg_lemote[] = {
@@ -3339,22 +4532,12 @@ static int patch_conexant_auto(struct hda_codec *codec)
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
- snd_hda_gen_spec_init(&spec->gen);
codec->spec = spec;
-
- cx_auto_parse_beep(codec);
- cx_auto_parse_eapd(codec);
- if (spec->gen.own_eapd_ctl)
- spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook;
+ snd_hda_gen_init(&spec->gen);
switch (codec->vendor_id) {
case 0x14f15045:
codec->single_adc_amp = 1;
- codec->power_filter = NULL; /* Needs speaker amp to D3 to avoid click */
- break;
- case 0x14f15047:
- codec->pin_amp_workaround = 1;
- spec->gen.mixer_nid = 0x19;
break;
case 0x14f15051:
add_cx5051_fake_mutes(codec);
@@ -3367,6 +4550,8 @@ static int patch_conexant_auto(struct hda_codec *codec)
break;
}
+ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
/* Show mute-led control only on HP laptops
* This is a sort of white-list: on HP laptops, EAPD corresponds
* only to the mute-LED without actualy amp function. Meanwhile,
@@ -3375,20 +4560,20 @@ static int patch_conexant_auto(struct hda_codec *codec)
*/
switch (codec->subsystem_id >> 16) {
case 0x103c:
- spec->gen.vmaster_mute_enum = 1;
+ spec->vmaster_mute_led = 1;
break;
}
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+ err = cx_auto_search_adcs(codec);
if (err < 0)
- goto error;
-
- err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
- if (err < 0)
- goto error;
-
+ return err;
+ err = cx_auto_parse_auto_config(codec);
+ if (err < 0) {
+ kfree(codec->spec);
+ codec->spec = NULL;
+ return err;
+ }
+ spec->capture_stream = &cx_auto_pcm_analog_capture;
codec->patch_ops = cx_auto_patch_ops;
if (spec->beep_amp)
snd_hda_attach_beep_device(codec, spec->beep_amp);
@@ -3405,19 +4590,8 @@ static int patch_conexant_auto(struct hda_codec *codec)
}
return 0;
-
- error:
- snd_hda_gen_free(codec);
- return err;
}
-#ifndef ENABLE_CXT_STATIC_QUIRKS
-#define patch_cxt5045 patch_conexant_auto
-#define patch_cxt5047 patch_conexant_auto
-#define patch_cxt5051 patch_conexant_auto
-#define patch_cxt5066 patch_conexant_auto
-#endif
-
/*
*/
diff --git a/trunk/sound/pci/hda/patch_hdmi.c b/trunk/sound/pci/hda/patch_hdmi.c
index b9af281b2ba4..807a2aa1ff38 100644
--- a/trunk/sound/pci/hda/patch_hdmi.c
+++ b/trunk/sound/pci/hda/patch_hdmi.c
@@ -64,9 +64,6 @@ struct hdmi_spec_per_cvt {
unsigned int maxbps;
};
-/* max. connections to a widget */
-#define HDA_MAX_CONNECTIONS 32
-
struct hdmi_spec_per_pin {
hda_nid_t pin_nid;
int num_mux_nids;
@@ -84,7 +81,6 @@ struct hdmi_spec_per_pin {
struct hdmi_spec {
int num_cvts;
struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS];
- hda_nid_t cvt_nids[MAX_HDMI_CVTS];
int num_pins;
struct hdmi_spec_per_pin pins[MAX_HDMI_PINS];
@@ -718,10 +714,9 @@ static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
static void hdmi_setup_channel_mapping(struct hda_codec *codec,
hda_nid_t pin_nid, bool non_pcm, int ca,
- int channels, unsigned char *map,
- bool chmap_set)
+ int channels, unsigned char *map)
{
- if (!non_pcm && chmap_set) {
+ if (!non_pcm && map) {
hdmi_manual_setup_channel_mapping(codec, pin_nid,
channels, map);
} else {
@@ -910,8 +905,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
pin_nid,
channels);
hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
- channels, per_pin->chmap,
- per_pin->chmap_set);
+ channels, per_pin->chmap);
hdmi_stop_infoframe_trans(codec, pin_nid);
hdmi_fill_audio_infoframe(codec, pin_nid,
ai.bytes, sizeof(ai));
@@ -921,8 +915,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
* accordingly */
if (per_pin->non_pcm != non_pcm)
hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
- channels, per_pin->chmap,
- per_pin->chmap_set);
+ channels, per_pin->chmap);
}
per_pin->non_pcm = non_pcm;
@@ -1107,12 +1100,8 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
if (!static_hdmi_pcm && eld->eld_valid) {
snd_hdmi_eld_update_pcm_info(eld, hinfo);
if (hinfo->channels_min > hinfo->channels_max ||
- !hinfo->rates || !hinfo->formats) {
- per_cvt->assigned = 0;
- hinfo->nid = 0;
- snd_hda_spdif_ctls_unassign(codec, pin_idx);
+ !hinfo->rates || !hinfo->formats)
return -ENODEV;
- }
}
/* Store the updated parameters */
@@ -1198,9 +1187,6 @@ static void hdmi_repoll_eld(struct work_struct *work)
hdmi_present_sense(per_pin, per_pin->repoll_count);
}
-static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
- hda_nid_t nid);
-
static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
{
struct hdmi_spec *spec = codec->spec;
@@ -1220,9 +1206,6 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS))
return -E2BIG;
- if (codec->vendor_id == 0x80862807)
- intel_haswell_fixup_connect_list(codec, pin_nid);
-
pin_idx = spec->num_pins;
per_pin = &spec->pins[pin_idx];
@@ -1270,7 +1253,7 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
if (err < 0)
return err;
- spec->cvt_nids[spec->num_cvts++] = cvt_nid;
+ spec->num_cvts++;
return 0;
}
@@ -1698,92 +1681,30 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
.unsol_event = hdmi_unsol_event,
};
-
-static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
- hda_nid_t nid)
-{
- struct hdmi_spec *spec = codec->spec;
- hda_nid_t conns[4];
- int nconns;
-
- nconns = snd_hda_get_connections(codec, nid, conns, ARRAY_SIZE(conns));
- if (nconns == spec->num_cvts &&
- !memcmp(conns, spec->cvt_nids, spec->num_cvts * sizeof(hda_nid_t)))
- return;
-
- /* override pins connection list */
- snd_printdd("hdmi: haswell: override pin connection 0x%x\n", nid);
- snd_hda_override_conn_list(codec, nid, spec->num_cvts, spec->cvt_nids);
-}
-
-#define INTEL_VENDOR_NID 0x08
-#define INTEL_GET_VENDOR_VERB 0xf81
-#define INTEL_SET_VENDOR_VERB 0x781
-#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */
-#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */
-
-static void intel_haswell_enable_all_pins(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+static void intel_haswell_fixup_connect_list(struct hda_codec *codec)
{
unsigned int vendor_param;
+ hda_nid_t list[3] = {0x2, 0x3, 0x4};
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ vendor_param = snd_hda_codec_read(codec, 0x08, 0, 0xf81, 0);
+ if (vendor_param == -1 || vendor_param & 0x02)
return;
- vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
- INTEL_GET_VENDOR_VERB, 0);
- if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
- return;
-
- vendor_param |= INTEL_EN_ALL_PIN_CVTS;
- vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
- INTEL_SET_VENDOR_VERB, vendor_param);
- if (vendor_param == -1)
- return;
-
- snd_hda_codec_update_widgets(codec);
- return;
-}
-static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
-{
- unsigned int vendor_param;
+ /* enable DP1.2 mode */
+ vendor_param |= 0x02;
+ snd_hda_codec_read(codec, 0x08, 0, 0x781, vendor_param);
- vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
- INTEL_GET_VENDOR_VERB, 0);
- if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
+ vendor_param = snd_hda_codec_read(codec, 0x08, 0, 0xf81, 0);
+ if (vendor_param == -1 || !(vendor_param & 0x02))
return;
- /* enable DP1.2 mode */
- vendor_param |= INTEL_EN_DP12;
- snd_hda_codec_write_cache(codec, INTEL_VENDOR_NID, 0,
- INTEL_SET_VENDOR_VERB, vendor_param);
+ /* override 3 pins connection list */
+ snd_hda_override_conn_list(codec, 0x05, 3, list);
+ snd_hda_override_conn_list(codec, 0x06, 3, list);
+ snd_hda_override_conn_list(codec, 0x07, 3, list);
}
-
-/* available models for fixup */
-enum {
- INTEL_HASWELL,
-};
-
-static const struct hda_model_fixup hdmi_models[] = {
- {.id = INTEL_HASWELL, .name = "Haswell"},
- {}
-};
-
-static const struct snd_pci_quirk hdmi_fixup_tbl[] = {
- SND_PCI_QUIRK(0x8086, 0x2010, "Haswell", INTEL_HASWELL),
- {} /* terminator */
-};
-
-static const struct hda_fixup hdmi_fixups[] = {
- [INTEL_HASWELL] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = intel_haswell_enable_all_pins,
- },
-};
-
-
static int patch_generic_hdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
@@ -1794,11 +1715,8 @@ static int patch_generic_hdmi(struct hda_codec *codec)
codec->spec = spec;
- snd_hda_pick_fixup(codec, hdmi_models, hdmi_fixup_tbl, hdmi_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
if (codec->vendor_id == 0x80862807)
- intel_haswell_fixup_enable_dp12(codec);
+ intel_haswell_fixup_connect_list(codec);
if (hdmi_parse_codec(codec) < 0) {
codec->spec = NULL;
diff --git a/trunk/sound/pci/hda/patch_realtek.c b/trunk/sound/pci/hda/patch_realtek.c
index 48c9d10301b7..5faaad219a7f 100644
--- a/trunk/sound/pci/hda/patch_realtek.c
+++ b/trunk/sound/pci/hda/patch_realtek.c
@@ -27,7 +27,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -36,10 +35,12 @@
#include "hda_auto_parser.h"
#include "hda_beep.h"
#include "hda_jack.h"
-#include "hda_generic.h"
/* unsol event tags */
-#define ALC_DCVOL_EVENT 0x08
+#define ALC_FRONT_EVENT 0x01
+#define ALC_DCVOL_EVENT 0x02
+#define ALC_HP_EVENT 0x04
+#define ALC_MIC_EVENT 0x08
/* for GPIO Poll */
#define GPIO_MASK 0x03
@@ -66,42 +67,355 @@ struct alc_customize_define {
unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */
};
+struct alc_multi_io {
+ hda_nid_t pin; /* multi-io widget pin NID */
+ hda_nid_t dac; /* DAC to be connected */
+ unsigned int ctl_in; /* cached input-pin control value */
+};
+
+enum {
+ ALC_AUTOMUTE_PIN, /* change the pin control */
+ ALC_AUTOMUTE_AMP, /* mute/unmute the pin AMP */
+ ALC_AUTOMUTE_MIXER, /* mute/unmute mixer widget AMP */
+};
+
+#define MAX_VOL_NIDS 0x40
+
+/* make compatible with old code */
+#define alc_apply_pincfgs snd_hda_apply_pincfgs
+#define alc_apply_fixup snd_hda_apply_fixup
+#define alc_pick_fixup snd_hda_pick_fixup
+#define alc_fixup hda_fixup
+#define alc_pincfg hda_pintbl
+#define alc_model_fixup hda_model_fixup
+
+#define ALC_FIXUP_PINS HDA_FIXUP_PINS
+#define ALC_FIXUP_VERBS HDA_FIXUP_VERBS
+#define ALC_FIXUP_FUNC HDA_FIXUP_FUNC
+
+#define ALC_FIXUP_ACT_PRE_PROBE HDA_FIXUP_ACT_PRE_PROBE
+#define ALC_FIXUP_ACT_PROBE HDA_FIXUP_ACT_PROBE
+#define ALC_FIXUP_ACT_INIT HDA_FIXUP_ACT_INIT
+#define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD
+
+
struct alc_spec {
- struct hda_gen_spec gen; /* must be at head */
+ struct hda_gen_spec gen;
/* codec parameterization */
const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */
unsigned int num_mixers;
+ const struct snd_kcontrol_new *cap_mixer; /* capture mixer */
unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
+ char stream_name_analog[32]; /* analog PCM stream */
+ const struct hda_pcm_stream *stream_analog_playback;
+ const struct hda_pcm_stream *stream_analog_capture;
+ const struct hda_pcm_stream *stream_analog_alt_playback;
+ const struct hda_pcm_stream *stream_analog_alt_capture;
+
+ char stream_name_digital[32]; /* digital PCM stream */
+ const struct hda_pcm_stream *stream_digital_playback;
+ const struct hda_pcm_stream *stream_digital_capture;
+
+ /* playback */
+ struct hda_multi_out multiout; /* playback set-up
+ * max_channels, dacs must be set
+ * dig_out_nid and hp_nid are optional
+ */
+ hda_nid_t alt_dac_nid;
+ hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */
+ int dig_out_type;
+
+ /* capture */
+ unsigned int num_adc_nids;
+ const hda_nid_t *adc_nids;
+ const hda_nid_t *capsrc_nids;
+ hda_nid_t dig_in_nid; /* digital-in NID; optional */
+ hda_nid_t mixer_nid; /* analog-mixer NID */
+ DECLARE_BITMAP(vol_ctls, MAX_VOL_NIDS << 1);
+ DECLARE_BITMAP(sw_ctls, MAX_VOL_NIDS << 1);
+
+ /* capture setup for dynamic dual-adc switch */
+ hda_nid_t cur_adc;
+ unsigned int cur_adc_stream_tag;
+ unsigned int cur_adc_format;
+
+ /* capture source */
+ unsigned int num_mux_defs;
+ const struct hda_input_mux *input_mux;
+ unsigned int cur_mux[3];
+ hda_nid_t ext_mic_pin;
+ hda_nid_t dock_mic_pin;
+ hda_nid_t int_mic_pin;
+
+ /* channel model */
+ const struct hda_channel_mode *channel_mode;
+ int num_channel_mode;
+ int need_dac_fix;
+ int const_channel_count; /* min. channel count (for speakers) */
+ int ext_channel_count; /* current channel count for multi-io */
+
+ /* PCM information */
+ struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
+
+ /* dynamic controls, init_verbs and input_mux */
+ struct auto_pin_cfg autocfg;
struct alc_customize_define cdefine;
- unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
-
- /* inverted dmic fix */
- unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
- unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
+ struct snd_array kctls;
+ struct hda_input_mux private_imux[3];
+ hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+ hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS];
+ hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS];
+ hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
+ unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
+ int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
hda_nid_t inv_dmic_pin;
- /* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */
- int mute_led_polarity;
- hda_nid_t mute_led_nid;
-
/* hooks */
void (*init_hook)(struct hda_codec *codec);
#ifdef CONFIG_PM
void (*power_hook)(struct hda_codec *codec);
#endif
void (*shutup)(struct hda_codec *codec);
+ void (*automute_hook)(struct hda_codec *codec);
+
+ /* for pin sensing */
+ unsigned int hp_jack_present:1;
+ unsigned int line_jack_present:1;
+ unsigned int master_mute:1;
+ unsigned int auto_mic:1;
+ unsigned int auto_mic_valid_imux:1; /* valid imux for auto-mic */
+ unsigned int automute_speaker:1; /* automute speaker outputs */
+ unsigned int automute_lo:1; /* automute LO outputs */
+ unsigned int detect_hp:1; /* Headphone detection enabled */
+ unsigned int detect_lo:1; /* Line-out detection enabled */
+ unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */
+ unsigned int automute_lo_possible:1; /* there are line outs and HP */
+ unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
+
+ /* other flags */
+ unsigned int no_analog :1; /* digital I/O only */
+ unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
+ unsigned int single_input_src:1;
+ unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */
+ unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
+ unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
+ unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
+ unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
+ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
+
+ /* auto-mute control */
+ int automute_mode;
+ hda_nid_t automute_mixer_nid[AUTO_CFG_MAX_OUTS];
int init_amp;
int codec_variant; /* flag for other variants */
+ /* for virtual master */
+ hda_nid_t vmaster_nid;
+ struct hda_vmaster_mute_hook vmaster_mute;
+#ifdef CONFIG_PM
+ struct hda_loopback_check loopback;
+ int num_loopbacks;
+ struct hda_amp_list loopback_list[8];
+#endif
+
/* for PLL fix */
hda_nid_t pll_nid;
unsigned int pll_coef_idx, pll_coef_bit;
unsigned int coef0;
+
+ /* multi-io */
+ int multi_ios;
+ struct alc_multi_io multi_io[4];
+
+ /* bind volumes */
+ struct snd_array bind_ctls;
};
+static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
+ int dir, unsigned int bits)
+{
+ if (!nid)
+ return false;
+ if (get_wcaps(codec, nid) & (1 << (dir + 1)))
+ if (query_amp_caps(codec, nid, dir) & bits)
+ return true;
+ return false;
+}
+
+#define nid_has_mute(codec, nid, dir) \
+ check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
+#define nid_has_volume(codec, nid, dir) \
+ check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
+
+/*
+ * input MUX handling
+ */
+static int alc_mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id);
+ if (mux_idx >= spec->num_mux_defs)
+ mux_idx = 0;
+ if (!spec->input_mux[mux_idx].num_items && mux_idx > 0)
+ mux_idx = 0;
+ return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo);
+}
+
+static int alc_mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+ return 0;
+}
+
+static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]];
+
+ if (spec->cur_adc && spec->cur_adc != new_adc) {
+ /* stream is running, let's swap the current ADC */
+ __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+ spec->cur_adc = new_adc;
+ snd_hda_codec_setup_stream(codec, new_adc,
+ spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+ return true;
+ }
+ return false;
+}
+
+static inline hda_nid_t get_capsrc(struct alc_spec *spec, int idx)
+{
+ return spec->capsrc_nids ?
+ spec->capsrc_nids[idx] : spec->adc_nids[idx];
+}
+
+static void call_update_outputs(struct hda_codec *codec);
+static void alc_inv_dmic_sync(struct hda_codec *codec, bool force);
+
+/* for shared I/O, change the pin-control accordingly */
+static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int val;
+ hda_nid_t pin = spec->autocfg.inputs[1].pin;
+ /* NOTE: this assumes that there are only two inputs, the
+ * first is the real internal mic and the second is HP/mic jack.
+ */
+
+ val = snd_hda_get_default_vref(codec, pin);
+
+ /* This pin does not have vref caps - let's enable vref on pin 0x18
+ instead, as suggested by Realtek */
+ if (val == AC_PINCTL_VREF_HIZ) {
+ const hda_nid_t vref_pin = 0x18;
+ /* Sanity check pin 0x18 */
+ if (get_wcaps_type(get_wcaps(codec, vref_pin)) == AC_WID_PIN &&
+ get_defcfg_connect(snd_hda_codec_get_pincfg(codec, vref_pin)) == AC_JACK_PORT_NONE) {
+ unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
+ if (vref_val != AC_PINCTL_VREF_HIZ)
+ snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0));
+ }
+ }
+
+ val = set_as_mic ? val | PIN_IN : PIN_HP;
+ snd_hda_set_pin_ctl(codec, pin, val);
+
+ spec->automute_speaker = !set_as_mic;
+ call_update_outputs(codec);
+}
+
+/* select the given imux item; either unmute exclusively or select the route */
+static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
+ unsigned int idx, bool force)
+{
+ struct alc_spec *spec = codec->spec;
+ const struct hda_input_mux *imux;
+ unsigned int mux_idx;
+ int i, type, num_conns;
+ hda_nid_t nid;
+
+ if (!spec->input_mux)
+ return 0;
+
+ mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
+ imux = &spec->input_mux[mux_idx];
+ if (!imux->num_items && mux_idx > 0)
+ imux = &spec->input_mux[0];
+ if (!imux->num_items)
+ return 0;
+
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ if (spec->cur_mux[adc_idx] == idx && !force)
+ return 0;
+ spec->cur_mux[adc_idx] = idx;
+
+ if (spec->shared_mic_hp)
+ update_shared_mic_hp(codec, spec->cur_mux[adc_idx]);
+
+ if (spec->dyn_adc_switch) {
+ alc_dyn_adc_pcm_resetup(codec, idx);
+ adc_idx = spec->dyn_adc_idx[idx];
+ }
+
+ nid = get_capsrc(spec, adc_idx);
+
+ /* no selection? */
+ num_conns = snd_hda_get_num_conns(codec, nid);
+ if (num_conns <= 1)
+ return 1;
+
+ type = get_wcaps_type(get_wcaps(codec, nid));
+ if (type == AC_WID_AUD_MIX) {
+ /* Matrix-mixer style (e.g. ALC882) */
+ int active = imux->items[idx].index;
+ for (i = 0; i < num_conns; i++) {
+ unsigned int v = (i == active) ? 0 : HDA_AMP_MUTE;
+ snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, i,
+ HDA_AMP_MUTE, v);
+ }
+ } else {
+ /* MUX style (e.g. ALC880) */
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ imux->items[idx].index);
+ }
+ alc_inv_dmic_sync(codec, true);
+ return 1;
+}
+
+static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ return alc_mux_select(codec, adc_idx,
+ ucontrol->value.enumerated.item[0], false);
+}
+
+/*
+ * set up the input pin config (depending on the given auto-pin type)
+ */
+static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
+ int auto_pin_type)
+{
+ unsigned int val = PIN_IN;
+ if (auto_pin_type == AUTO_PIN_MIC)
+ val |= snd_hda_get_default_vref(codec, nid);
+ snd_hda_set_pin_ctl(codec, nid, val);
+}
+
/*
* Append the given mixer and verb elements for the later use
* The mixer array is referred in build_controls(), and init_verbs are
@@ -171,6 +485,171 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
alc_fix_pll(codec);
}
+/*
+ * Jack detections for HP auto-mute and mic-switch
+ */
+
+/* check each pin in the given array; returns true if any of them is plugged */
+static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
+{
+ int i, present = 0;
+
+ for (i = 0; i < num_pins; i++) {
+ hda_nid_t nid = pins[i];
+ if (!nid)
+ break;
+ present |= snd_hda_jack_detect(codec, nid);
+ }
+ return present;
+}
+
+/* standard HP/line-out auto-mute helper */
+static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
+ bool mute, bool hp_out)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int mute_bits = mute ? HDA_AMP_MUTE : 0;
+ unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT);
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ hda_nid_t nid = pins[i];
+ unsigned int val;
+ if (!nid)
+ break;
+ switch (spec->automute_mode) {
+ case ALC_AUTOMUTE_PIN:
+ /* don't reset VREF value in case it's controlling
+ * the amp (see alc861_fixup_asus_amp_vref_0f())
+ */
+ if (spec->keep_vref_in_automute) {
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ val &= ~PIN_HP;
+ } else
+ val = 0;
+ val |= pin_bits;
+ snd_hda_set_pin_ctl(codec, nid, val);
+ break;
+ case ALC_AUTOMUTE_AMP:
+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute_bits);
+ break;
+ case ALC_AUTOMUTE_MIXER:
+ nid = spec->automute_mixer_nid[i];
+ if (!nid)
+ break;
+ snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0,
+ HDA_AMP_MUTE, mute_bits);
+ snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 1,
+ HDA_AMP_MUTE, mute_bits);
+ break;
+ }
+ }
+}
+
+/* Toggle outputs muting */
+static void update_outputs(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int on;
+
+ /* Control HP pins/amps depending on master_mute state;
+ * in general, HP pins/amps control should be enabled in all cases,
+ * but currently set only for master_mute, just to be safe
+ */
+ if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
+ spec->autocfg.hp_pins, spec->master_mute, true);
+
+ if (!spec->automute_speaker)
+ on = 0;
+ else
+ on = spec->hp_jack_present | spec->line_jack_present;
+ on |= spec->master_mute;
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
+ spec->autocfg.speaker_pins, on, false);
+
+ /* toggle line-out mutes if needed, too */
+ /* if LO is a copy of either HP or Speaker, don't need to handle it */
+ if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] ||
+ spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0])
+ return;
+ if (!spec->automute_lo)
+ on = 0;
+ else
+ on = spec->hp_jack_present;
+ on |= spec->master_mute;
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
+ spec->autocfg.line_out_pins, on, false);
+}
+
+static void call_update_outputs(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ if (spec->automute_hook)
+ spec->automute_hook(codec);
+ else
+ update_outputs(codec);
+}
+
+/* standard HP-automute helper */
+static void alc_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+ struct alc_spec *spec = codec->spec;
+
+ spec->hp_jack_present =
+ detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
+ spec->autocfg.hp_pins);
+ if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo))
+ return;
+ call_update_outputs(codec);
+}
+
+/* standard line-out-automute helper */
+static void alc_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+ return;
+ /* check LO jack only when it's different from HP */
+ if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0])
+ return;
+
+ spec->line_jack_present =
+ detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
+ spec->autocfg.line_out_pins);
+ if (!spec->automute_speaker || !spec->detect_lo)
+ return;
+ call_update_outputs(codec);
+}
+
+#define get_connection_index(codec, mux, nid) \
+ snd_hda_get_conn_index(codec, mux, nid, 0)
+
+/* standard mic auto-switch helper */
+static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t *pins = spec->imux_pins;
+
+ if (!spec->auto_mic || !spec->auto_mic_valid_imux)
+ return;
+ if (snd_BUG_ON(!spec->adc_nids))
+ return;
+ if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0))
+ return;
+
+ if (snd_hda_jack_detect(codec, pins[spec->ext_mic_idx]))
+ alc_mux_select(codec, 0, spec->ext_mic_idx, false);
+ else if (spec->dock_mic_idx >= 0 &&
+ snd_hda_jack_detect(codec, pins[spec->dock_mic_idx]))
+ alc_mux_select(codec, 0, spec->dock_mic_idx, false);
+ else
+ alc_mux_select(codec, 0, spec->int_mic_idx, false);
+}
+
/* update the master volume per volume-knob's unsol event */
static void alc_update_knob_master(struct hda_codec *codec, struct hda_jack_tbl *jack)
{
@@ -200,6 +679,14 @@ static void alc880_unsol_event(struct hda_codec *codec, unsigned int res)
snd_hda_jack_unsol_event(codec, res >> 2);
}
+/* call init functions of standard auto-mute helpers */
+static void alc_inithook(struct hda_codec *codec)
+{
+ alc_hp_automute(codec, NULL);
+ alc_line_automute(codec, NULL);
+ alc_mic_automute(codec, NULL);
+}
+
/* additional initialization for ALC888 variants */
static void alc888_coef_init(struct hda_codec *codec)
{
@@ -320,88 +807,184 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
}
}
-
/*
- * Realtek SSID verification
+ * Auto-Mute mode mixer enum support
*/
+static int alc_automute_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ static const char * const texts3[] = {
+ "Disabled", "Speaker Only", "Line Out+Speaker"
+ };
-/* Could be any non-zero and even value. When used as fixup, tells
- * the driver to ignore any present sku defines.
- */
-#define ALC_FIXUP_SKU_IGNORE (2)
+ if (spec->automute_speaker_possible && spec->automute_lo_possible)
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
+ return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
+}
-static void alc_fixup_sku_ignore(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+static int alc_automute_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->cdefine.fixup = 1;
- spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE;
+ unsigned int val = 0;
+ if (spec->automute_speaker)
+ val++;
+ if (spec->automute_lo)
+ val++;
+
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int alc_automute_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+
+ switch (ucontrol->value.enumerated.item[0]) {
+ case 0:
+ if (!spec->automute_speaker && !spec->automute_lo)
+ return 0;
+ spec->automute_speaker = 0;
+ spec->automute_lo = 0;
+ break;
+ case 1:
+ if (spec->automute_speaker_possible) {
+ if (!spec->automute_lo && spec->automute_speaker)
+ return 0;
+ spec->automute_speaker = 1;
+ spec->automute_lo = 0;
+ } else if (spec->automute_lo_possible) {
+ if (spec->automute_lo)
+ return 0;
+ spec->automute_lo = 1;
+ } else
+ return -EINVAL;
+ break;
+ case 2:
+ if (!spec->automute_lo_possible || !spec->automute_speaker_possible)
+ return -EINVAL;
+ if (spec->automute_speaker && spec->automute_lo)
+ return 0;
+ spec->automute_speaker = 1;
+ spec->automute_lo = 1;
+ break;
+ default:
+ return -EINVAL;
}
+ call_update_outputs(codec);
+ return 1;
}
-static int alc_auto_parse_customize_define(struct hda_codec *codec)
+static const struct snd_kcontrol_new alc_automute_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Auto-Mute Mode",
+ .info = alc_automute_mode_info,
+ .get = alc_automute_mode_get,
+ .put = alc_automute_mode_put,
+};
+
+static struct snd_kcontrol_new *
+alc_kcontrol_new(struct alc_spec *spec, const char *name,
+ const struct snd_kcontrol_new *temp)
+{
+ struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return NULL;
+ *knew = *temp;
+ knew->name = kstrdup(name, GFP_KERNEL);
+ if (!knew->name)
+ return NULL;
+ return knew;
+}
+
+static int alc_add_automute_mode_enum(struct hda_codec *codec)
{
- unsigned int ass, tmp, i;
- unsigned nid = 0;
struct alc_spec *spec = codec->spec;
- spec->cdefine.enable_pcbeep = 1; /* assume always enabled */
+ if (!alc_kcontrol_new(spec, "Auto-Mute Mode", &alc_automute_mode_enum))
+ return -ENOMEM;
+ return 0;
+}
- if (spec->cdefine.fixup) {
- ass = spec->cdefine.sku_cfg;
- if (ass == ALC_FIXUP_SKU_IGNORE)
- return -1;
- goto do_sku;
+/*
+ * Check the availability of HP/line-out auto-mute;
+ * Set up appropriately if really supported
+ */
+static int alc_init_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int present = 0;
+ int i, err;
+
+ if (cfg->hp_pins[0])
+ present++;
+ if (cfg->line_out_pins[0])
+ present++;
+ if (cfg->speaker_pins[0])
+ present++;
+ if (present < 2) /* need two different output types */
+ return 0;
+
+ if (!cfg->speaker_pins[0] &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = cfg->line_outs;
}
- ass = codec->subsystem_id & 0xffff;
- if (ass != codec->bus->pci->subsystem_device && (ass & 1))
- goto do_sku;
+ if (!cfg->hp_pins[0] &&
+ cfg->line_out_type == AUTO_PIN_HP_OUT) {
+ memcpy(cfg->hp_pins, cfg->line_out_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = cfg->line_outs;
+ }
- nid = 0x1d;
- if (codec->vendor_id == 0x10ec0260)
- nid = 0x17;
- ass = snd_hda_codec_get_pincfg(codec, nid);
+ spec->automute_mode = ALC_AUTOMUTE_PIN;
- if (!(ass & 1)) {
- printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n",
- codec->chip_name, ass);
- return -1;
+ for (i = 0; i < cfg->hp_outs; i++) {
+ hda_nid_t nid = cfg->hp_pins[i];
+ if (!is_jack_detectable(codec, nid))
+ continue;
+ snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
+ nid);
+ snd_hda_jack_detect_enable_callback(codec, nid, ALC_HP_EVENT,
+ alc_hp_automute);
+ spec->detect_hp = 1;
}
- /* check sum */
- tmp = 0;
- for (i = 1; i < 16; i++) {
- if ((ass >> i) & 1)
- tmp++;
+ if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) {
+ if (cfg->speaker_outs)
+ for (i = 0; i < cfg->line_outs; i++) {
+ hda_nid_t nid = cfg->line_out_pins[i];
+ if (!is_jack_detectable(codec, nid))
+ continue;
+ snd_printdd("realtek: Enable Line-Out "
+ "auto-muting on NID 0x%x\n", nid);
+ snd_hda_jack_detect_enable_callback(codec, nid, ALC_FRONT_EVENT,
+ alc_line_automute);
+ spec->detect_lo = 1;
+ }
+ spec->automute_lo_possible = spec->detect_hp;
}
- if (((ass >> 16) & 0xf) != tmp)
- return -1;
- spec->cdefine.port_connectivity = ass >> 30;
- spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20;
- spec->cdefine.check_sum = (ass >> 16) & 0xf;
- spec->cdefine.customization = ass >> 8;
-do_sku:
- spec->cdefine.sku_cfg = ass;
- spec->cdefine.external_amp = (ass & 0x38) >> 3;
- spec->cdefine.platform_type = (ass & 0x4) >> 2;
- spec->cdefine.swap = (ass & 0x2) >> 1;
- spec->cdefine.override = ass & 0x1;
+ spec->automute_speaker_possible = cfg->speaker_outs &&
+ (spec->detect_hp || spec->detect_lo);
- snd_printd("SKU: Nid=0x%x sku_cfg=0x%08x\n",
- nid, spec->cdefine.sku_cfg);
- snd_printd("SKU: port_connectivity=0x%x\n",
- spec->cdefine.port_connectivity);
- snd_printd("SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep);
- snd_printd("SKU: check_sum=0x%08x\n", spec->cdefine.check_sum);
- snd_printd("SKU: customization=0x%08x\n", spec->cdefine.customization);
- snd_printd("SKU: external_amp=0x%x\n", spec->cdefine.external_amp);
- snd_printd("SKU: platform_type=0x%x\n", spec->cdefine.platform_type);
- snd_printd("SKU: swap=0x%x\n", spec->cdefine.swap);
- snd_printd("SKU: override=0x%x\n", spec->cdefine.override);
+ spec->automute_lo = spec->automute_lo_possible;
+ spec->automute_speaker = spec->automute_speaker_possible;
+ if (spec->automute_speaker_possible || spec->automute_lo_possible) {
+ /* create a control for automute mode */
+ err = alc_add_automute_mode_enum(codec);
+ if (err < 0)
+ return err;
+ }
return 0;
}
@@ -414,6 +997,261 @@ static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
return i;
return -1;
}
+
+/* check whether dynamic ADC-switching is available */
+static bool alc_check_dyn_adc_switch(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->private_imux[0];
+ int i, n, idx;
+ hda_nid_t cap, pin;
+
+ if (imux != spec->input_mux) /* no dynamic imux? */
+ return false;
+
+ for (n = 0; n < spec->num_adc_nids; n++) {
+ cap = spec->private_capsrc_nids[n];
+ for (i = 0; i < imux->num_items; i++) {
+ pin = spec->imux_pins[i];
+ if (!pin)
+ return false;
+ if (get_connection_index(codec, cap, pin) < 0)
+ break;
+ }
+ if (i >= imux->num_items)
+ return true; /* no ADC-switch is needed */
+ }
+
+ for (i = 0; i < imux->num_items; i++) {
+ pin = spec->imux_pins[i];
+ for (n = 0; n < spec->num_adc_nids; n++) {
+ cap = spec->private_capsrc_nids[n];
+ idx = get_connection_index(codec, cap, pin);
+ if (idx >= 0) {
+ imux->items[i].index = idx;
+ spec->dyn_adc_idx[i] = n;
+ break;
+ }
+ }
+ }
+
+ snd_printdd("realtek: enabling ADC switching\n");
+ spec->dyn_adc_switch = 1;
+ return true;
+}
+
+/* check whether all auto-mic pins are valid; setup indices if OK */
+static bool alc_auto_mic_check_imux(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ const struct hda_input_mux *imux;
+
+ if (!spec->auto_mic)
+ return false;
+ if (spec->auto_mic_valid_imux)
+ return true; /* already checked */
+
+ /* fill up imux indices */
+ if (!alc_check_dyn_adc_switch(codec)) {
+ spec->auto_mic = 0;
+ return false;
+ }
+
+ imux = spec->input_mux;
+ spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin,
+ spec->imux_pins, imux->num_items);
+ spec->int_mic_idx = find_idx_in_nid_list(spec->int_mic_pin,
+ spec->imux_pins, imux->num_items);
+ spec->dock_mic_idx = find_idx_in_nid_list(spec->dock_mic_pin,
+ spec->imux_pins, imux->num_items);
+ if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) {
+ spec->auto_mic = 0;
+ return false; /* no corresponding imux */
+ }
+
+ snd_hda_jack_detect_enable_callback(codec, spec->ext_mic_pin,
+ ALC_MIC_EVENT, alc_mic_automute);
+ if (spec->dock_mic_pin)
+ snd_hda_jack_detect_enable_callback(codec, spec->dock_mic_pin,
+ ALC_MIC_EVENT,
+ alc_mic_automute);
+
+ spec->auto_mic_valid_imux = 1;
+ spec->auto_mic = 1;
+ return true;
+}
+
+/*
+ * Check the availability of auto-mic switch;
+ * Set up if really supported
+ */
+static int alc_init_auto_mic(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t fixed, ext, dock;
+ int i;
+
+ if (spec->shared_mic_hp)
+ return 0; /* no auto-mic for the shared I/O */
+
+ spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1;
+
+ fixed = ext = dock = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ unsigned int defcfg;
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ switch (snd_hda_get_input_pin_attr(defcfg)) {
+ case INPUT_PIN_ATTR_INT:
+ if (fixed)
+ return 0; /* already occupied */
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ return 0; /* invalid type */
+ fixed = nid;
+ break;
+ case INPUT_PIN_ATTR_UNUSED:
+ return 0; /* invalid entry */
+ case INPUT_PIN_ATTR_DOCK:
+ if (dock)
+ return 0; /* already occupied */
+ if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+ return 0; /* invalid type */
+ dock = nid;
+ break;
+ default:
+ if (ext)
+ return 0; /* already occupied */
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ return 0; /* invalid type */
+ ext = nid;
+ break;
+ }
+ }
+ if (!ext && dock) {
+ ext = dock;
+ dock = 0;
+ }
+ if (!ext || !fixed)
+ return 0;
+ if (!is_jack_detectable(codec, ext))
+ return 0; /* no unsol support */
+ if (dock && !is_jack_detectable(codec, dock))
+ return 0; /* no unsol support */
+
+ /* check imux indices */
+ spec->ext_mic_pin = ext;
+ spec->int_mic_pin = fixed;
+ spec->dock_mic_pin = dock;
+
+ spec->auto_mic = 1;
+ if (!alc_auto_mic_check_imux(codec))
+ return 0;
+
+ snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
+ ext, fixed, dock);
+
+ return 0;
+}
+
+/* check the availabilities of auto-mute and auto-mic switches */
+static int alc_auto_check_switches(struct hda_codec *codec)
+{
+ int err;
+
+ err = alc_init_automute(codec);
+ if (err < 0)
+ return err;
+ err = alc_init_auto_mic(codec);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/*
+ * Realtek SSID verification
+ */
+
+/* Could be any non-zero and even value. When used as fixup, tells
+ * the driver to ignore any present sku defines.
+ */
+#define ALC_FIXUP_SKU_IGNORE (2)
+
+static void alc_fixup_sku_ignore(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->cdefine.fixup = 1;
+ spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE;
+ }
+}
+
+static int alc_auto_parse_customize_define(struct hda_codec *codec)
+{
+ unsigned int ass, tmp, i;
+ unsigned nid = 0;
+ struct alc_spec *spec = codec->spec;
+
+ spec->cdefine.enable_pcbeep = 1; /* assume always enabled */
+
+ if (spec->cdefine.fixup) {
+ ass = spec->cdefine.sku_cfg;
+ if (ass == ALC_FIXUP_SKU_IGNORE)
+ return -1;
+ goto do_sku;
+ }
+
+ ass = codec->subsystem_id & 0xffff;
+ if (ass != codec->bus->pci->subsystem_device && (ass & 1))
+ goto do_sku;
+
+ nid = 0x1d;
+ if (codec->vendor_id == 0x10ec0260)
+ nid = 0x17;
+ ass = snd_hda_codec_get_pincfg(codec, nid);
+
+ if (!(ass & 1)) {
+ printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n",
+ codec->chip_name, ass);
+ return -1;
+ }
+
+ /* check sum */
+ tmp = 0;
+ for (i = 1; i < 16; i++) {
+ if ((ass >> i) & 1)
+ tmp++;
+ }
+ if (((ass >> 16) & 0xf) != tmp)
+ return -1;
+
+ spec->cdefine.port_connectivity = ass >> 30;
+ spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20;
+ spec->cdefine.check_sum = (ass >> 16) & 0xf;
+ spec->cdefine.customization = ass >> 8;
+do_sku:
+ spec->cdefine.sku_cfg = ass;
+ spec->cdefine.external_amp = (ass & 0x38) >> 3;
+ spec->cdefine.platform_type = (ass & 0x4) >> 2;
+ spec->cdefine.swap = (ass & 0x2) >> 1;
+ spec->cdefine.override = ass & 0x1;
+
+ snd_printd("SKU: Nid=0x%x sku_cfg=0x%08x\n",
+ nid, spec->cdefine.sku_cfg);
+ snd_printd("SKU: port_connectivity=0x%x\n",
+ spec->cdefine.port_connectivity);
+ snd_printd("SKU: enable_pcbeep=0x%x\n", spec->cdefine.enable_pcbeep);
+ snd_printd("SKU: check_sum=0x%08x\n", spec->cdefine.check_sum);
+ snd_printd("SKU: customization=0x%08x\n", spec->cdefine.customization);
+ snd_printd("SKU: external_amp=0x%x\n", spec->cdefine.external_amp);
+ snd_printd("SKU: platform_type=0x%x\n", spec->cdefine.platform_type);
+ snd_printd("SKU: swap=0x%x\n", spec->cdefine.swap);
+ snd_printd("SKU: override=0x%x\n", spec->cdefine.override);
+
+ return 0;
+}
+
/* return true if the given NID is found in the list */
static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
{
@@ -516,9 +1354,9 @@ static int alc_subsystem_id(struct hda_codec *codec,
* 15 : 1 --> enable the function "Mute internal speaker
* when the external headphone out jack is plugged"
*/
- if (!spec->gen.autocfg.hp_pins[0] &&
- !(spec->gen.autocfg.line_out_pins[0] &&
- spec->gen.autocfg.line_out_type == AUTO_PIN_HP_OUT)) {
+ if (!spec->autocfg.hp_pins[0] &&
+ !(spec->autocfg.line_out_pins[0] &&
+ spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)) {
hda_nid_t nid;
tmp = (ass >> 11) & 0x3; /* HP to chassis */
if (tmp == 0)
@@ -531,10 +1369,10 @@ static int alc_subsystem_id(struct hda_codec *codec,
nid = porti;
else
return 1;
- if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins,
- spec->gen.autocfg.line_outs))
+ if (found_in_nid_list(nid, spec->autocfg.line_out_pins,
+ spec->autocfg.line_outs))
return 1;
- spec->gen.autocfg.hp_pins[0] = nid;
+ spec->autocfg.hp_pins[0] = nid;
}
return 1;
}
@@ -584,54 +1422,252 @@ static unsigned int alc_get_coef0(struct hda_codec *codec)
}
/*
+ * Digital I/O handling
*/
-static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx)
+/* set right pin controls for digital I/O */
+static void alc_auto_init_digital(struct hda_codec *codec)
{
- struct hda_gen_spec *spec = codec->spec;
- if (spec->dyn_adc_switch)
- adc_idx = spec->dyn_adc_idx[imux_idx];
- return spec->adc_nids[adc_idx];
+ struct alc_spec *spec = codec->spec;
+ int i;
+ hda_nid_t pin, dac;
+
+ for (i = 0; i < spec->autocfg.dig_outs; i++) {
+ pin = spec->autocfg.dig_out_pins[i];
+ if (!pin)
+ continue;
+ snd_hda_set_pin_ctl(codec, pin, PIN_OUT);
+ if (!i)
+ dac = spec->multiout.dig_out_nid;
+ else
+ dac = spec->slave_dig_outs[i - 1];
+ if (!dac || !(get_wcaps(codec, dac) & AC_WCAP_OUT_AMP))
+ continue;
+ snd_hda_codec_write(codec, dac, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ }
+ pin = spec->autocfg.dig_in_pin;
+ if (pin)
+ snd_hda_set_pin_ctl(codec, pin, PIN_IN);
}
-static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx)
+/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */
+static void alc_auto_parse_digital(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- struct hda_input_mux *imux = &spec->gen.input_mux;
- struct nid_path *path;
- hda_nid_t nid;
- int i, dir, parm;
- unsigned int val;
+ int i, err, nums;
+ hda_nid_t dig_nid;
+
+ /* support multiple SPDIFs; the secondary is set up as a slave */
+ nums = 0;
+ for (i = 0; i < spec->autocfg.dig_outs; i++) {
+ hda_nid_t conn[4];
+ err = snd_hda_get_connections(codec,
+ spec->autocfg.dig_out_pins[i],
+ conn, ARRAY_SIZE(conn));
+ if (err <= 0)
+ continue;
+ dig_nid = conn[0]; /* assume the first element is audio-out */
+ if (!nums) {
+ spec->multiout.dig_out_nid = dig_nid;
+ spec->dig_out_type = spec->autocfg.dig_out_type[0];
+ } else {
+ spec->multiout.slave_dig_outs = spec->slave_dig_outs;
+ if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1)
+ break;
+ spec->slave_dig_outs[nums - 1] = dig_nid;
+ }
+ nums++;
+ }
- for (i = 0; i < imux->num_items; i++) {
- if (spec->gen.imux_pins[i] == spec->inv_dmic_pin)
- break;
+ if (spec->autocfg.dig_in_pin) {
+ dig_nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
+ unsigned int wcaps = get_wcaps(codec, dig_nid);
+ if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
+ continue;
+ if (!(wcaps & AC_WCAP_DIGITAL))
+ continue;
+ if (!(wcaps & AC_WCAP_CONN_LIST))
+ continue;
+ err = get_connection_index(codec, dig_nid,
+ spec->autocfg.dig_in_pin);
+ if (err >= 0) {
+ spec->dig_in_nid = dig_nid;
+ break;
+ }
+ }
}
- if (i >= imux->num_items)
- return;
+}
- path = snd_hda_get_nid_path(codec, spec->inv_dmic_pin,
- get_adc_nid(codec, adc_idx, i));
- val = path->ctls[NID_PATH_MUTE_CTL];
- if (!val)
- return;
- nid = get_amp_nid_(val);
- dir = get_amp_direction_(val);
- parm = AC_AMP_SET_RIGHT |
- (dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT);
+/*
+ * capture mixer elements
+ */
+static int alc_cap_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ unsigned long val;
+ int err;
+
+ mutex_lock(&codec->control_mutex);
+ if (spec->vol_in_capsrc)
+ val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT);
+ else
+ val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT);
+ kcontrol->private_value = val;
+ err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+ mutex_unlock(&codec->control_mutex);
+ return err;
+}
- /* flush all cached amps at first */
- snd_hda_codec_flush_cache(codec);
+static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ unsigned long val;
+ int err;
- /* we care only right channel */
- val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
- if (val & 0x80) /* if already muted, we don't need to touch */
- return;
- val |= 0x80;
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- parm | val);
+ mutex_lock(&codec->control_mutex);
+ if (spec->vol_in_capsrc)
+ val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT);
+ else
+ val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT);
+ kcontrol->private_value = val;
+ err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+ mutex_unlock(&codec->control_mutex);
+ return err;
+}
+
+typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol,
+ getput_call_t func, bool is_put)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ int i, err = 0;
+
+ mutex_lock(&codec->control_mutex);
+ if (is_put && spec->dyn_adc_switch) {
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ kcontrol->private_value =
+ HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
+ 3, 0, HDA_INPUT);
+ err = func(kcontrol, ucontrol);
+ if (err < 0)
+ goto error;
+ }
+ } else {
+ i = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ if (spec->vol_in_capsrc)
+ kcontrol->private_value =
+ HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[i],
+ 3, 0, HDA_OUTPUT);
+ else
+ kcontrol->private_value =
+ HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
+ 3, 0, HDA_INPUT);
+ err = func(kcontrol, ucontrol);
+ }
+ if (err >= 0 && is_put)
+ alc_inv_dmic_sync(codec, false);
+ error:
+ mutex_unlock(&codec->control_mutex);
+ return err;
+}
+
+static int alc_cap_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return alc_cap_getput_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_volume_get, false);
+}
+
+static int alc_cap_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return alc_cap_getput_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_volume_put, true);
+}
+
+/* capture mixer elements */
+#define alc_cap_sw_info snd_ctl_boolean_stereo_info
+
+static int alc_cap_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return alc_cap_getput_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_switch_get, false);
+}
+
+static int alc_cap_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return alc_cap_getput_caller(kcontrol, ucontrol,
+ snd_hda_mixer_amp_switch_put, true);
+}
+
+#define _DEFINE_CAPMIX(num) \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = "Capture Switch", \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .count = num, \
+ .info = alc_cap_sw_info, \
+ .get = alc_cap_sw_get, \
+ .put = alc_cap_sw_put, \
+ }, \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = "Capture Volume", \
+ .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \
+ .count = num, \
+ .info = alc_cap_vol_info, \
+ .get = alc_cap_vol_get, \
+ .put = alc_cap_vol_put, \
+ .tlv = { .c = alc_cap_vol_tlv }, \
+ }
+
+#define _DEFINE_CAPSRC(num) \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ /* .name = "Capture Source", */ \
+ .name = "Input Source", \
+ .count = num, \
+ .info = alc_mux_enum_info, \
+ .get = alc_mux_enum_get, \
+ .put = alc_mux_enum_put, \
+ }
+
+#define DEFINE_CAPMIX(num) \
+static const struct snd_kcontrol_new alc_capture_mixer ## num[] = { \
+ _DEFINE_CAPMIX(num), \
+ _DEFINE_CAPSRC(num), \
+ { } /* end */ \
+}
+
+#define DEFINE_CAPMIX_NOSRC(num) \
+static const struct snd_kcontrol_new alc_capture_mixer_nosrc ## num[] = { \
+ _DEFINE_CAPMIX(num), \
+ { } /* end */ \
}
+/* up to three ADCs */
+DEFINE_CAPMIX(1);
+DEFINE_CAPMIX(2);
+DEFINE_CAPMIX(3);
+DEFINE_CAPMIX_NOSRC(1);
+DEFINE_CAPMIX_NOSRC(2);
+DEFINE_CAPMIX_NOSRC(3);
+
/*
* Inverted digital-mic handling
*
@@ -650,31 +1686,43 @@ static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx)
static void alc_inv_dmic_sync(struct hda_codec *codec, bool force)
{
struct alc_spec *spec = codec->spec;
- int src, nums;
+ int i;
if (!spec->inv_dmic_fixup)
return;
if (!spec->inv_dmic_muted && !force)
return;
- nums = spec->gen.dyn_adc_switch ? 1 : spec->gen.num_adc_nids;
- for (src = 0; src < nums; src++) {
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ int src = spec->dyn_adc_switch ? 0 : i;
bool dmic_fixup = false;
+ hda_nid_t nid;
+ int parm, dir, v;
if (spec->inv_dmic_muted &&
- spec->gen.imux_pins[spec->gen.cur_mux[src]] == spec->inv_dmic_pin)
+ spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin)
dmic_fixup = true;
if (!dmic_fixup && !force)
continue;
- alc_inv_dmic_sync_adc(codec, src);
+ if (spec->vol_in_capsrc) {
+ nid = spec->capsrc_nids[i];
+ parm = AC_AMP_SET_RIGHT | AC_AMP_SET_OUTPUT;
+ dir = HDA_OUTPUT;
+ } else {
+ nid = spec->adc_nids[i];
+ parm = AC_AMP_SET_RIGHT | AC_AMP_SET_INPUT;
+ dir = HDA_INPUT;
+ }
+ /* we care only right channel */
+ v = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
+ if (v & 0x80) /* if already muted, we don't need to touch */
+ continue;
+ if (dmic_fixup) /* add mute for d-mic */
+ v |= 0x80;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ parm | v);
}
}
-static void alc_inv_dmic_hook(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol)
-{
- alc_inv_dmic_sync(codec, false);
-}
-
static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -701,7 +1749,6 @@ static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol,
static const struct snd_kcontrol_new alc_inv_dmic_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Inverted Internal Mic Capture Switch",
.info = snd_ctl_boolean_mono_info,
.get = alc_inv_dmic_sw_get,
.put = alc_inv_dmic_sw_put,
@@ -711,23 +1758,51 @@ static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid)
{
struct alc_spec *spec = codec->spec;
- if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &alc_inv_dmic_sw))
+ if (!alc_kcontrol_new(spec, "Inverted Internal Mic Capture Switch",
+ &alc_inv_dmic_sw))
return -ENOMEM;
spec->inv_dmic_fixup = 1;
spec->inv_dmic_muted = 0;
spec->inv_dmic_pin = nid;
- spec->gen.cap_sync_hook = alc_inv_dmic_hook;
return 0;
}
/* typically the digital mic is put at node 0x12 */
static void alc_fixup_inv_dmic_0x12(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
- if (action == HDA_FIXUP_ACT_PROBE)
+ if (action == ALC_FIXUP_ACT_PROBE)
alc_add_inv_dmic_mixer(codec, 0x12);
}
+/*
+ * virtual master controls
+ */
+
+/*
+ * slave controls for virtual master
+ */
+static const char * const alc_slave_pfxs[] = {
+ "Front", "Surround", "Center", "LFE", "Side",
+ "Headphone", "Speaker", "Mono", "Line Out",
+ "CLFE", "Bass Speaker", "PCM",
+ NULL,
+};
+
+/*
+ * build control elements
+ */
+
+#define NID_MAPPING (-1)
+
+#define SUBDEV_SPEAKER_ (0 << 6)
+#define SUBDEV_HP_ (1 << 6)
+#define SUBDEV_LINE_ (2 << 6)
+#define SUBDEV_SPEAKER(x) (SUBDEV_SPEAKER_ | ((x) & 0x3f))
+#define SUBDEV_HP(x) (SUBDEV_HP_ | ((x) & 0x3f))
+#define SUBDEV_LINE(x) (SUBDEV_LINE_ | ((x) & 0x3f))
+
+static void alc_free_kctls(struct hda_codec *codec);
#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* additional beep mixers; the actual parameters are overwritten at build */
@@ -738,20 +1813,45 @@ static const struct snd_kcontrol_new alc_beep_mixer[] = {
};
#endif
-static int alc_build_controls(struct hda_codec *codec)
+static int __alc_build_controls(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- int i, err;
-
- err = snd_hda_gen_build_controls(codec);
- if (err < 0)
- return err;
+ struct snd_kcontrol *kctl = NULL;
+ const struct snd_kcontrol_new *knew;
+ int i, j, err;
+ unsigned int u;
+ hda_nid_t nid;
for (i = 0; i < spec->num_mixers; i++) {
err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
if (err < 0)
return err;
}
+ if (spec->cap_mixer) {
+ err = snd_hda_add_new_ctls(codec, spec->cap_mixer);
+ if (err < 0)
+ return err;
+ }
+ if (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) {
+ err = snd_hda_create_spdif_share_sw(codec,
+ &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
+ }
+ }
+ if (spec->dig_in_nid) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+ if (err < 0)
+ return err;
+ }
#ifdef CONFIG_SND_HDA_INPUT_BEEP
/* create beep controls if needed */
@@ -768,155 +1868,2369 @@ static int alc_build_controls(struct hda_codec *codec)
return err;
}
}
-#endif
+#endif
+
+ /* if we have no master control, let's create it */
+ if (!spec->no_analog &&
+ !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+ unsigned int vmaster_tlv[4];
+ snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
+ HDA_OUTPUT, vmaster_tlv);
+ err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+ vmaster_tlv, alc_slave_pfxs,
+ "Playback Volume");
+ if (err < 0)
+ return err;
+ }
+ if (!spec->no_analog &&
+ !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+ err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, alc_slave_pfxs,
+ "Playback Switch",
+ true, &spec->vmaster_mute.sw_kctl);
+ if (err < 0)
+ return err;
+ }
+
+ /* assign Capture Source enums to NID */
+ if (spec->capsrc_nids || spec->adc_nids) {
+ kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
+ if (!kctl)
+ kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
+ for (i = 0; kctl && i < kctl->count; i++) {
+ err = snd_hda_add_nid(codec, kctl, i,
+ get_capsrc(spec, i));
+ if (err < 0)
+ return err;
+ }
+ }
+ if (spec->cap_mixer && spec->adc_nids) {
+ const char *kname = kctl ? kctl->id.name : NULL;
+ for (knew = spec->cap_mixer; knew->name; knew++) {
+ if (kname && strcmp(knew->name, kname) == 0)
+ continue;
+ kctl = snd_hda_find_mixer_ctl(codec, knew->name);
+ for (i = 0; kctl && i < kctl->count; i++) {
+ err = snd_hda_add_nid(codec, kctl, i,
+ spec->adc_nids[i]);
+ if (err < 0)
+ return err;
+ }
+ }
+ }
+
+ /* other nid->control mapping */
+ for (i = 0; i < spec->num_mixers; i++) {
+ for (knew = spec->mixers[i]; knew->name; knew++) {
+ if (knew->iface != NID_MAPPING)
+ continue;
+ kctl = snd_hda_find_mixer_ctl(codec, knew->name);
+ if (kctl == NULL)
+ continue;
+ u = knew->subdevice;
+ for (j = 0; j < 4; j++, u >>= 8) {
+ nid = u & 0x3f;
+ if (nid == 0)
+ continue;
+ switch (u & 0xc0) {
+ case SUBDEV_SPEAKER_:
+ nid = spec->autocfg.speaker_pins[nid];
+ break;
+ case SUBDEV_LINE_:
+ nid = spec->autocfg.line_out_pins[nid];
+ break;
+ case SUBDEV_HP_:
+ nid = spec->autocfg.hp_pins[nid];
+ break;
+ default:
+ continue;
+ }
+ err = snd_hda_add_nid(codec, kctl, 0, nid);
+ if (err < 0)
+ return err;
+ }
+ u = knew->private_value;
+ for (j = 0; j < 4; j++, u >>= 8) {
+ nid = u & 0xff;
+ if (nid == 0)
+ continue;
+ err = snd_hda_add_nid(codec, kctl, 0, nid);
+ if (err < 0)
+ return err;
+ }
+ }
+ }
+
+ alc_free_kctls(codec); /* no longer needed */
+
+ return 0;
+}
+
+static int alc_build_jacks(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->shared_mic_hp) {
+ int err;
+ int nid = spec->autocfg.inputs[1].pin;
+ err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0);
+ if (err < 0)
+ return err;
+ err = snd_hda_jack_detect_enable(codec, nid, 0);
+ if (err < 0)
+ return err;
+ }
+
+ return snd_hda_jack_add_kctls(codec, &spec->autocfg);
+}
+
+static int alc_build_controls(struct hda_codec *codec)
+{
+ int err = __alc_build_controls(codec);
+ if (err < 0)
+ return err;
+
+ err = alc_build_jacks(codec);
+ if (err < 0)
+ return err;
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD);
+ return 0;
+}
+
+
+/*
+ * Common callbacks
+ */
+
+static void alc_init_special_input_src(struct hda_codec *codec);
+static void alc_auto_init_std(struct hda_codec *codec);
+
+static int alc_init(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->init_hook)
+ spec->init_hook(codec);
+
+ alc_fix_pll(codec);
+ alc_auto_init_amp(codec, spec->init_amp);
+
+ snd_hda_gen_apply_verbs(codec);
+ alc_init_special_input_src(codec);
+ alc_auto_init_std(codec);
+
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT);
+
+ hda_call_check_power_status(codec, 0x01);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
+}
+#endif
+
+/*
+ * Analog playback callbacks
+ */
+static int alc_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
+}
+
+static int alc_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int alc_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int alc_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int alc_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int alc_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
+static int alc_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int alc_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
+ stream_tag, 0, format);
+ return 0;
+}
+
+static int alc_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+
+ snd_hda_codec_cleanup_stream(codec,
+ spec->adc_nids[substream->number + 1]);
+ return 0;
+}
+
+/* analog capture with dynamic dual-adc changes */
+static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]];
+ spec->cur_adc_stream_tag = stream_tag;
+ spec->cur_adc_format = format;
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+ return 0;
+}
+
+static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct alc_spec *spec = codec->spec;
+ snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+ spec->cur_adc = 0;
+ return 0;
+}
+
+static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0, /* fill later */
+ .ops = {
+ .prepare = dyn_adc_capture_pcm_prepare,
+ .cleanup = dyn_adc_capture_pcm_cleanup
+ },
+};
+
+/*
+ */
+static const struct hda_pcm_stream alc_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ /* NID is set in alc_build_pcms */
+ .ops = {
+ .open = alc_playback_pcm_open,
+ .prepare = alc_playback_pcm_prepare,
+ .cleanup = alc_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream alc_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in alc_build_pcms */
+};
+
+static const struct hda_pcm_stream alc_pcm_analog_alt_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in alc_build_pcms */
+};
+
+static const struct hda_pcm_stream alc_pcm_analog_alt_capture = {
+ .substreams = 2, /* can be overridden */
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in alc_build_pcms */
+ .ops = {
+ .prepare = alc_alt_capture_pcm_prepare,
+ .cleanup = alc_alt_capture_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream alc_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in alc_build_pcms */
+ .ops = {
+ .open = alc_dig_playback_pcm_open,
+ .close = alc_dig_playback_pcm_close,
+ .prepare = alc_dig_playback_pcm_prepare,
+ .cleanup = alc_dig_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream alc_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in alc_build_pcms */
+};
+
+/* Used by alc_build_pcms to flag that a PCM has no playback stream */
+static const struct hda_pcm_stream alc_pcm_null_stream = {
+ .substreams = 0,
+ .channels_min = 0,
+ .channels_max = 0,
+};
+
+static int alc_build_pcms(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+ const struct hda_pcm_stream *p;
+ bool have_multi_adcs;
+ int i;
+
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
+ if (spec->no_analog)
+ goto skip_analog;
+
+ snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
+ "%s Analog", codec->chip_name);
+ info->name = spec->stream_name_analog;
+
+ if (spec->multiout.num_dacs > 0) {
+ p = spec->stream_analog_playback;
+ if (!p)
+ p = &alc_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ spec->autocfg.line_outs == 2)
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap =
+ snd_pcm_2_1_chmaps;
+ }
+ if (spec->adc_nids) {
+ p = spec->stream_analog_capture;
+ if (!p) {
+ if (spec->dyn_adc_switch)
+ p = &dyn_adc_pcm_analog_capture;
+ else
+ p = &alc_pcm_analog_capture;
+ }
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+ }
+
+ if (spec->channel_mode) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
+ for (i = 0; i < spec->num_channel_mode; i++) {
+ if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
+ }
+ }
+ }
+
+ skip_analog:
+ /* SPDIF for stream index #1 */
+ if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+ snprintf(spec->stream_name_digital,
+ sizeof(spec->stream_name_digital),
+ "%s Digital", codec->chip_name);
+ codec->num_pcms = 2;
+ codec->slave_dig_outs = spec->multiout.slave_dig_outs;
+ info = spec->pcm_rec + 1;
+ info->name = spec->stream_name_digital;
+ if (spec->dig_out_type)
+ info->pcm_type = spec->dig_out_type;
+ else
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->multiout.dig_out_nid) {
+ p = spec->stream_digital_playback;
+ if (!p)
+ p = &alc_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+ }
+ if (spec->dig_in_nid) {
+ p = spec->stream_digital_capture;
+ if (!p)
+ p = &alc_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
+ }
+ /* FIXME: do we need this for all Realtek codec models? */
+ codec->spdif_status_reset = 1;
+ }
+
+ if (spec->no_analog)
+ return 0;
+
+ /* If the use of more than one ADC is requested for the current
+ * model, configure a second analog capture-only PCM.
+ */
+ have_multi_adcs = (spec->num_adc_nids > 1) &&
+ !spec->dyn_adc_switch && !spec->auto_mic &&
+ (!spec->input_mux || spec->input_mux->num_items > 1);
+ /* Additional Analaog capture for index #2 */
+ if (spec->alt_dac_nid || have_multi_adcs) {
+ codec->num_pcms = 3;
+ info = spec->pcm_rec + 2;
+ info->name = spec->stream_name_analog;
+ if (spec->alt_dac_nid) {
+ p = spec->stream_analog_alt_playback;
+ if (!p)
+ p = &alc_pcm_analog_alt_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->alt_dac_nid;
+ } else {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ alc_pcm_null_stream;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
+ }
+ if (have_multi_adcs) {
+ p = spec->stream_analog_alt_capture;
+ if (!p)
+ p = &alc_pcm_analog_alt_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+ spec->adc_nids[1];
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
+ spec->num_adc_nids - 1;
+ } else {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ alc_pcm_null_stream;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
+ }
+ }
+
+ return 0;
+}
+
+static inline void alc_shutup(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec && spec->shutup)
+ spec->shutup(codec);
+ snd_hda_shutup_pins(codec);
+}
+
+static void alc_free_kctls(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
+ }
+ snd_array_free(&spec->kctls);
+}
+
+static void alc_free_bind_ctls(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ if (spec->bind_ctls.list) {
+ struct hda_bind_ctls **ctl = spec->bind_ctls.list;
+ int i;
+ for (i = 0; i < spec->bind_ctls.used; i++)
+ kfree(ctl[i]);
+ }
+ snd_array_free(&spec->bind_ctls);
+}
+
+static void alc_free(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (!spec)
+ return;
+
+ alc_free_kctls(codec);
+ alc_free_bind_ctls(codec);
+ snd_hda_gen_free(&spec->gen);
+ kfree(spec);
+ snd_hda_detach_beep_device(codec);
+}
+
+#ifdef CONFIG_PM
+static void alc_power_eapd(struct hda_codec *codec)
+{
+ alc_auto_setup_eapd(codec, false);
+}
+
+static int alc_suspend(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ alc_shutup(codec);
+ if (spec && spec->power_hook)
+ spec->power_hook(codec);
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int alc_resume(struct hda_codec *codec)
+{
+ msleep(150); /* to avoid pop noise */
+ codec->patch_ops.init(codec);
+ snd_hda_codec_resume_amp(codec);
+ snd_hda_codec_resume_cache(codec);
+ alc_inv_dmic_sync(codec, true);
+ hda_call_check_power_status(codec, 0x01);
+ return 0;
+}
+#endif
+
+/*
+ */
+static const struct hda_codec_ops alc_patch_ops = {
+ .build_controls = alc_build_controls,
+ .build_pcms = alc_build_pcms,
+ .init = alc_init,
+ .free = alc_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .resume = alc_resume,
+#endif
+#ifdef CONFIG_PM
+ .suspend = alc_suspend,
+ .check_power_status = alc_check_power_status,
+#endif
+ .reboot_notify = alc_shutup,
+};
+
+
+/* replace the codec chip_name with the given string */
+static int alc_codec_rename(struct hda_codec *codec, const char *name)
+{
+ kfree(codec->chip_name);
+ codec->chip_name = kstrdup(name, GFP_KERNEL);
+ if (!codec->chip_name) {
+ alc_free(codec);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/*
+ * Rename codecs appropriately from COEF value
+ */
+struct alc_codec_rename_table {
+ unsigned int vendor_id;
+ unsigned short coef_mask;
+ unsigned short coef_bits;
+ const char *name;
+};
+
+static struct alc_codec_rename_table rename_tbl[] = {
+ { 0x10ec0269, 0xfff0, 0x3010, "ALC277" },
+ { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" },
+ { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" },
+ { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" },
+ { 0x10ec0269, 0xffff, 0xa023, "ALC259" },
+ { 0x10ec0269, 0xffff, 0x6023, "ALC281X" },
+ { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" },
+ { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" },
+ { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" },
+ { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" },
+ { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" },
+ { 0x10ec0899, 0x2000, 0x2000, "ALC899" },
+ { 0x10ec0892, 0xffff, 0x8020, "ALC661" },
+ { 0x10ec0892, 0xffff, 0x8011, "ALC661" },
+ { 0x10ec0892, 0xffff, 0x4011, "ALC656" },
+ { } /* terminator */
+};
+
+static int alc_codec_rename_from_preset(struct hda_codec *codec)
+{
+ const struct alc_codec_rename_table *p;
+
+ for (p = rename_tbl; p->vendor_id; p++) {
+ if (p->vendor_id != codec->vendor_id)
+ continue;
+ if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits)
+ return alc_codec_rename(codec, p->name);
+ }
+ return 0;
+}
+
+/*
+ * Automatic parse of I/O pins from the BIOS configuration
+ */
+
+enum {
+ ALC_CTL_WIDGET_VOL,
+ ALC_CTL_WIDGET_MUTE,
+ ALC_CTL_BIND_MUTE,
+ ALC_CTL_BIND_VOL,
+ ALC_CTL_BIND_SW,
+};
+static const struct snd_kcontrol_new alc_control_templates[] = {
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+ HDA_CODEC_MUTE(NULL, 0, 0, 0),
+ HDA_BIND_MUTE(NULL, 0, 0, 0),
+ HDA_BIND_VOL(NULL, 0),
+ HDA_BIND_SW(NULL, 0),
+};
+
+/* add dynamic controls */
+static int add_control(struct alc_spec *spec, int type, const char *name,
+ int cidx, unsigned long val)
+{
+ struct snd_kcontrol_new *knew;
+
+ knew = alc_kcontrol_new(spec, name, &alc_control_templates[type]);
+ if (!knew)
+ return -ENOMEM;
+ knew->index = cidx;
+ if (get_amp_nid_(val))
+ knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+ knew->private_value = val;
+ return 0;
+}
+
+static int add_control_with_pfx(struct alc_spec *spec, int type,
+ const char *pfx, const char *dir,
+ const char *sfx, int cidx, unsigned long val)
+{
+ char name[32];
+ snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
+ return add_control(spec, type, name, cidx, val);
+}
+
+#define add_pb_vol_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
+#define add_pb_sw_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
+#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
+#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
+
+static const char * const channel_name[4] = {
+ "Front", "Surround", "CLFE", "Side"
+};
+
+static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch,
+ bool can_be_master, int *index)
+{
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ *index = 0;
+ if (cfg->line_outs == 1 && !spec->multi_ios &&
+ !cfg->hp_outs && !cfg->speaker_outs && can_be_master)
+ return "Master";
+
+ switch (cfg->line_out_type) {
+ case AUTO_PIN_SPEAKER_OUT:
+ if (cfg->line_outs == 1)
+ return "Speaker";
+ if (cfg->line_outs == 2)
+ return ch ? "Bass Speaker" : "Speaker";
+ break;
+ case AUTO_PIN_HP_OUT:
+ /* for multi-io case, only the primary out */
+ if (ch && spec->multi_ios)
+ break;
+ *index = ch;
+ return "Headphone";
+ default:
+ if (cfg->line_outs == 1 && !spec->multi_ios)
+ return "PCM";
+ break;
+ }
+ if (ch >= ARRAY_SIZE(channel_name)) {
+ snd_BUG();
+ return "PCM";
+ }
+
+ return channel_name[ch];
+}
+
+#ifdef CONFIG_PM
+/* add the powersave loopback-list entry */
+static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx)
+{
+ struct hda_amp_list *list;
+
+ if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
+ return;
+ list = spec->loopback_list + spec->num_loopbacks;
+ list->nid = mix;
+ list->dir = HDA_INPUT;
+ list->idx = idx;
+ spec->num_loopbacks++;
+ spec->loopback.amplist = spec->loopback_list;
+}
+#else
+#define add_loopback_list(spec, mix, idx) /* NOP */
+#endif
+
+/* create input playback/capture controls for the given pin */
+static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
+ const char *ctlname, int ctlidx,
+ int idx, hda_nid_t mix_nid)
+{
+ int err;
+
+ err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
+ HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
+ if (err < 0)
+ return err;
+ err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx,
+ HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
+ if (err < 0)
+ return err;
+ add_loopback_list(spec, mix_nid, idx);
+ return 0;
+}
+
+static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
+ return (pincap & AC_PINCAP_IN) != 0;
+}
+
+/* Parse the codec tree and retrieve ADCs and corresponding capsrc MUXs */
+static int alc_auto_fill_adc_caps(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t nid;
+ hda_nid_t *adc_nids = spec->private_adc_nids;
+ hda_nid_t *cap_nids = spec->private_capsrc_nids;
+ int max_nums = ARRAY_SIZE(spec->private_adc_nids);
+ int i, nums = 0;
+
+ nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ hda_nid_t src;
+ unsigned int caps = get_wcaps(codec, nid);
+ int type = get_wcaps_type(caps);
+
+ if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL))
+ continue;
+ adc_nids[nums] = nid;
+ cap_nids[nums] = nid;
+ src = nid;
+ for (;;) {
+ int n;
+ type = get_wcaps_type(get_wcaps(codec, src));
+ if (type == AC_WID_PIN)
+ break;
+ if (type == AC_WID_AUD_SEL) {
+ cap_nids[nums] = src;
+ break;
+ }
+ n = snd_hda_get_num_conns(codec, src);
+ if (n > 1) {
+ cap_nids[nums] = src;
+ break;
+ } else if (n != 1)
+ break;
+ if (snd_hda_get_connections(codec, src, &src, 1) != 1)
+ break;
+ }
+ if (++nums >= max_nums)
+ break;
+ }
+ spec->adc_nids = spec->private_adc_nids;
+ spec->capsrc_nids = spec->private_capsrc_nids;
+ spec->num_adc_nids = nums;
+ return nums;
+}
+
+/* create playback/capture controls for input pins */
+static int alc_auto_create_input_ctls(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t mixer = spec->mixer_nid;
+ struct hda_input_mux *imux = &spec->private_imux[0];
+ int num_adcs;
+ int i, c, err, idx, type_idx = 0;
+ const char *prev_label = NULL;
+
+ num_adcs = alc_auto_fill_adc_caps(codec);
+ if (num_adcs < 0)
+ return 0;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin;
+ const char *label;
+
+ pin = cfg->inputs[i].pin;
+ if (!alc_is_input_pin(codec, pin))
+ continue;
+
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ if (spec->shared_mic_hp && !strcmp(label, "Misc"))
+ label = "Headphone Mic";
+ if (prev_label && !strcmp(label, prev_label))
+ type_idx++;
+ else
+ type_idx = 0;
+ prev_label = label;
+
+ if (mixer) {
+ idx = get_connection_index(codec, mixer, pin);
+ if (idx >= 0) {
+ err = new_analog_input(spec, pin,
+ label, type_idx,
+ idx, mixer);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ for (c = 0; c < num_adcs; c++) {
+ hda_nid_t cap = get_capsrc(spec, c);
+ idx = get_connection_index(codec, cap, pin);
+ if (idx >= 0) {
+ spec->imux_pins[imux->num_items] = pin;
+ snd_hda_add_imux_item(imux, label, idx, NULL);
+ break;
+ }
+ }
+ }
+
+ spec->num_mux_defs = 1;
+ spec->input_mux = imux;
+
+ return 0;
+}
+
+/* create a shared input with the headphone out */
+static int alc_auto_create_shared_input(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int defcfg;
+ hda_nid_t nid;
+
+ /* only one internal input pin? */
+ if (cfg->num_inputs != 1)
+ return 0;
+ defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin);
+ if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
+ return 0;
+
+ if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */
+ else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT)
+ nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */
+ else
+ return 0; /* both not available */
+
+ if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN))
+ return 0; /* no input */
+
+ cfg->inputs[1].pin = nid;
+ cfg->inputs[1].type = AUTO_PIN_MIC;
+ cfg->num_inputs = 2;
+ spec->shared_mic_hp = 1;
+ snd_printdd("realtek: Enable shared I/O jack on NID 0x%x\n", nid);
+ return 0;
+}
+
+static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int pin_type)
+{
+ snd_hda_set_pin_ctl(codec, nid, pin_type);
+ /* unmute pin */
+ if (nid_has_mute(codec, nid, HDA_OUTPUT))
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+}
+
+static int get_pin_type(int line_out_type)
+{
+ if (line_out_type == AUTO_PIN_HP_OUT)
+ return PIN_HP;
+ else
+ return PIN_OUT;
+}
+
+static void alc_auto_init_analog_input(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ if (alc_is_input_pin(codec, nid)) {
+ alc_set_input_pin(codec, nid, cfg->inputs[i].type);
+ if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE);
+ }
+ }
+
+ /* mute all loopback inputs */
+ if (spec->mixer_nid) {
+ int nums = snd_hda_get_num_conns(codec, spec->mixer_nid);
+ for (i = 0; i < nums; i++)
+ snd_hda_codec_write(codec, spec->mixer_nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_MUTE(i));
+ }
+}
+
+/* convert from MIX nid to DAC */
+static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+ hda_nid_t list[5];
+ int i, num;
+
+ if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_AUD_OUT)
+ return nid;
+ num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list));
+ for (i = 0; i < num; i++) {
+ if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT)
+ return list[i];
+ }
+ return 0;
+}
+
+/* go down to the selector widget before the mixer */
+static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin)
+{
+ hda_nid_t srcs[5];
+ int num = snd_hda_get_connections(codec, pin, srcs,
+ ARRAY_SIZE(srcs));
+ if (num != 1 ||
+ get_wcaps_type(get_wcaps(codec, srcs[0])) != AC_WID_AUD_SEL)
+ return pin;
+ return srcs[0];
+}
+
+/* get MIX nid connected to the given pin targeted to DAC */
+static hda_nid_t alc_auto_dac_to_mix(struct hda_codec *codec, hda_nid_t pin,
+ hda_nid_t dac)
+{
+ hda_nid_t mix[5];
+ int i, num;
+
+ pin = alc_go_down_to_selector(codec, pin);
+ num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix));
+ for (i = 0; i < num; i++) {
+ if (alc_auto_mix_to_dac(codec, mix[i]) == dac)
+ return mix[i];
+ }
+ return 0;
+}
+
+/* select the connection from pin to DAC if needed */
+static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin,
+ hda_nid_t dac)
+{
+ hda_nid_t mix[5];
+ int i, num;
+
+ pin = alc_go_down_to_selector(codec, pin);
+ num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix));
+ if (num < 2)
+ return 0;
+ for (i = 0; i < num; i++) {
+ if (alc_auto_mix_to_dac(codec, mix[i]) == dac) {
+ snd_hda_codec_update_cache(codec, pin, 0,
+ AC_VERB_SET_CONNECT_SEL, i);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+ if (found_in_nid_list(nid, spec->multiout.dac_nids,
+ ARRAY_SIZE(spec->private_dac_nids)) ||
+ found_in_nid_list(nid, spec->multiout.hp_out_nid,
+ ARRAY_SIZE(spec->multiout.hp_out_nid)) ||
+ found_in_nid_list(nid, spec->multiout.extra_out_nid,
+ ARRAY_SIZE(spec->multiout.extra_out_nid)))
+ return true;
+ for (i = 0; i < spec->multi_ios; i++) {
+ if (spec->multi_io[i].dac == nid)
+ return true;
+ }
+ return false;
+}
+
+/* look for an empty DAC slot */
+static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
+{
+ hda_nid_t srcs[5];
+ int i, num;
+
+ pin = alc_go_down_to_selector(codec, pin);
+ num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
+ for (i = 0; i < num; i++) {
+ hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]);
+ if (!nid)
+ continue;
+ if (!alc_is_dac_already_used(codec, nid))
+ return nid;
+ }
+ return 0;
+}
+
+/* check whether the DAC is reachable from the pin */
+static bool alc_auto_is_dac_reachable(struct hda_codec *codec,
+ hda_nid_t pin, hda_nid_t dac)
+{
+ hda_nid_t srcs[5];
+ int i, num;
+
+ if (!pin || !dac)
+ return false;
+ pin = alc_go_down_to_selector(codec, pin);
+ num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
+ for (i = 0; i < num; i++) {
+ hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]);
+ if (nid == dac)
+ return true;
+ }
+ return false;
+}
+
+static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t sel = alc_go_down_to_selector(codec, pin);
+ hda_nid_t nid, nid_found, srcs[5];
+ int i, num = snd_hda_get_connections(codec, sel, srcs,
+ ARRAY_SIZE(srcs));
+ if (num == 1)
+ return alc_auto_look_for_dac(codec, pin);
+ nid_found = 0;
+ for (i = 0; i < num; i++) {
+ if (srcs[i] == spec->mixer_nid)
+ continue;
+ nid = alc_auto_mix_to_dac(codec, srcs[i]);
+ if (nid && !alc_is_dac_already_used(codec, nid)) {
+ if (nid_found)
+ return 0;
+ nid_found = nid;
+ }
+ }
+ return nid_found;
+}
+
+/* mark up volume and mute control NIDs: used during badness parsing and
+ * at creating actual controls
+ */
+static inline unsigned int get_ctl_pos(unsigned int data)
+{
+ hda_nid_t nid = get_amp_nid_(data);
+ unsigned int dir;
+ if (snd_BUG_ON(nid >= MAX_VOL_NIDS))
+ return 0;
+ dir = get_amp_direction_(data);
+ return (nid << 1) | dir;
+}
+
+#define is_ctl_used(bits, data) \
+ test_bit(get_ctl_pos(data), bits)
+#define mark_ctl_usage(bits, data) \
+ set_bit(get_ctl_pos(data), bits)
+
+static void clear_vol_marks(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ memset(spec->vol_ctls, 0, sizeof(spec->vol_ctls));
+ memset(spec->sw_ctls, 0, sizeof(spec->sw_ctls));
+}
+
+/* badness definition */
+enum {
+ /* No primary DAC is found for the main output */
+ BAD_NO_PRIMARY_DAC = 0x10000,
+ /* No DAC is found for the extra output */
+ BAD_NO_DAC = 0x4000,
+ /* No possible multi-ios */
+ BAD_MULTI_IO = 0x103,
+ /* No individual DAC for extra output */
+ BAD_NO_EXTRA_DAC = 0x102,
+ /* No individual DAC for extra surrounds */
+ BAD_NO_EXTRA_SURR_DAC = 0x101,
+ /* Primary DAC shared with main surrounds */
+ BAD_SHARED_SURROUND = 0x100,
+ /* Primary DAC shared with main CLFE */
+ BAD_SHARED_CLFE = 0x10,
+ /* Primary DAC shared with extra surrounds */
+ BAD_SHARED_EXTRA_SURROUND = 0x10,
+ /* Volume widget is shared */
+ BAD_SHARED_VOL = 0x10,
+};
+
+static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
+ hda_nid_t pin, hda_nid_t dac);
+static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
+ hda_nid_t pin, hda_nid_t dac);
+
+static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin,
+ hda_nid_t dac)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t nid;
+ unsigned int val;
+ int badness = 0;
+
+ nid = alc_look_for_out_vol_nid(codec, pin, dac);
+ if (nid) {
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ if (is_ctl_used(spec->vol_ctls, nid))
+ badness += BAD_SHARED_VOL;
+ else
+ mark_ctl_usage(spec->vol_ctls, val);
+ } else
+ badness += BAD_SHARED_VOL;
+ nid = alc_look_for_out_mute_nid(codec, pin, dac);
+ if (nid) {
+ unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid));
+ if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT)
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ else
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT);
+ if (is_ctl_used(spec->sw_ctls, val))
+ badness += BAD_SHARED_VOL;
+ else
+ mark_ctl_usage(spec->sw_ctls, val);
+ } else
+ badness += BAD_SHARED_VOL;
+ return badness;
+}
+
+struct badness_table {
+ int no_primary_dac; /* no primary DAC */
+ int no_dac; /* no secondary DACs */
+ int shared_primary; /* primary DAC is shared with main output */
+ int shared_surr; /* secondary DAC shared with main or primary */
+ int shared_clfe; /* third DAC shared with main or primary */
+ int shared_surr_main; /* secondary DAC sahred with main/DAC0 */
+};
+
+static struct badness_table main_out_badness = {
+ .no_primary_dac = BAD_NO_PRIMARY_DAC,
+ .no_dac = BAD_NO_DAC,
+ .shared_primary = BAD_NO_PRIMARY_DAC,
+ .shared_surr = BAD_SHARED_SURROUND,
+ .shared_clfe = BAD_SHARED_CLFE,
+ .shared_surr_main = BAD_SHARED_SURROUND,
+};
+
+static struct badness_table extra_out_badness = {
+ .no_primary_dac = BAD_NO_DAC,
+ .no_dac = BAD_NO_DAC,
+ .shared_primary = BAD_NO_EXTRA_DAC,
+ .shared_surr = BAD_SHARED_EXTRA_SURROUND,
+ .shared_clfe = BAD_SHARED_EXTRA_SURROUND,
+ .shared_surr_main = BAD_NO_EXTRA_SURR_DAC,
+};
+
+/* try to assign DACs to pins and return the resultant badness */
+static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs,
+ const hda_nid_t *pins, hda_nid_t *dacs,
+ const struct badness_table *bad)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, j;
+ int badness = 0;
+ hda_nid_t dac;
+
+ if (!num_outs)
+ return 0;
+
+ for (i = 0; i < num_outs; i++) {
+ hda_nid_t pin = pins[i];
+ if (!dacs[i])
+ dacs[i] = alc_auto_look_for_dac(codec, pin);
+ if (!dacs[i] && !i) {
+ for (j = 1; j < num_outs; j++) {
+ if (alc_auto_is_dac_reachable(codec, pin, dacs[j])) {
+ dacs[0] = dacs[j];
+ dacs[j] = 0;
+ break;
+ }
+ }
+ }
+ dac = dacs[i];
+ if (!dac) {
+ if (alc_auto_is_dac_reachable(codec, pin, dacs[0]))
+ dac = dacs[0];
+ else if (cfg->line_outs > i &&
+ alc_auto_is_dac_reachable(codec, pin,
+ spec->private_dac_nids[i]))
+ dac = spec->private_dac_nids[i];
+ if (dac) {
+ if (!i)
+ badness += bad->shared_primary;
+ else if (i == 1)
+ badness += bad->shared_surr;
+ else
+ badness += bad->shared_clfe;
+ } else if (alc_auto_is_dac_reachable(codec, pin,
+ spec->private_dac_nids[0])) {
+ dac = spec->private_dac_nids[0];
+ badness += bad->shared_surr_main;
+ } else if (!i)
+ badness += bad->no_primary_dac;
+ else
+ badness += bad->no_dac;
+ }
+ if (dac)
+ badness += eval_shared_vol_badness(codec, pin, dac);
+ }
+
+ return badness;
+}
+
+static int alc_auto_fill_multi_ios(struct hda_codec *codec,
+ hda_nid_t reference_pin,
+ bool hardwired, int offset);
+
+static bool alc_map_singles(struct hda_codec *codec, int outs,
+ const hda_nid_t *pins, hda_nid_t *dacs)
+{
+ int i;
+ bool found = false;
+ for (i = 0; i < outs; i++) {
+ if (dacs[i])
+ continue;
+ dacs[i] = get_dac_if_single(codec, pins[i]);
+ if (dacs[i])
+ found = true;
+ }
+ return found;
+}
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int fill_and_eval_dacs(struct hda_codec *codec,
+ bool fill_hardwired,
+ bool fill_mio_first)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, err, badness;
+
+ /* set num_dacs once to full for alc_auto_look_for_dac() */
+ spec->multiout.num_dacs = cfg->line_outs;
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
+ memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid));
+ memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid));
+ spec->multi_ios = 0;
+ clear_vol_marks(codec);
+ badness = 0;
+
+ /* fill hard-wired DACs first */
+ if (fill_hardwired) {
+ bool mapped;
+ do {
+ mapped = alc_map_singles(codec, cfg->line_outs,
+ cfg->line_out_pins,
+ spec->private_dac_nids);
+ mapped |= alc_map_singles(codec, cfg->hp_outs,
+ cfg->hp_pins,
+ spec->multiout.hp_out_nid);
+ mapped |= alc_map_singles(codec, cfg->speaker_outs,
+ cfg->speaker_pins,
+ spec->multiout.extra_out_nid);
+ if (fill_mio_first && cfg->line_outs == 1 &&
+ cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], true, 0);
+ if (!err)
+ mapped = true;
+ }
+ } while (mapped);
+ }
+
+ badness += alc_auto_fill_dacs(codec, cfg->line_outs, cfg->line_out_pins,
+ spec->private_dac_nids,
+ &main_out_badness);
+
+ /* re-count num_dacs and squash invalid entries */
+ spec->multiout.num_dacs = 0;
+ for (i = 0; i < cfg->line_outs; i++) {
+ if (spec->private_dac_nids[i])
+ spec->multiout.num_dacs++;
+ else {
+ memmove(spec->private_dac_nids + i,
+ spec->private_dac_nids + i + 1,
+ sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
+ spec->private_dac_nids[cfg->line_outs - 1] = 0;
+ }
+ }
+
+ if (fill_mio_first &&
+ cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ /* try to fill multi-io first */
+ err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0);
+ if (err < 0)
+ return err;
+ /* we don't count badness at this stage yet */
+ }
+
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = alc_auto_fill_dacs(codec, cfg->hp_outs, cfg->hp_pins,
+ spec->multiout.hp_out_nid,
+ &extra_out_badness);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = alc_auto_fill_dacs(codec, cfg->speaker_outs,
+ cfg->speaker_pins,
+ spec->multiout.extra_out_nid,
+ &extra_out_badness);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+ if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+ if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ /* try multi-ios with HP + inputs */
+ int offset = 0;
+ if (cfg->line_outs >= 3)
+ offset = 1;
+ err = alc_auto_fill_multi_ios(codec, cfg->hp_pins[0], false,
+ offset);
+ if (err < 0)
+ return err;
+ badness += err;
+ }
+
+ if (spec->multi_ios == 2) {
+ for (i = 0; i < 2; i++)
+ spec->private_dac_nids[spec->multiout.num_dacs++] =
+ spec->multi_io[i].dac;
+ spec->ext_channel_count = 2;
+ } else if (spec->multi_ios) {
+ spec->multi_ios = 0;
+ badness += BAD_MULTI_IO;
+ }
+
+ return badness;
+}
+
+#define DEBUG_BADNESS
+
+#ifdef DEBUG_BADNESS
+#define debug_badness snd_printdd
+#else
+#define debug_badness(...)
+#endif
+
+static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg)
+{
+ debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+ cfg->line_out_pins[0], cfg->line_out_pins[1],
+ cfg->line_out_pins[2], cfg->line_out_pins[2],
+ spec->multiout.dac_nids[0],
+ spec->multiout.dac_nids[1],
+ spec->multiout.dac_nids[2],
+ spec->multiout.dac_nids[3]);
+ if (spec->multi_ios > 0)
+ debug_badness("multi_ios(%d) = %x/%x : %x/%x\n",
+ spec->multi_ios,
+ spec->multi_io[0].pin, spec->multi_io[1].pin,
+ spec->multi_io[0].dac, spec->multi_io[1].dac);
+ debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+ cfg->hp_pins[0], cfg->hp_pins[1],
+ cfg->hp_pins[2], cfg->hp_pins[2],
+ spec->multiout.hp_out_nid[0],
+ spec->multiout.hp_out_nid[1],
+ spec->multiout.hp_out_nid[2],
+ spec->multiout.hp_out_nid[3]);
+ debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n",
+ cfg->speaker_pins[0], cfg->speaker_pins[1],
+ cfg->speaker_pins[2], cfg->speaker_pins[3],
+ spec->multiout.extra_out_nid[0],
+ spec->multiout.extra_out_nid[1],
+ spec->multiout.extra_out_nid[2],
+ spec->multiout.extra_out_nid[3]);
+}
+
+static int alc_auto_fill_dac_nids(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct auto_pin_cfg *best_cfg;
+ int best_badness = INT_MAX;
+ int badness;
+ bool fill_hardwired = true, fill_mio_first = true;
+ bool best_wired = true, best_mio = true;
+ bool hp_spk_swapped = false;
+
+ best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL);
+ if (!best_cfg)
+ return -ENOMEM;
+ *best_cfg = *cfg;
+
+ for (;;) {
+ badness = fill_and_eval_dacs(codec, fill_hardwired,
+ fill_mio_first);
+ if (badness < 0) {
+ kfree(best_cfg);
+ return badness;
+ }
+ debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n",
+ cfg->line_out_type, fill_hardwired, fill_mio_first,
+ badness);
+ debug_show_configs(spec, cfg);
+ if (badness < best_badness) {
+ best_badness = badness;
+ *best_cfg = *cfg;
+ best_wired = fill_hardwired;
+ best_mio = fill_mio_first;
+ }
+ if (!badness)
+ break;
+ fill_mio_first = !fill_mio_first;
+ if (!fill_mio_first)
+ continue;
+ fill_hardwired = !fill_hardwired;
+ if (!fill_hardwired)
+ continue;
+ if (hp_spk_swapped)
+ break;
+ hp_spk_swapped = true;
+ if (cfg->speaker_outs > 0 &&
+ cfg->line_out_type == AUTO_PIN_HP_OUT) {
+ cfg->hp_outs = cfg->line_outs;
+ memcpy(cfg->hp_pins, cfg->line_out_pins,
+ sizeof(cfg->hp_pins));
+ cfg->line_outs = cfg->speaker_outs;
+ memcpy(cfg->line_out_pins, cfg->speaker_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = 0;
+ memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+ cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+ fill_hardwired = true;
+ continue;
+ }
+ if (cfg->hp_outs > 0 &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+ cfg->speaker_outs = cfg->line_outs;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ fill_hardwired = true;
+ continue;
+ }
+ break;
+ }
+
+ if (badness) {
+ *cfg = *best_cfg;
+ fill_and_eval_dacs(codec, best_wired, best_mio);
+ }
+ debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n",
+ cfg->line_out_type, best_wired, best_mio);
+ debug_show_configs(spec, cfg);
+
+ if (cfg->line_out_pins[0])
+ spec->vmaster_nid =
+ alc_look_for_out_vol_nid(codec, cfg->line_out_pins[0],
+ spec->multiout.dac_nids[0]);
+
+ /* clear the bitmap flags for creating controls */
+ clear_vol_marks(codec);
+ kfree(best_cfg);
+ return 0;
+}
+
+static int alc_auto_add_vol_ctl(struct hda_codec *codec,
+ const char *pfx, int cidx,
+ hda_nid_t nid, unsigned int chs)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int val;
+ if (!nid)
+ return 0;
+ val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
+ if (is_ctl_used(spec->vol_ctls, val) && chs != 2) /* exclude LFE */
+ return 0;
+ mark_ctl_usage(spec->vol_ctls, val);
+ return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx,
+ val);
+}
+
+static int alc_auto_add_stereo_vol(struct hda_codec *codec,
+ const char *pfx, int cidx,
+ hda_nid_t nid)
+{
+ int chs = 1;
+ if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
+ chs = 3;
+ return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs);
+}
+
+/* create a mute-switch for the given mixer widget;
+ * if it has multiple sources (e.g. DAC and loopback), create a bind-mute
+ */
+static int alc_auto_add_sw_ctl(struct hda_codec *codec,
+ const char *pfx, int cidx,
+ hda_nid_t nid, unsigned int chs)
+{
+ struct alc_spec *spec = codec->spec;
+ int wid_type;
+ int type;
+ unsigned long val;
+ if (!nid)
+ return 0;
+ wid_type = get_wcaps_type(get_wcaps(codec, nid));
+ if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) {
+ type = ALC_CTL_WIDGET_MUTE;
+ val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
+ } else if (snd_hda_get_num_conns(codec, nid) == 1) {
+ type = ALC_CTL_WIDGET_MUTE;
+ val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT);
+ } else {
+ type = ALC_CTL_BIND_MUTE;
+ val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT);
+ }
+ if (is_ctl_used(spec->sw_ctls, val) && chs != 2) /* exclude LFE */
+ return 0;
+ mark_ctl_usage(spec->sw_ctls, val);
+ return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val);
+}
+
+static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx,
+ int cidx, hda_nid_t nid)
+{
+ int chs = 1;
+ if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
+ chs = 3;
+ return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs);
+}
+
+static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec,
+ hda_nid_t pin, hda_nid_t dac)
+{
+ hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac);
+ if (nid_has_mute(codec, pin, HDA_OUTPUT))
+ return pin;
+ else if (mix && nid_has_mute(codec, mix, HDA_INPUT))
+ return mix;
+ else if (nid_has_mute(codec, dac, HDA_OUTPUT))
+ return dac;
+ return 0;
+}
+
+static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec,
+ hda_nid_t pin, hda_nid_t dac)
+{
+ hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac);
+ if (nid_has_volume(codec, dac, HDA_OUTPUT))
+ return dac;
+ else if (nid_has_volume(codec, mix, HDA_OUTPUT))
+ return mix;
+ else if (nid_has_volume(codec, pin, HDA_OUTPUT))
+ return pin;
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int alc_auto_create_multi_out_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ struct alc_spec *spec = codec->spec;
+ int i, err, noutputs;
+
+ noutputs = cfg->line_outs;
+ if (spec->multi_ios > 0 && cfg->line_outs < 3)
+ noutputs += spec->multi_ios;
+
+ for (i = 0; i < noutputs; i++) {
+ const char *name;
+ int index;
+ hda_nid_t dac, pin;
+ hda_nid_t sw, vol;
+
+ dac = spec->multiout.dac_nids[i];
+ if (!dac)
+ continue;
+ if (i >= cfg->line_outs) {
+ pin = spec->multi_io[i - 1].pin;
+ index = 0;
+ name = channel_name[i];
+ } else {
+ pin = cfg->line_out_pins[i];
+ name = alc_get_line_out_pfx(spec, i, true, &index);
+ }
+
+ sw = alc_look_for_out_mute_nid(codec, pin, dac);
+ vol = alc_look_for_out_vol_nid(codec, pin, dac);
+ if (!name || !strcmp(name, "CLFE")) {
+ /* Center/LFE */
+ err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1);
+ if (err < 0)
+ return err;
+ err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2);
+ if (err < 0)
+ return err;
+ err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1);
+ if (err < 0)
+ return err;
+ err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2);
+ if (err < 0)
+ return err;
+ } else {
+ err = alc_auto_add_stereo_vol(codec, name, index, vol);
+ if (err < 0)
+ return err;
+ err = alc_auto_add_stereo_sw(codec, name, index, sw);
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
+ hda_nid_t dac, const char *pfx,
+ int cidx)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t sw, vol;
+ int err;
+
+ if (!dac) {
+ unsigned int val;
+ /* the corresponding DAC is already occupied */
+ if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
+ return 0; /* no way */
+ /* create a switch only */
+ val = HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT);
+ if (is_ctl_used(spec->sw_ctls, val))
+ return 0; /* already created */
+ mark_ctl_usage(spec->sw_ctls, val);
+ return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, cidx, val);
+ }
+
+ sw = alc_look_for_out_mute_nid(codec, pin, dac);
+ vol = alc_look_for_out_vol_nid(codec, pin, dac);
+ err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol);
+ if (err < 0)
+ return err;
+ err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec,
+ unsigned int nums,
+ struct hda_ctl_ops *ops)
+{
+ struct alc_spec *spec = codec->spec;
+ struct hda_bind_ctls **ctlp, *ctl;
+ ctlp = snd_array_new(&spec->bind_ctls);
+ if (!ctlp)
+ return NULL;
+ ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL);
+ *ctlp = ctl;
+ if (ctl)
+ ctl->ops = ops;
+ return ctl;
+}
+
+/* add playback controls for speaker and HP outputs */
+static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins,
+ const hda_nid_t *dacs,
+ const char *pfx)
+{
+ struct alc_spec *spec = codec->spec;
+ struct hda_bind_ctls *ctl;
+ char name[32];
+ int i, n, err;
+
+ if (!num_pins || !pins[0])
+ return 0;
+
+ if (num_pins == 1) {
+ hda_nid_t dac = *dacs;
+ if (!dac)
+ dac = spec->multiout.dac_nids[0];
+ return alc_auto_create_extra_out(codec, *pins, dac, pfx, 0);
+ }
+
+ for (i = 0; i < num_pins; i++) {
+ hda_nid_t dac;
+ if (dacs[num_pins - 1])
+ dac = dacs[i]; /* with individual volumes */
+ else
+ dac = 0;
+ if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) {
+ err = alc_auto_create_extra_out(codec, pins[i], dac,
+ "Bass Speaker", 0);
+ } else if (num_pins >= 3) {
+ snprintf(name, sizeof(name), "%s %s",
+ pfx, channel_name[i]);
+ err = alc_auto_create_extra_out(codec, pins[i], dac,
+ name, 0);
+ } else {
+ err = alc_auto_create_extra_out(codec, pins[i], dac,
+ pfx, i);
+ }
+ if (err < 0)
+ return err;
+ }
+ if (dacs[num_pins - 1])
+ return 0;
+
+ /* Let's create a bind-controls for volumes */
+ ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol);
+ if (!ctl)
+ return -ENOMEM;
+ n = 0;
+ for (i = 0; i < num_pins; i++) {
+ hda_nid_t vol;
+ if (!pins[i] || !dacs[i])
+ continue;
+ vol = alc_look_for_out_vol_nid(codec, pins[i], dacs[i]);
+ if (vol)
+ ctl->values[n++] =
+ HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT);
+ }
+ if (n) {
+ snprintf(name, sizeof(name), "%s Playback Volume", pfx);
+ err = add_control(spec, ALC_CTL_BIND_VOL, name, 0, (long)ctl);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int alc_auto_create_hp_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ return alc_auto_create_extra_outs(codec, spec->autocfg.hp_outs,
+ spec->autocfg.hp_pins,
+ spec->multiout.hp_out_nid,
+ "Headphone");
+}
+
+static int alc_auto_create_speaker_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ return alc_auto_create_extra_outs(codec, spec->autocfg.speaker_outs,
+ spec->autocfg.speaker_pins,
+ spec->multiout.extra_out_nid,
+ "Speaker");
+}
+
+static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
+ hda_nid_t pin, int pin_type,
+ hda_nid_t dac)
+{
+ int i, num;
+ hda_nid_t nid, mix = 0;
+ hda_nid_t srcs[HDA_MAX_CONNECTIONS];
+
+ alc_set_pin_output(codec, pin, pin_type);
+ nid = alc_go_down_to_selector(codec, pin);
+ num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs));
+ for (i = 0; i < num; i++) {
+ if (alc_auto_mix_to_dac(codec, srcs[i]) != dac)
+ continue;
+ mix = srcs[i];
+ break;
+ }
+ if (!mix)
+ return;
+
+ /* need the manual connection? */
+ if (num > 1)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i);
+ /* unmute mixer widget inputs */
+ if (nid_has_mute(codec, mix, HDA_INPUT)) {
+ snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+ snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(1));
+ }
+ /* initialize volume */
+ nid = alc_look_for_out_vol_nid(codec, pin, dac);
+ if (nid)
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_ZERO);
+
+ /* unmute DAC if it's not assigned to a mixer */
+ nid = alc_look_for_out_mute_nid(codec, pin, dac);
+ if (nid == mix && nid_has_mute(codec, dac, HDA_OUTPUT))
+ snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_ZERO);
+}
+
+static void alc_auto_init_multi_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int pin_type = get_pin_type(spec->autocfg.line_out_type);
+ int i;
+
+ for (i = 0; i <= HDA_SIDE; i++) {
+ hda_nid_t nid = spec->autocfg.line_out_pins[i];
+ if (nid)
+ alc_auto_set_output_and_unmute(codec, nid, pin_type,
+ spec->multiout.dac_nids[i]);
+ }
+}
+
+static void alc_auto_init_extra_out(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+ hda_nid_t pin, dac;
+
+ for (i = 0; i < spec->autocfg.hp_outs; i++) {
+ if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+ break;
+ pin = spec->autocfg.hp_pins[i];
+ if (!pin)
+ break;
+ dac = spec->multiout.hp_out_nid[i];
+ if (!dac) {
+ if (i > 0 && spec->multiout.hp_out_nid[0])
+ dac = spec->multiout.hp_out_nid[0];
+ else
+ dac = spec->multiout.dac_nids[0];
+ }
+ alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
+ }
+ for (i = 0; i < spec->autocfg.speaker_outs; i++) {
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+ break;
+ pin = spec->autocfg.speaker_pins[i];
+ if (!pin)
+ break;
+ dac = spec->multiout.extra_out_nid[i];
+ if (!dac) {
+ if (i > 0 && spec->multiout.extra_out_nid[0])
+ dac = spec->multiout.extra_out_nid[0];
+ else
+ dac = spec->multiout.dac_nids[0];
+ }
+ alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
+ }
+}
+
+/* check whether the given pin can be a multi-io pin */
+static bool can_be_multiio_pin(struct hda_codec *codec,
+ unsigned int location, hda_nid_t nid)
+{
+ unsigned int defcfg, caps;
+
+ defcfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX)
+ return false;
+ if (location && get_defcfg_location(defcfg) != location)
+ return false;
+ caps = snd_hda_query_pin_caps(codec, nid);
+ if (!(caps & AC_PINCAP_OUT))
+ return false;
+ return true;
+}
+
+/*
+ * multi-io helper
+ *
+ * When hardwired is set, try to fill ony hardwired pins, and returns
+ * zero if any pins are filled, non-zero if nothing found.
+ * When hardwired is off, try to fill possible input pins, and returns
+ * the badness value.
+ */
+static int alc_auto_fill_multi_ios(struct hda_codec *codec,
+ hda_nid_t reference_pin,
+ bool hardwired, int offset)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int type, i, j, dacs, num_pins, old_pins;
+ unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin);
+ unsigned int location = get_defcfg_location(defcfg);
+ int badness = 0;
+
+ old_pins = spec->multi_ios;
+ if (old_pins >= 2)
+ goto end_fill;
+
+ num_pins = 0;
+ for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type != type)
+ continue;
+ if (can_be_multiio_pin(codec, location,
+ cfg->inputs[i].pin))
+ num_pins++;
+ }
+ }
+ if (num_pins < 2)
+ goto end_fill;
+
+ dacs = spec->multiout.num_dacs;
+ for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ hda_nid_t dac = 0;
+
+ if (cfg->inputs[i].type != type)
+ continue;
+ if (!can_be_multiio_pin(codec, location, nid))
+ continue;
+ for (j = 0; j < spec->multi_ios; j++) {
+ if (nid == spec->multi_io[j].pin)
+ break;
+ }
+ if (j < spec->multi_ios)
+ continue;
+
+ if (offset && offset + spec->multi_ios < dacs) {
+ dac = spec->private_dac_nids[offset + spec->multi_ios];
+ if (!alc_auto_is_dac_reachable(codec, nid, dac))
+ dac = 0;
+ }
+ if (hardwired)
+ dac = get_dac_if_single(codec, nid);
+ else if (!dac)
+ dac = alc_auto_look_for_dac(codec, nid);
+ if (!dac) {
+ badness++;
+ continue;
+ }
+ spec->multi_io[spec->multi_ios].pin = nid;
+ spec->multi_io[spec->multi_ios].dac = dac;
+ spec->multi_ios++;
+ if (spec->multi_ios >= 2)
+ break;
+ }
+ }
+ end_fill:
+ if (badness)
+ badness = BAD_MULTI_IO;
+ if (old_pins == spec->multi_ios) {
+ if (hardwired)
+ return 1; /* nothing found */
+ else
+ return badness; /* no badness if nothing found */
+ }
+ if (!hardwired && spec->multi_ios < 2) {
+ spec->multi_ios = old_pins;
+ return badness;
+ }
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
return 0;
}
+static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
-/*
- * Common callbacks
- */
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = spec->multi_ios + 1;
+ if (uinfo->value.enumerated.item > spec->multi_ios)
+ uinfo->value.enumerated.item = spec->multi_ios;
+ sprintf(uinfo->value.enumerated.name, "%dch",
+ (uinfo->value.enumerated.item + 1) * 2);
+ return 0;
+}
-static int alc_init(struct hda_codec *codec)
+static int alc_auto_ch_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2;
+ return 0;
+}
- if (spec->init_hook)
- spec->init_hook(codec);
+static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
+{
+ struct alc_spec *spec = codec->spec;
+ hda_nid_t nid = spec->multi_io[idx].pin;
+
+ if (!spec->multi_io[idx].ctl_in)
+ spec->multi_io[idx].ctl_in =
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ if (output) {
+ snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
+ if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, 0);
+ alc_auto_select_dac(codec, nid, spec->multi_io[idx].dac);
+ } else {
+ if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ snd_hda_set_pin_ctl_cache(codec, nid,
+ spec->multi_io[idx].ctl_in);
+ }
+ return 0;
+}
- alc_fix_pll(codec);
- alc_auto_init_amp(codec, spec->init_amp);
+static int alc_auto_ch_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ int i, ch;
+
+ ch = ucontrol->value.enumerated.item[0];
+ if (ch < 0 || ch > spec->multi_ios)
+ return -EINVAL;
+ if (ch == (spec->ext_channel_count - 1) / 2)
+ return 0;
+ spec->ext_channel_count = (ch + 1) * 2;
+ for (i = 0; i < spec->multi_ios; i++)
+ alc_set_multi_io(codec, i, i < ch);
+ spec->multiout.max_channels = max(spec->ext_channel_count,
+ spec->const_channel_count);
+ if (spec->need_dac_fix)
+ spec->multiout.num_dacs = spec->multiout.max_channels / 2;
+ return 1;
+}
- snd_hda_gen_init(codec);
+static const struct snd_kcontrol_new alc_auto_channel_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Channel Mode",
+ .info = alc_auto_ch_mode_info,
+ .get = alc_auto_ch_mode_get,
+ .put = alc_auto_ch_mode_put,
+};
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+static int alc_auto_add_multi_channel_mode(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ if (spec->multi_ios > 0) {
+ if (!alc_kcontrol_new(spec, "Channel Mode",
+ &alc_auto_channel_mode_enum))
+ return -ENOMEM;
+ }
return 0;
}
-static inline void alc_shutup(struct hda_codec *codec)
+/* filter out invalid adc_nids (and capsrc_nids) that don't give all
+ * active input pins
+ */
+static void alc_remove_invalid_adc_nids(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ const struct hda_input_mux *imux;
+ hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)];
+ hda_nid_t capsrc_nids[ARRAY_SIZE(spec->private_adc_nids)];
+ int i, n, nums;
- if (spec && spec->shutup)
- spec->shutup(codec);
- snd_hda_shutup_pins(codec);
+ imux = spec->input_mux;
+ if (!imux)
+ return;
+ if (spec->dyn_adc_switch)
+ return;
+
+ again:
+ nums = 0;
+ for (n = 0; n < spec->num_adc_nids; n++) {
+ hda_nid_t cap = spec->private_capsrc_nids[n];
+ int num_conns = snd_hda_get_num_conns(codec, cap);
+ for (i = 0; i < imux->num_items; i++) {
+ hda_nid_t pin = spec->imux_pins[i];
+ if (pin) {
+ if (get_connection_index(codec, cap, pin) < 0)
+ break;
+ } else if (num_conns <= imux->items[i].index)
+ break;
+ }
+ if (i >= imux->num_items) {
+ adc_nids[nums] = spec->private_adc_nids[n];
+ capsrc_nids[nums++] = cap;
+ }
+ }
+ if (!nums) {
+ /* check whether ADC-switch is possible */
+ if (!alc_check_dyn_adc_switch(codec)) {
+ if (spec->shared_mic_hp) {
+ spec->shared_mic_hp = 0;
+ spec->private_imux[0].num_items = 1;
+ goto again;
+ }
+ printk(KERN_WARNING "hda_codec: %s: no valid ADC found;"
+ " using fallback 0x%x\n",
+ codec->chip_name, spec->private_adc_nids[0]);
+ spec->num_adc_nids = 1;
+ spec->auto_mic = 0;
+ return;
+ }
+ } else if (nums != spec->num_adc_nids) {
+ memcpy(spec->private_adc_nids, adc_nids,
+ nums * sizeof(hda_nid_t));
+ memcpy(spec->private_capsrc_nids, capsrc_nids,
+ nums * sizeof(hda_nid_t));
+ spec->num_adc_nids = nums;
+ }
+
+ if (spec->auto_mic)
+ alc_auto_mic_check_imux(codec); /* check auto-mic setups */
+ else if (spec->input_mux->num_items == 1 || spec->shared_mic_hp)
+ spec->num_adc_nids = 1; /* reduce to a single ADC */
}
-static void alc_free(struct hda_codec *codec)
+/*
+ * initialize ADC paths
+ */
+static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx)
{
struct alc_spec *spec = codec->spec;
+ hda_nid_t nid;
- if (!spec)
+ nid = spec->adc_nids[adc_idx];
+ /* mute ADC */
+ if (nid_has_mute(codec, nid, HDA_INPUT)) {
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_MUTE(0));
return;
-
- snd_hda_gen_spec_free(&spec->gen);
- snd_hda_detach_beep_device(codec);
- kfree(spec);
+ }
+ if (!spec->capsrc_nids)
+ return;
+ nid = spec->capsrc_nids[adc_idx];
+ if (nid_has_mute(codec, nid, HDA_OUTPUT))
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE);
}
-#ifdef CONFIG_PM
-static void alc_power_eapd(struct hda_codec *codec)
+static void alc_auto_init_input_src(struct hda_codec *codec)
{
- alc_auto_setup_eapd(codec, false);
+ struct alc_spec *spec = codec->spec;
+ int c, nums;
+
+ for (c = 0; c < spec->num_adc_nids; c++)
+ alc_auto_init_adc(codec, c);
+ if (spec->dyn_adc_switch)
+ nums = 1;
+ else
+ nums = spec->num_adc_nids;
+ for (c = 0; c < nums; c++)
+ alc_mux_select(codec, c, spec->cur_mux[c], true);
}
-static int alc_suspend(struct hda_codec *codec)
+/* add mic boosts if needed */
+static int alc_auto_add_mic_boost(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- alc_shutup(codec);
- if (spec && spec->power_hook)
- spec->power_hook(codec);
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, err;
+ int type_idx = 0;
+ hda_nid_t nid;
+ const char *prev_label = NULL;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type > AUTO_PIN_MIC)
+ break;
+ nid = cfg->inputs[i].pin;
+ if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
+ const char *label;
+ char boost_label[32];
+
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ if (spec->shared_mic_hp && !strcmp(label, "Misc"))
+ label = "Headphone Mic";
+ if (prev_label && !strcmp(label, prev_label))
+ type_idx++;
+ else
+ type_idx = 0;
+ prev_label = label;
+
+ snprintf(boost_label, sizeof(boost_label),
+ "%s Boost Volume", label);
+ err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ boost_label, type_idx,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
+ if (err < 0)
+ return err;
+ }
+ }
return 0;
}
-#endif
-#ifdef CONFIG_PM
-static int alc_resume(struct hda_codec *codec)
+/* select or unmute the given capsrc route */
+static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap,
+ int idx)
{
- msleep(150); /* to avoid pop noise */
- codec->patch_ops.init(codec);
- snd_hda_codec_resume_amp(codec);
- snd_hda_codec_resume_cache(codec);
- alc_inv_dmic_sync(codec, true);
- hda_call_check_power_status(codec, 0x01);
- return 0;
+ if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
+ snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
+ HDA_AMP_MUTE, 0);
+ } else if (snd_hda_get_num_conns(codec, cap) > 1) {
+ snd_hda_codec_write_cache(codec, cap, 0,
+ AC_VERB_SET_CONNECT_SEL, idx);
+ }
}
-#endif
-/*
- */
-static const struct hda_codec_ops alc_patch_ops = {
- .build_controls = alc_build_controls,
- .build_pcms = snd_hda_gen_build_pcms,
- .init = alc_init,
- .free = alc_free,
- .unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
- .resume = alc_resume,
- .suspend = alc_suspend,
- .check_power_status = snd_hda_gen_check_power_status,
-#endif
- .reboot_notify = alc_shutup,
-};
+/* set the default connection to that pin */
+static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
+ if (!pin)
+ return 0;
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ hda_nid_t cap = get_capsrc(spec, i);
+ int idx;
-/* replace the codec chip_name with the given string */
-static int alc_codec_rename(struct hda_codec *codec, const char *name)
-{
- kfree(codec->chip_name);
- codec->chip_name = kstrdup(name, GFP_KERNEL);
- if (!codec->chip_name) {
- alc_free(codec);
- return -ENOMEM;
+ idx = get_connection_index(codec, cap, pin);
+ if (idx < 0)
+ continue;
+ select_or_unmute_capsrc(codec, cap, idx);
+ return i; /* return the found index */
}
- return 0;
+ return -1; /* not found */
}
-/*
- * Rename codecs appropriately from COEF value
- */
-struct alc_codec_rename_table {
- unsigned int vendor_id;
- unsigned short coef_mask;
- unsigned short coef_bits;
- const char *name;
-};
+/* initialize some special cases for input sources */
+static void alc_init_special_input_src(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int i;
-static struct alc_codec_rename_table rename_tbl[] = {
- { 0x10ec0269, 0xfff0, 0x3010, "ALC277" },
- { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" },
- { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" },
- { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" },
- { 0x10ec0269, 0xffff, 0xa023, "ALC259" },
- { 0x10ec0269, 0xffff, 0x6023, "ALC281X" },
- { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" },
- { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" },
- { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" },
- { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" },
- { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" },
- { 0x10ec0899, 0x2000, 0x2000, "ALC899" },
- { 0x10ec0892, 0xffff, 0x8020, "ALC661" },
- { 0x10ec0892, 0xffff, 0x8011, "ALC661" },
- { 0x10ec0892, 0xffff, 0x4011, "ALC656" },
- { } /* terminator */
-};
+ for (i = 0; i < spec->autocfg.num_inputs; i++)
+ init_capsrc_for_pin(codec, spec->autocfg.inputs[i].pin);
+}
-static int alc_codec_rename_from_preset(struct hda_codec *codec)
+/* assign appropriate capture mixers */
+static void set_capture_mixer(struct hda_codec *codec)
{
- const struct alc_codec_rename_table *p;
+ struct alc_spec *spec = codec->spec;
+ static const struct snd_kcontrol_new *caps[2][3] = {
+ { alc_capture_mixer_nosrc1,
+ alc_capture_mixer_nosrc2,
+ alc_capture_mixer_nosrc3 },
+ { alc_capture_mixer1,
+ alc_capture_mixer2,
+ alc_capture_mixer3 },
+ };
- for (p = rename_tbl; p->vendor_id; p++) {
- if (p->vendor_id != codec->vendor_id)
- continue;
- if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits)
- return alc_codec_rename(codec, p->name);
+ /* check whether either of ADC or MUX has a volume control */
+ if (!nid_has_volume(codec, spec->adc_nids[0], HDA_INPUT)) {
+ if (!spec->capsrc_nids)
+ return; /* no volume */
+ if (!nid_has_volume(codec, spec->capsrc_nids[0], HDA_OUTPUT))
+ return; /* no volume in capsrc, too */
+ spec->vol_in_capsrc = 1;
+ }
+
+ if (spec->num_adc_nids > 0) {
+ int mux = 0;
+ int num_adcs = 0;
+
+ if (spec->input_mux && spec->input_mux->num_items > 1)
+ mux = 1;
+ if (spec->auto_mic) {
+ num_adcs = 1;
+ mux = 0;
+ } else if (spec->dyn_adc_switch)
+ num_adcs = 1;
+ if (!num_adcs) {
+ if (spec->num_adc_nids > 3)
+ spec->num_adc_nids = 3;
+ else if (!spec->num_adc_nids)
+ return;
+ num_adcs = spec->num_adc_nids;
+ }
+ spec->cap_mixer = caps[mux][num_adcs - 1];
}
- return 0;
}
+/*
+ * standard auto-parser initializations
+ */
+static void alc_auto_init_std(struct hda_codec *codec)
+{
+ alc_auto_init_multi_out(codec);
+ alc_auto_init_extra_out(codec);
+ alc_auto_init_analog_input(codec);
+ alc_auto_init_input_src(codec);
+ alc_auto_init_digital(codec);
+ alc_inithook(codec);
+}
/*
* Digital-beep handlers
@@ -959,21 +4273,94 @@ static int alc_parse_auto_config(struct hda_codec *codec,
const hda_nid_t *ssid_nids)
{
struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int err;
err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids,
spec->parse_flags);
if (err < 0)
return err;
+ if (!cfg->line_outs) {
+ if (cfg->dig_outs || cfg->dig_in_pin) {
+ spec->multiout.max_channels = 2;
+ spec->no_analog = 1;
+ goto dig_only;
+ }
+ return 0; /* can't find valid BIOS pin config */
+ }
- if (ssid_nids)
- alc_ssid_check(codec, ssid_nids);
+ if (!spec->no_primary_hp &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ cfg->line_outs <= cfg->hp_outs) {
+ /* use HP as primary out */
+ cfg->speaker_outs = cfg->line_outs;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ }
- err = snd_hda_gen_parse_auto_config(codec, cfg);
+ err = alc_auto_fill_dac_nids(codec);
+ if (err < 0)
+ return err;
+ err = alc_auto_add_multi_channel_mode(codec);
+ if (err < 0)
+ return err;
+ err = alc_auto_create_multi_out_ctls(codec, cfg);
+ if (err < 0)
+ return err;
+ err = alc_auto_create_hp_out(codec);
+ if (err < 0)
+ return err;
+ err = alc_auto_create_speaker_out(codec);
+ if (err < 0)
+ return err;
+ err = alc_auto_create_shared_input(codec);
+ if (err < 0)
+ return err;
+ err = alc_auto_create_input_ctls(codec);
if (err < 0)
return err;
+ /* check the multiple speaker pins */
+ if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+ spec->const_channel_count = cfg->line_outs * 2;
+ else
+ spec->const_channel_count = cfg->speaker_outs * 2;
+
+ if (spec->multi_ios > 0)
+ spec->multiout.max_channels = max(spec->ext_channel_count,
+ spec->const_channel_count);
+ else
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ dig_only:
+ alc_auto_parse_digital(codec);
+
+ if (!spec->no_analog)
+ alc_remove_invalid_adc_nids(codec);
+
+ if (ssid_nids)
+ alc_ssid_check(codec, ssid_nids);
+
+ if (!spec->no_analog) {
+ err = alc_auto_check_switches(codec);
+ if (err < 0)
+ return err;
+ err = alc_auto_add_mic_boost(codec);
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->kctls.list)
+ add_mixer(spec, spec->kctls.list);
+
+ if (!spec->no_analog && !spec->cap_mixer)
+ set_capture_mixer(codec);
+
return 1;
}
@@ -986,12 +4373,11 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
if (!spec)
return -ENOMEM;
codec->spec = spec;
- snd_hda_gen_spec_init(&spec->gen);
- spec->gen.mixer_nid = mixer_nid;
- spec->gen.own_eapd_ctl = 1;
codec->single_adc_amp = 1;
- /* FIXME: do we need this for all Realtek codec models? */
- codec->spdif_status_reset = 1;
+ spec->mixer_nid = mixer_nid;
+ snd_hda_gen_init(&spec->gen);
+ snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
+ snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8);
err = alc_codec_rename_from_preset(codec);
if (err < 0) {
@@ -1034,28 +4420,27 @@ enum {
ALC880_FIXUP_6ST_BASE,
ALC880_FIXUP_6ST,
ALC880_FIXUP_6ST_DIG,
- ALC880_FIXUP_6ST_AUTOMUTE,
};
/* enable the volume-knob widget support on NID 0x21 */
static void alc880_fixup_vol_knob(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
- if (action == HDA_FIXUP_ACT_PROBE)
+ if (action == ALC_FIXUP_ACT_PROBE)
snd_hda_jack_detect_enable_callback(codec, 0x21, ALC_DCVOL_EVENT, alc_update_knob_master);
}
-static const struct hda_fixup alc880_fixups[] = {
+static const struct alc_fixup alc880_fixups[] = {
[ALC880_FIXUP_GPIO1] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = alc_gpio1_init_verbs,
},
[ALC880_FIXUP_GPIO2] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = alc_gpio2_init_verbs,
},
[ALC880_FIXUP_MEDION_RIM] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
{ 0x20, AC_VERB_SET_PROC_COEF, 0x3060 },
@@ -1065,8 +4450,8 @@ static const struct hda_fixup alc880_fixups[] = {
.chain_id = ALC880_FIXUP_GPIO2,
},
[ALC880_FIXUP_LG] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
/* disable bogus unused pins */
{ 0x16, 0x411111f0 },
{ 0x18, 0x411111f0 },
@@ -1075,8 +4460,8 @@ static const struct hda_fixup alc880_fixups[] = {
}
},
[ALC880_FIXUP_W810] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
/* disable bogus unused pins */
{ 0x17, 0x411111f0 },
{ }
@@ -1085,7 +4470,7 @@ static const struct hda_fixup alc880_fixups[] = {
.chain_id = ALC880_FIXUP_GPIO2,
},
[ALC880_FIXUP_EAPD_COEF] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* change to EAPD mode */
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
@@ -1094,7 +4479,7 @@ static const struct hda_fixup alc880_fixups[] = {
},
},
[ALC880_FIXUP_TCL_S700] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* change to EAPD mode */
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
@@ -1105,13 +4490,13 @@ static const struct hda_fixup alc880_fixups[] = {
.chain_id = ALC880_FIXUP_GPIO2,
},
[ALC880_FIXUP_VOL_KNOB] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc880_fixup_vol_knob,
},
[ALC880_FIXUP_FUJITSU] = {
/* override all pins as BIOS on old Amilo is broken */
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x0121411f }, /* HP */
{ 0x15, 0x99030120 }, /* speaker */
{ 0x16, 0x99030130 }, /* bass speaker */
@@ -1130,8 +4515,8 @@ static const struct hda_fixup alc880_fixups[] = {
},
[ALC880_FIXUP_F1734] = {
/* almost compatible with FUJITSU, but no bass and SPDIF */
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x0121411f }, /* HP */
{ 0x15, 0x99030120 }, /* speaker */
{ 0x16, 0x411111f0 }, /* N/A */
@@ -1150,8 +4535,8 @@ static const struct hda_fixup alc880_fixups[] = {
},
[ALC880_FIXUP_UNIWILL] = {
/* need to fix HP and speaker pins to be parsed correctly */
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x0121411f }, /* HP */
{ 0x15, 0x99030120 }, /* speaker */
{ 0x16, 0x99030130 }, /* bass speaker */
@@ -1159,8 +4544,8 @@ static const struct hda_fixup alc880_fixups[] = {
},
},
[ALC880_FIXUP_UNIWILL_DIG] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
/* disable bogus unused pins */
{ 0x17, 0x411111f0 },
{ 0x19, 0x411111f0 },
@@ -1170,8 +4555,8 @@ static const struct hda_fixup alc880_fixups[] = {
}
},
[ALC880_FIXUP_Z71V] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
/* set up the whole pins as BIOS is utterly broken */
{ 0x14, 0x99030120 }, /* speaker */
{ 0x15, 0x0121411f }, /* HP */
@@ -1188,8 +4573,8 @@ static const struct hda_fixup alc880_fixups[] = {
}
},
[ALC880_FIXUP_3ST_BASE] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x01014010 }, /* line-out */
{ 0x15, 0x411111f0 }, /* N/A */
{ 0x16, 0x411111f0 }, /* N/A */
@@ -1206,8 +4591,8 @@ static const struct hda_fixup alc880_fixups[] = {
}
},
[ALC880_FIXUP_3ST] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x1e, 0x411111f0 }, /* N/A */
{ }
},
@@ -1215,8 +4600,8 @@ static const struct hda_fixup alc880_fixups[] = {
.chain_id = ALC880_FIXUP_3ST_BASE,
},
[ALC880_FIXUP_3ST_DIG] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x1e, 0x0144111e }, /* SPDIF */
{ }
},
@@ -1224,8 +4609,8 @@ static const struct hda_fixup alc880_fixups[] = {
.chain_id = ALC880_FIXUP_3ST_BASE,
},
[ALC880_FIXUP_5ST_BASE] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x01014010 }, /* front */
{ 0x15, 0x411111f0 }, /* N/A */
{ 0x16, 0x01011411 }, /* CLFE */
@@ -1242,8 +4627,8 @@ static const struct hda_fixup alc880_fixups[] = {
}
},
[ALC880_FIXUP_5ST] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x1e, 0x411111f0 }, /* N/A */
{ }
},
@@ -1251,8 +4636,8 @@ static const struct hda_fixup alc880_fixups[] = {
.chain_id = ALC880_FIXUP_5ST_BASE,
},
[ALC880_FIXUP_5ST_DIG] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x1e, 0x0144111e }, /* SPDIF */
{ }
},
@@ -1260,8 +4645,8 @@ static const struct hda_fixup alc880_fixups[] = {
.chain_id = ALC880_FIXUP_5ST_BASE,
},
[ALC880_FIXUP_6ST_BASE] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x01014010 }, /* front */
{ 0x15, 0x01016412 }, /* surr */
{ 0x16, 0x01011411 }, /* CLFE */
@@ -1278,8 +4663,8 @@ static const struct hda_fixup alc880_fixups[] = {
}
},
[ALC880_FIXUP_6ST] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x1e, 0x411111f0 }, /* N/A */
{ }
},
@@ -1287,23 +4672,14 @@ static const struct hda_fixup alc880_fixups[] = {
.chain_id = ALC880_FIXUP_6ST_BASE,
},
[ALC880_FIXUP_6ST_DIG] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x1e, 0x0144111e }, /* SPDIF */
{ }
},
.chained = true,
.chain_id = ALC880_FIXUP_6ST_BASE,
},
- [ALC880_FIXUP_6ST_AUTOMUTE] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x1b, 0x0121401f }, /* HP with jack detect */
- { }
- },
- .chained_before = true,
- .chain_id = ALC880_FIXUP_6ST_BASE,
- },
};
static const struct snd_pci_quirk alc880_fixup_tbl[] = {
@@ -1318,7 +4694,7 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = {
SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_FIXUP_VOL_KNOB),
SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810),
SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM),
- SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST_AUTOMUTE),
+ SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST),
SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_FIXUP_F1734),
SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU),
SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734),
@@ -1374,14 +4750,13 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = {
{}
};
-static const struct hda_model_fixup alc880_fixup_models[] = {
+static const struct alc_model_fixup alc880_fixup_models[] = {
{.id = ALC880_FIXUP_3ST, .name = "3stack"},
{.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"},
{.id = ALC880_FIXUP_5ST, .name = "5stack"},
{.id = ALC880_FIXUP_5ST_DIG, .name = "5stack-digout"},
{.id = ALC880_FIXUP_6ST, .name = "6stack"},
{.id = ALC880_FIXUP_6ST_DIG, .name = "6stack-digout"},
- {.id = ALC880_FIXUP_6ST_AUTOMUTE, .name = "6stack-automute"},
{}
};
@@ -1399,18 +4774,18 @@ static int patch_alc880(struct hda_codec *codec)
return err;
spec = codec->spec;
- spec->gen.need_dac_fix = 1;
+ spec->need_dac_fix = 1;
- snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
+ alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
alc880_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
/* automatic parse from the BIOS config */
err = alc880_parse_auto_config(codec);
if (err < 0)
goto error;
- if (!spec->gen.no_analog) {
+ if (!spec->no_analog) {
err = snd_hda_attach_beep_device(codec, 0x1);
if (err < 0)
goto error;
@@ -1421,7 +4796,7 @@ static int patch_alc880(struct hda_codec *codec)
codec->patch_ops.unsol_event = alc880_unsol_event;
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
return 0;
@@ -1453,39 +4828,38 @@ enum {
ALC260_FIXUP_REPLACER,
ALC260_FIXUP_HP_B1900,
ALC260_FIXUP_KN1,
- ALC260_FIXUP_FSC_S7020,
};
static void alc260_gpio1_automute(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- spec->gen.hp_jack_present);
+ spec->hp_jack_present);
}
static void alc260_fixup_gpio1_toggle(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- if (action == HDA_FIXUP_ACT_PROBE) {
+ if (action == ALC_FIXUP_ACT_PROBE) {
/* although the machine has only one output pin, we need to
* toggle GPIO1 according to the jack state
*/
- spec->gen.automute_hook = alc260_gpio1_automute;
- spec->gen.detect_hp = 1;
- spec->gen.automute_speaker = 1;
- spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
- snd_hda_jack_detect_enable_callback(codec, 0x0f, HDA_GEN_HP_EVENT,
- snd_hda_gen_hp_automute);
- snd_hda_add_verbs(codec, alc_gpio1_init_verbs);
+ spec->automute_hook = alc260_gpio1_automute;
+ spec->detect_hp = 1;
+ spec->automute_speaker = 1;
+ spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
+ snd_hda_jack_detect_enable_callback(codec, 0x0f, ALC_HP_EVENT,
+ alc_hp_automute);
+ snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs);
}
}
static void alc260_fixup_kn1(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- static const struct hda_pintbl pincfgs[] = {
+ static const struct alc_pincfg pincfgs[] = {
{ 0x0f, 0x02214000 }, /* HP/speaker */
{ 0x12, 0x90a60160 }, /* int mic */
{ 0x13, 0x02a19000 }, /* ext mic */
@@ -1502,41 +4876,32 @@ static void alc260_fixup_kn1(struct hda_codec *codec,
};
switch (action) {
- case HDA_FIXUP_ACT_PRE_PROBE:
- snd_hda_apply_pincfgs(codec, pincfgs);
+ case ALC_FIXUP_ACT_PRE_PROBE:
+ alc_apply_pincfgs(codec, pincfgs);
break;
- case HDA_FIXUP_ACT_PROBE:
+ case ALC_FIXUP_ACT_PROBE:
spec->init_amp = ALC_INIT_NONE;
break;
}
}
-static void alc260_fixup_fsc_s7020(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct alc_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
- spec->gen.add_out_jack_modes = 1;
-}
-
-static const struct hda_fixup alc260_fixups[] = {
+static const struct alc_fixup alc260_fixups[] = {
[ALC260_FIXUP_HP_DC5750] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x11, 0x90130110 }, /* speaker */
{ }
}
},
[ALC260_FIXUP_HP_PIN_0F] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x0f, 0x01214000 }, /* HP */
{ }
}
},
[ALC260_FIXUP_COEF] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
{ 0x20, AC_VERB_SET_PROC_COEF, 0x3040 },
@@ -1546,17 +4911,17 @@ static const struct hda_fixup alc260_fixups[] = {
.chain_id = ALC260_FIXUP_HP_PIN_0F,
},
[ALC260_FIXUP_GPIO1] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = alc_gpio1_init_verbs,
},
[ALC260_FIXUP_GPIO1_TOGGLE] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc260_fixup_gpio1_toggle,
.chained = true,
.chain_id = ALC260_FIXUP_HP_PIN_0F,
},
[ALC260_FIXUP_REPLACER] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
{ 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
@@ -1566,19 +4931,15 @@ static const struct hda_fixup alc260_fixups[] = {
.chain_id = ALC260_FIXUP_GPIO1_TOGGLE,
},
[ALC260_FIXUP_HP_B1900] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc260_fixup_gpio1_toggle,
.chained = true,
.chain_id = ALC260_FIXUP_COEF,
},
[ALC260_FIXUP_KN1] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc260_fixup_kn1,
},
- [ALC260_FIXUP_FSC_S7020] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = alc260_fixup_fsc_s7020,
- },
};
static const struct snd_pci_quirk alc260_fixup_tbl[] = {
@@ -1587,7 +4948,6 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1),
SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750),
SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900),
- SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020),
SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1),
SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1),
SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER),
@@ -1607,21 +4967,16 @@ static int patch_alc260(struct hda_codec *codec)
return err;
spec = codec->spec;
- /* as quite a few machines require HP amp for speaker outputs,
- * it's easier to enable it unconditionally; even if it's unneeded,
- * it's almost harmless.
- */
- spec->gen.prefer_hp_amp = 1;
- snd_hda_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
/* automatic parse from the BIOS config */
err = alc260_parse_auto_config(codec);
if (err < 0)
goto error;
- if (!spec->gen.no_analog) {
+ if (!spec->no_analog) {
err = snd_hda_attach_beep_device(codec, 0x1);
if (err < 0)
goto error;
@@ -1631,7 +4986,7 @@ static int patch_alc260(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops;
spec->shutup = alc_eapd_shutup;
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
return 0;
@@ -1685,9 +5040,9 @@ enum {
};
static void alc889_fixup_coef(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
- if (action != HDA_FIXUP_ACT_INIT)
+ if (action != ALC_FIXUP_ACT_INIT)
return;
alc889_coef_init(codec);
}
@@ -1727,9 +5082,9 @@ static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted)
/* set up GPIO at initialization */
static void alc885_fixup_macpro_gpio(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
- if (action != HDA_FIXUP_ACT_INIT)
+ if (action != ALC_FIXUP_ACT_INIT)
return;
alc882_gpio_mute(codec, 0, 0);
alc882_gpio_mute(codec, 1, 0);
@@ -1740,9 +5095,9 @@ static void alc885_fixup_macpro_gpio(struct hda_codec *codec,
* work correctly (bko#42740)
*/
static void alc889_fixup_dac_route(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ if (action == ALC_FIXUP_ACT_PRE_PROBE) {
/* fake the connections during parsing the tree */
hda_nid_t conn1[2] = { 0x0c, 0x0d };
hda_nid_t conn2[2] = { 0x0e, 0x0f };
@@ -1750,7 +5105,7 @@ static void alc889_fixup_dac_route(struct hda_codec *codec,
snd_hda_override_conn_list(codec, 0x15, 2, conn1);
snd_hda_override_conn_list(codec, 0x18, 2, conn2);
snd_hda_override_conn_list(codec, 0x1a, 2, conn2);
- } else if (action == HDA_FIXUP_ACT_PROBE) {
+ } else if (action == ALC_FIXUP_ACT_PROBE) {
/* restore the connections */
hda_nid_t conn[5] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 };
snd_hda_override_conn_list(codec, 0x14, 5, conn);
@@ -1762,61 +5117,62 @@ static void alc889_fixup_dac_route(struct hda_codec *codec,
/* Set VREF on HP pin */
static void alc889_fixup_mbp_vref(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
static hda_nid_t nids[2] = { 0x14, 0x15 };
int i;
- if (action != HDA_FIXUP_ACT_INIT)
+ if (action != ALC_FIXUP_ACT_INIT)
return;
for (i = 0; i < ARRAY_SIZE(nids); i++) {
unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]);
if (get_defcfg_device(val) != AC_JACK_HP_OUT)
continue;
- val = snd_hda_codec_get_pin_target(codec, nids[i]);
+ val = snd_hda_codec_read(codec, nids[i], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
val |= AC_PINCTL_VREF_80;
snd_hda_set_pin_ctl(codec, nids[i], val);
- spec->gen.keep_vref_in_automute = 1;
+ spec->keep_vref_in_automute = 1;
break;
}
}
/* Set VREF on speaker pins on imac91 */
static void alc889_fixup_imac91_vref(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
static hda_nid_t nids[2] = { 0x18, 0x1a };
int i;
- if (action != HDA_FIXUP_ACT_INIT)
+ if (action != ALC_FIXUP_ACT_INIT)
return;
for (i = 0; i < ARRAY_SIZE(nids); i++) {
unsigned int val;
- val = snd_hda_codec_get_pin_target(codec, nids[i]);
+ val = snd_hda_codec_read(codec, nids[i], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
val |= AC_PINCTL_VREF_50;
snd_hda_set_pin_ctl(codec, nids[i], val);
}
- spec->gen.keep_vref_in_automute = 1;
+ spec->keep_vref_in_automute = 1;
}
/* Don't take HP output as primary
- * Strangely, the speaker output doesn't work on Vaio Z and some Vaio
- * all-in-one desktop PCs (for example VGC-LN51JGB) through DAC 0x05
+ * strangely, the speaker output doesn't work on VAIO Z through DAC 0x05
*/
static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
- spec->gen.no_primary_hp = 1;
+ if (action == ALC_FIXUP_ACT_PRE_PROBE)
+ spec->no_primary_hp = 1;
}
-static const struct hda_fixup alc882_fixups[] = {
+static const struct alc_fixup alc882_fixups[] = {
[ALC882_FIXUP_ABIT_AW9D_MAX] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x15, 0x01080104 }, /* side */
{ 0x16, 0x01011012 }, /* rear */
{ 0x17, 0x01016011 }, /* clfe */
@@ -1824,47 +5180,47 @@ static const struct hda_fixup alc882_fixups[] = {
}
},
[ALC882_FIXUP_LENOVO_Y530] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x15, 0x99130112 }, /* rear int speakers */
{ 0x16, 0x99130111 }, /* subwoofer */
{ }
}
},
[ALC882_FIXUP_PB_M5210] = {
- .type = HDA_FIXUP_PINCTLS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x19, PIN_VREF50 },
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
{}
}
},
[ALC882_FIXUP_ACER_ASPIRE_7736] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc_fixup_sku_ignore,
},
[ALC882_FIXUP_ASUS_W90V] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x16, 0x99130110 }, /* fix sequence for CLFE */
{ }
}
},
[ALC889_FIXUP_CD] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x1c, 0x993301f0 }, /* CD */
{ }
}
},
[ALC889_FIXUP_VAIO_TT] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x17, 0x90170111 }, /* hidden surround speaker */
{ }
}
},
[ALC888_FIXUP_EEE1601] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x0b },
{ 0x20, AC_VERB_SET_PROC_COEF, 0x0838 },
@@ -1872,7 +5228,7 @@ static const struct hda_fixup alc882_fixups[] = {
}
},
[ALC882_FIXUP_EAPD] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* change to EAPD mode */
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
@@ -1881,7 +5237,7 @@ static const struct hda_fixup alc882_fixups[] = {
}
},
[ALC883_FIXUP_EAPD] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* change to EAPD mode */
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
@@ -1890,7 +5246,7 @@ static const struct hda_fixup alc882_fixups[] = {
}
},
[ALC883_FIXUP_ACER_EAPD] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* eanable EAPD on Acer laptops */
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
@@ -1899,30 +5255,30 @@ static const struct hda_fixup alc882_fixups[] = {
}
},
[ALC882_FIXUP_GPIO1] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = alc_gpio1_init_verbs,
},
[ALC882_FIXUP_GPIO2] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = alc_gpio2_init_verbs,
},
[ALC882_FIXUP_GPIO3] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = alc_gpio3_init_verbs,
},
[ALC882_FIXUP_ASUS_W2JC] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = alc_gpio1_init_verbs,
.chained = true,
.chain_id = ALC882_FIXUP_EAPD,
},
[ALC889_FIXUP_COEF] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc889_fixup_coef,
},
[ALC882_FIXUP_ACER_ASPIRE_4930G] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x16, 0x99130111 }, /* CLFE speaker */
{ 0x17, 0x99130112 }, /* surround speaker */
{ }
@@ -1931,8 +5287,8 @@ static const struct hda_fixup alc882_fixups[] = {
.chain_id = ALC882_FIXUP_GPIO1,
},
[ALC882_FIXUP_ACER_ASPIRE_8930G] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x16, 0x99130111 }, /* CLFE speaker */
{ 0x1b, 0x99130112 }, /* surround speaker */
{ }
@@ -1942,7 +5298,7 @@ static const struct hda_fixup alc882_fixups[] = {
},
[ALC882_FIXUP_ASPIRE_8930G_VERBS] = {
/* additional init verbs for Acer Aspire 8930G */
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* Enable all DACs */
/* DAC DISABLE/MUTE 1? */
@@ -1976,31 +5332,31 @@ static const struct hda_fixup alc882_fixups[] = {
.chain_id = ALC882_FIXUP_GPIO1,
},
[ALC885_FIXUP_MACPRO_GPIO] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc885_fixup_macpro_gpio,
},
[ALC889_FIXUP_DAC_ROUTE] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc889_fixup_dac_route,
},
[ALC889_FIXUP_MBP_VREF] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc889_fixup_mbp_vref,
.chained = true,
.chain_id = ALC882_FIXUP_GPIO1,
},
[ALC889_FIXUP_IMAC91_VREF] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc889_fixup_imac91_vref,
.chained = true,
.chain_id = ALC882_FIXUP_GPIO1,
},
[ALC882_FIXUP_INV_DMIC] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc_fixup_inv_dmic_0x12,
},
[ALC882_FIXUP_NO_PRIMARY_HP] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc882_fixup_no_primary_hp,
},
};
@@ -2038,7 +5394,6 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601),
SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP),
- SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP),
/* All Apple entries are in codec SSIDs */
SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF),
@@ -2076,7 +5431,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
{}
};
-static const struct hda_model_fixup alc882_fixup_models[] = {
+static const struct alc_model_fixup alc882_fixup_models[] = {
{.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"},
{.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"},
{.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"},
@@ -2119,9 +5474,9 @@ static int patch_alc882(struct hda_codec *codec)
break;
}
- snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl,
+ alc_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl,
alc882_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
alc_auto_parse_customize_define(codec);
@@ -2130,7 +5485,7 @@ static int patch_alc882(struct hda_codec *codec)
if (err < 0)
goto error;
- if (!spec->gen.no_analog && has_cdefine_beep(codec)) {
+ if (!spec->no_analog && has_cdefine_beep(codec)) {
err = snd_hda_attach_beep_device(codec, 0x1);
if (err < 0)
goto error;
@@ -2139,7 +5494,7 @@ static int patch_alc882(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops;
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
return 0;
@@ -2164,7 +5519,6 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
*/
enum {
ALC262_FIXUP_FSC_H270,
- ALC262_FIXUP_FSC_S7110,
ALC262_FIXUP_HP_Z200,
ALC262_FIXUP_TYAN,
ALC262_FIXUP_LENOVO_3000,
@@ -2173,50 +5527,41 @@ enum {
ALC262_FIXUP_INV_DMIC,
};
-static const struct hda_fixup alc262_fixups[] = {
+static const struct alc_fixup alc262_fixups[] = {
[ALC262_FIXUP_FSC_H270] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x15, 0x0221142f }, /* front HP */
{ 0x1b, 0x0121141f }, /* rear HP */
{ }
}
},
- [ALC262_FIXUP_FSC_S7110] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x15, 0x90170110 }, /* speaker */
- { }
- },
- .chained = true,
- .chain_id = ALC262_FIXUP_BENQ,
- },
[ALC262_FIXUP_HP_Z200] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x16, 0x99130120 }, /* internal speaker */
{ }
}
},
[ALC262_FIXUP_TYAN] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x1993e1f0 }, /* int AUX */
{ }
}
},
[ALC262_FIXUP_LENOVO_3000] = {
- .type = HDA_FIXUP_PINCTLS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x19, PIN_VREF50 },
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
{}
},
.chained = true,
.chain_id = ALC262_FIXUP_BENQ,
},
[ALC262_FIXUP_BENQ] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
{ 0x20, AC_VERB_SET_PROC_COEF, 0x3070 },
@@ -2224,7 +5569,7 @@ static const struct hda_fixup alc262_fixups[] = {
}
},
[ALC262_FIXUP_BENQ_T31] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{ 0x20, AC_VERB_SET_COEF_INDEX, 0x07 },
{ 0x20, AC_VERB_SET_PROC_COEF, 0x3050 },
@@ -2232,14 +5577,14 @@ static const struct hda_fixup alc262_fixups[] = {
}
},
[ALC262_FIXUP_INV_DMIC] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc_fixup_inv_dmic_0x12,
},
};
static const struct snd_pci_quirk alc262_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200),
- SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110),
+ SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FIXUP_BENQ),
SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ),
SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN),
SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270),
@@ -2249,7 +5594,7 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = {
{}
};
-static const struct hda_model_fixup alc262_fixup_models[] = {
+static const struct alc_model_fixup alc262_fixup_models[] = {
{.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"},
{}
};
@@ -2266,7 +5611,6 @@ static int patch_alc262(struct hda_codec *codec)
return err;
spec = codec->spec;
- spec->gen.shared_mic_vref_pin = 0x18;
#if 0
/* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is
@@ -2282,9 +5626,9 @@ static int patch_alc262(struct hda_codec *codec)
#endif
alc_fix_pll_init(codec, 0x20, 0x0a, 10);
- snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl,
+ alc_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl,
alc262_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
alc_auto_parse_customize_define(codec);
@@ -2293,7 +5637,7 @@ static int patch_alc262(struct hda_codec *codec)
if (err < 0)
goto error;
- if (!spec->gen.no_analog && has_cdefine_beep(codec)) {
+ if (!spec->no_analog && has_cdefine_beep(codec)) {
err = snd_hda_attach_beep_device(codec, 0x1);
if (err < 0)
goto error;
@@ -2303,7 +5647,7 @@ static int patch_alc262(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops;
spec->shutup = alc_eapd_shutup;
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
return 0;
@@ -2344,13 +5688,13 @@ enum {
ALC268_FIXUP_HP_EAPD,
};
-static const struct hda_fixup alc268_fixups[] = {
+static const struct alc_fixup alc268_fixups[] = {
[ALC268_FIXUP_INV_DMIC] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc_fixup_inv_dmic_0x12,
},
[ALC268_FIXUP_HP_EAPD] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{0x15, AC_VERB_SET_EAPD_BTLENABLE, 0},
{}
@@ -2358,7 +5702,7 @@ static const struct hda_fixup alc268_fixups[] = {
},
};
-static const struct hda_model_fixup alc268_fixup_models[] = {
+static const struct alc_model_fixup alc268_fixup_models[] = {
{.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"},
{.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"},
{}
@@ -2382,10 +5726,9 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
struct alc_spec *spec = codec->spec;
int err = alc_parse_auto_config(codec, NULL, alc268_ssids);
if (err > 0) {
- if (!spec->gen.no_analog &&
- spec->gen.autocfg.speaker_pins[0] != 0x1d) {
+ if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) {
add_mixer(spec, alc268_beep_mixer);
- snd_hda_add_verbs(codec, alc268_beep_init_verbs);
+ snd_hda_gen_add_verbs(&spec->gen, alc268_beep_init_verbs);
}
}
return err;
@@ -2405,8 +5748,8 @@ static int patch_alc268(struct hda_codec *codec)
spec = codec->spec;
- snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ alc_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
/* automatic parse from the BIOS config */
err = alc268_parse_auto_config(codec);
@@ -2437,7 +5780,7 @@ static int patch_alc268(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops;
spec->shutup = alc_eapd_shutup;
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
return 0;
@@ -2449,35 +5792,6 @@ static int patch_alc268(struct hda_codec *codec)
/*
* ALC269
*/
-
-static int playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hda_gen_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
- hinfo);
-}
-
-static int playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct hda_gen_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
- stream_tag, format, substream);
-}
-
-static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hda_gen_spec *spec = codec->spec;
- return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
.substreams = 1,
.channels_min = 2,
@@ -2485,9 +5799,9 @@ static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = {
.rates = SNDRV_PCM_RATE_44100, /* fixed rate */
/* NID is set in alc_build_pcms */
.ops = {
- .open = playback_pcm_open,
- .prepare = playback_pcm_prepare,
- .cleanup = playback_pcm_cleanup
+ .open = alc_playback_pcm_open,
+ .prepare = alc_playback_pcm_prepare,
+ .cleanup = alc_playback_pcm_cleanup
},
};
@@ -2595,27 +5909,27 @@ static int alc269_resume(struct hda_codec *codec)
#endif /* CONFIG_PM */
static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ if (action == ALC_FIXUP_ACT_PRE_PROBE)
spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
}
static void alc269_fixup_hweq(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
int coef;
- if (action != HDA_FIXUP_ACT_INIT)
+ if (action != ALC_FIXUP_ACT_INIT)
return;
coef = alc_read_coef_idx(codec, 0x1e);
alc_write_coef_idx(codec, 0x1e, coef | 0x80);
}
static void alc271_fixup_dmic(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
static const struct hda_verb verbs[] = {
{0x20, AC_VERB_SET_COEF_INDEX, 0x0d},
@@ -2632,26 +5946,26 @@ static void alc271_fixup_dmic(struct hda_codec *codec,
}
static void alc269_fixup_pcm_44k(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- if (action != HDA_FIXUP_ACT_PROBE)
+ if (action != ALC_FIXUP_ACT_PROBE)
return;
/* Due to a hardware problem on Lenovo Ideadpad, we need to
* fix the sample rate of analog I/O to 44.1kHz
*/
- spec->gen.stream_analog_playback = &alc269_44k_pcm_analog_playback;
- spec->gen.stream_analog_capture = &alc269_44k_pcm_analog_capture;
+ spec->stream_analog_playback = &alc269_44k_pcm_analog_playback;
+ spec->stream_analog_capture = &alc269_44k_pcm_analog_capture;
}
static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
int coef;
- if (action != HDA_FIXUP_ACT_INIT)
+ if (action != ALC_FIXUP_ACT_INIT)
return;
/* The digital-mic unit sends PDM (differential signal) instead of
* the standard PCM, thus you can't record a valid mono stream as is.
@@ -2664,7 +5978,7 @@ static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
static void alc269_quanta_automute(struct hda_codec *codec)
{
- snd_hda_gen_update_outputs(codec);
+ update_outputs(codec);
snd_hda_codec_write(codec, 0x20, 0,
AC_VERB_SET_COEF_INDEX, 0x0c);
@@ -2678,79 +5992,70 @@ static void alc269_quanta_automute(struct hda_codec *codec)
}
static void alc269_fixup_quanta_mute(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- if (action != HDA_FIXUP_ACT_PROBE)
+ if (action != ALC_FIXUP_ACT_PROBE)
return;
- spec->gen.automute_hook = alc269_quanta_automute;
+ spec->automute_hook = alc269_quanta_automute;
}
-/* update mute-LED according to the speaker mute state via mic VREF pin */
-static void alc269_fixup_mic_mute_hook(void *private_data, int enabled)
+/* update mute-LED according to the speaker mute state via mic1 VREF pin */
+static void alc269_fixup_mic1_mute_hook(void *private_data, int enabled)
{
struct hda_codec *codec = private_data;
- struct alc_spec *spec = codec->spec;
- unsigned int pinval;
-
- if (spec->mute_led_polarity)
- enabled = !enabled;
- pinval = AC_PINCTL_IN_EN |
- (enabled ? AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80);
- if (spec->mute_led_nid)
- snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval);
+ unsigned int pinval = AC_PINCTL_IN_EN + (enabled ?
+ AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80);
+ snd_hda_set_pin_ctl_cache(codec, 0x18, pinval);
}
-static void alc269_fixup_hp_mute_led(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+static void alc269_fixup_mic1_mute(struct hda_codec *codec,
+ const struct alc_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- const struct dmi_device *dev = NULL;
-
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
- int pol, pin;
- if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &pol, &pin) != 2)
- continue;
- if (pin < 0x0a || pin >= 0x10)
- break;
- spec->mute_led_polarity = pol;
- spec->mute_led_nid = pin - 0x0a + 0x18;
- spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
- spec->gen.vmaster_mute_enum = 1;
- snd_printd("Detected mute LED for %x:%d\n", spec->mute_led_nid,
- spec->mute_led_polarity);
+ switch (action) {
+ case ALC_FIXUP_ACT_BUILD:
+ spec->vmaster_mute.hook = alc269_fixup_mic1_mute_hook;
+ snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true);
+ /* fallthru */
+ case ALC_FIXUP_ACT_INIT:
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
break;
}
}
-static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+/* update mute-LED according to the speaker mute state via mic2 VREF pin */
+static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ unsigned int pinval = enabled ? 0x20 : 0x24;
+ snd_hda_set_pin_ctl_cache(codec, 0x19, pinval);
+}
+
+static void alc269_fixup_mic2_mute(struct hda_codec *codec,
+ const struct alc_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->mute_led_polarity = 0;
- spec->mute_led_nid = 0x19;
- spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook;
- spec->gen.vmaster_mute_enum = 1;
+ switch (action) {
+ case ALC_FIXUP_ACT_BUILD:
+ spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook;
+ snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true);
+ /* fallthru */
+ case ALC_FIXUP_ACT_INIT:
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+ break;
}
}
static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
- const struct hda_fixup *fix,
+ const struct alc_fixup *fix,
int action)
{
struct alc_spec *spec = codec->spec;
- if (action == HDA_FIXUP_ACT_PROBE) {
- if (snd_BUG_ON(!spec->gen.am_entry[1].pin ||
- !spec->gen.autocfg.hp_pins[0]))
- return;
- snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin,
- spec->gen.autocfg.hp_pins[0]);
- }
+ if (action == ALC_FIXUP_ACT_PROBE)
+ snd_hda_jack_set_gating_jack(codec, spec->ext_mic_pin,
+ spec->autocfg.hp_pins[0]);
}
enum {
@@ -2770,8 +6075,8 @@ enum {
ALC269_FIXUP_DMIC,
ALC269VB_FIXUP_AMIC,
ALC269VB_FIXUP_DMIC,
- ALC269_FIXUP_HP_MUTE_LED,
- ALC269_FIXUP_HP_MUTE_LED_MIC2,
+ ALC269_FIXUP_MIC1_MUTE_LED,
+ ALC269_FIXUP_MIC2_MUTE_LED,
ALC269_FIXUP_INV_DMIC,
ALC269_FIXUP_LENOVO_DOCK,
ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT,
@@ -2779,16 +6084,16 @@ enum {
ALC271_FIXUP_HP_GATE_MIC_JACK,
};
-static const struct hda_fixup alc269_fixups[] = {
+static const struct alc_fixup alc269_fixups[] = {
[ALC269_FIXUP_SONY_VAIO] = {
- .type = HDA_FIXUP_PINCTLS,
- .v.pins = (const struct hda_pintbl[]) {
- {0x19, PIN_VREFGRD},
+ .type = ALC_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
{}
}
},
[ALC275_FIXUP_SONY_VAIO_GPIO2] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{0x01, AC_VERB_SET_GPIO_MASK, 0x04},
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04},
@@ -2799,7 +6104,7 @@ static const struct hda_fixup alc269_fixups[] = {
.chain_id = ALC269_FIXUP_SONY_VAIO
},
[ALC269_FIXUP_DELL_M101Z] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* Enables internal speaker */
{0x20, AC_VERB_SET_COEF_INDEX, 13},
@@ -2808,50 +6113,50 @@ static const struct hda_fixup alc269_fixups[] = {
}
},
[ALC269_FIXUP_SKU_IGNORE] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc_fixup_sku_ignore,
},
[ALC269_FIXUP_ASUS_G73JW] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x17, 0x99130111 }, /* subwoofer */
{ }
}
},
[ALC269_FIXUP_LENOVO_EAPD] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{0x14, AC_VERB_SET_EAPD_BTLENABLE, 0},
{}
}
},
[ALC275_FIXUP_SONY_HWEQ] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc269_fixup_hweq,
.chained = true,
.chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2
},
[ALC271_FIXUP_DMIC] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc271_fixup_dmic,
},
[ALC269_FIXUP_PCM_44K] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc269_fixup_pcm_44k,
.chained = true,
.chain_id = ALC269_FIXUP_QUANTA_MUTE
},
[ALC269_FIXUP_STEREO_DMIC] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc269_fixup_stereo_dmic,
},
[ALC269_FIXUP_QUANTA_MUTE] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc269_fixup_quanta_mute,
},
[ALC269_FIXUP_LIFEBOOK] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x1a, 0x2101103f }, /* dock line-out */
{ 0x1b, 0x23a11040 }, /* dock mic-in */
{ }
@@ -2860,8 +6165,8 @@ static const struct hda_fixup alc269_fixups[] = {
.chain_id = ALC269_FIXUP_QUANTA_MUTE
},
[ALC269_FIXUP_AMIC] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x15, 0x0121401f }, /* HP out */
{ 0x18, 0x01a19c20 }, /* mic */
@@ -2870,8 +6175,8 @@ static const struct hda_fixup alc269_fixups[] = {
},
},
[ALC269_FIXUP_DMIC] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x12, 0x99a3092f }, /* int-mic */
{ 0x14, 0x99130110 }, /* speaker */
{ 0x15, 0x0121401f }, /* HP out */
@@ -2880,8 +6185,8 @@ static const struct hda_fixup alc269_fixups[] = {
},
},
[ALC269VB_FIXUP_AMIC] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x18, 0x01a19c20 }, /* mic */
{ 0x19, 0x99a3092f }, /* int-mic */
@@ -2890,8 +6195,8 @@ static const struct hda_fixup alc269_fixups[] = {
},
},
[ALC269VB_FIXUP_DMIC] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x12, 0x99a3092f }, /* int-mic */
{ 0x14, 0x99130110 }, /* speaker */
{ 0x18, 0x01a19c20 }, /* mic */
@@ -2899,21 +6204,21 @@ static const struct hda_fixup alc269_fixups[] = {
{ }
},
},
- [ALC269_FIXUP_HP_MUTE_LED] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = alc269_fixup_hp_mute_led,
+ [ALC269_FIXUP_MIC1_MUTE_LED] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc269_fixup_mic1_mute,
},
- [ALC269_FIXUP_HP_MUTE_LED_MIC2] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = alc269_fixup_hp_mute_led_mic2,
+ [ALC269_FIXUP_MIC2_MUTE_LED] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc269_fixup_mic2_mute,
},
[ALC269_FIXUP_INV_DMIC] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc_fixup_inv_dmic_0x12,
},
[ALC269_FIXUP_LENOVO_DOCK] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x19, 0x23a11040 }, /* dock mic */
{ 0x1b, 0x2121103f }, /* dock headphone */
{ }
@@ -2922,12 +6227,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT
},
[ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc269_fixup_pincfg_no_hp_to_lineout,
},
[ALC271_FIXUP_AMIC_MIC2] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x19, 0x01a19c20 }, /* mic */
{ 0x1b, 0x99a7012f }, /* int-mic */
@@ -2936,7 +6241,7 @@ static const struct hda_fixup alc269_fixups[] = {
},
},
[ALC271_FIXUP_HP_GATE_MIC_JACK] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc271_hp_gate_mic_jack,
.chained = true,
.chain_id = ALC271_FIXUP_AMIC_MIC2,
@@ -2946,8 +6251,9 @@ static const struct hda_fixup alc269_fixups[] = {
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
- SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
- SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x1972, "HP Pavilion 17", ALC269_FIXUP_MIC1_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x1977, "HP Pavilion 14", ALC269_FIXUP_MIC1_MUTE_LED),
SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC),
SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_DMIC),
SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
@@ -3030,7 +6336,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
{}
};
-static const struct hda_model_fixup alc269_fixup_models[] = {
+static const struct alc_model_fixup alc269_fixup_models[] = {
{.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"},
{.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"},
{.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"},
@@ -3097,11 +6403,10 @@ static int patch_alc269(struct hda_codec *codec)
return err;
spec = codec->spec;
- spec->gen.shared_mic_vref_pin = 0x18;
- snd_hda_pick_fixup(codec, alc269_fixup_models,
+ alc_pick_fixup(codec, alc269_fixup_models,
alc269_fixup_tbl, alc269_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
alc_auto_parse_customize_define(codec);
@@ -3152,7 +6457,7 @@ static int patch_alc269(struct hda_codec *codec)
if (err < 0)
goto error;
- if (!spec->gen.no_analog && has_cdefine_beep(codec)) {
+ if (!spec->no_analog && has_cdefine_beep(codec)) {
err = snd_hda_attach_beep_device(codec, 0x1);
if (err < 0)
goto error;
@@ -3165,7 +6470,7 @@ static int patch_alc269(struct hda_codec *codec)
#endif
spec->shutup = alc269_shutup;
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
return 0;
@@ -3195,48 +6500,49 @@ enum {
/* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */
static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
unsigned int val;
- if (action != HDA_FIXUP_ACT_INIT)
+ if (action != ALC_FIXUP_ACT_INIT)
return;
- val = snd_hda_codec_get_pin_target(codec, 0x0f);
+ val = snd_hda_codec_read(codec, 0x0f, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)))
val |= AC_PINCTL_IN_EN;
val |= AC_PINCTL_VREF_50;
snd_hda_set_pin_ctl(codec, 0x0f, val);
- spec->gen.keep_vref_in_automute = 1;
+ spec->keep_vref_in_automute = 1;
}
/* suppress the jack-detection */
static void alc_fixup_no_jack_detect(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
+ if (action == ALC_FIXUP_ACT_PRE_PROBE)
codec->no_jack_detect = 1;
}
-static const struct hda_fixup alc861_fixups[] = {
+static const struct alc_fixup alc861_fixups[] = {
[ALC861_FIXUP_FSC_AMILO_PI1505] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x0b, 0x0221101f }, /* HP */
{ 0x0f, 0x90170310 }, /* speaker */
{ }
}
},
[ALC861_FIXUP_AMP_VREF_0F] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc861_fixup_asus_amp_vref_0f,
},
[ALC861_FIXUP_NO_JACK_DETECT] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc_fixup_no_jack_detect,
},
[ALC861_FIXUP_ASUS_A6RP] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc861_fixup_asus_amp_vref_0f,
.chained = true,
.chain_id = ALC861_FIXUP_NO_JACK_DETECT,
@@ -3266,15 +6572,15 @@ static int patch_alc861(struct hda_codec *codec)
spec = codec->spec;
- snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
/* automatic parse from the BIOS config */
err = alc861_parse_auto_config(codec);
if (err < 0)
goto error;
- if (!spec->gen.no_analog) {
+ if (!spec->no_analog) {
err = snd_hda_attach_beep_device(codec, 0x23);
if (err < 0)
goto error;
@@ -3286,7 +6592,7 @@ static int patch_alc861(struct hda_codec *codec)
spec->power_hook = alc_power_eapd;
#endif
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
return 0;
@@ -3316,17 +6622,17 @@ enum {
/* exclude VREF80 */
static void alc861vd_fixup_dallas(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ if (action == ALC_FIXUP_ACT_PRE_PROBE) {
snd_hda_override_pin_caps(codec, 0x18, 0x00000734);
snd_hda_override_pin_caps(codec, 0x19, 0x0000073c);
}
}
-static const struct hda_fixup alc861vd_fixups[] = {
+static const struct alc_fixup alc861vd_fixups[] = {
[ALC660VD_FIX_ASUS_GPIO1] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* reset GPIO1 */
{0x01, AC_VERB_SET_GPIO_MASK, 0x03},
@@ -3336,7 +6642,7 @@ static const struct hda_fixup alc861vd_fixups[] = {
}
},
[ALC861VD_FIX_DALLAS] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc861vd_fixup_dallas,
},
};
@@ -3361,15 +6667,15 @@ static int patch_alc861vd(struct hda_codec *codec)
spec = codec->spec;
- snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
/* automatic parse from the BIOS config */
err = alc861vd_parse_auto_config(codec);
if (err < 0)
goto error;
- if (!spec->gen.no_analog) {
+ if (!spec->no_analog) {
err = snd_hda_attach_beep_device(codec, 0x23);
if (err < 0)
goto error;
@@ -3380,7 +6686,7 @@ static int patch_alc861vd(struct hda_codec *codec)
spec->shutup = alc_eapd_shutup;
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
return 0;
@@ -3421,9 +6727,9 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
}
static void alc272_fixup_mario(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+ const struct alc_fixup *fix, int action)
{
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ if (action != ALC_FIXUP_ACT_PROBE)
return;
if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT,
(0x3b << AC_AMPCAP_OFFSET_SHIFT) |
@@ -3454,39 +6760,39 @@ enum {
ALC662_FIXUP_INV_DMIC,
};
-static const struct hda_fixup alc662_fixups[] = {
+static const struct alc_fixup alc662_fixups[] = {
[ALC662_FIXUP_ASPIRE] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x15, 0x99130112 }, /* subwoofer */
{ }
}
},
[ALC662_FIXUP_IDEAPAD] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x17, 0x99130112 }, /* subwoofer */
{ }
}
},
[ALC272_FIXUP_MARIO] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc272_fixup_mario,
},
[ALC662_FIXUP_CZC_P10T] = {
- .type = HDA_FIXUP_VERBS,
+ .type = ALC_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
{0x14, AC_VERB_SET_EAPD_BTLENABLE, 0},
{}
}
},
[ALC662_FIXUP_SKU_IGNORE] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc_fixup_sku_ignore,
},
[ALC662_FIXUP_HP_RP5800] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x0221201f }, /* HP out */
{ }
},
@@ -3494,8 +6800,8 @@ static const struct hda_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE1] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x18, 0x01a19c20 }, /* mic */
{ 0x19, 0x99a3092f }, /* int-mic */
@@ -3506,8 +6812,8 @@ static const struct hda_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE2] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x18, 0x01a19820 }, /* mic */
{ 0x19, 0x99a3092f }, /* int-mic */
@@ -3518,8 +6824,8 @@ static const struct hda_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE3] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x15, 0x0121441f }, /* HP */
{ 0x18, 0x01a19840 }, /* mic */
@@ -3531,8 +6837,8 @@ static const struct hda_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE4] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x16, 0x99130111 }, /* speaker */
{ 0x18, 0x01a19840 }, /* mic */
@@ -3544,8 +6850,8 @@ static const struct hda_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE5] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x15, 0x0121441f }, /* HP */
{ 0x16, 0x99130111 }, /* speaker */
@@ -3557,8 +6863,8 @@ static const struct hda_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE6] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x15, 0x01211420 }, /* HP2 */
{ 0x18, 0x01a19840 }, /* mic */
@@ -3570,8 +6876,8 @@ static const struct hda_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE7] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x17, 0x99130111 }, /* speaker */
{ 0x18, 0x01a19840 }, /* mic */
@@ -3584,8 +6890,8 @@ static const struct hda_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_ASUS_MODE8] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x14, 0x99130110 }, /* speaker */
{ 0x12, 0x99a30970 }, /* int-mic */
{ 0x15, 0x01214020 }, /* HP */
@@ -3598,18 +6904,18 @@ static const struct hda_fixup alc662_fixups[] = {
.chain_id = ALC662_FIXUP_SKU_IGNORE
},
[ALC662_FIXUP_NO_JACK_DETECT] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc_fixup_no_jack_detect,
},
[ALC662_FIXUP_ZOTAC_Z68] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
{ 0x1b, 0x02214020 }, /* Front HP */
{ }
}
},
[ALC662_FIXUP_INV_DMIC] = {
- .type = HDA_FIXUP_FUNC,
+ .type = ALC_FIXUP_FUNC,
.v.func = alc_fixup_inv_dmic_0x12,
},
};
@@ -3689,7 +6995,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
{}
};
-static const struct hda_model_fixup alc662_fixup_models[] = {
+static const struct alc_model_fixup alc662_fixup_models[] = {
{.id = ALC272_FIXUP_MARIO, .name = "mario"},
{.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"},
{.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"},
@@ -3750,9 +7056,9 @@ static int patch_alc662(struct hda_codec *codec)
spec->init_hook = alc662_fill_coef;
alc662_fill_coef(codec);
- snd_hda_pick_fixup(codec, alc662_fixup_models,
+ alc_pick_fixup(codec, alc662_fixup_models,
alc662_fixup_tbl, alc662_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
alc_auto_parse_customize_define(codec);
@@ -3768,7 +7074,7 @@ static int patch_alc662(struct hda_codec *codec)
if (err < 0)
goto error;
- if (!spec->gen.no_analog && has_cdefine_beep(codec)) {
+ if (!spec->no_analog && has_cdefine_beep(codec)) {
err = snd_hda_attach_beep_device(codec, 0x1);
if (err < 0)
goto error;
@@ -3790,7 +7096,7 @@ static int patch_alc662(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops;
spec->shutup = alc_eapd_shutup;
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
return 0;
diff --git a/trunk/sound/pci/hda/patch_sigmatel.c b/trunk/sound/pci/hda/patch_sigmatel.c
index 83d5335ac348..a86547ca17c8 100644
--- a/trunk/sound/pci/hda/patch_sigmatel.c
+++ b/trunk/sound/pci/hda/patch_sigmatel.c
@@ -31,6 +31,7 @@
#include
#include
#include
+#include
#include
#include
#include "hda_codec.h"
@@ -38,14 +39,18 @@
#include "hda_auto_parser.h"
#include "hda_beep.h"
#include "hda_jack.h"
-#include "hda_generic.h"
enum {
- STAC_VREF_EVENT = 8,
+ STAC_VREF_EVENT = 1,
+ STAC_INSERT_EVENT,
STAC_PWR_EVENT,
+ STAC_HP_EVENT,
+ STAC_LO_EVENT,
+ STAC_MIC_EVENT,
};
enum {
+ STAC_AUTO,
STAC_REF,
STAC_9200_OQO,
STAC_9200_DELL_D21,
@@ -61,11 +66,11 @@ enum {
STAC_9200_M4,
STAC_9200_M4_2,
STAC_9200_PANASONIC,
- STAC_9200_EAPD_INIT,
STAC_9200_MODELS
};
enum {
+ STAC_9205_AUTO,
STAC_9205_REF,
STAC_9205_DELL_M42,
STAC_9205_DELL_M43,
@@ -75,6 +80,7 @@ enum {
};
enum {
+ STAC_92HD73XX_AUTO,
STAC_92HD73XX_NO_JD, /* no jack-detection */
STAC_92HD73XX_REF,
STAC_92HD73XX_INTEL,
@@ -87,6 +93,7 @@ enum {
};
enum {
+ STAC_92HD83XXX_AUTO,
STAC_92HD83XXX_REF,
STAC_92HD83XXX_PWR_REF,
STAC_DELL_S14,
@@ -98,12 +105,11 @@ enum {
STAC_92HD83XXX_HP_INV_LED,
STAC_92HD83XXX_HP_MIC_LED,
STAC_92HD83XXX_HEADSET_JACK,
- STAC_92HD83XXX_HP,
- STAC_HP_ENVY_BASS,
STAC_92HD83XXX_MODELS
};
enum {
+ STAC_92HD71BXX_AUTO,
STAC_92HD71BXX_REF,
STAC_DELL_M4_1,
STAC_DELL_M4_2,
@@ -112,13 +118,12 @@ enum {
STAC_HP_DV4,
STAC_HP_DV5,
STAC_HP_HDX,
- STAC_92HD71BXX_HP,
- STAC_92HD71BXX_NO_DMIC,
- STAC_92HD71BXX_NO_SMUX,
+ STAC_HP_DV4_1222NR,
STAC_92HD71BXX_MODELS
};
enum {
+ STAC_925x_AUTO,
STAC_925x_REF,
STAC_M1,
STAC_M1_2,
@@ -131,6 +136,7 @@ enum {
};
enum {
+ STAC_922X_AUTO,
STAC_D945_REF,
STAC_D945GTP3,
STAC_D945GTP5,
@@ -139,45 +145,67 @@ enum {
STAC_INTEL_MAC_V3,
STAC_INTEL_MAC_V4,
STAC_INTEL_MAC_V5,
- STAC_INTEL_MAC_AUTO,
+ STAC_INTEL_MAC_AUTO, /* This model is selected if no module parameter
+ * is given, one of the above models will be
+ * chosen according to the subsystem id. */
+ /* for backward compatibility */
+ STAC_MACMINI,
+ STAC_MACBOOK,
+ STAC_MACBOOK_PRO_V1,
+ STAC_MACBOOK_PRO_V2,
+ STAC_IMAC_INTEL,
+ STAC_IMAC_INTEL_20,
STAC_ECS_202,
STAC_922X_DELL_D81,
STAC_922X_DELL_D82,
STAC_922X_DELL_M81,
STAC_922X_DELL_M82,
- STAC_922X_INTEL_MAC_GPIO,
STAC_922X_MODELS
};
enum {
+ STAC_927X_AUTO,
STAC_D965_REF_NO_JD, /* no jack-detection */
STAC_D965_REF,
STAC_D965_3ST,
STAC_D965_5ST,
STAC_D965_5ST_NO_FP,
- STAC_D965_VERBS,
STAC_DELL_3ST,
STAC_DELL_BIOS,
- STAC_DELL_BIOS_SPDIF,
- STAC_927X_DELL_DMIC,
STAC_927X_VOLKNOB,
STAC_927X_MODELS
};
enum {
+ STAC_9872_AUTO,
STAC_9872_VAIO,
STAC_9872_MODELS
};
+struct sigmatel_mic_route {
+ hda_nid_t pin;
+ signed char mux_idx;
+ signed char dmux_idx;
+};
+
+#define MAX_PINS_NUM 16
+#define MAX_ADCS_NUM 4
+#define MAX_DMICS_NUM 4
+
struct sigmatel_spec {
- struct hda_gen_spec gen;
+ struct snd_kcontrol_new *mixers[4];
+ unsigned int num_mixers;
+ int board_config;
unsigned int eapd_switch: 1;
+ unsigned int surr_switch: 1;
+ unsigned int alt_switch: 1;
+ unsigned int hp_detect: 1;
+ unsigned int spdif_mute: 1;
+ unsigned int check_volume_offset:1;
+ unsigned int auto_mic:1;
unsigned int linear_tone_beep:1;
unsigned int headset_jack:1; /* 4-pin headset jack (hp + mono mic) */
- unsigned int volknob_init:1; /* special volume-knob initialization */
- unsigned int powerdown_adcs:1;
- unsigned int have_spdif_mux:1;
/* gpio lines */
unsigned int eapd_mask;
@@ -189,7 +217,6 @@ struct sigmatel_spec {
unsigned int gpio_led_polarity;
unsigned int vref_mute_led_nid; /* pin NID for mute-LED vref control */
unsigned int vref_led;
- int default_polarity;
unsigned int mic_mute_led_gpio; /* capture mute LED GPIO */
bool mic_mute_led_on; /* current mic mute state */
@@ -199,7 +226,6 @@ struct sigmatel_spec {
/* analog loopback */
const struct snd_kcontrol_new *aloopback_ctl;
- unsigned int aloopback;
unsigned char aloopback_mask;
unsigned char aloopback_shift;
@@ -207,141 +233,449 @@ struct sigmatel_spec {
unsigned int power_map_bits;
unsigned int num_pwrs;
const hda_nid_t *pwr_nids;
- unsigned int active_adcs;
+ const hda_nid_t *dac_list;
+
+ /* playback */
+ struct hda_input_mux *mono_mux;
+ unsigned int cur_mmux;
+ struct hda_multi_out multiout;
+ hda_nid_t dac_nids[5];
+ hda_nid_t hp_dacs[5];
+ hda_nid_t speaker_dacs[5];
+
+ int volume_offset;
+
+ /* capture */
+ const hda_nid_t *adc_nids;
+ unsigned int num_adcs;
+ const hda_nid_t *mux_nids;
+ unsigned int num_muxes;
+ const hda_nid_t *dmic_nids;
+ unsigned int num_dmics;
+ const hda_nid_t *dmux_nids;
+ unsigned int num_dmuxes;
+ const hda_nid_t *smux_nids;
+ unsigned int num_smuxes;
+ unsigned int num_analog_muxes;
+
+ const unsigned long *capvols; /* amp-volume attr: HDA_COMPOSE_AMP_VAL() */
+ const unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */
+ unsigned int num_caps; /* number of capture volume/switch elements */
+
+ struct sigmatel_mic_route ext_mic;
+ struct sigmatel_mic_route int_mic;
+ struct sigmatel_mic_route dock_mic;
+
+ const char * const *spdif_labels;
- /* beep widgets */
+ hda_nid_t dig_in_nid;
+ hda_nid_t mono_nid;
hda_nid_t anabeep_nid;
hda_nid_t digbeep_nid;
- /* SPDIF-out mux */
- const char * const *spdif_labels;
- struct hda_input_mux spdif_mux;
+ /* pin widgets */
+ const hda_nid_t *pin_nids;
+ unsigned int num_pins;
+
+ /* codec specific stuff */
+ const struct hda_verb *init;
+ const struct snd_kcontrol_new *mixer;
+
+ /* capture source */
+ struct hda_input_mux *dinput_mux;
+ unsigned int cur_dmux[2];
+ struct hda_input_mux *input_mux;
+ unsigned int cur_mux[3];
+ struct hda_input_mux *sinput_mux;
unsigned int cur_smux[2];
+ unsigned int cur_amux;
+ hda_nid_t *amp_nids;
+ unsigned int powerdown_adcs;
+
+ /* i/o switches */
+ unsigned int io_switch[2];
+ unsigned int clfe_swap;
+ hda_nid_t line_switch; /* shared line-in for input and output */
+ hda_nid_t mic_switch; /* shared mic-in for input and output */
+ hda_nid_t hp_switch; /* NID of HP as line-out */
+ unsigned int aloopback;
+
+ struct hda_pcm pcm_rec[2]; /* PCM information */
+
+ /* dynamic controls and input_mux */
+ struct auto_pin_cfg autocfg;
+ struct snd_array kctls;
+ struct hda_input_mux private_dimux;
+ struct hda_input_mux private_imux;
+ struct hda_input_mux private_smux;
+ struct hda_input_mux private_mono_mux;
+
+ /* auto spec */
+ unsigned auto_pin_cnt;
+ hda_nid_t auto_pin_nids[MAX_PINS_NUM];
+ unsigned auto_adc_cnt;
+ hda_nid_t auto_adc_nids[MAX_ADCS_NUM];
+ hda_nid_t auto_mux_nids[MAX_ADCS_NUM];
+ hda_nid_t auto_dmux_nids[MAX_ADCS_NUM];
+ unsigned long auto_capvols[MAX_ADCS_NUM];
+ unsigned auto_dmic_cnt;
+ hda_nid_t auto_dmic_nids[MAX_DMICS_NUM];
+
+ struct hda_vmaster_mute_hook vmaster_mute;
};
#define AC_VERB_IDT_SET_POWER_MAP 0x7ec
#define AC_VERB_IDT_GET_POWER_MAP 0xfec
+static const hda_nid_t stac9200_adc_nids[1] = {
+ 0x03,
+};
+
+static const hda_nid_t stac9200_mux_nids[1] = {
+ 0x0c,
+};
+
+static const hda_nid_t stac9200_dac_nids[1] = {
+ 0x02,
+};
+
static const hda_nid_t stac92hd73xx_pwr_nids[8] = {
0x0a, 0x0b, 0x0c, 0xd, 0x0e,
0x0f, 0x10, 0x11
};
+static const hda_nid_t stac92hd73xx_slave_dig_outs[2] = {
+ 0x26, 0,
+};
+
+static const hda_nid_t stac92hd73xx_adc_nids[2] = {
+ 0x1a, 0x1b
+};
+
+#define STAC92HD73XX_NUM_DMICS 2
+static const hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
+ 0x13, 0x14, 0
+};
+
+#define STAC92HD73_DAC_COUNT 5
+
+static const hda_nid_t stac92hd73xx_mux_nids[2] = {
+ 0x20, 0x21,
+};
+
+static const hda_nid_t stac92hd73xx_dmux_nids[2] = {
+ 0x20, 0x21,
+};
+
+static const hda_nid_t stac92hd73xx_smux_nids[2] = {
+ 0x22, 0x23,
+};
+
+#define STAC92HD73XX_NUM_CAPS 2
+static const unsigned long stac92hd73xx_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x20, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
+};
+#define stac92hd73xx_capsws stac92hd73xx_capvols
+
+#define STAC92HD83_DAC_COUNT 3
+
static const hda_nid_t stac92hd83xxx_pwr_nids[7] = {
0x0a, 0x0b, 0x0c, 0xd, 0x0e,
0x0f, 0x10
};
+static const hda_nid_t stac92hd83xxx_slave_dig_outs[2] = {
+ 0x1e, 0,
+};
+
+static const hda_nid_t stac92hd83xxx_dmic_nids[] = {
+ 0x11, 0x20,
+};
+
static const hda_nid_t stac92hd71bxx_pwr_nids[3] = {
0x0a, 0x0d, 0x0f
};
+static const hda_nid_t stac92hd71bxx_adc_nids[2] = {
+ 0x12, 0x13,
+};
-/*
- * PCM hooks
- */
-static void stac_playback_pcm_hook(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream,
- int action)
-{
- struct sigmatel_spec *spec = codec->spec;
- if (action == HDA_GEN_PCM_ACT_OPEN && spec->stream_delay)
- msleep(spec->stream_delay);
-}
+static const hda_nid_t stac92hd71bxx_mux_nids[2] = {
+ 0x1a, 0x1b
+};
-static void stac_capture_pcm_hook(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream,
- int action)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i, idx = 0;
+static const hda_nid_t stac92hd71bxx_dmux_nids[2] = {
+ 0x1c, 0x1d,
+};
- if (!spec->powerdown_adcs)
- return;
+static const hda_nid_t stac92hd71bxx_smux_nids[2] = {
+ 0x24, 0x25,
+};
- for (i = 0; i < spec->gen.num_all_adcs; i++) {
- if (spec->gen.all_adcs[i] == hinfo->nid) {
- idx = i;
- break;
- }
- }
+#define STAC92HD71BXX_NUM_DMICS 2
+static const hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
+ 0x18, 0x19, 0
+};
- switch (action) {
- case HDA_GEN_PCM_ACT_OPEN:
- msleep(40);
- snd_hda_codec_write(codec, hinfo->nid, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
- spec->active_adcs |= (1 << idx);
- break;
- case HDA_GEN_PCM_ACT_CLOSE:
- snd_hda_codec_write(codec, hinfo->nid, 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
- spec->active_adcs &= ~(1 << idx);
- break;
- }
-}
+static const hda_nid_t stac92hd71bxx_dmic_5port_nids[STAC92HD71BXX_NUM_DMICS] = {
+ 0x18, 0
+};
-/*
- * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
- * funky external mute control using GPIO pins.
- */
+static const hda_nid_t stac92hd71bxx_slave_dig_outs[2] = {
+ 0x22, 0
+};
-static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
- unsigned int dir_mask, unsigned int data)
-{
- unsigned int gpiostate, gpiomask, gpiodir;
+#define STAC92HD71BXX_NUM_CAPS 2
+static const unsigned long stac92hd71bxx_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
+};
+#define stac92hd71bxx_capsws stac92hd71bxx_capvols
- snd_printdd("%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data);
+static const hda_nid_t stac925x_adc_nids[1] = {
+ 0x03,
+};
- gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0);
- gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
+static const hda_nid_t stac925x_mux_nids[1] = {
+ 0x0f,
+};
- gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_MASK, 0);
- gpiomask |= mask;
+static const hda_nid_t stac925x_dac_nids[1] = {
+ 0x02,
+};
- gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DIRECTION, 0);
- gpiodir |= dir_mask;
+#define STAC925X_NUM_DMICS 1
+static const hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = {
+ 0x15, 0
+};
- /* Configure GPIOx as CMOS */
- snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
+static const hda_nid_t stac925x_dmux_nids[1] = {
+ 0x14,
+};
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_MASK, gpiomask);
- snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
+static const unsigned long stac925x_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT),
+};
+static const unsigned long stac925x_capsws[] = {
+ HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+};
- msleep(1);
+static const hda_nid_t stac922x_adc_nids[2] = {
+ 0x06, 0x07,
+};
- snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
+static const hda_nid_t stac922x_mux_nids[2] = {
+ 0x12, 0x13,
+};
+
+#define STAC922X_NUM_CAPS 2
+static const unsigned long stac922x_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT),
+};
+#define stac922x_capsws stac922x_capvols
+
+static const hda_nid_t stac927x_slave_dig_outs[2] = {
+ 0x1f, 0,
+};
+
+static const hda_nid_t stac927x_adc_nids[3] = {
+ 0x07, 0x08, 0x09
+};
+
+static const hda_nid_t stac927x_mux_nids[3] = {
+ 0x15, 0x16, 0x17
+};
+
+static const hda_nid_t stac927x_smux_nids[1] = {
+ 0x21,
+};
+
+static const hda_nid_t stac927x_dac_nids[6] = {
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0
+};
+
+static const hda_nid_t stac927x_dmux_nids[1] = {
+ 0x1b,
+};
+
+#define STAC927X_NUM_DMICS 2
+static const hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = {
+ 0x13, 0x14, 0
+};
+
+#define STAC927X_NUM_CAPS 3
+static const unsigned long stac927x_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x19, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_INPUT),
+};
+static const unsigned long stac927x_capsws[] = {
+ HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
+};
+
+static const char * const stac927x_spdif_labels[5] = {
+ "Digital Playback", "ADAT", "Analog Mux 1",
+ "Analog Mux 2", "Analog Mux 3"
+};
+
+static const hda_nid_t stac9205_adc_nids[2] = {
+ 0x12, 0x13
+};
+
+static const hda_nid_t stac9205_mux_nids[2] = {
+ 0x19, 0x1a
+};
+
+static const hda_nid_t stac9205_dmux_nids[1] = {
+ 0x1d,
+};
+
+static const hda_nid_t stac9205_smux_nids[1] = {
+ 0x21,
+};
+
+#define STAC9205_NUM_DMICS 2
+static const hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
+ 0x17, 0x18, 0
+};
+
+#define STAC9205_NUM_CAPS 2
+static const unsigned long stac9205_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_INPUT),
+ HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_INPUT),
+};
+static const unsigned long stac9205_capsws[] = {
+ HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
+ HDA_COMPOSE_AMP_VAL(0x1e, 3, 0, HDA_OUTPUT),
+};
+
+static const hda_nid_t stac9200_pin_nids[8] = {
+ 0x08, 0x09, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12,
+};
+
+static const hda_nid_t stac925x_pin_nids[8] = {
+ 0x07, 0x08, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x10, 0x11,
+};
+
+static const hda_nid_t stac922x_pin_nids[10] = {
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x15, 0x1b,
+};
+
+static const hda_nid_t stac92hd73xx_pin_nids[13] = {
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13,
+ 0x14, 0x22, 0x23
+};
+
+#define STAC92HD71BXX_NUM_PINS 13
+static const hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = {
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x00,
+ 0x00, 0x14, 0x18, 0x19, 0x1e,
+ 0x1f, 0x20, 0x27
+};
+static const hda_nid_t stac92hd71bxx_pin_nids_6port[STAC92HD71BXX_NUM_PINS] = {
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x14, 0x18, 0x19, 0x1e,
+ 0x1f, 0x20, 0x27
+};
+
+static const hda_nid_t stac927x_pin_nids[14] = {
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13,
+ 0x14, 0x21, 0x22, 0x23,
+};
+
+static const hda_nid_t stac9205_pin_nids[12] = {
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x14, 0x16, 0x17, 0x18,
+ 0x21, 0x22,
+};
+
+static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(spec->dinput_mux, uinfo);
}
-/* hook for controlling mic-mute LED GPIO */
-static void stac_capture_led_hook(struct hda_codec *codec,
- struct snd_ctl_elem_value *ucontrol)
+static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- bool mute;
+ unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- if (!ucontrol)
- return;
+ ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx];
+ return 0;
+}
- mute = !(ucontrol->value.integer.value[0] ||
- ucontrol->value.integer.value[1]);
- if (spec->mic_mute_led_on != mute) {
- spec->mic_mute_led_on = mute;
- if (mute)
- spec->gpio_data |= spec->mic_mute_led_gpio;
+static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol,
+ spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]);
+}
+
+static int stac92xx_smux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(spec->sinput_mux, uinfo);
+}
+
+static int stac92xx_smux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx];
+ return 0;
+}
+
+static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_input_mux *smux = &spec->private_smux;
+ unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ int err, val;
+ hda_nid_t nid;
+
+ err = snd_hda_input_mux_put(codec, spec->sinput_mux, ucontrol,
+ spec->smux_nids[smux_idx], &spec->cur_smux[smux_idx]);
+ if (err < 0)
+ return err;
+
+ if (spec->spdif_mute) {
+ if (smux_idx == 0)
+ nid = spec->multiout.dig_out_nid;
else
- spec->gpio_data &= ~spec->mic_mute_led_gpio;
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data);
+ nid = codec->slave_dig_outs[smux_idx - 1];
+ if (spec->cur_smux[smux_idx] == smux->num_items - 1)
+ val = HDA_AMP_MUTE;
+ else
+ val = 0;
+ /* un/mute SPDIF out */
+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, val);
}
+ return 0;
}
static int stac_vrefout_set(struct hda_codec *codec,
@@ -367,228 +701,129 @@ static int stac_vrefout_set(struct hda_codec *codec,
return 1;
}
-/* update mute-LED accoring to the master switch */
-static void stac_update_led_status(struct hda_codec *codec, int enabled)
+static unsigned int stac92xx_vref_set(struct hda_codec *codec,
+ hda_nid_t nid, unsigned int new_vref)
{
- struct sigmatel_spec *spec = codec->spec;
- int muted = !enabled;
+ int error;
+ unsigned int pincfg;
+ pincfg = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- if (!spec->gpio_led)
- return;
+ pincfg &= 0xff;
+ pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+ pincfg |= new_vref;
- /* LED state is inverted on these systems */
- if (spec->gpio_led_polarity)
- muted = !muted;
+ if (new_vref == AC_PINCTL_VREF_HIZ)
+ pincfg |= AC_PINCTL_OUT_EN;
+ else
+ pincfg |= AC_PINCTL_IN_EN;
- if (!spec->vref_mute_led_nid) {
- if (muted)
- spec->gpio_data |= spec->gpio_led;
- else
- spec->gpio_data &= ~spec->gpio_led;
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data);
- } else {
- spec->vref_led = muted ? AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD;
- stac_vrefout_set(codec, spec->vref_mute_led_nid,
- spec->vref_led);
- }
+ error = snd_hda_set_pin_ctl_cache(codec, nid, pincfg);
+ if (error < 0)
+ return error;
+ else
+ return 1;
}
-/* vmaster hook to update mute LED */
-static void stac_vmaster_hook(void *private_data, int val)
+static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid)
{
- stac_update_led_status(private_data, val);
+ unsigned int vref;
+ vref = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ vref &= AC_PINCTL_VREFEN;
+ return vref;
}
-/* automute hook to handle GPIO mute and EAPD updates */
-static void stac_update_outputs(struct hda_codec *codec)
+static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
+ return snd_hda_input_mux_info(spec->input_mux, uinfo);
+}
- if (spec->gpio_mute)
- spec->gen.master_mute =
- !(snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute);
+static int stac92xx_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- snd_hda_gen_update_outputs(codec);
+ ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+ return 0;
+}
- if (spec->eapd_mask && spec->eapd_switch) {
- unsigned int val = spec->gpio_data;
- if (spec->gen.speaker_muted)
- val &= ~spec->eapd_mask;
- else
- val |= spec->eapd_mask;
- if (spec->gpio_data != val)
- stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir,
- val);
+static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ const struct hda_input_mux *imux = spec->input_mux;
+ unsigned int idx, prev_idx, didx;
+
+ idx = ucontrol->value.enumerated.item[0];
+ if (idx >= imux->num_items)
+ idx = imux->num_items - 1;
+ prev_idx = spec->cur_mux[adc_idx];
+ if (prev_idx == idx)
+ return 0;
+ if (idx < spec->num_analog_muxes) {
+ snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ imux->items[idx].index);
+ if (prev_idx >= spec->num_analog_muxes &&
+ spec->mux_nids[adc_idx] != spec->dmux_nids[adc_idx]) {
+ imux = spec->dinput_mux;
+ /* 0 = analog */
+ snd_hda_codec_write_cache(codec,
+ spec->dmux_nids[adc_idx], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ imux->items[0].index);
+ }
+ } else {
+ imux = spec->dinput_mux;
+ /* first dimux item is hardcoded to select analog imux,
+ * so lets skip it
+ */
+ didx = idx - spec->num_analog_muxes + 1;
+ snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ imux->items[didx].index);
}
+ spec->cur_mux[adc_idx] = idx;
+ return 1;
}
-static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
- bool enable, bool do_write)
+static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- unsigned int idx, val;
+ return snd_hda_input_mux_info(spec->mono_mux, uinfo);
+}
- for (idx = 0; idx < spec->num_pwrs; idx++) {
- if (spec->pwr_nids[idx] == nid)
- break;
- }
- if (idx >= spec->num_pwrs)
- return;
+static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
- idx = 1 << idx;
-
- val = spec->power_map_bits;
- if (enable)
- val &= ~idx;
- else
- val |= idx;
-
- /* power down unused output ports */
- if (val != spec->power_map_bits) {
- spec->power_map_bits = val;
- if (do_write)
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_IDT_SET_POWER_MAP, val);
- }
-}
-
-/* update power bit per jack plug/unplug */
-static void jack_update_power(struct hda_codec *codec,
- struct hda_jack_tbl *jack)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i;
-
- if (!spec->num_pwrs)
- return;
-
- if (jack && jack->nid) {
- stac_toggle_power_map(codec, jack->nid,
- snd_hda_jack_detect(codec, jack->nid),
- true);
- return;
- }
-
- /* update all jacks */
- for (i = 0; i < spec->num_pwrs; i++) {
- hda_nid_t nid = spec->pwr_nids[i];
- jack = snd_hda_jack_tbl_get(codec, nid);
- if (!jack || !jack->action)
- continue;
- if (jack->action == STAC_PWR_EVENT ||
- jack->action <= HDA_GEN_LAST_EVENT)
- stac_toggle_power_map(codec, nid,
- snd_hda_jack_detect(codec, nid),
- false);
- }
-
- snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_IDT_SET_POWER_MAP,
- spec->power_map_bits);
-}
-
-static void stac_hp_automute(struct hda_codec *codec,
- struct hda_jack_tbl *jack)
-{
- snd_hda_gen_hp_automute(codec, jack);
- jack_update_power(codec, jack);
-}
-
-static void stac_line_automute(struct hda_codec *codec,
- struct hda_jack_tbl *jack)
-{
- snd_hda_gen_line_automute(codec, jack);
- jack_update_power(codec, jack);
-}
-
-static void stac_mic_autoswitch(struct hda_codec *codec,
- struct hda_jack_tbl *jack)
-{
- snd_hda_gen_mic_autoswitch(codec, jack);
- jack_update_power(codec, jack);
-}
-
-static void stac_vref_event(struct hda_codec *codec, struct hda_jack_tbl *event)
-{
- unsigned int data;
-
- data = snd_hda_codec_read(codec, codec->afg, 0,
- AC_VERB_GET_GPIO_DATA, 0);
- /* toggle VREF state based on GPIOx status */
- snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
- !!(data & (1 << event->private_data)));
-}
-
-/* initialize the power map and enable the power event to jacks that
- * haven't been assigned to automute
- */
-static void stac_init_power_map(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_pwrs; i++) {
- hda_nid_t nid = spec->pwr_nids[i];
- unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
- def_conf = get_defcfg_connect(def_conf);
- if (snd_hda_jack_tbl_get(codec, nid))
- continue;
- if (def_conf == AC_JACK_PORT_COMPLEX &&
- !(spec->vref_mute_led_nid == nid ||
- is_jack_detectable(codec, nid))) {
- snd_hda_jack_detect_enable_callback(codec, nid,
- STAC_PWR_EVENT,
- jack_update_power);
- } else {
- if (def_conf == AC_JACK_PORT_NONE)
- stac_toggle_power_map(codec, nid, false, false);
- else
- stac_toggle_power_map(codec, nid, true, false);
- }
- }
-}
-
-/*
- */
-
-static inline bool get_int_hint(struct hda_codec *codec, const char *key,
- int *valp)
-{
- return !snd_hda_get_int_hint(codec, key, valp);
+ ucontrol->value.enumerated.item[0] = spec->cur_mmux;
+ return 0;
}
-/* override some hints from the hwdep entry */
-static void stac_store_hints(struct hda_codec *codec)
+static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- int val;
- if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) {
- spec->eapd_mask = spec->gpio_dir = spec->gpio_data =
- spec->gpio_mask;
- }
- if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir))
- spec->gpio_mask &= spec->gpio_mask;
- if (get_int_hint(codec, "gpio_data", &spec->gpio_data))
- spec->gpio_dir &= spec->gpio_mask;
- if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask))
- spec->eapd_mask &= spec->gpio_mask;
- if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute))
- spec->gpio_mute &= spec->gpio_mask;
- val = snd_hda_get_bool_hint(codec, "eapd_switch");
- if (val >= 0)
- spec->eapd_switch = val;
+ return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol,
+ spec->mono_nid, &spec->cur_mmux);
}
-/*
- * loopback controls
- */
-
-#define stac_aloopback_info snd_ctl_boolean_mono_info
+#define stac92xx_aloopback_info snd_ctl_boolean_mono_info
-static int stac_aloopback_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
@@ -599,8 +834,8 @@ static int stac_aloopback_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int stac_aloopback_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
@@ -639,815 +874,498 @@ static int stac_aloopback_put(struct snd_kcontrol *kcontrol,
return 1;
}
+static const struct hda_verb stac9200_core_init[] = {
+ /* set dac0mux for dac converter */
+ { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {}
+};
+
+static const struct hda_verb stac9200_eapd_init[] = {
+ /* set dac0mux for dac converter */
+ {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+ {}
+};
+
+static const struct hda_verb dell_eq_core_init[] = {
+ /* set master volume to max value without distortion
+ * and direct control */
+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
+ {}
+};
+
+static const struct hda_verb stac92hd73xx_core_init[] = {
+ /* set master volume and direct control */
+ { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ {}
+};
+
+static const struct hda_verb stac92hd83xxx_core_init[] = {
+ /* power state controls amps */
+ { 0x01, AC_VERB_SET_EAPD, 1 << 2},
+ {}
+};
+
+static const struct hda_verb stac92hd83xxx_hp_zephyr_init[] = {
+ { 0x22, 0x785, 0x43 },
+ { 0x22, 0x782, 0xe0 },
+ { 0x22, 0x795, 0x00 },
+ {}
+};
+
+static const struct hda_verb stac92hd71bxx_core_init[] = {
+ /* set master volume and direct control */
+ { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ {}
+};
+
+static const struct hda_verb stac92hd71bxx_unmute_core_init[] = {
+ /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
+ { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {}
+};
+
+static const struct hda_verb stac925x_core_init[] = {
+ /* set dac0mux for dac converter */
+ { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* mute the master volume */
+ { 0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+ {}
+};
+
+static const struct hda_verb stac922x_core_init[] = {
+ /* set master volume and direct control */
+ { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ {}
+};
+
+static const struct hda_verb d965_core_init[] = {
+ /* set master volume and direct control */
+ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* unmute node 0x1b */
+ { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* select node 0x03 as DAC */
+ { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {}
+};
+
+static const struct hda_verb dell_3st_core_init[] = {
+ /* don't set delta bit */
+ {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
+ /* unmute node 0x1b */
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ /* select node 0x03 as DAC */
+ {0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {}
+};
+
+static const struct hda_verb stac927x_core_init[] = {
+ /* set master volume and direct control */
+ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* enable analog pc beep path */
+ { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
+ {}
+};
+
+static const struct hda_verb stac927x_volknob_core_init[] = {
+ /* don't set delta bit */
+ {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
+ /* enable analog pc beep path */
+ {0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
+ {}
+};
+
+static const struct hda_verb stac9205_core_init[] = {
+ /* set master volume and direct control */
+ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
+ /* enable analog pc beep path */
+ { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
+ {}
+};
+
+#define STAC_MONO_MUX \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = "Mono Mux", \
+ .count = 1, \
+ .info = stac92xx_mono_mux_enum_info, \
+ .get = stac92xx_mono_mux_enum_get, \
+ .put = stac92xx_mono_mux_enum_put, \
+ }
+
#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = "Analog Loopback", \
.count = cnt, \
- .info = stac_aloopback_info, \
- .get = stac_aloopback_get, \
- .put = stac_aloopback_put, \
+ .info = stac92xx_aloopback_info, \
+ .get = stac92xx_aloopback_get, \
+ .put = stac92xx_aloopback_put, \
.private_value = verb_read | (verb_write << 16), \
}
-/*
- * Mute LED handling on HP laptops
- */
+#define DC_BIAS(xname, idx, nid) \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = idx, \
+ .info = stac92xx_dc_bias_info, \
+ .get = stac92xx_dc_bias_get, \
+ .put = stac92xx_dc_bias_put, \
+ .private_value = nid, \
+ }
-/* check whether it's a HP laptop with a docking port */
-static bool hp_bnb2011_with_dock(struct hda_codec *codec)
-{
- if (codec->vendor_id != 0x111d7605 &&
- codec->vendor_id != 0x111d76d1)
- return false;
+static const struct snd_kcontrol_new stac9200_mixer[] = {
+ HDA_CODEC_VOLUME_MIN_MUTE("PCM Playback Volume", 0xb, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("PCM Playback Switch", 0xb, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
+ { } /* end */
+};
- switch (codec->subsystem_id) {
- case 0x103c1618:
- case 0x103c1619:
- case 0x103c161a:
- case 0x103c161b:
- case 0x103c161c:
- case 0x103c161d:
- case 0x103c161e:
- case 0x103c161f:
+static const struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
+ {}
+};
- case 0x103c162a:
- case 0x103c162b:
+static const struct snd_kcontrol_new stac92hd73xx_8ch_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
+ {}
+};
- case 0x103c1630:
- case 0x103c1631:
+static const struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
+ {}
+};
- case 0x103c1633:
- case 0x103c1634:
- case 0x103c1635:
- case 0x103c3587:
- case 0x103c3588:
- case 0x103c3589:
- case 0x103c358a:
+static const struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2)
+};
- case 0x103c3667:
- case 0x103c3668:
- case 0x103c3669:
+static const struct snd_kcontrol_new stac925x_mixer[] = {
+ HDA_CODEC_VOLUME_MIN_MUTE("PCM Playback Volume", 0xe, 0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("PCM Playback Switch", 0x0e, 0, HDA_OUTPUT),
+ { } /* end */
+};
- return true;
- }
- return false;
+static const struct snd_kcontrol_new stac9205_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
+ {}
+};
+
+static const struct snd_kcontrol_new stac927x_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
+ {}
+};
+
+static struct snd_kcontrol_new stac_dmux_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Digital Input Source",
+ /* count set later */
+ .info = stac92xx_dmux_enum_info,
+ .get = stac92xx_dmux_enum_get,
+ .put = stac92xx_dmux_enum_put,
+};
+
+static struct snd_kcontrol_new stac_smux_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "IEC958 Playback Source",
+ /* count set later */
+ .info = stac92xx_smux_enum_info,
+ .get = stac92xx_smux_enum_get,
+ .put = stac92xx_smux_enum_put,
+};
+
+static const char * const slave_pfxs[] = {
+ "Front", "Surround", "Center", "LFE", "Side",
+ "Headphone", "Speaker", "Bass Speaker", "IEC958", "PCM",
+ NULL
+};
+
+static void stac92xx_update_led_status(struct hda_codec *codec, int enabled);
+
+static void stac92xx_vmaster_hook(void *private_data, int val)
+{
+ stac92xx_update_led_status(private_data, val);
}
-static bool hp_blike_system(u32 subsystem_id)
-{
- switch (subsystem_id) {
- case 0x103c1520:
- case 0x103c1521:
- case 0x103c1523:
- case 0x103c1524:
- case 0x103c1525:
- case 0x103c1722:
- case 0x103c1723:
- case 0x103c1724:
- case 0x103c1725:
- case 0x103c1726:
- case 0x103c1727:
- case 0x103c1728:
- case 0x103c1729:
- case 0x103c172a:
- case 0x103c172b:
- case 0x103c307e:
- case 0x103c307f:
- case 0x103c3080:
- case 0x103c3081:
- case 0x103c7007:
- case 0x103c7008:
- return true;
- }
- return false;
-}
-
-static void set_hp_led_gpio(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int gpio;
-
- if (spec->gpio_led)
- return;
-
- gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
- gpio &= AC_GPIO_IO_COUNT;
- if (gpio > 3)
- spec->gpio_led = 0x08; /* GPIO 3 */
- else
- spec->gpio_led = 0x01; /* GPIO 0 */
-}
-
-/*
- * This method searches for the mute LED GPIO configuration
- * provided as OEM string in SMBIOS. The format of that string
- * is HP_Mute_LED_P_G or HP_Mute_LED_P
- * where P can be 0 or 1 and defines mute LED GPIO control state (low/high)
- * that corresponds to the NOT muted state of the master volume
- * and G is the index of the GPIO to use as the mute LED control (0..9)
- * If _G portion is missing it is assigned based on the codec ID
- *
- * So, HP B-series like systems may have HP_Mute_LED_0 (current models)
- * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings
- *
- *
- * The dv-series laptops don't seem to have the HP_Mute_LED* strings in
- * SMBIOS - at least the ones I have seen do not have them - which include
- * my own system (HP Pavilion dv6-1110ax) and my cousin's
- * HP Pavilion dv9500t CTO.
- * Need more information on whether it is true across the entire series.
- * -- kunal
- */
-static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
+static void stac92xx_free_kctls(struct hda_codec *codec);
+
+static int stac92xx_build_controls(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- const struct dmi_device *dev = NULL;
+ unsigned int vmaster_tlv[4];
+ int err;
+ int i;
- if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
- get_int_hint(codec, "gpio_led_polarity",
- &spec->gpio_led_polarity);
- return 1;
+ if (spec->mixer) {
+ err = snd_hda_add_new_ctls(codec, spec->mixer);
+ if (err < 0)
+ return err;
}
- while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
- if (sscanf(dev->name, "HP_Mute_LED_%d_%x",
- &spec->gpio_led_polarity,
- &spec->gpio_led) == 2) {
- unsigned int max_gpio;
- max_gpio = snd_hda_param_read(codec, codec->afg,
- AC_PAR_GPIO_CAP);
- max_gpio &= AC_GPIO_IO_COUNT;
- if (spec->gpio_led < max_gpio)
- spec->gpio_led = 1 << spec->gpio_led;
- else
- spec->vref_mute_led_nid = spec->gpio_led;
- return 1;
- }
- if (sscanf(dev->name, "HP_Mute_LED_%d",
- &spec->gpio_led_polarity) == 1) {
- set_hp_led_gpio(codec);
- return 1;
- }
- /* BIOS bug: unfilled OEM string */
- if (strstr(dev->name, "HP_Mute_LED_P_G")) {
- set_hp_led_gpio(codec);
- if (default_polarity >= 0)
- spec->gpio_led_polarity = default_polarity;
- else
- spec->gpio_led_polarity = 1;
- return 1;
+ for (i = 0; i < spec->num_mixers; i++) {
+ err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+ if (err < 0)
+ return err;
+ }
+ if (!spec->auto_mic && spec->num_dmuxes > 0 &&
+ snd_hda_get_bool_hint(codec, "separate_dmux") == 1) {
+ stac_dmux_mixer.count = spec->num_dmuxes;
+ err = snd_hda_ctl_add(codec, 0,
+ snd_ctl_new1(&stac_dmux_mixer, codec));
+ if (err < 0)
+ return err;
+ }
+ if (spec->num_smuxes > 0) {
+ int wcaps = get_wcaps(codec, spec->multiout.dig_out_nid);
+ struct hda_input_mux *smux = &spec->private_smux;
+ /* check for mute support on SPDIF out */
+ if (wcaps & AC_WCAP_OUT_AMP) {
+ snd_hda_add_imux_item(smux, "Off", 0, NULL);
+ spec->spdif_mute = 1;
}
+ stac_smux_mixer.count = spec->num_smuxes;
+ err = snd_hda_ctl_add(codec, 0,
+ snd_ctl_new1(&stac_smux_mixer, codec));
+ if (err < 0)
+ return err;
}
- /*
- * Fallback case - if we don't find the DMI strings,
- * we statically set the GPIO - if not a B-series system
- * and default polarity is provided
- */
- if (!hp_blike_system(codec->subsystem_id) &&
- (default_polarity == 0 || default_polarity == 1)) {
- set_hp_led_gpio(codec);
- spec->gpio_led_polarity = default_polarity;
- return 1;
+ if (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,
+ &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
+ }
+ if (spec->dig_in_nid && !(spec->gpio_dir & 0x01)) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+ if (err < 0)
+ return err;
}
- return 0;
-}
-/*
- * PC beep controls
- */
+ /* if we have no master control, let's create it */
+ snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
+ HDA_OUTPUT, vmaster_tlv);
+ /* correct volume offset */
+ vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
+ /* minimum value is actually mute */
+ vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
+ err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+ vmaster_tlv, slave_pfxs,
+ "Playback Volume");
+ if (err < 0)
+ return err;
-/* create PC beep volume controls */
-static int stac_auto_create_beep_ctls(struct hda_codec *codec,
- hda_nid_t nid)
-{
- struct sigmatel_spec *spec = codec->spec;
- u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
- struct snd_kcontrol_new *knew;
- static struct snd_kcontrol_new abeep_mute_ctl =
- HDA_CODEC_MUTE(NULL, 0, 0, 0);
- static struct snd_kcontrol_new dbeep_mute_ctl =
- HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0);
- static struct snd_kcontrol_new beep_vol_ctl =
- HDA_CODEC_VOLUME(NULL, 0, 0, 0);
+ err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, slave_pfxs,
+ "Playback Switch", true,
+ &spec->vmaster_mute.sw_kctl);
+ if (err < 0)
+ return err;
- /* check for mute support for the the amp */
- if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
- const struct snd_kcontrol_new *temp;
- if (spec->anabeep_nid == nid)
- temp = &abeep_mute_ctl;
- else
- temp = &dbeep_mute_ctl;
- knew = snd_hda_gen_add_kctl(&spec->gen,
- "Beep Playback Switch", temp);
- if (!knew)
- return -ENOMEM;
- knew->private_value =
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT);
+ if (spec->gpio_led) {
+ spec->vmaster_mute.hook = stac92xx_vmaster_hook;
+ err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true);
+ if (err < 0)
+ return err;
}
- /* check to see if there is volume support for the amp */
- if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
- knew = snd_hda_gen_add_kctl(&spec->gen,
- "Beep Playback Volume",
- &beep_vol_ctl);
- if (!knew)
- return -ENOMEM;
- knew->private_value =
- HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT);
+ if (spec->aloopback_ctl &&
+ snd_hda_get_bool_hint(codec, "loopback") == 1) {
+ err = snd_hda_add_new_ctls(codec, spec->aloopback_ctl);
+ if (err < 0)
+ return err;
}
- return 0;
-}
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
-#define stac_dig_beep_switch_info snd_ctl_boolean_mono_info
+ stac92xx_free_kctls(codec); /* no longer needed */
-static int stac_dig_beep_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- ucontrol->value.integer.value[0] = codec->beep->enabled;
- return 0;
-}
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
-static int stac_dig_beep_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
+ return 0;
}
-static const struct snd_kcontrol_new stac_dig_beep_ctrl = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Beep Playback Switch",
- .info = stac_dig_beep_switch_info,
- .get = stac_dig_beep_switch_get,
- .put = stac_dig_beep_switch_put,
+static const unsigned int ref9200_pin_configs[8] = {
+ 0x01c47010, 0x01447010, 0x0221401f, 0x01114010,
+ 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
};
-static int stac_beep_switch_ctl(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_dig_beep_ctrl))
- return -ENOMEM;
- return 0;
-}
-#endif
+static const unsigned int gateway9200_m4_pin_configs[8] = {
+ 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010,
+ 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3,
+};
+static const unsigned int gateway9200_m4_2_pin_configs[8] = {
+ 0x400000fe, 0x404500f4, 0x400100f0, 0x90110010,
+ 0x400100f1, 0x02a1902e, 0x500000f2, 0x500000f3,
+};
/*
- * SPDIF-out mux controls
- */
+ STAC 9200 pin configs for
+ 102801A8
+ 102801DE
+ 102801E8
+*/
+static const unsigned int dell9200_d21_pin_configs[8] = {
+ 0x400001f0, 0x400001f1, 0x02214030, 0x01014010,
+ 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
+};
-static int stac_smux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- return snd_hda_input_mux_info(&spec->spdif_mux, uinfo);
-}
+/*
+ STAC 9200 pin configs for
+ 102801C0
+ 102801C1
+*/
+static const unsigned int dell9200_d22_pin_configs[8] = {
+ 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010,
+ 0x01813020, 0x02a19021, 0x90100140, 0x400001f2,
+};
-static int stac_smux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+/*
+ STAC 9200 pin configs for
+ 102801C4 (Dell Dimension E310)
+ 102801C5
+ 102801C7
+ 102801D9
+ 102801DA
+ 102801E3
+*/
+static const unsigned int dell9200_d23_pin_configs[8] = {
+ 0x400001f0, 0x400001f1, 0x0221401f, 0x01014010,
+ 0x01813020, 0x01a19021, 0x90100140, 0x400001f2,
+};
- ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx];
- return 0;
-}
-static int stac_smux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+/*
+ STAC 9200-32 pin configs for
+ 102801B5 (Dell Inspiron 630m)
+ 102801D8 (Dell Inspiron 640m)
+*/
+static const unsigned int dell9200_m21_pin_configs[8] = {
+ 0x40c003fa, 0x03441340, 0x0321121f, 0x90170310,
+ 0x408003fb, 0x03a11020, 0x401003fc, 0x403003fd,
+};
- return snd_hda_input_mux_put(codec, &spec->spdif_mux, ucontrol,
- spec->gen.autocfg.dig_out_pins[smux_idx],
- &spec->cur_smux[smux_idx]);
-}
+/*
+ STAC 9200-32 pin configs for
+ 102801C2 (Dell Latitude D620)
+ 102801C8
+ 102801CC (Dell Latitude D820)
+ 102801D4
+ 102801D6
+*/
+static const unsigned int dell9200_m22_pin_configs[8] = {
+ 0x40c003fa, 0x0144131f, 0x0321121f, 0x90170310,
+ 0x90a70321, 0x03a11020, 0x401003fb, 0x40f000fc,
+};
-static struct snd_kcontrol_new stac_smux_mixer = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "IEC958 Playback Source",
- /* count set later */
- .info = stac_smux_enum_info,
- .get = stac_smux_enum_get,
- .put = stac_smux_enum_put,
+/*
+ STAC 9200-32 pin configs for
+ 102801CE (Dell XPS M1710)
+ 102801CF (Dell Precision M90)
+*/
+static const unsigned int dell9200_m23_pin_configs[8] = {
+ 0x40c003fa, 0x01441340, 0x0421421f, 0x90170310,
+ 0x408003fb, 0x04a1102e, 0x90170311, 0x403003fc,
};
-static const char * const stac_spdif_labels[] = {
- "Digital Playback", "Analog Mux 1", "Analog Mux 2", NULL
+/*
+ STAC 9200-32 pin configs for
+ 102801C9
+ 102801CA
+ 102801CB (Dell Latitude 120L)
+ 102801D3
+*/
+static const unsigned int dell9200_m24_pin_configs[8] = {
+ 0x40c003fa, 0x404003fb, 0x0321121f, 0x90170310,
+ 0x408003fc, 0x03a11020, 0x401003fd, 0x403003fe,
};
-static int stac_create_spdif_mux_ctls(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->gen.autocfg;
- const char * const *labels = spec->spdif_labels;
- struct snd_kcontrol_new *kctl;
- int i, num_cons;
+/*
+ STAC 9200-32 pin configs for
+ 102801BD (Dell Inspiron E1505n)
+ 102801EE
+ 102801EF
+*/
+static const unsigned int dell9200_m25_pin_configs[8] = {
+ 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310,
+ 0x408003fb, 0x04a11020, 0x401003fc, 0x403003fd,
+};
- if (cfg->dig_outs < 1)
- return 0;
+/*
+ STAC 9200-32 pin configs for
+ 102801F5 (Dell Inspiron 1501)
+ 102801F6
+*/
+static const unsigned int dell9200_m26_pin_configs[8] = {
+ 0x40c003fa, 0x404003fb, 0x0421121f, 0x90170310,
+ 0x408003fc, 0x04a11020, 0x401003fd, 0x403003fe,
+};
- num_cons = snd_hda_get_num_conns(codec, cfg->dig_out_pins[0]);
- if (num_cons <= 1)
- return 0;
+/*
+ STAC 9200-32
+ 102801CD (Dell Inspiron E1705/9400)
+*/
+static const unsigned int dell9200_m27_pin_configs[8] = {
+ 0x40c003fa, 0x01441340, 0x0421121f, 0x90170310,
+ 0x90170310, 0x04a11020, 0x90170310, 0x40f003fc,
+};
- if (!labels)
- labels = stac_spdif_labels;
- for (i = 0; i < num_cons; i++) {
- if (snd_BUG_ON(!labels[i]))
- return -EINVAL;
- snd_hda_add_imux_item(&spec->spdif_mux, labels[i], i, NULL);
- }
+static const unsigned int oqo9200_pin_configs[8] = {
+ 0x40c000f0, 0x404000f1, 0x0221121f, 0x02211210,
+ 0x90170111, 0x90a70120, 0x400000f2, 0x400000f3,
+};
- kctl = snd_hda_gen_add_kctl(&spec->gen, NULL, &stac_smux_mixer);
- if (!kctl)
- return -ENOMEM;
- kctl->count = cfg->dig_outs;
- return 0;
-}
+static const unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
+ [STAC_REF] = ref9200_pin_configs,
+ [STAC_9200_OQO] = oqo9200_pin_configs,
+ [STAC_9200_DELL_D21] = dell9200_d21_pin_configs,
+ [STAC_9200_DELL_D22] = dell9200_d22_pin_configs,
+ [STAC_9200_DELL_D23] = dell9200_d23_pin_configs,
+ [STAC_9200_DELL_M21] = dell9200_m21_pin_configs,
+ [STAC_9200_DELL_M22] = dell9200_m22_pin_configs,
+ [STAC_9200_DELL_M23] = dell9200_m23_pin_configs,
+ [STAC_9200_DELL_M24] = dell9200_m24_pin_configs,
+ [STAC_9200_DELL_M25] = dell9200_m25_pin_configs,
+ [STAC_9200_DELL_M26] = dell9200_m26_pin_configs,
+ [STAC_9200_DELL_M27] = dell9200_m27_pin_configs,
+ [STAC_9200_M4] = gateway9200_m4_pin_configs,
+ [STAC_9200_M4_2] = gateway9200_m4_2_pin_configs,
+ [STAC_9200_PANASONIC] = ref9200_pin_configs,
+};
-/*
- */
+static const char * const stac9200_models[STAC_9200_MODELS] = {
+ [STAC_AUTO] = "auto",
+ [STAC_REF] = "ref",
+ [STAC_9200_OQO] = "oqo",
+ [STAC_9200_DELL_D21] = "dell-d21",
+ [STAC_9200_DELL_D22] = "dell-d22",
+ [STAC_9200_DELL_D23] = "dell-d23",
+ [STAC_9200_DELL_M21] = "dell-m21",
+ [STAC_9200_DELL_M22] = "dell-m22",
+ [STAC_9200_DELL_M23] = "dell-m23",
+ [STAC_9200_DELL_M24] = "dell-m24",
+ [STAC_9200_DELL_M25] = "dell-m25",
+ [STAC_9200_DELL_M26] = "dell-m26",
+ [STAC_9200_DELL_M27] = "dell-m27",
+ [STAC_9200_M4] = "gateway-m4",
+ [STAC_9200_M4_2] = "gateway-m4-2",
+ [STAC_9200_PANASONIC] = "panasonic",
+};
-static const struct hda_verb stac9200_core_init[] = {
- /* set dac0mux for dac converter */
- { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {}
-};
-
-static const struct hda_verb stac9200_eapd_init[] = {
- /* set dac0mux for dac converter */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- {}
-};
-
-static const struct hda_verb dell_eq_core_init[] = {
- /* set master volume to max value without distortion
- * and direct control */
- { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
- {}
-};
-
-static const struct hda_verb stac92hd73xx_core_init[] = {
- /* set master volume and direct control */
- { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- {}
-};
-
-static const struct hda_verb stac92hd83xxx_core_init[] = {
- /* power state controls amps */
- { 0x01, AC_VERB_SET_EAPD, 1 << 2},
- {}
-};
-
-static const struct hda_verb stac92hd83xxx_hp_zephyr_init[] = {
- { 0x22, 0x785, 0x43 },
- { 0x22, 0x782, 0xe0 },
- { 0x22, 0x795, 0x00 },
- {}
-};
-
-static const struct hda_verb stac92hd71bxx_core_init[] = {
- /* set master volume and direct control */
- { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- {}
-};
-
-static const struct hda_verb stac92hd71bxx_unmute_core_init[] = {
- /* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
- { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {}
-};
-
-static const struct hda_verb stac925x_core_init[] = {
- /* set dac0mux for dac converter */
- { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00},
- /* mute the master volume */
- { 0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
- {}
-};
-
-static const struct hda_verb stac922x_core_init[] = {
- /* set master volume and direct control */
- { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- {}
-};
-
-static const struct hda_verb d965_core_init[] = {
- /* unmute node 0x1b */
- { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* select node 0x03 as DAC */
- { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
- {}
-};
-
-static const struct hda_verb dell_3st_core_init[] = {
- /* don't set delta bit */
- {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
- /* unmute node 0x1b */
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
- /* select node 0x03 as DAC */
- {0x0b, AC_VERB_SET_CONNECT_SEL, 0x01},
- {}
-};
-
-static const struct hda_verb stac927x_core_init[] = {
- /* set master volume and direct control */
- { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* enable analog pc beep path */
- { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
- {}
-};
-
-static const struct hda_verb stac927x_volknob_core_init[] = {
- /* don't set delta bit */
- {0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0x7f},
- /* enable analog pc beep path */
- {0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
- {}
-};
-
-static const struct hda_verb stac9205_core_init[] = {
- /* set master volume and direct control */
- { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
- /* enable analog pc beep path */
- { 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},
- {}
-};
-
-static const struct snd_kcontrol_new stac92hd73xx_6ch_loopback =
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3);
-
-static const struct snd_kcontrol_new stac92hd73xx_8ch_loopback =
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4);
-
-static const struct snd_kcontrol_new stac92hd73xx_10ch_loopback =
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5);
-
-static const struct snd_kcontrol_new stac92hd71bxx_loopback =
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2);
-
-static const struct snd_kcontrol_new stac9205_loopback =
- STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1);
-
-static const struct snd_kcontrol_new stac927x_loopback =
- STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1);
-
-static const struct hda_pintbl ref9200_pin_configs[] = {
- { 0x08, 0x01c47010 },
- { 0x09, 0x01447010 },
- { 0x0d, 0x0221401f },
- { 0x0e, 0x01114010 },
- { 0x0f, 0x02a19020 },
- { 0x10, 0x01a19021 },
- { 0x11, 0x90100140 },
- { 0x12, 0x01813122 },
- {}
-};
-
-static const struct hda_pintbl gateway9200_m4_pin_configs[] = {
- { 0x08, 0x400000fe },
- { 0x09, 0x404500f4 },
- { 0x0d, 0x400100f0 },
- { 0x0e, 0x90110010 },
- { 0x0f, 0x400100f1 },
- { 0x10, 0x02a1902e },
- { 0x11, 0x500000f2 },
- { 0x12, 0x500000f3 },
- {}
-};
-
-static const struct hda_pintbl gateway9200_m4_2_pin_configs[] = {
- { 0x08, 0x400000fe },
- { 0x09, 0x404500f4 },
- { 0x0d, 0x400100f0 },
- { 0x0e, 0x90110010 },
- { 0x0f, 0x400100f1 },
- { 0x10, 0x02a1902e },
- { 0x11, 0x500000f2 },
- { 0x12, 0x500000f3 },
- {}
-};
-
-/*
- STAC 9200 pin configs for
- 102801A8
- 102801DE
- 102801E8
-*/
-static const struct hda_pintbl dell9200_d21_pin_configs[] = {
- { 0x08, 0x400001f0 },
- { 0x09, 0x400001f1 },
- { 0x0d, 0x02214030 },
- { 0x0e, 0x01014010 },
- { 0x0f, 0x02a19020 },
- { 0x10, 0x01a19021 },
- { 0x11, 0x90100140 },
- { 0x12, 0x01813122 },
- {}
-};
-
-/*
- STAC 9200 pin configs for
- 102801C0
- 102801C1
-*/
-static const struct hda_pintbl dell9200_d22_pin_configs[] = {
- { 0x08, 0x400001f0 },
- { 0x09, 0x400001f1 },
- { 0x0d, 0x0221401f },
- { 0x0e, 0x01014010 },
- { 0x0f, 0x01813020 },
- { 0x10, 0x02a19021 },
- { 0x11, 0x90100140 },
- { 0x12, 0x400001f2 },
- {}
-};
-
-/*
- STAC 9200 pin configs for
- 102801C4 (Dell Dimension E310)
- 102801C5
- 102801C7
- 102801D9
- 102801DA
- 102801E3
-*/
-static const struct hda_pintbl dell9200_d23_pin_configs[] = {
- { 0x08, 0x400001f0 },
- { 0x09, 0x400001f1 },
- { 0x0d, 0x0221401f },
- { 0x0e, 0x01014010 },
- { 0x0f, 0x01813020 },
- { 0x10, 0x01a19021 },
- { 0x11, 0x90100140 },
- { 0x12, 0x400001f2 },
- {}
-};
-
-
-/*
- STAC 9200-32 pin configs for
- 102801B5 (Dell Inspiron 630m)
- 102801D8 (Dell Inspiron 640m)
-*/
-static const struct hda_pintbl dell9200_m21_pin_configs[] = {
- { 0x08, 0x40c003fa },
- { 0x09, 0x03441340 },
- { 0x0d, 0x0321121f },
- { 0x0e, 0x90170310 },
- { 0x0f, 0x408003fb },
- { 0x10, 0x03a11020 },
- { 0x11, 0x401003fc },
- { 0x12, 0x403003fd },
- {}
-};
-
-/*
- STAC 9200-32 pin configs for
- 102801C2 (Dell Latitude D620)
- 102801C8
- 102801CC (Dell Latitude D820)
- 102801D4
- 102801D6
-*/
-static const struct hda_pintbl dell9200_m22_pin_configs[] = {
- { 0x08, 0x40c003fa },
- { 0x09, 0x0144131f },
- { 0x0d, 0x0321121f },
- { 0x0e, 0x90170310 },
- { 0x0f, 0x90a70321 },
- { 0x10, 0x03a11020 },
- { 0x11, 0x401003fb },
- { 0x12, 0x40f000fc },
- {}
-};
-
-/*
- STAC 9200-32 pin configs for
- 102801CE (Dell XPS M1710)
- 102801CF (Dell Precision M90)
-*/
-static const struct hda_pintbl dell9200_m23_pin_configs[] = {
- { 0x08, 0x40c003fa },
- { 0x09, 0x01441340 },
- { 0x0d, 0x0421421f },
- { 0x0e, 0x90170310 },
- { 0x0f, 0x408003fb },
- { 0x10, 0x04a1102e },
- { 0x11, 0x90170311 },
- { 0x12, 0x403003fc },
- {}
-};
-
-/*
- STAC 9200-32 pin configs for
- 102801C9
- 102801CA
- 102801CB (Dell Latitude 120L)
- 102801D3
-*/
-static const struct hda_pintbl dell9200_m24_pin_configs[] = {
- { 0x08, 0x40c003fa },
- { 0x09, 0x404003fb },
- { 0x0d, 0x0321121f },
- { 0x0e, 0x90170310 },
- { 0x0f, 0x408003fc },
- { 0x10, 0x03a11020 },
- { 0x11, 0x401003fd },
- { 0x12, 0x403003fe },
- {}
-};
-
-/*
- STAC 9200-32 pin configs for
- 102801BD (Dell Inspiron E1505n)
- 102801EE
- 102801EF
-*/
-static const struct hda_pintbl dell9200_m25_pin_configs[] = {
- { 0x08, 0x40c003fa },
- { 0x09, 0x01441340 },
- { 0x0d, 0x0421121f },
- { 0x0e, 0x90170310 },
- { 0x0f, 0x408003fb },
- { 0x10, 0x04a11020 },
- { 0x11, 0x401003fc },
- { 0x12, 0x403003fd },
- {}
-};
-
-/*
- STAC 9200-32 pin configs for
- 102801F5 (Dell Inspiron 1501)
- 102801F6
-*/
-static const struct hda_pintbl dell9200_m26_pin_configs[] = {
- { 0x08, 0x40c003fa },
- { 0x09, 0x404003fb },
- { 0x0d, 0x0421121f },
- { 0x0e, 0x90170310 },
- { 0x0f, 0x408003fc },
- { 0x10, 0x04a11020 },
- { 0x11, 0x401003fd },
- { 0x12, 0x403003fe },
- {}
-};
-
-/*
- STAC 9200-32
- 102801CD (Dell Inspiron E1705/9400)
-*/
-static const struct hda_pintbl dell9200_m27_pin_configs[] = {
- { 0x08, 0x40c003fa },
- { 0x09, 0x01441340 },
- { 0x0d, 0x0421121f },
- { 0x0e, 0x90170310 },
- { 0x0f, 0x90170310 },
- { 0x10, 0x04a11020 },
- { 0x11, 0x90170310 },
- { 0x12, 0x40f003fc },
- {}
-};
-
-static const struct hda_pintbl oqo9200_pin_configs[] = {
- { 0x08, 0x40c000f0 },
- { 0x09, 0x404000f1 },
- { 0x0d, 0x0221121f },
- { 0x0e, 0x02211210 },
- { 0x0f, 0x90170111 },
- { 0x10, 0x90a70120 },
- { 0x11, 0x400000f2 },
- { 0x12, 0x400000f3 },
- {}
-};
-
-
-static void stac9200_fixup_panasonic(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gpio_mask = spec->gpio_dir = 0x09;
- spec->gpio_data = 0x00;
- /* CF-74 has no headphone detection, and the driver should *NOT*
- * do detection and HP/speaker toggle because the hardware does it.
- */
- spec->gen.suppress_auto_mute = 1;
- }
-}
-
-
-static const struct hda_fixup stac9200_fixups[] = {
- [STAC_REF] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = ref9200_pin_configs,
- },
- [STAC_9200_OQO] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = oqo9200_pin_configs,
- .chained = true,
- .chain_id = STAC_9200_EAPD_INIT,
- },
- [STAC_9200_DELL_D21] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell9200_d21_pin_configs,
- },
- [STAC_9200_DELL_D22] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell9200_d22_pin_configs,
- },
- [STAC_9200_DELL_D23] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell9200_d23_pin_configs,
- },
- [STAC_9200_DELL_M21] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell9200_m21_pin_configs,
- },
- [STAC_9200_DELL_M22] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell9200_m22_pin_configs,
- },
- [STAC_9200_DELL_M23] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell9200_m23_pin_configs,
- },
- [STAC_9200_DELL_M24] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell9200_m24_pin_configs,
- },
- [STAC_9200_DELL_M25] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell9200_m25_pin_configs,
- },
- [STAC_9200_DELL_M26] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell9200_m26_pin_configs,
- },
- [STAC_9200_DELL_M27] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell9200_m27_pin_configs,
- },
- [STAC_9200_M4] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = gateway9200_m4_pin_configs,
- .chained = true,
- .chain_id = STAC_9200_EAPD_INIT,
- },
- [STAC_9200_M4_2] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = gateway9200_m4_2_pin_configs,
- .chained = true,
- .chain_id = STAC_9200_EAPD_INIT,
- },
- [STAC_9200_PANASONIC] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac9200_fixup_panasonic,
- },
- [STAC_9200_EAPD_INIT] = {
- .type = HDA_FIXUP_VERBS,
- .v.verbs = (const struct hda_verb[]) {
- {0x08, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
- {}
- },
- },
-};
-
-static const struct hda_model_fixup stac9200_models[] = {
- { .id = STAC_REF, .name = "ref" },
- { .id = STAC_9200_OQO, .name = "oqo" },
- { .id = STAC_9200_DELL_D21, .name = "dell-d21" },
- { .id = STAC_9200_DELL_D22, .name = "dell-d22" },
- { .id = STAC_9200_DELL_D23, .name = "dell-d23" },
- { .id = STAC_9200_DELL_M21, .name = "dell-m21" },
- { .id = STAC_9200_DELL_M22, .name = "dell-m22" },
- { .id = STAC_9200_DELL_M23, .name = "dell-m23" },
- { .id = STAC_9200_DELL_M24, .name = "dell-m24" },
- { .id = STAC_9200_DELL_M25, .name = "dell-m25" },
- { .id = STAC_9200_DELL_M26, .name = "dell-m26" },
- { .id = STAC_9200_DELL_M27, .name = "dell-m27" },
- { .id = STAC_9200_M4, .name = "gateway-m4" },
- { .id = STAC_9200_M4_2, .name = "gateway-m4-2" },
- { .id = STAC_9200_PANASONIC, .name = "panasonic" },
- {}
-};
-
-static const struct snd_pci_quirk stac9200_fixup_tbl[] = {
+static const struct snd_pci_quirk stac9200_cfg_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_REF),
@@ -1523,159 +1441,70 @@ static const struct snd_pci_quirk stac9200_fixup_tbl[] = {
{} /* terminator */
};
-static const struct hda_pintbl ref925x_pin_configs[] = {
- { 0x07, 0x40c003f0 },
- { 0x08, 0x424503f2 },
- { 0x0a, 0x01813022 },
- { 0x0b, 0x02a19021 },
- { 0x0c, 0x90a70320 },
- { 0x0d, 0x02214210 },
- { 0x10, 0x01019020 },
- { 0x11, 0x9033032e },
- {}
+static const unsigned int ref925x_pin_configs[8] = {
+ 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021,
+ 0x90a70320, 0x02214210, 0x01019020, 0x9033032e,
};
-static const struct hda_pintbl stac925xM1_pin_configs[] = {
- { 0x07, 0x40c003f4 },
- { 0x08, 0x424503f2 },
- { 0x0a, 0x400000f3 },
- { 0x0b, 0x02a19020 },
- { 0x0c, 0x40a000f0 },
- { 0x0d, 0x90100210 },
- { 0x10, 0x400003f1 },
- { 0x11, 0x9033032e },
- {}
+static const unsigned int stac925xM1_pin_configs[8] = {
+ 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+ 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
};
-static const struct hda_pintbl stac925xM1_2_pin_configs[] = {
- { 0x07, 0x40c003f4 },
- { 0x08, 0x424503f2 },
- { 0x0a, 0x400000f3 },
- { 0x0b, 0x02a19020 },
- { 0x0c, 0x40a000f0 },
- { 0x0d, 0x90100210 },
- { 0x10, 0x400003f1 },
- { 0x11, 0x9033032e },
- {}
+static const unsigned int stac925xM1_2_pin_configs[8] = {
+ 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+ 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
};
-static const struct hda_pintbl stac925xM2_pin_configs[] = {
- { 0x07, 0x40c003f4 },
- { 0x08, 0x424503f2 },
- { 0x0a, 0x400000f3 },
- { 0x0b, 0x02a19020 },
- { 0x0c, 0x40a000f0 },
- { 0x0d, 0x90100210 },
- { 0x10, 0x400003f1 },
- { 0x11, 0x9033032e },
- {}
+static const unsigned int stac925xM2_pin_configs[8] = {
+ 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+ 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
};
-static const struct hda_pintbl stac925xM2_2_pin_configs[] = {
- { 0x07, 0x40c003f4 },
- { 0x08, 0x424503f2 },
- { 0x0a, 0x400000f3 },
- { 0x0b, 0x02a19020 },
- { 0x0c, 0x40a000f0 },
- { 0x0d, 0x90100210 },
- { 0x10, 0x400003f1 },
- { 0x11, 0x9033032e },
- {}
+static const unsigned int stac925xM2_2_pin_configs[8] = {
+ 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+ 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
};
-static const struct hda_pintbl stac925xM3_pin_configs[] = {
- { 0x07, 0x40c003f4 },
- { 0x08, 0x424503f2 },
- { 0x0a, 0x400000f3 },
- { 0x0b, 0x02a19020 },
- { 0x0c, 0x40a000f0 },
- { 0x0d, 0x90100210 },
- { 0x10, 0x400003f1 },
- { 0x11, 0x503303f3 },
- {}
+static const unsigned int stac925xM3_pin_configs[8] = {
+ 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+ 0x40a000f0, 0x90100210, 0x400003f1, 0x503303f3,
};
-static const struct hda_pintbl stac925xM5_pin_configs[] = {
- { 0x07, 0x40c003f4 },
- { 0x08, 0x424503f2 },
- { 0x0a, 0x400000f3 },
- { 0x0b, 0x02a19020 },
- { 0x0c, 0x40a000f0 },
- { 0x0d, 0x90100210 },
- { 0x10, 0x400003f1 },
- { 0x11, 0x9033032e },
- {}
+static const unsigned int stac925xM5_pin_configs[8] = {
+ 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+ 0x40a000f0, 0x90100210, 0x400003f1, 0x9033032e,
};
-static const struct hda_pintbl stac925xM6_pin_configs[] = {
- { 0x07, 0x40c003f4 },
- { 0x08, 0x424503f2 },
- { 0x0a, 0x400000f3 },
- { 0x0b, 0x02a19020 },
- { 0x0c, 0x40a000f0 },
- { 0x0d, 0x90100210 },
- { 0x10, 0x400003f1 },
- { 0x11, 0x90330320 },
- {}
+static const unsigned int stac925xM6_pin_configs[8] = {
+ 0x40c003f4, 0x424503f2, 0x400000f3, 0x02a19020,
+ 0x40a000f0, 0x90100210, 0x400003f1, 0x90330320,
};
-static const struct hda_fixup stac925x_fixups[] = {
- [STAC_REF] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = ref925x_pin_configs,
- },
- [STAC_M1] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = stac925xM1_pin_configs,
- },
- [STAC_M1_2] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = stac925xM1_2_pin_configs,
- },
- [STAC_M2] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = stac925xM2_pin_configs,
- },
- [STAC_M2_2] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = stac925xM2_2_pin_configs,
- },
- [STAC_M3] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = stac925xM3_pin_configs,
- },
- [STAC_M5] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = stac925xM5_pin_configs,
- },
- [STAC_M6] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = stac925xM6_pin_configs,
- },
+static const unsigned int *stac925x_brd_tbl[STAC_925x_MODELS] = {
+ [STAC_REF] = ref925x_pin_configs,
+ [STAC_M1] = stac925xM1_pin_configs,
+ [STAC_M1_2] = stac925xM1_2_pin_configs,
+ [STAC_M2] = stac925xM2_pin_configs,
+ [STAC_M2_2] = stac925xM2_2_pin_configs,
+ [STAC_M3] = stac925xM3_pin_configs,
+ [STAC_M5] = stac925xM5_pin_configs,
+ [STAC_M6] = stac925xM6_pin_configs,
};
-static const struct hda_model_fixup stac925x_models[] = {
- { .id = STAC_REF, .name = "ref" },
- { .id = STAC_M1, .name = "m1" },
- { .id = STAC_M1_2, .name = "m1-2" },
- { .id = STAC_M2, .name = "m2" },
- { .id = STAC_M2_2, .name = "m2-2" },
- { .id = STAC_M3, .name = "m3" },
- { .id = STAC_M5, .name = "m5" },
- { .id = STAC_M6, .name = "m6" },
- {}
+static const char * const stac925x_models[STAC_925x_MODELS] = {
+ [STAC_925x_AUTO] = "auto",
+ [STAC_REF] = "ref",
+ [STAC_M1] = "m1",
+ [STAC_M1_2] = "m1-2",
+ [STAC_M2] = "m2",
+ [STAC_M2_2] = "m2-2",
+ [STAC_M3] = "m3",
+ [STAC_M5] = "m5",
+ [STAC_M6] = "m6",
};
-static const struct snd_pci_quirk stac925x_fixup_tbl[] = {
- /* SigmaTel reference board */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF),
- SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF),
-
- /* Default table for unknown ID */
- SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2),
-
- /* gateway machines are checked via codec ssid */
+static const struct snd_pci_quirk stac925x_codec_id_cfg_tbl[] = {
SND_PCI_QUIRK(0x107b, 0x0316, "Gateway M255", STAC_M2),
SND_PCI_QUIRK(0x107b, 0x0366, "Gateway MP6954", STAC_M5),
SND_PCI_QUIRK(0x107b, 0x0461, "Gateway NX560XL", STAC_M1),
@@ -1689,202 +1518,67 @@ static const struct snd_pci_quirk stac925x_fixup_tbl[] = {
{} /* terminator */
};
-static const struct hda_pintbl ref92hd73xx_pin_configs[] = {
- { 0x0a, 0x02214030 },
- { 0x0b, 0x02a19040 },
- { 0x0c, 0x01a19020 },
- { 0x0d, 0x02214030 },
- { 0x0e, 0x0181302e },
- { 0x0f, 0x01014010 },
- { 0x10, 0x01014020 },
- { 0x11, 0x01014030 },
- { 0x12, 0x02319040 },
- { 0x13, 0x90a000f0 },
- { 0x14, 0x90a000f0 },
- { 0x22, 0x01452050 },
- { 0x23, 0x01452050 },
- {}
-};
+static const struct snd_pci_quirk stac925x_cfg_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101, "DFI LanParty", STAC_REF),
+ SND_PCI_QUIRK(0x8384, 0x7632, "Stac9202 Reference Board", STAC_REF),
-static const struct hda_pintbl dell_m6_pin_configs[] = {
- { 0x0a, 0x0321101f },
- { 0x0b, 0x4f00000f },
- { 0x0c, 0x4f0000f0 },
- { 0x0d, 0x90170110 },
- { 0x0e, 0x03a11020 },
- { 0x0f, 0x0321101f },
- { 0x10, 0x4f0000f0 },
- { 0x11, 0x4f0000f0 },
- { 0x12, 0x4f0000f0 },
- { 0x13, 0x90a60160 },
- { 0x14, 0x4f0000f0 },
- { 0x22, 0x4f0000f0 },
- { 0x23, 0x4f0000f0 },
- {}
-};
+ /* Default table for unknown ID */
+ SND_PCI_QUIRK(0x1002, 0x437b, "Gateway mobile", STAC_M2_2),
-static const struct hda_pintbl alienware_m17x_pin_configs[] = {
- { 0x0a, 0x0321101f },
- { 0x0b, 0x0321101f },
- { 0x0c, 0x03a11020 },
- { 0x0d, 0x03014020 },
- { 0x0e, 0x90170110 },
- { 0x0f, 0x4f0000f0 },
- { 0x10, 0x4f0000f0 },
- { 0x11, 0x4f0000f0 },
- { 0x12, 0x4f0000f0 },
- { 0x13, 0x90a60160 },
- { 0x14, 0x4f0000f0 },
- { 0x22, 0x4f0000f0 },
- { 0x23, 0x904601b0 },
- {}
+ {} /* terminator */
};
-static const struct hda_pintbl intel_dg45id_pin_configs[] = {
- { 0x0a, 0x02214230 },
- { 0x0b, 0x02A19240 },
- { 0x0c, 0x01013214 },
- { 0x0d, 0x01014210 },
- { 0x0e, 0x01A19250 },
- { 0x0f, 0x01011212 },
- { 0x10, 0x01016211 },
- {}
+static const unsigned int ref92hd73xx_pin_configs[13] = {
+ 0x02214030, 0x02a19040, 0x01a19020, 0x02214030,
+ 0x0181302e, 0x01014010, 0x01014020, 0x01014030,
+ 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050,
+ 0x01452050,
};
-static void stac92hd73xx_fixup_ref(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- snd_hda_apply_pincfgs(codec, ref92hd73xx_pin_configs);
- spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0;
-}
-
-static void stac92hd73xx_fixup_dell(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- snd_hda_apply_pincfgs(codec, dell_m6_pin_configs);
- spec->eapd_switch = 0;
-}
-
-static void stac92hd73xx_fixup_dell_eq(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- stac92hd73xx_fixup_dell(codec);
- snd_hda_add_verbs(codec, dell_eq_core_init);
- spec->volknob_init = 1;
-}
-
-/* Analog Mics */
-static void stac92hd73xx_fixup_dell_m6_amic(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- stac92hd73xx_fixup_dell(codec);
- snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
-}
-
-/* Digital Mics */
-static void stac92hd73xx_fixup_dell_m6_dmic(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- stac92hd73xx_fixup_dell(codec);
- snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
-}
-
-/* Both */
-static void stac92hd73xx_fixup_dell_m6_both(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- stac92hd73xx_fixup_dell(codec);
- snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
- snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
-}
-
-static void stac92hd73xx_fixup_alienware_m17x(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
+static const unsigned int dell_m6_pin_configs[13] = {
+ 0x0321101f, 0x4f00000f, 0x4f0000f0, 0x90170110,
+ 0x03a11020, 0x0321101f, 0x4f0000f0, 0x4f0000f0,
+ 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
+ 0x4f0000f0,
+};
- snd_hda_apply_pincfgs(codec, alienware_m17x_pin_configs);
- spec->eapd_switch = 0;
-}
+static const unsigned int alienware_m17x_pin_configs[13] = {
+ 0x0321101f, 0x0321101f, 0x03a11020, 0x03014020,
+ 0x90170110, 0x4f0000f0, 0x4f0000f0, 0x4f0000f0,
+ 0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
+ 0x904601b0,
+};
-static void stac92hd73xx_fixup_no_jd(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
- codec->no_jack_detect = 1;
-}
+static const unsigned int intel_dg45id_pin_configs[13] = {
+ 0x02214230, 0x02A19240, 0x01013214, 0x01014210,
+ 0x01A19250, 0x01011212, 0x01016211
+};
-static const struct hda_fixup stac92hd73xx_fixups[] = {
- [STAC_92HD73XX_REF] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd73xx_fixup_ref,
- },
- [STAC_DELL_M6_AMIC] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd73xx_fixup_dell_m6_amic,
- },
- [STAC_DELL_M6_DMIC] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd73xx_fixup_dell_m6_dmic,
- },
- [STAC_DELL_M6_BOTH] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd73xx_fixup_dell_m6_both,
- },
- [STAC_DELL_EQ] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd73xx_fixup_dell_eq,
- },
- [STAC_ALIENWARE_M17X] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd73xx_fixup_alienware_m17x,
- },
- [STAC_92HD73XX_INTEL] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = intel_dg45id_pin_configs,
- },
- [STAC_92HD73XX_NO_JD] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd73xx_fixup_no_jd,
- }
+static const unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
+ [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs,
+ [STAC_DELL_M6_AMIC] = dell_m6_pin_configs,
+ [STAC_DELL_M6_DMIC] = dell_m6_pin_configs,
+ [STAC_DELL_M6_BOTH] = dell_m6_pin_configs,
+ [STAC_DELL_EQ] = dell_m6_pin_configs,
+ [STAC_ALIENWARE_M17X] = alienware_m17x_pin_configs,
+ [STAC_92HD73XX_INTEL] = intel_dg45id_pin_configs,
};
-static const struct hda_model_fixup stac92hd73xx_models[] = {
- { .id = STAC_92HD73XX_NO_JD, .name = "no-jd" },
- { .id = STAC_92HD73XX_REF, .name = "ref" },
- { .id = STAC_92HD73XX_INTEL, .name = "intel" },
- { .id = STAC_DELL_M6_AMIC, .name = "dell-m6-amic" },
- { .id = STAC_DELL_M6_DMIC, .name = "dell-m6-dmic" },
- { .id = STAC_DELL_M6_BOTH, .name = "dell-m6" },
- { .id = STAC_DELL_EQ, .name = "dell-eq" },
- { .id = STAC_ALIENWARE_M17X, .name = "alienware" },
- {}
+static const char * const stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
+ [STAC_92HD73XX_AUTO] = "auto",
+ [STAC_92HD73XX_NO_JD] = "no-jd",
+ [STAC_92HD73XX_REF] = "ref",
+ [STAC_92HD73XX_INTEL] = "intel",
+ [STAC_DELL_M6_AMIC] = "dell-m6-amic",
+ [STAC_DELL_M6_DMIC] = "dell-m6-dmic",
+ [STAC_DELL_M6_BOTH] = "dell-m6",
+ [STAC_DELL_EQ] = "dell-eq",
+ [STAC_ALIENWARE_M17X] = "alienware",
};
-static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = {
+static const struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD73XX_REF),
@@ -1922,7 +1616,10 @@ static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = {
"Dell Studio XPS 1645", STAC_DELL_M6_DMIC),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413,
"Dell Studio 1558", STAC_DELL_M6_DMIC),
- /* codec SSID matching */
+ {} /* terminator */
+};
+
+static const struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a1,
"Alienware M17x", STAC_ALIENWARE_M17X),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x043a,
@@ -1932,240 +1629,68 @@ static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = {
{} /* terminator */
};
-static const struct hda_pintbl ref92hd83xxx_pin_configs[] = {
- { 0x0a, 0x02214030 },
- { 0x0b, 0x02211010 },
- { 0x0c, 0x02a19020 },
- { 0x0d, 0x02170130 },
- { 0x0e, 0x01014050 },
- { 0x0f, 0x01819040 },
- { 0x10, 0x01014020 },
- { 0x11, 0x90a3014e },
- { 0x1f, 0x01451160 },
- { 0x20, 0x98560170 },
- {}
+static const unsigned int ref92hd83xxx_pin_configs[10] = {
+ 0x02214030, 0x02211010, 0x02a19020, 0x02170130,
+ 0x01014050, 0x01819040, 0x01014020, 0x90a3014e,
+ 0x01451160, 0x98560170,
};
-static const struct hda_pintbl dell_s14_pin_configs[] = {
- { 0x0a, 0x0221403f },
- { 0x0b, 0x0221101f },
- { 0x0c, 0x02a19020 },
- { 0x0d, 0x90170110 },
- { 0x0e, 0x40f000f0 },
- { 0x0f, 0x40f000f0 },
- { 0x10, 0x40f000f0 },
- { 0x11, 0x90a60160 },
- { 0x1f, 0x40f000f0 },
- { 0x20, 0x40f000f0 },
- {}
+static const unsigned int dell_s14_pin_configs[10] = {
+ 0x0221403f, 0x0221101f, 0x02a19020, 0x90170110,
+ 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a60160,
+ 0x40f000f0, 0x40f000f0,
};
-static const struct hda_pintbl dell_vostro_3500_pin_configs[] = {
- { 0x0a, 0x02a11020 },
- { 0x0b, 0x0221101f },
- { 0x0c, 0x400000f0 },
- { 0x0d, 0x90170110 },
- { 0x0e, 0x400000f1 },
- { 0x0f, 0x400000f2 },
- { 0x10, 0x400000f3 },
- { 0x11, 0x90a60160 },
- { 0x1f, 0x400000f4 },
- { 0x20, 0x400000f5 },
- {}
+static const unsigned int dell_vostro_3500_pin_configs[10] = {
+ 0x02a11020, 0x0221101f, 0x400000f0, 0x90170110,
+ 0x400000f1, 0x400000f2, 0x400000f3, 0x90a60160,
+ 0x400000f4, 0x400000f5,
};
-static const struct hda_pintbl hp_dv7_4000_pin_configs[] = {
- { 0x0a, 0x03a12050 },
- { 0x0b, 0x0321201f },
- { 0x0c, 0x40f000f0 },
- { 0x0d, 0x90170110 },
- { 0x0e, 0x40f000f0 },
- { 0x0f, 0x40f000f0 },
- { 0x10, 0x90170110 },
- { 0x11, 0xd5a30140 },
- { 0x1f, 0x40f000f0 },
- { 0x20, 0x40f000f0 },
- {}
+static const unsigned int hp_dv7_4000_pin_configs[10] = {
+ 0x03a12050, 0x0321201f, 0x40f000f0, 0x90170110,
+ 0x40f000f0, 0x40f000f0, 0x90170110, 0xd5a30140,
+ 0x40f000f0, 0x40f000f0,
};
-static const struct hda_pintbl hp_zephyr_pin_configs[] = {
- { 0x0a, 0x01813050 },
- { 0x0b, 0x0421201f },
- { 0x0c, 0x04a1205e },
- { 0x0d, 0x96130310 },
- { 0x0e, 0x96130310 },
- { 0x0f, 0x0101401f },
- { 0x10, 0x1111611f },
- { 0x11, 0xd5a30130 },
- {}
+static const unsigned int hp_zephyr_pin_configs[10] = {
+ 0x01813050, 0x0421201f, 0x04a1205e, 0x96130310,
+ 0x96130310, 0x0101401f, 0x1111611f, 0xd5a30130,
+ 0, 0,
};
-static const struct hda_pintbl hp_cNB11_intquad_pin_configs[] = {
- { 0x0a, 0x40f000f0 },
- { 0x0b, 0x0221101f },
- { 0x0c, 0x02a11020 },
- { 0x0d, 0x92170110 },
- { 0x0e, 0x40f000f0 },
- { 0x0f, 0x92170110 },
- { 0x10, 0x40f000f0 },
- { 0x11, 0xd5a30130 },
- { 0x1f, 0x40f000f0 },
- { 0x20, 0x40f000f0 },
- {}
+static const unsigned int hp_cNB11_intquad_pin_configs[10] = {
+ 0x40f000f0, 0x0221101f, 0x02a11020, 0x92170110,
+ 0x40f000f0, 0x92170110, 0x40f000f0, 0xd5a30130,
+ 0x40f000f0, 0x40f000f0,
};
-static void stac92hd83xxx_fixup_hp(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- if (hp_bnb2011_with_dock(codec)) {
- snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f);
- snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e);
- }
-
- if (find_mute_led_cfg(codec, spec->default_polarity))
- snd_printd("mute LED gpio %d polarity %d\n",
- spec->gpio_led,
- spec->gpio_led_polarity);
-}
-
-static void stac92hd83xxx_fixup_hp_zephyr(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- snd_hda_apply_pincfgs(codec, hp_zephyr_pin_configs);
- snd_hda_add_verbs(codec, stac92hd83xxx_hp_zephyr_init);
-}
-
-static void stac92hd83xxx_fixup_hp_led(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
- spec->default_polarity = 0;
-}
-
-static void stac92hd83xxx_fixup_hp_inv_led(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
- spec->default_polarity = 1;
-}
-
-static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
- spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
-}
-
-static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
- spec->headset_jack = 1;
-}
-
-static const struct hda_fixup stac92hd83xxx_fixups[] = {
- [STAC_92HD83XXX_REF] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = ref92hd83xxx_pin_configs,
- },
- [STAC_92HD83XXX_PWR_REF] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = ref92hd83xxx_pin_configs,
- },
- [STAC_DELL_S14] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell_s14_pin_configs,
- },
- [STAC_DELL_VOSTRO_3500] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell_vostro_3500_pin_configs,
- },
- [STAC_92HD83XXX_HP_cNB11_INTQUAD] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = hp_cNB11_intquad_pin_configs,
- .chained = true,
- .chain_id = STAC_92HD83XXX_HP,
- },
- [STAC_92HD83XXX_HP] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd83xxx_fixup_hp,
- },
- [STAC_HP_DV7_4000] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = hp_dv7_4000_pin_configs,
- .chained = true,
- .chain_id = STAC_92HD83XXX_HP,
- },
- [STAC_HP_ZEPHYR] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd83xxx_fixup_hp_zephyr,
- .chained = true,
- .chain_id = STAC_92HD83XXX_HP,
- },
- [STAC_92HD83XXX_HP_LED] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd83xxx_fixup_hp_led,
- .chained = true,
- .chain_id = STAC_92HD83XXX_HP,
- },
- [STAC_92HD83XXX_HP_INV_LED] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd83xxx_fixup_hp_inv_led,
- .chained = true,
- .chain_id = STAC_92HD83XXX_HP,
- },
- [STAC_92HD83XXX_HP_MIC_LED] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd83xxx_fixup_hp_mic_led,
- .chained = true,
- .chain_id = STAC_92HD83XXX_HP,
- },
- [STAC_92HD83XXX_HEADSET_JACK] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd83xxx_fixup_headset_jack,
- },
- [STAC_HP_ENVY_BASS] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- { 0x0f, 0x90170111 },
- {}
- },
- },
+static const unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = {
+ [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs,
+ [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs,
+ [STAC_DELL_S14] = dell_s14_pin_configs,
+ [STAC_DELL_VOSTRO_3500] = dell_vostro_3500_pin_configs,
+ [STAC_92HD83XXX_HP_cNB11_INTQUAD] = hp_cNB11_intquad_pin_configs,
+ [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs,
+ [STAC_HP_ZEPHYR] = hp_zephyr_pin_configs,
};
-static const struct hda_model_fixup stac92hd83xxx_models[] = {
- { .id = STAC_92HD83XXX_REF, .name = "ref" },
- { .id = STAC_92HD83XXX_PWR_REF, .name = "mic-ref" },
- { .id = STAC_DELL_S14, .name = "dell-s14" },
- { .id = STAC_DELL_VOSTRO_3500, .name = "dell-vostro-3500" },
- { .id = STAC_92HD83XXX_HP_cNB11_INTQUAD, .name = "hp_cNB11_intquad" },
- { .id = STAC_HP_DV7_4000, .name = "hp-dv7-4000" },
- { .id = STAC_HP_ZEPHYR, .name = "hp-zephyr" },
- { .id = STAC_92HD83XXX_HP_LED, .name = "hp-led" },
- { .id = STAC_92HD83XXX_HP_INV_LED, .name = "hp-inv-led" },
- { .id = STAC_92HD83XXX_HP_MIC_LED, .name = "hp-mic-led" },
- { .id = STAC_92HD83XXX_HEADSET_JACK, .name = "headset-jack" },
- { .id = STAC_HP_ENVY_BASS, .name = "hp-envy-bass" },
- {}
+static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
+ [STAC_92HD83XXX_AUTO] = "auto",
+ [STAC_92HD83XXX_REF] = "ref",
+ [STAC_92HD83XXX_PWR_REF] = "mic-ref",
+ [STAC_DELL_S14] = "dell-s14",
+ [STAC_DELL_VOSTRO_3500] = "dell-vostro-3500",
+ [STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad",
+ [STAC_HP_DV7_4000] = "hp-dv7-4000",
+ [STAC_HP_ZEPHYR] = "hp-zephyr",
+ [STAC_92HD83XXX_HP_LED] = "hp-led",
+ [STAC_92HD83XXX_HP_INV_LED] = "hp-inv-led",
+ [STAC_92HD83XXX_HP_MIC_LED] = "hp-mic-led",
+ [STAC_92HD83XXX_HEADSET_JACK] = "headset-jack",
};
-static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = {
+static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_92HD83XXX_REF),
@@ -2205,12 +1730,8 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = {
"HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B,
"HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1888,
- "HP Envy Spectre", STAC_HP_ENVY_BASS),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18df,
"HP Folio", STAC_92HD83XXX_HP_MIC_LED),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x1900,
- "HP", STAC_92HD83XXX_HP_MIC_LED),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388,
"HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389,
@@ -2245,360 +1766,130 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = {
"HP Mini", STAC_92HD83XXX_HP_LED),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x144E,
"HP Pavilion dv5", STAC_92HD83XXX_HP_INV_LED),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x148a,
- "HP Mini", STAC_92HD83XXX_HP_LED),
- SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD83XXX_HP),
{} /* terminator */
};
-/* HP dv7 bass switch - GPIO5 */
-#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info
-static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20);
- return 0;
-}
-
-static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct sigmatel_spec *spec = codec->spec;
- unsigned int gpio_data;
-
- gpio_data = (spec->gpio_data & ~0x20) |
- (ucontrol->value.integer.value[0] ? 0x20 : 0);
- if (gpio_data == spec->gpio_data)
- return 0;
- spec->gpio_data = gpio_data;
- stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
- return 1;
-}
-
-static const struct snd_kcontrol_new stac_hp_bass_sw_ctrl = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .info = stac_hp_bass_gpio_info,
- .get = stac_hp_bass_gpio_get,
- .put = stac_hp_bass_gpio_put,
+static const struct snd_pci_quirk stac92hd83xxx_codec_id_cfg_tbl[] = {
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561,
+ "HP", STAC_HP_ZEPHYR),
+ {} /* terminator */
};
-static int stac_add_hp_bass_switch(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (!snd_hda_gen_add_kctl(&spec->gen, "Bass Speaker Playback Switch",
- &stac_hp_bass_sw_ctrl))
- return -ENOMEM;
+static const unsigned int ref92hd71bxx_pin_configs[STAC92HD71BXX_NUM_PINS] = {
+ 0x02214030, 0x02a19040, 0x01a19020, 0x01014010,
+ 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0,
+ 0x90a000f0, 0x01452050, 0x01452050, 0x00000000,
+ 0x00000000
+};
- spec->gpio_mask |= 0x20;
- spec->gpio_dir |= 0x20;
- spec->gpio_data |= 0x20;
- return 0;
-}
+static const unsigned int dell_m4_1_pin_configs[STAC92HD71BXX_NUM_PINS] = {
+ 0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,
+ 0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0,
+ 0x40f000f0, 0x4f0000f0, 0x4f0000f0, 0x00000000,
+ 0x00000000
+};
-static const struct hda_pintbl ref92hd71bxx_pin_configs[] = {
- { 0x0a, 0x02214030 },
- { 0x0b, 0x02a19040 },
- { 0x0c, 0x01a19020 },
- { 0x0d, 0x01014010 },
- { 0x0e, 0x0181302e },
- { 0x0f, 0x01014010 },
- { 0x14, 0x01019020 },
- { 0x18, 0x90a000f0 },
- { 0x19, 0x90a000f0 },
- { 0x1e, 0x01452050 },
- { 0x1f, 0x01452050 },
- {}
+static const unsigned int dell_m4_2_pin_configs[STAC92HD71BXX_NUM_PINS] = {
+ 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
+ 0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0,
+ 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000,
+ 0x00000000
};
-static const struct hda_pintbl dell_m4_1_pin_configs[] = {
- { 0x0a, 0x0421101f },
- { 0x0b, 0x04a11221 },
- { 0x0c, 0x40f000f0 },
- { 0x0d, 0x90170110 },
- { 0x0e, 0x23a1902e },
- { 0x0f, 0x23014250 },
- { 0x14, 0x40f000f0 },
- { 0x18, 0x90a000f0 },
- { 0x19, 0x40f000f0 },
- { 0x1e, 0x4f0000f0 },
- { 0x1f, 0x4f0000f0 },
- {}
+static const unsigned int dell_m4_3_pin_configs[STAC92HD71BXX_NUM_PINS] = {
+ 0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,
+ 0x40f000f0, 0x40f000f0, 0x40f000f0, 0x90a000f0,
+ 0x40f000f0, 0x044413b0, 0x044413b0, 0x00000000,
+ 0x00000000
};
-static const struct hda_pintbl dell_m4_2_pin_configs[] = {
- { 0x0a, 0x0421101f },
- { 0x0b, 0x04a11221 },
- { 0x0c, 0x90a70330 },
- { 0x0d, 0x90170110 },
- { 0x0e, 0x23a1902e },
- { 0x0f, 0x23014250 },
- { 0x14, 0x40f000f0 },
- { 0x18, 0x40f000f0 },
- { 0x19, 0x40f000f0 },
- { 0x1e, 0x044413b0 },
- { 0x1f, 0x044413b0 },
- {}
+static const unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {
+ [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,
+ [STAC_DELL_M4_1] = dell_m4_1_pin_configs,
+ [STAC_DELL_M4_2] = dell_m4_2_pin_configs,
+ [STAC_DELL_M4_3] = dell_m4_3_pin_configs,
+ [STAC_HP_M4] = NULL,
+ [STAC_HP_DV4] = NULL,
+ [STAC_HP_DV5] = NULL,
+ [STAC_HP_HDX] = NULL,
+ [STAC_HP_DV4_1222NR] = NULL,
};
-static const struct hda_pintbl dell_m4_3_pin_configs[] = {
- { 0x0a, 0x0421101f },
- { 0x0b, 0x04a11221 },
- { 0x0c, 0x90a70330 },
- { 0x0d, 0x90170110 },
- { 0x0e, 0x40f000f0 },
- { 0x0f, 0x40f000f0 },
- { 0x14, 0x40f000f0 },
- { 0x18, 0x90a000f0 },
- { 0x19, 0x40f000f0 },
- { 0x1e, 0x044413b0 },
- { 0x1f, 0x044413b0 },
- {}
+static const char * const stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
+ [STAC_92HD71BXX_AUTO] = "auto",
+ [STAC_92HD71BXX_REF] = "ref",
+ [STAC_DELL_M4_1] = "dell-m4-1",
+ [STAC_DELL_M4_2] = "dell-m4-2",
+ [STAC_DELL_M4_3] = "dell-m4-3",
+ [STAC_HP_M4] = "hp-m4",
+ [STAC_HP_DV4] = "hp-dv4",
+ [STAC_HP_DV5] = "hp-dv5",
+ [STAC_HP_HDX] = "hp-hdx",
+ [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr",
};
-static void stac92hd71bxx_fixup_ref(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
+static const struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_92HD71BXX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_92HD71BXX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb,
+ "HP dv4-1222nr", STAC_HP_DV4_1222NR),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720,
+ "HP", STAC_HP_DV5),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
+ "HP", STAC_HP_DV5),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
+ "HP dv4-7", STAC_HP_DV4),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3600,
+ "HP dv4-7", STAC_HP_DV5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3610,
+ "HP HDX", STAC_HP_HDX), /* HDX18 */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
+ "HP mini 1000", STAC_HP_M4),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b,
+ "HP HDX", STAC_HP_HDX), /* HDX16 */
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3620,
+ "HP dv6", STAC_HP_DV5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3061,
+ "HP dv6", STAC_HP_DV5), /* HP dv6-1110ax */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x363e,
+ "HP DV6", STAC_HP_DV5),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010,
+ "HP", STAC_HP_DV5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0250,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024f,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024d,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0251,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0277,
+ "unknown Dell", STAC_DELL_M4_1),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0263,
+ "unknown Dell", STAC_DELL_M4_2),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0265,
+ "unknown Dell", STAC_DELL_M4_2),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0262,
+ "unknown Dell", STAC_DELL_M4_2),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264,
+ "unknown Dell", STAC_DELL_M4_2),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02aa,
+ "unknown Dell", STAC_DELL_M4_3),
+ {} /* terminator */
+};
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- snd_hda_apply_pincfgs(codec, ref92hd71bxx_pin_configs);
- spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0;
-}
-
-static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_jack_tbl *jack;
-
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- /* Enable VREF power saving on GPIO1 detect */
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
- snd_hda_jack_detect_enable_callback(codec, codec->afg,
- STAC_VREF_EVENT,
- stac_vref_event);
- jack = snd_hda_jack_tbl_get(codec, codec->afg);
- if (jack)
- jack->private_data = 0x02;
-
- spec->gpio_mask |= 0x02;
-
- /* enable internal microphone */
- snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040);
-}
-
-static void stac92hd71bxx_fixup_hp_dv4(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
- spec->gpio_led = 0x01;
-}
-
-static void stac92hd71bxx_fixup_hp_dv5(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- unsigned int cap;
-
- switch (action) {
- case HDA_FIXUP_ACT_PRE_PROBE:
- snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
- break;
-
- case HDA_FIXUP_ACT_PROBE:
- /* enable bass on HP dv7 */
- cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP);
- cap &= AC_GPIO_IO_COUNT;
- if (cap >= 6)
- stac_add_hp_bass_switch(codec);
- break;
- }
-}
-
-static void stac92hd71bxx_fixup_hp_hdx(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
- spec->gpio_led = 0x08;
-}
-
-
-static void stac92hd71bxx_fixup_hp(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- if (hp_blike_system(codec->subsystem_id)) {
- unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f);
- if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
- get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER ||
- get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) {
- /* It was changed in the BIOS to just satisfy MS DTM.
- * Lets turn it back into slaved HP
- */
- pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE))
- | (AC_JACK_HP_OUT <<
- AC_DEFCFG_DEVICE_SHIFT);
- pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC
- | AC_DEFCFG_SEQUENCE)))
- | 0x1f;
- snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg);
- }
- }
-
- if (find_mute_led_cfg(codec, 1))
- snd_printd("mute LED gpio %d polarity %d\n",
- spec->gpio_led,
- spec->gpio_led_polarity);
-
-}
-
-static const struct hda_fixup stac92hd71bxx_fixups[] = {
- [STAC_92HD71BXX_REF] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd71bxx_fixup_ref,
- },
- [STAC_DELL_M4_1] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell_m4_1_pin_configs,
- },
- [STAC_DELL_M4_2] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell_m4_2_pin_configs,
- },
- [STAC_DELL_M4_3] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell_m4_3_pin_configs,
- },
- [STAC_HP_M4] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd71bxx_fixup_hp_m4,
- .chained = true,
- .chain_id = STAC_92HD71BXX_HP,
- },
- [STAC_HP_DV4] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd71bxx_fixup_hp_dv4,
- .chained = true,
- .chain_id = STAC_HP_DV5,
- },
- [STAC_HP_DV5] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd71bxx_fixup_hp_dv5,
- .chained = true,
- .chain_id = STAC_92HD71BXX_HP,
- },
- [STAC_HP_HDX] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd71bxx_fixup_hp_hdx,
- .chained = true,
- .chain_id = STAC_92HD71BXX_HP,
- },
- [STAC_92HD71BXX_HP] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac92hd71bxx_fixup_hp,
- },
-};
-
-static const struct hda_model_fixup stac92hd71bxx_models[] = {
- { .id = STAC_92HD71BXX_REF, .name = "ref" },
- { .id = STAC_DELL_M4_1, .name = "dell-m4-1" },
- { .id = STAC_DELL_M4_2, .name = "dell-m4-2" },
- { .id = STAC_DELL_M4_3, .name = "dell-m4-3" },
- { .id = STAC_HP_M4, .name = "hp-m4" },
- { .id = STAC_HP_DV4, .name = "hp-dv4" },
- { .id = STAC_HP_DV5, .name = "hp-dv5" },
- { .id = STAC_HP_HDX, .name = "hp-hdx" },
- { .id = STAC_HP_DV4, .name = "hp-dv4-1222nr" },
- {}
-};
-
-static const struct snd_pci_quirk stac92hd71bxx_fixup_tbl[] = {
- /* SigmaTel reference board */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
- "DFI LanParty", STAC_92HD71BXX_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
- "DFI LanParty", STAC_92HD71BXX_REF),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x1720,
- "HP", STAC_HP_DV5),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
- "HP", STAC_HP_DV5),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
- "HP dv4-7", STAC_HP_DV4),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3600,
- "HP dv4-7", STAC_HP_DV5),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3610,
- "HP HDX", STAC_HP_HDX), /* HDX18 */
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
- "HP mini 1000", STAC_HP_M4),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b,
- "HP HDX", STAC_HP_HDX), /* HDX16 */
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3620,
- "HP dv6", STAC_HP_DV5),
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3061,
- "HP dv6", STAC_HP_DV5), /* HP dv6-1110ax */
- SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x363e,
- "HP DV6", STAC_HP_DV5),
- SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010,
- "HP", STAC_HP_DV5),
- SND_PCI_QUIRK_VENDOR(PCI_VENDOR_ID_HP, "HP", STAC_92HD71BXX_HP),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0250,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024f,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x024d,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0251,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0277,
- "unknown Dell", STAC_DELL_M4_1),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0263,
- "unknown Dell", STAC_DELL_M4_2),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0265,
- "unknown Dell", STAC_DELL_M4_2),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0262,
- "unknown Dell", STAC_DELL_M4_2),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0264,
- "unknown Dell", STAC_DELL_M4_2),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02aa,
- "unknown Dell", STAC_DELL_M4_3),
- {} /* terminator */
-};
-
-static const struct hda_pintbl ref922x_pin_configs[] = {
- { 0x0a, 0x01014010 },
- { 0x0b, 0x01016011 },
- { 0x0c, 0x01012012 },
- { 0x0d, 0x0221401f },
- { 0x0e, 0x01813122 },
- { 0x0f, 0x01011014 },
- { 0x10, 0x01441030 },
- { 0x11, 0x01c41030 },
- { 0x15, 0x40000100 },
- { 0x1b, 0x40000100 },
- {}
-};
+static const unsigned int ref922x_pin_configs[10] = {
+ 0x01014010, 0x01016011, 0x01012012, 0x0221401f,
+ 0x01813122, 0x01011014, 0x01441030, 0x01c41030,
+ 0x40000100, 0x40000100,
+};
/*
STAC 922X pin configs for
@@ -2608,18 +1899,10 @@ static const struct hda_pintbl ref922x_pin_configs[] = {
102801D1
102801D2
*/
-static const struct hda_pintbl dell_922x_d81_pin_configs[] = {
- { 0x0a, 0x02214030 },
- { 0x0b, 0x01a19021 },
- { 0x0c, 0x01111012 },
- { 0x0d, 0x01114010 },
- { 0x0e, 0x02a19020 },
- { 0x0f, 0x01117011 },
- { 0x10, 0x400001f0 },
- { 0x11, 0x400001f1 },
- { 0x15, 0x01813122 },
- { 0x1b, 0x400001f2 },
- {}
+static const unsigned int dell_922x_d81_pin_configs[10] = {
+ 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
+ 0x02a19020, 0x01117011, 0x400001f0, 0x400001f1,
+ 0x01813122, 0x400001f2,
};
/*
@@ -2627,311 +1910,130 @@ static const struct hda_pintbl dell_922x_d81_pin_configs[] = {
102801AC
102801D0
*/
-static const struct hda_pintbl dell_922x_d82_pin_configs[] = {
- { 0x0a, 0x02214030 },
- { 0x0b, 0x01a19021 },
- { 0x0c, 0x01111012 },
- { 0x0d, 0x01114010 },
- { 0x0e, 0x02a19020 },
- { 0x0f, 0x01117011 },
- { 0x10, 0x01451140 },
- { 0x11, 0x400001f0 },
- { 0x15, 0x01813122 },
- { 0x1b, 0x400001f1 },
- {}
+static const unsigned int dell_922x_d82_pin_configs[10] = {
+ 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
+ 0x02a19020, 0x01117011, 0x01451140, 0x400001f0,
+ 0x01813122, 0x400001f1,
};
/*
STAC 922X pin configs for
102801BF
*/
-static const struct hda_pintbl dell_922x_m81_pin_configs[] = {
- { 0x0a, 0x0321101f },
- { 0x0b, 0x01112024 },
- { 0x0c, 0x01111222 },
- { 0x0d, 0x91174220 },
- { 0x0e, 0x03a11050 },
- { 0x0f, 0x01116221 },
- { 0x10, 0x90a70330 },
- { 0x11, 0x01452340 },
- { 0x15, 0x40C003f1 },
- { 0x1b, 0x405003f0 },
- {}
+static const unsigned int dell_922x_m81_pin_configs[10] = {
+ 0x0321101f, 0x01112024, 0x01111222, 0x91174220,
+ 0x03a11050, 0x01116221, 0x90a70330, 0x01452340,
+ 0x40C003f1, 0x405003f0,
};
/*
STAC 9221 A1 pin configs for
102801D7 (Dell XPS M1210)
*/
-static const struct hda_pintbl dell_922x_m82_pin_configs[] = {
- { 0x0a, 0x02211211 },
- { 0x0b, 0x408103ff },
- { 0x0c, 0x02a1123e },
- { 0x0d, 0x90100310 },
- { 0x0e, 0x408003f1 },
- { 0x0f, 0x0221121f },
- { 0x10, 0x03451340 },
- { 0x11, 0x40c003f2 },
- { 0x15, 0x508003f3 },
- { 0x1b, 0x405003f4 },
- {}
-};
-
-static const struct hda_pintbl d945gtp3_pin_configs[] = {
- { 0x0a, 0x0221401f },
- { 0x0b, 0x01a19022 },
- { 0x0c, 0x01813021 },
- { 0x0d, 0x01014010 },
- { 0x0e, 0x40000100 },
- { 0x0f, 0x40000100 },
- { 0x10, 0x40000100 },
- { 0x11, 0x40000100 },
- { 0x15, 0x02a19120 },
- { 0x1b, 0x40000100 },
- {}
+static const unsigned int dell_922x_m82_pin_configs[10] = {
+ 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310,
+ 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2,
+ 0x508003f3, 0x405003f4,
};
-static const struct hda_pintbl d945gtp5_pin_configs[] = {
- { 0x0a, 0x0221401f },
- { 0x0b, 0x01011012 },
- { 0x0c, 0x01813024 },
- { 0x0d, 0x01014010 },
- { 0x0e, 0x01a19021 },
- { 0x0f, 0x01016011 },
- { 0x10, 0x01452130 },
- { 0x11, 0x40000100 },
- { 0x15, 0x02a19320 },
- { 0x1b, 0x40000100 },
- {}
+static const unsigned int d945gtp3_pin_configs[10] = {
+ 0x0221401f, 0x01a19022, 0x01813021, 0x01014010,
+ 0x40000100, 0x40000100, 0x40000100, 0x40000100,
+ 0x02a19120, 0x40000100,
};
-static const struct hda_pintbl intel_mac_v1_pin_configs[] = {
- { 0x0a, 0x0121e21f },
- { 0x0b, 0x400000ff },
- { 0x0c, 0x9017e110 },
- { 0x0d, 0x400000fd },
- { 0x0e, 0x400000fe },
- { 0x0f, 0x0181e020 },
- { 0x10, 0x1145e030 },
- { 0x11, 0x11c5e240 },
- { 0x15, 0x400000fc },
- { 0x1b, 0x400000fb },
- {}
+static const unsigned int d945gtp5_pin_configs[10] = {
+ 0x0221401f, 0x01011012, 0x01813024, 0x01014010,
+ 0x01a19021, 0x01016011, 0x01452130, 0x40000100,
+ 0x02a19320, 0x40000100,
};
-static const struct hda_pintbl intel_mac_v2_pin_configs[] = {
- { 0x0a, 0x0121e21f },
- { 0x0b, 0x90a7012e },
- { 0x0c, 0x9017e110 },
- { 0x0d, 0x400000fd },
- { 0x0e, 0x400000fe },
- { 0x0f, 0x0181e020 },
- { 0x10, 0x1145e230 },
- { 0x11, 0x500000fa },
- { 0x15, 0x400000fc },
- { 0x1b, 0x400000fb },
- {}
+static const unsigned int intel_mac_v1_pin_configs[10] = {
+ 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd,
+ 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240,
+ 0x400000fc, 0x400000fb,
};
-static const struct hda_pintbl intel_mac_v3_pin_configs[] = {
- { 0x0a, 0x0121e21f },
- { 0x0b, 0x90a7012e },
- { 0x0c, 0x9017e110 },
- { 0x0d, 0x400000fd },
- { 0x0e, 0x400000fe },
- { 0x0f, 0x0181e020 },
- { 0x10, 0x1145e230 },
- { 0x11, 0x11c5e240 },
- { 0x15, 0x400000fc },
- { 0x1b, 0x400000fb },
- {}
+static const unsigned int intel_mac_v2_pin_configs[10] = {
+ 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
+ 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa,
+ 0x400000fc, 0x400000fb,
};
-static const struct hda_pintbl intel_mac_v4_pin_configs[] = {
- { 0x0a, 0x0321e21f },
- { 0x0b, 0x03a1e02e },
- { 0x0c, 0x9017e110 },
- { 0x0d, 0x9017e11f },
- { 0x0e, 0x400000fe },
- { 0x0f, 0x0381e020 },
- { 0x10, 0x1345e230 },
- { 0x11, 0x13c5e240 },
- { 0x15, 0x400000fc },
- { 0x1b, 0x400000fb },
- {}
+static const unsigned int intel_mac_v3_pin_configs[10] = {
+ 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
+ 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240,
+ 0x400000fc, 0x400000fb,
};
-static const struct hda_pintbl intel_mac_v5_pin_configs[] = {
- { 0x0a, 0x0321e21f },
- { 0x0b, 0x03a1e02e },
- { 0x0c, 0x9017e110 },
- { 0x0d, 0x9017e11f },
- { 0x0e, 0x400000fe },
- { 0x0f, 0x0381e020 },
- { 0x10, 0x1345e230 },
- { 0x11, 0x13c5e240 },
- { 0x15, 0x400000fc },
- { 0x1b, 0x400000fb },
- {}
+static const unsigned int intel_mac_v4_pin_configs[10] = {
+ 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
+ 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
+ 0x400000fc, 0x400000fb,
};
-static const struct hda_pintbl ecs202_pin_configs[] = {
- { 0x0a, 0x0221401f },
- { 0x0b, 0x02a19020 },
- { 0x0c, 0x01a19020 },
- { 0x0d, 0x01114010 },
- { 0x0e, 0x408000f0 },
- { 0x0f, 0x01813022 },
- { 0x10, 0x074510a0 },
- { 0x11, 0x40c400f1 },
- { 0x15, 0x9037012e },
- { 0x1b, 0x40e000f2 },
- {}
+static const unsigned int intel_mac_v5_pin_configs[10] = {
+ 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
+ 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
+ 0x400000fc, 0x400000fb,
};
-/* codec SSIDs for Intel Mac sharing the same PCI SSID 8384:7680 */
-static const struct snd_pci_quirk stac922x_intel_mac_fixup_tbl[] = {
- SND_PCI_QUIRK(0x106b, 0x0800, "Mac", STAC_INTEL_MAC_V1),
- SND_PCI_QUIRK(0x106b, 0x0600, "Mac", STAC_INTEL_MAC_V2),
- SND_PCI_QUIRK(0x106b, 0x0700, "Mac", STAC_INTEL_MAC_V2),
- SND_PCI_QUIRK(0x106b, 0x0e00, "Mac", STAC_INTEL_MAC_V3),
- SND_PCI_QUIRK(0x106b, 0x0f00, "Mac", STAC_INTEL_MAC_V3),
- SND_PCI_QUIRK(0x106b, 0x1600, "Mac", STAC_INTEL_MAC_V3),
- SND_PCI_QUIRK(0x106b, 0x1700, "Mac", STAC_INTEL_MAC_V3),
- SND_PCI_QUIRK(0x106b, 0x0200, "Mac", STAC_INTEL_MAC_V3),
- SND_PCI_QUIRK(0x106b, 0x1e00, "Mac", STAC_INTEL_MAC_V3),
- SND_PCI_QUIRK(0x106b, 0x1a00, "Mac", STAC_INTEL_MAC_V4),
- SND_PCI_QUIRK(0x106b, 0x0a00, "Mac", STAC_INTEL_MAC_V5),
- SND_PCI_QUIRK(0x106b, 0x2200, "Mac", STAC_INTEL_MAC_V5),
- {}
+static const unsigned int ecs202_pin_configs[10] = {
+ 0x0221401f, 0x02a19020, 0x01a19020, 0x01114010,
+ 0x408000f0, 0x01813022, 0x074510a0, 0x40c400f1,
+ 0x9037012e, 0x40e000f2,
};
-static const struct hda_fixup stac922x_fixups[];
-
-/* remap the fixup from codec SSID and apply it */
-static void stac922x_fixup_intel_mac_auto(struct hda_codec *codec,
- const struct hda_fixup *fix,
- int action)
-{
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
- snd_hda_pick_fixup(codec, NULL, stac922x_intel_mac_fixup_tbl,
- stac922x_fixups);
- if (codec->fixup_id != STAC_INTEL_MAC_AUTO)
- snd_hda_apply_fixup(codec, action);
-}
-
-static void stac922x_fixup_intel_mac_gpio(struct hda_codec *codec,
- const struct hda_fixup *fix,
- int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gpio_mask = spec->gpio_dir = 0x03;
- spec->gpio_data = 0x03;
- }
-}
-
-static const struct hda_fixup stac922x_fixups[] = {
- [STAC_D945_REF] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = ref922x_pin_configs,
- },
- [STAC_D945GTP3] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = d945gtp3_pin_configs,
- },
- [STAC_D945GTP5] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = d945gtp5_pin_configs,
- },
- [STAC_INTEL_MAC_AUTO] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac922x_fixup_intel_mac_auto,
- },
- [STAC_INTEL_MAC_V1] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = intel_mac_v1_pin_configs,
- .chained = true,
- .chain_id = STAC_922X_INTEL_MAC_GPIO,
- },
- [STAC_INTEL_MAC_V2] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = intel_mac_v2_pin_configs,
- .chained = true,
- .chain_id = STAC_922X_INTEL_MAC_GPIO,
- },
- [STAC_INTEL_MAC_V3] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = intel_mac_v3_pin_configs,
- .chained = true,
- .chain_id = STAC_922X_INTEL_MAC_GPIO,
- },
- [STAC_INTEL_MAC_V4] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = intel_mac_v4_pin_configs,
- .chained = true,
- .chain_id = STAC_922X_INTEL_MAC_GPIO,
- },
- [STAC_INTEL_MAC_V5] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = intel_mac_v5_pin_configs,
- .chained = true,
- .chain_id = STAC_922X_INTEL_MAC_GPIO,
- },
- [STAC_922X_INTEL_MAC_GPIO] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac922x_fixup_intel_mac_gpio,
- },
- [STAC_ECS_202] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = ecs202_pin_configs,
- },
- [STAC_922X_DELL_D81] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell_922x_d81_pin_configs,
- },
- [STAC_922X_DELL_D82] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell_922x_d82_pin_configs,
- },
- [STAC_922X_DELL_M81] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell_922x_m81_pin_configs,
- },
- [STAC_922X_DELL_M82] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell_922x_m82_pin_configs,
- },
+static const unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
+ [STAC_D945_REF] = ref922x_pin_configs,
+ [STAC_D945GTP3] = d945gtp3_pin_configs,
+ [STAC_D945GTP5] = d945gtp5_pin_configs,
+ [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
+ [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
+ [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
+ [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
+ [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
+ [STAC_INTEL_MAC_AUTO] = intel_mac_v3_pin_configs,
+ /* for backward compatibility */
+ [STAC_MACMINI] = intel_mac_v3_pin_configs,
+ [STAC_MACBOOK] = intel_mac_v5_pin_configs,
+ [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs,
+ [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,
+ [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,
+ [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs,
+ [STAC_ECS_202] = ecs202_pin_configs,
+ [STAC_922X_DELL_D81] = dell_922x_d81_pin_configs,
+ [STAC_922X_DELL_D82] = dell_922x_d82_pin_configs,
+ [STAC_922X_DELL_M81] = dell_922x_m81_pin_configs,
+ [STAC_922X_DELL_M82] = dell_922x_m82_pin_configs,
};
-static const struct hda_model_fixup stac922x_models[] = {
- { .id = STAC_D945_REF, .name = "ref" },
- { .id = STAC_D945GTP5, .name = "5stack" },
- { .id = STAC_D945GTP3, .name = "3stack" },
- { .id = STAC_INTEL_MAC_V1, .name = "intel-mac-v1" },
- { .id = STAC_INTEL_MAC_V2, .name = "intel-mac-v2" },
- { .id = STAC_INTEL_MAC_V3, .name = "intel-mac-v3" },
- { .id = STAC_INTEL_MAC_V4, .name = "intel-mac-v4" },
- { .id = STAC_INTEL_MAC_V5, .name = "intel-mac-v5" },
- { .id = STAC_INTEL_MAC_AUTO, .name = "intel-mac-auto" },
- { .id = STAC_ECS_202, .name = "ecs202" },
- { .id = STAC_922X_DELL_D81, .name = "dell-d81" },
- { .id = STAC_922X_DELL_D82, .name = "dell-d82" },
- { .id = STAC_922X_DELL_M81, .name = "dell-m81" },
- { .id = STAC_922X_DELL_M82, .name = "dell-m82" },
+static const char * const stac922x_models[STAC_922X_MODELS] = {
+ [STAC_922X_AUTO] = "auto",
+ [STAC_D945_REF] = "ref",
+ [STAC_D945GTP5] = "5stack",
+ [STAC_D945GTP3] = "3stack",
+ [STAC_INTEL_MAC_V1] = "intel-mac-v1",
+ [STAC_INTEL_MAC_V2] = "intel-mac-v2",
+ [STAC_INTEL_MAC_V3] = "intel-mac-v3",
+ [STAC_INTEL_MAC_V4] = "intel-mac-v4",
+ [STAC_INTEL_MAC_V5] = "intel-mac-v5",
+ [STAC_INTEL_MAC_AUTO] = "intel-mac-auto",
/* for backward compatibility */
- { .id = STAC_INTEL_MAC_V3, .name = "macmini" },
- { .id = STAC_INTEL_MAC_V5, .name = "macbook" },
- { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro-v1" },
- { .id = STAC_INTEL_MAC_V3, .name = "macbook-pro" },
- { .id = STAC_INTEL_MAC_V2, .name = "imac-intel" },
- { .id = STAC_INTEL_MAC_V3, .name = "imac-intel-20" },
- {}
+ [STAC_MACMINI] = "macmini",
+ [STAC_MACBOOK] = "macbook",
+ [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1",
+ [STAC_MACBOOK_PRO_V2] = "macbook-pro",
+ [STAC_IMAC_INTEL] = "imac-intel",
+ [STAC_IMAC_INTEL_20] = "imac-intel-20",
+ [STAC_ECS_202] = "ecs202",
+ [STAC_922X_DELL_D81] = "dell-d81",
+ [STAC_922X_DELL_D82] = "dell-d82",
+ [STAC_922X_DELL_M81] = "dell-m81",
+ [STAC_922X_DELL_M82] = "dell-m82",
};
-static const struct snd_pci_quirk stac922x_fixup_tbl[] = {
+static const struct snd_pci_quirk stac922x_cfg_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_D945_REF),
@@ -2994,10 +2096,9 @@ static const struct snd_pci_quirk stac922x_fixup_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x0204,
"Intel D945", STAC_D945_REF),
/* other systems */
-
/* Apple Intel Mac (Mac Mini, MacBook, MacBook Pro...) */
- SND_PCI_QUIRK(0x8384, 0x7680, "Mac", STAC_INTEL_MAC_AUTO),
-
+ SND_PCI_QUIRK(0x8384, 0x7680,
+ "Mac", STAC_INTEL_MAC_AUTO),
/* Dell systems */
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7,
"unknown Dell", STAC_922X_DELL_D81),
@@ -3023,229 +2124,65 @@ static const struct snd_pci_quirk stac922x_fixup_tbl[] = {
{} /* terminator */
};
-static const struct hda_pintbl ref927x_pin_configs[] = {
- { 0x0a, 0x02214020 },
- { 0x0b, 0x02a19080 },
- { 0x0c, 0x0181304e },
- { 0x0d, 0x01014010 },
- { 0x0e, 0x01a19040 },
- { 0x0f, 0x01011012 },
- { 0x10, 0x01016011 },
- { 0x11, 0x0101201f },
- { 0x12, 0x183301f0 },
- { 0x13, 0x18a001f0 },
- { 0x14, 0x18a001f0 },
- { 0x21, 0x01442070 },
- { 0x22, 0x01c42190 },
- { 0x23, 0x40000100 },
- {}
+static const unsigned int ref927x_pin_configs[14] = {
+ 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
+ 0x01a19040, 0x01011012, 0x01016011, 0x0101201f,
+ 0x183301f0, 0x18a001f0, 0x18a001f0, 0x01442070,
+ 0x01c42190, 0x40000100,
};
-static const struct hda_pintbl d965_3st_pin_configs[] = {
- { 0x0a, 0x0221401f },
- { 0x0b, 0x02a19120 },
- { 0x0c, 0x40000100 },
- { 0x0d, 0x01014011 },
- { 0x0e, 0x01a19021 },
- { 0x0f, 0x01813024 },
- { 0x10, 0x40000100 },
- { 0x11, 0x40000100 },
- { 0x12, 0x40000100 },
- { 0x13, 0x40000100 },
- { 0x14, 0x40000100 },
- { 0x21, 0x40000100 },
- { 0x22, 0x40000100 },
- { 0x23, 0x40000100 },
- {}
+static const unsigned int d965_3st_pin_configs[14] = {
+ 0x0221401f, 0x02a19120, 0x40000100, 0x01014011,
+ 0x01a19021, 0x01813024, 0x40000100, 0x40000100,
+ 0x40000100, 0x40000100, 0x40000100, 0x40000100,
+ 0x40000100, 0x40000100
};
-static const struct hda_pintbl d965_5st_pin_configs[] = {
- { 0x0a, 0x02214020 },
- { 0x0b, 0x02a19080 },
- { 0x0c, 0x0181304e },
- { 0x0d, 0x01014010 },
- { 0x0e, 0x01a19040 },
- { 0x0f, 0x01011012 },
- { 0x10, 0x01016011 },
- { 0x11, 0x40000100 },
- { 0x12, 0x40000100 },
- { 0x13, 0x40000100 },
- { 0x14, 0x40000100 },
- { 0x21, 0x01442070 },
- { 0x22, 0x40000100 },
- { 0x23, 0x40000100 },
- {}
+static const unsigned int d965_5st_pin_configs[14] = {
+ 0x02214020, 0x02a19080, 0x0181304e, 0x01014010,
+ 0x01a19040, 0x01011012, 0x01016011, 0x40000100,
+ 0x40000100, 0x40000100, 0x40000100, 0x01442070,
+ 0x40000100, 0x40000100
};
-static const struct hda_pintbl d965_5st_no_fp_pin_configs[] = {
- { 0x0a, 0x40000100 },
- { 0x0b, 0x40000100 },
- { 0x0c, 0x0181304e },
- { 0x0d, 0x01014010 },
- { 0x0e, 0x01a19040 },
- { 0x0f, 0x01011012 },
- { 0x10, 0x01016011 },
- { 0x11, 0x40000100 },
- { 0x12, 0x40000100 },
- { 0x13, 0x40000100 },
- { 0x14, 0x40000100 },
- { 0x21, 0x01442070 },
- { 0x22, 0x40000100 },
- { 0x23, 0x40000100 },
- {}
+static const unsigned int d965_5st_no_fp_pin_configs[14] = {
+ 0x40000100, 0x40000100, 0x0181304e, 0x01014010,
+ 0x01a19040, 0x01011012, 0x01016011, 0x40000100,
+ 0x40000100, 0x40000100, 0x40000100, 0x01442070,
+ 0x40000100, 0x40000100
};
-static const struct hda_pintbl dell_3st_pin_configs[] = {
- { 0x0a, 0x02211230 },
- { 0x0b, 0x02a11220 },
- { 0x0c, 0x01a19040 },
- { 0x0d, 0x01114210 },
- { 0x0e, 0x01111212 },
- { 0x0f, 0x01116211 },
- { 0x10, 0x01813050 },
- { 0x11, 0x01112214 },
- { 0x12, 0x403003fa },
- { 0x13, 0x90a60040 },
- { 0x14, 0x90a60040 },
- { 0x21, 0x404003fb },
- { 0x22, 0x40c003fc },
- { 0x23, 0x40000100 },
- {}
+static const unsigned int dell_3st_pin_configs[14] = {
+ 0x02211230, 0x02a11220, 0x01a19040, 0x01114210,
+ 0x01111212, 0x01116211, 0x01813050, 0x01112214,
+ 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb,
+ 0x40c003fc, 0x40000100
};
-static void stac927x_fixup_ref_no_jd(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- /* no jack detecion for ref-no-jd model */
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
- codec->no_jack_detect = 1;
-}
-
-static void stac927x_fixup_ref(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- snd_hda_apply_pincfgs(codec, ref927x_pin_configs);
- spec->eapd_mask = spec->gpio_mask = 0;
- spec->gpio_dir = spec->gpio_data = 0;
- }
-}
-
-static void stac927x_fixup_dell_dmic(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action != HDA_FIXUP_ACT_PRE_PROBE)
- return;
-
- if (codec->subsystem_id != 0x1028022f) {
- /* GPIO2 High = Enable EAPD */
- spec->eapd_mask = spec->gpio_mask = 0x04;
- spec->gpio_dir = spec->gpio_data = 0x04;
- }
-
- snd_hda_add_verbs(codec, dell_3st_core_init);
- spec->volknob_init = 1;
-}
-
-static void stac927x_fixup_volknob(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- snd_hda_add_verbs(codec, stac927x_volknob_core_init);
- spec->volknob_init = 1;
- }
-}
-
-static const struct hda_fixup stac927x_fixups[] = {
- [STAC_D965_REF_NO_JD] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac927x_fixup_ref_no_jd,
- .chained = true,
- .chain_id = STAC_D965_REF,
- },
- [STAC_D965_REF] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac927x_fixup_ref,
- },
- [STAC_D965_3ST] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = d965_3st_pin_configs,
- .chained = true,
- .chain_id = STAC_D965_VERBS,
- },
- [STAC_D965_5ST] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = d965_5st_pin_configs,
- .chained = true,
- .chain_id = STAC_D965_VERBS,
- },
- [STAC_D965_VERBS] = {
- .type = HDA_FIXUP_VERBS,
- .v.verbs = d965_core_init,
- },
- [STAC_D965_5ST_NO_FP] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = d965_5st_no_fp_pin_configs,
- },
- [STAC_DELL_3ST] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell_3st_pin_configs,
- .chained = true,
- .chain_id = STAC_927X_DELL_DMIC,
- },
- [STAC_DELL_BIOS] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- /* configure the analog microphone on some laptops */
- { 0x0c, 0x90a79130 },
- /* correct the front output jack as a hp out */
- { 0x0f, 0x0227011f },
- /* correct the front input jack as a mic */
- { 0x0e, 0x02a79130 },
- {}
- },
- .chained = true,
- .chain_id = STAC_927X_DELL_DMIC,
- },
- [STAC_DELL_BIOS_SPDIF] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = (const struct hda_pintbl[]) {
- /* correct the device field to SPDIF out */
- { 0x21, 0x01442070 },
- {}
- },
- .chained = true,
- .chain_id = STAC_DELL_BIOS,
- },
- [STAC_927X_DELL_DMIC] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac927x_fixup_dell_dmic,
- },
- [STAC_927X_VOLKNOB] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac927x_fixup_volknob,
- },
+static const unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = {
+ [STAC_D965_REF_NO_JD] = ref927x_pin_configs,
+ [STAC_D965_REF] = ref927x_pin_configs,
+ [STAC_D965_3ST] = d965_3st_pin_configs,
+ [STAC_D965_5ST] = d965_5st_pin_configs,
+ [STAC_D965_5ST_NO_FP] = d965_5st_no_fp_pin_configs,
+ [STAC_DELL_3ST] = dell_3st_pin_configs,
+ [STAC_DELL_BIOS] = NULL,
+ [STAC_927X_VOLKNOB] = NULL,
};
-static const struct hda_model_fixup stac927x_models[] = {
- { .id = STAC_D965_REF_NO_JD, .name = "ref-no-jd" },
- { .id = STAC_D965_REF, .name = "ref" },
- { .id = STAC_D965_3ST, .name = "3stack" },
- { .id = STAC_D965_5ST, .name = "5stack" },
- { .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" },
- { .id = STAC_DELL_3ST, .name = "dell-3stack" },
- { .id = STAC_DELL_BIOS, .name = "dell-bios" },
- { .id = STAC_927X_VOLKNOB, .name = "volknob" },
- {}
+static const char * const stac927x_models[STAC_927X_MODELS] = {
+ [STAC_927X_AUTO] = "auto",
+ [STAC_D965_REF_NO_JD] = "ref-no-jd",
+ [STAC_D965_REF] = "ref",
+ [STAC_D965_3ST] = "3stack",
+ [STAC_D965_5ST] = "5stack",
+ [STAC_D965_5ST_NO_FP] = "5stack-no-fp",
+ [STAC_DELL_3ST] = "dell-3stack",
+ [STAC_DELL_BIOS] = "dell-bios",
+ [STAC_927X_VOLKNOB] = "volknob",
};
-static const struct snd_pci_quirk stac927x_fixup_tbl[] = {
+static const struct snd_pci_quirk stac927x_cfg_tbl[] = {
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_D965_REF),
@@ -3267,12 +2204,12 @@ static const struct snd_pci_quirk stac927x_fixup_tbl[] = {
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS_SPDIF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS_SPDIF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS),
/* 965 based 5 stack systems */
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2300,
"Intel D965", STAC_D965_5ST),
@@ -3283,20 +2220,10 @@ static const struct snd_pci_quirk stac927x_fixup_tbl[] = {
{} /* terminator */
};
-static const struct hda_pintbl ref9205_pin_configs[] = {
- { 0x0a, 0x40000100 },
- { 0x0b, 0x40000100 },
- { 0x0c, 0x01016011 },
- { 0x0d, 0x01014010 },
- { 0x0e, 0x01813122 },
- { 0x0f, 0x01a19021 },
- { 0x14, 0x01019020 },
- { 0x16, 0x40000100 },
- { 0x17, 0x90a000f0 },
- { 0x18, 0x90a000f0 },
- { 0x21, 0x01441030 },
- { 0x22, 0x01c41030 },
- {}
+static const unsigned int ref9205_pin_configs[12] = {
+ 0x40000100, 0x40000100, 0x01016011, 0x01014010,
+ 0x01813122, 0x01a19021, 0x01019020, 0x40000100,
+ 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
};
/*
@@ -3310,340 +2237,2795 @@ static const struct hda_pintbl ref9205_pin_configs[] = {
10280228 (Dell Vostro 1500)
10280229 (Dell Vostro 1700)
*/
-static const struct hda_pintbl dell_9205_m42_pin_configs[] = {
- { 0x0a, 0x0321101F },
- { 0x0b, 0x03A11020 },
- { 0x0c, 0x400003FA },
- { 0x0d, 0x90170310 },
- { 0x0e, 0x400003FB },
- { 0x0f, 0x400003FC },
- { 0x14, 0x400003FD },
- { 0x16, 0x40F000F9 },
- { 0x17, 0x90A60330 },
- { 0x18, 0x400003FF },
- { 0x21, 0x0144131F },
- { 0x22, 0x40C003FE },
- {}
+static const unsigned int dell_9205_m42_pin_configs[12] = {
+ 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310,
+ 0x400003FB, 0x400003FC, 0x400003FD, 0x40F000F9,
+ 0x90A60330, 0x400003FF, 0x0144131F, 0x40C003FE,
+};
+
+/*
+ STAC 9205 pin configs for
+ 102801F9
+ 102801FA
+ 102801FE
+ 102801FF (Dell Precision M4300)
+ 10280206
+ 10280200
+ 10280201
+*/
+static const unsigned int dell_9205_m43_pin_configs[12] = {
+ 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310,
+ 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9,
+ 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8,
+};
+
+static const unsigned int dell_9205_m44_pin_configs[12] = {
+ 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310,
+ 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9,
+ 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe,
+};
+
+static const unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
+ [STAC_9205_REF] = ref9205_pin_configs,
+ [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs,
+ [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs,
+ [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs,
+ [STAC_9205_EAPD] = NULL,
+};
+
+static const char * const stac9205_models[STAC_9205_MODELS] = {
+ [STAC_9205_AUTO] = "auto",
+ [STAC_9205_REF] = "ref",
+ [STAC_9205_DELL_M42] = "dell-m42",
+ [STAC_9205_DELL_M43] = "dell-m43",
+ [STAC_9205_DELL_M44] = "dell-m44",
+ [STAC_9205_EAPD] = "eapd",
};
-/*
- STAC 9205 pin configs for
- 102801F9
- 102801FA
- 102801FE
- 102801FF (Dell Precision M4300)
- 10280206
- 10280200
- 10280201
-*/
-static const struct hda_pintbl dell_9205_m43_pin_configs[] = {
- { 0x0a, 0x0321101f },
- { 0x0b, 0x03a11020 },
- { 0x0c, 0x90a70330 },
- { 0x0d, 0x90170310 },
- { 0x0e, 0x400000fe },
- { 0x0f, 0x400000ff },
- { 0x14, 0x400000fd },
- { 0x16, 0x40f000f9 },
- { 0x17, 0x400000fa },
- { 0x18, 0x400000fc },
- { 0x21, 0x0144131f },
- { 0x22, 0x40c003f8 },
- /* Enable SPDIF in/out */
- { 0x1f, 0x01441030 },
- { 0x20, 0x1c410030 },
- {}
-};
+static const struct snd_pci_quirk stac9205_cfg_tbl[] = {
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_9205_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xfb30,
+ "SigmaTel", STAC_9205_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
+ "DFI LanParty", STAC_9205_REF),
+ /* Dell */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
+ "unknown Dell", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
+ "unknown Dell", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
+ "unknown Dell", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
+ "unknown Dell", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff,
+ "Dell Precision M4300", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204,
+ "unknown Dell", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c,
+ "Dell Precision", STAC_9205_DELL_M43),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
+ "Dell Inspiron", STAC_9205_DELL_M44),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
+ "Dell Vostro 1500", STAC_9205_DELL_M42),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0229,
+ "Dell Vostro 1700", STAC_9205_DELL_M42),
+ /* Gateway */
+ SND_PCI_QUIRK(0x107b, 0x0560, "Gateway T6834c", STAC_9205_EAPD),
+ SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD),
+ {} /* terminator */
+};
+
+static void stac92xx_set_config_regs(struct hda_codec *codec,
+ const unsigned int *pincfgs)
+{
+ int i;
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (!pincfgs)
+ return;
+
+ for (i = 0; i < spec->num_pins; i++)
+ if (spec->pin_nids[i] && pincfgs[i])
+ snd_hda_codec_set_pincfg(codec, spec->pin_nids[i],
+ pincfgs[i]);
+}
+
+/*
+ * Analog playback callbacks
+ */
+static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ if (spec->stream_delay)
+ msleep(spec->stream_delay);
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
+}
+
+static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream);
+}
+
+static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital playback callbacks
+ */
+static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int stac92xx_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int stac92xx_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
+
+/*
+ * Analog capture callbacks
+ */
+static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid = spec->adc_nids[substream->number];
+
+ if (spec->powerdown_adcs) {
+ msleep(40);
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ }
+ snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
+ return 0;
+}
+
+static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid = spec->adc_nids[substream->number];
+
+ snd_hda_codec_cleanup_stream(codec, nid);
+ if (spec->powerdown_adcs)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ return 0;
+}
+
+static const struct hda_pcm_stream stac92xx_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in stac92xx_build_pcms */
+ .ops = {
+ .open = stac92xx_dig_playback_pcm_open,
+ .close = stac92xx_dig_playback_pcm_close,
+ .prepare = stac92xx_dig_playback_pcm_prepare,
+ .cleanup = stac92xx_dig_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream stac92xx_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in stac92xx_build_pcms */
+};
+
+static const struct hda_pcm_stream stac92xx_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .nid = 0x02, /* NID to query formats and rates */
+ .ops = {
+ .open = stac92xx_playback_pcm_open,
+ .prepare = stac92xx_playback_pcm_prepare,
+ .cleanup = stac92xx_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream stac92xx_pcm_analog_alt_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x06, /* NID to query formats and rates */
+ .ops = {
+ .open = stac92xx_playback_pcm_open,
+ .prepare = stac92xx_playback_pcm_prepare,
+ .cleanup = stac92xx_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream stac92xx_pcm_analog_capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID + .substreams is set in stac92xx_build_pcms */
+ .ops = {
+ .prepare = stac92xx_capture_pcm_prepare,
+ .cleanup = stac92xx_capture_pcm_cleanup
+ },
+};
+
+static int stac92xx_build_pcms(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
+ info->name = "STAC92xx Analog";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->multiout.dac_nids[0];
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ spec->autocfg.line_outs == 2)
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap =
+ snd_pcm_2_1_chmaps;
+
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adcs;
+
+ if (spec->alt_switch) {
+ codec->num_pcms++;
+ info++;
+ info->name = "STAC92xx Analog Alt";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_alt_playback;
+ }
+
+ if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+ codec->num_pcms++;
+ info++;
+ info->name = "STAC92xx Digital";
+ info->pcm_type = spec->autocfg.dig_out_type[0];
+ if (spec->multiout.dig_out_nid) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+ }
+ if (spec->dig_in_nid) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
+ }
+ }
+
+ return 0;
+}
+
+static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
+
+{
+ snd_hda_set_pin_ctl_cache(codec, nid, pin_type);
+}
+
+#define stac92xx_hp_switch_info snd_ctl_boolean_mono_info
+
+static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+
+ ucontrol->value.integer.value[0] = !!spec->hp_switch;
+ return 0;
+}
+
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid);
+
+static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ int nid = kcontrol->private_value;
+
+ spec->hp_switch = ucontrol->value.integer.value[0] ? nid : 0;
+
+ /* check to be sure that the ports are up to date with
+ * switch changes
+ */
+ stac_issue_unsol_event(codec, nid);
+
+ return 1;
+}
+
+static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int i;
+ static const char * const texts[] = {
+ "Mic In", "Line In", "Line Out"
+ };
+
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid = kcontrol->private_value;
+
+ if (nid == spec->mic_switch || nid == spec->line_switch)
+ i = 3;
+ else
+ i = 2;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->value.enumerated.items = i;
+ uinfo->count = 1;
+ if (uinfo->value.enumerated.item >= i)
+ uinfo->value.enumerated.item = i-1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ unsigned int vref = stac92xx_vref_get(codec, nid);
+
+ if (vref == snd_hda_get_default_vref(codec, nid))
+ ucontrol->value.enumerated.item[0] = 0;
+ else if (vref == AC_PINCTL_VREF_GRD)
+ ucontrol->value.enumerated.item[0] = 1;
+ else if (vref == AC_PINCTL_VREF_HIZ)
+ ucontrol->value.enumerated.item[0] = 2;
+
+ return 0;
+}
+
+static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int new_vref = 0;
+ int error;
+ hda_nid_t nid = kcontrol->private_value;
+
+ if (ucontrol->value.enumerated.item[0] == 0)
+ new_vref = snd_hda_get_default_vref(codec, nid);
+ else if (ucontrol->value.enumerated.item[0] == 1)
+ new_vref = AC_PINCTL_VREF_GRD;
+ else if (ucontrol->value.enumerated.item[0] == 2)
+ new_vref = AC_PINCTL_VREF_HIZ;
+ else
+ return 0;
+
+ if (new_vref != stac92xx_vref_get(codec, nid)) {
+ error = stac92xx_vref_set(codec, nid, new_vref);
+ return error;
+ }
+
+ return 0;
+}
+
+static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ char *texts[2];
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (kcontrol->private_value == spec->line_switch)
+ texts[0] = "Line In";
+ else
+ texts[0] = "Mic In";
+ texts[1] = "Line Out";
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->value.enumerated.items = 2;
+ uinfo->count = 1;
+
+ if (uinfo->value.enumerated.item >= 2)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid = kcontrol->private_value;
+ int io_idx = (nid == spec->mic_switch) ? 1 : 0;
+
+ ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx];
+ return 0;
+}
+
+static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid = kcontrol->private_value;
+ int io_idx = (nid == spec->mic_switch) ? 1 : 0;
+ unsigned short val = !!ucontrol->value.enumerated.item[0];
+
+ spec->io_switch[io_idx] = val;
+
+ if (val)
+ stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
+ else {
+ unsigned int pinctl = AC_PINCTL_IN_EN;
+ if (io_idx) /* set VREF for mic */
+ pinctl |= snd_hda_get_default_vref(codec, nid);
+ stac92xx_auto_set_pinctl(codec, nid, pinctl);
+ }
+
+ /* check the auto-mute again: we need to mute/unmute the speaker
+ * appropriately according to the pin direction
+ */
+ if (spec->hp_detect)
+ stac_issue_unsol_event(codec, nid);
+
+ return 1;
+}
+
+#define stac92xx_clfe_switch_info snd_ctl_boolean_mono_info
+
+static int stac92xx_clfe_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+
+ ucontrol->value.integer.value[0] = spec->clfe_swap;
+ return 0;
+}
+
+static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid = kcontrol->private_value & 0xff;
+ unsigned int val = !!ucontrol->value.integer.value[0];
+
+ if (spec->clfe_swap == val)
+ return 0;
+
+ spec->clfe_swap = val;
+
+ snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ spec->clfe_swap ? 0x4 : 0x0);
+
+ return 1;
+}
+
+#define STAC_CODEC_HP_SWITCH(xname) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = 0, \
+ .info = stac92xx_hp_switch_info, \
+ .get = stac92xx_hp_switch_get, \
+ .put = stac92xx_hp_switch_put, \
+ }
+
+#define STAC_CODEC_IO_SWITCH(xname, xpval) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = 0, \
+ .info = stac92xx_io_switch_info, \
+ .get = stac92xx_io_switch_get, \
+ .put = stac92xx_io_switch_put, \
+ .private_value = xpval, \
+ }
+
+#define STAC_CODEC_CLFE_SWITCH(xname, xpval) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = 0, \
+ .info = stac92xx_clfe_switch_info, \
+ .get = stac92xx_clfe_switch_get, \
+ .put = stac92xx_clfe_switch_put, \
+ .private_value = xpval, \
+ }
+
+enum {
+ STAC_CTL_WIDGET_VOL,
+ STAC_CTL_WIDGET_MUTE,
+ STAC_CTL_WIDGET_MUTE_BEEP,
+ STAC_CTL_WIDGET_MONO_MUX,
+ STAC_CTL_WIDGET_HP_SWITCH,
+ STAC_CTL_WIDGET_IO_SWITCH,
+ STAC_CTL_WIDGET_CLFE_SWITCH,
+ STAC_CTL_WIDGET_DC_BIAS
+};
+
+static const struct snd_kcontrol_new stac92xx_control_templates[] = {
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+ HDA_CODEC_MUTE(NULL, 0, 0, 0),
+ HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0),
+ STAC_MONO_MUX,
+ STAC_CODEC_HP_SWITCH(NULL),
+ STAC_CODEC_IO_SWITCH(NULL, 0),
+ STAC_CODEC_CLFE_SWITCH(NULL, 0),
+ DC_BIAS(NULL, 0, 0),
+};
+
+/* add dynamic controls */
+static struct snd_kcontrol_new *
+stac_control_new(struct sigmatel_spec *spec,
+ const struct snd_kcontrol_new *ktemp,
+ const char *name,
+ unsigned int subdev)
+{
+ struct snd_kcontrol_new *knew;
+
+ knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return NULL;
+ *knew = *ktemp;
+ knew->name = kstrdup(name, GFP_KERNEL);
+ if (!knew->name) {
+ /* roolback */
+ memset(knew, 0, sizeof(*knew));
+ spec->kctls.alloced--;
+ return NULL;
+ }
+ knew->subdevice = subdev;
+ return knew;
+}
+
+static struct snd_kcontrol_new *
+add_control_temp(struct sigmatel_spec *spec,
+ const struct snd_kcontrol_new *ktemp,
+ int idx, const char *name,
+ unsigned long val)
+{
+ struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name,
+ HDA_SUBDEV_AMP_FLAG);
+ if (!knew)
+ return NULL;
+ knew->index = idx;
+ knew->private_value = val;
+ return knew;
+}
+
+static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
+ const struct snd_kcontrol_new *ktemp,
+ int idx, const char *name,
+ unsigned long val)
+{
+ return add_control_temp(spec, ktemp, idx, name, val) ? 0 : -ENOMEM;
+}
+
+static inline int stac92xx_add_control_idx(struct sigmatel_spec *spec,
+ int type, int idx, const char *name,
+ unsigned long val)
+{
+ return stac92xx_add_control_temp(spec,
+ &stac92xx_control_templates[type],
+ idx, name, val);
+}
+
+
+/* add dynamic controls */
+static inline int stac92xx_add_control(struct sigmatel_spec *spec, int type,
+ const char *name, unsigned long val)
+{
+ return stac92xx_add_control_idx(spec, type, 0, name, val);
+}
+
+static const struct snd_kcontrol_new stac_input_src_temp = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Source",
+ .info = stac92xx_mux_enum_info,
+ .get = stac92xx_mux_enum_get,
+ .put = stac92xx_mux_enum_put,
+};
+
+static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
+ hda_nid_t nid, int idx)
+{
+ int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ int control = 0;
+ struct sigmatel_spec *spec = codec->spec;
+ char name[22];
+
+ if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
+ if (spec->headset_jack && snd_hda_get_input_pin_attr(def_conf)
+ != INPUT_PIN_ATTR_DOCK)
+ return 0;
+ if (snd_hda_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
+ && nid == spec->line_switch)
+ control = STAC_CTL_WIDGET_IO_SWITCH;
+ else if (snd_hda_query_pin_caps(codec, nid)
+ & (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT))
+ control = STAC_CTL_WIDGET_DC_BIAS;
+ else if (nid == spec->mic_switch)
+ control = STAC_CTL_WIDGET_IO_SWITCH;
+ }
+
+ if (control) {
+ snd_hda_get_pin_label(codec, nid, &spec->autocfg,
+ name, sizeof(name), NULL);
+ return stac92xx_add_control(codec->spec, control,
+ strcat(name, " Jack Mode"), nid);
+ }
+
+ return 0;
+}
+
+static int stac92xx_add_input_source(struct sigmatel_spec *spec)
+{
+ struct snd_kcontrol_new *knew;
+ struct hda_input_mux *imux = &spec->private_imux;
+
+ if (spec->auto_mic)
+ return 0; /* no need for input source */
+ if (!spec->num_adcs || imux->num_items <= 1)
+ return 0; /* no need for input source control */
+ knew = stac_control_new(spec, &stac_input_src_temp,
+ stac_input_src_temp.name, 0);
+ if (!knew)
+ return -ENOMEM;
+ knew->count = spec->num_adcs;
+ return 0;
+}
+
+/* check whether the line-input can be used as line-out */
+static hda_nid_t check_line_out_switch(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
+ unsigned int pincap;
+ int i;
+
+ if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
+ return 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) {
+ nid = cfg->inputs[i].pin;
+ pincap = snd_hda_query_pin_caps(codec, nid);
+ if (pincap & AC_PINCAP_OUT)
+ return nid;
+ }
+ }
+ return 0;
+}
+
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid);
+
+/* check whether the mic-input can be used as line-out */
+static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int def_conf, pincap;
+ int i;
+
+ *dac = 0;
+ if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
+ return 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ continue;
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ /* some laptops have an internal analog microphone
+ * which can't be used as a output */
+ if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
+ pincap = snd_hda_query_pin_caps(codec, nid);
+ if (pincap & AC_PINCAP_OUT) {
+ *dac = get_unassigned_dac(codec, nid);
+ if (*dac)
+ return nid;
+ }
+ }
+ }
+ return 0;
+}
+
+static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+ int i;
+
+ for (i = 0; i < spec->multiout.num_dacs; i++) {
+ if (spec->multiout.dac_nids[i] == nid)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int check_all_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+ int i;
+ if (is_in_dac_nids(spec, nid))
+ return 1;
+ for (i = 0; i < spec->autocfg.hp_outs; i++)
+ if (spec->hp_dacs[i] == nid)
+ return 1;
+ for (i = 0; i < spec->autocfg.speaker_outs; i++)
+ if (spec->speaker_dacs[i] == nid)
+ return 1;
+ return 0;
+}
+
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int j, conn_len;
+ hda_nid_t conn[HDA_MAX_CONNECTIONS], fallback_dac;
+ unsigned int wcaps, wtype;
+
+ conn_len = snd_hda_get_connections(codec, nid, conn,
+ HDA_MAX_CONNECTIONS);
+ /* 92HD88: trace back up the link of nids to find the DAC */
+ while (conn_len == 1 && (get_wcaps_type(get_wcaps(codec, conn[0]))
+ != AC_WID_AUD_OUT)) {
+ nid = conn[0];
+ conn_len = snd_hda_get_connections(codec, nid, conn,
+ HDA_MAX_CONNECTIONS);
+ }
+ for (j = 0; j < conn_len; j++) {
+ wcaps = get_wcaps(codec, conn[j]);
+ wtype = get_wcaps_type(wcaps);
+ /* we check only analog outputs */
+ if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL))
+ continue;
+ /* if this route has a free DAC, assign it */
+ if (!check_all_dac_nids(spec, conn[j])) {
+ if (conn_len > 1) {
+ /* select this DAC in the pin's input mux */
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL, j);
+ }
+ return conn[j];
+ }
+ }
+
+ /* if all DACs are already assigned, connect to the primary DAC,
+ unless we're assigning a secondary headphone */
+ fallback_dac = spec->multiout.dac_nids[0];
+ if (spec->multiout.hp_nid) {
+ for (j = 0; j < cfg->hp_outs; j++)
+ if (cfg->hp_pins[j] == nid) {
+ fallback_dac = spec->multiout.hp_nid;
+ break;
+ }
+ }
+
+ if (conn_len > 1) {
+ for (j = 0; j < conn_len; j++) {
+ if (conn[j] == fallback_dac) {
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL, j);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
+static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
+
+/*
+ * Fill in the dac_nids table from the parsed pin configuration
+ * This function only works when every pin in line_out_pins[]
+ * contains atleast one DAC in its connection list. Some 92xx
+ * codecs are not connected directly to a DAC, such as the 9200
+ * and 9202/925x. For those, dac_nids[] must be hard-coded.
+ */
+static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+ hda_nid_t nid, dac;
+
+ for (i = 0; i < cfg->line_outs; i++) {
+ nid = cfg->line_out_pins[i];
+ dac = get_unassigned_dac(codec, nid);
+ if (!dac) {
+ if (spec->multiout.num_dacs > 0) {
+ /* we have already working output pins,
+ * so let's drop the broken ones again
+ */
+ cfg->line_outs = spec->multiout.num_dacs;
+ break;
+ }
+ /* error out, no available DAC found */
+ snd_printk(KERN_ERR
+ "%s: No available DAC for pin 0x%x\n",
+ __func__, nid);
+ return -ENODEV;
+ }
+ add_spec_dacs(spec, dac);
+ }
+
+ for (i = 0; i < cfg->hp_outs; i++) {
+ nid = cfg->hp_pins[i];
+ dac = get_unassigned_dac(codec, nid);
+ if (dac) {
+ if (!spec->multiout.hp_nid)
+ spec->multiout.hp_nid = dac;
+ else
+ add_spec_extra_dacs(spec, dac);
+ }
+ spec->hp_dacs[i] = dac;
+ }
+
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ nid = cfg->speaker_pins[i];
+ dac = get_unassigned_dac(codec, nid);
+ if (dac)
+ add_spec_extra_dacs(spec, dac);
+ spec->speaker_dacs[i] = dac;
+ }
+
+ /* add line-in as output */
+ nid = check_line_out_switch(codec);
+ if (nid) {
+ dac = get_unassigned_dac(codec, nid);
+ if (dac) {
+ snd_printdd("STAC: Add line-in 0x%x as output %d\n",
+ nid, cfg->line_outs);
+ cfg->line_out_pins[cfg->line_outs] = nid;
+ cfg->line_outs++;
+ spec->line_switch = nid;
+ add_spec_dacs(spec, dac);
+ }
+ }
+ /* add mic as output */
+ nid = check_mic_out_switch(codec, &dac);
+ if (nid && dac) {
+ snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
+ nid, cfg->line_outs);
+ cfg->line_out_pins[cfg->line_outs] = nid;
+ cfg->line_outs++;
+ spec->mic_switch = nid;
+ add_spec_dacs(spec, dac);
+ }
+
+ snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+ spec->multiout.num_dacs,
+ spec->multiout.dac_nids[0],
+ spec->multiout.dac_nids[1],
+ spec->multiout.dac_nids[2],
+ spec->multiout.dac_nids[3],
+ spec->multiout.dac_nids[4]);
+
+ return 0;
+}
+
+/* create volume control/switch for the given prefx type */
+static int create_controls_idx(struct hda_codec *codec, const char *pfx,
+ int idx, hda_nid_t nid, int chs)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ char name[32];
+ int err;
+
+ if (!spec->check_volume_offset) {
+ unsigned int caps, step, nums, db_scale;
+ caps = query_amp_caps(codec, nid, HDA_OUTPUT);
+ step = (caps & AC_AMPCAP_STEP_SIZE) >>
+ AC_AMPCAP_STEP_SIZE_SHIFT;
+ step = (step + 1) * 25; /* in .01dB unit */
+ nums = (caps & AC_AMPCAP_NUM_STEPS) >>
+ AC_AMPCAP_NUM_STEPS_SHIFT;
+ db_scale = nums * step;
+ /* if dB scale is over -64dB, and finer enough,
+ * let's reduce it to half
+ */
+ if (db_scale > 6400 && nums >= 0x1f)
+ spec->volume_offset = nums / 2;
+ spec->check_volume_offset = 1;
+ }
+
+ sprintf(name, "%s Playback Volume", pfx);
+ err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, idx, name,
+ HDA_COMPOSE_AMP_VAL_OFS(nid, chs, 0, HDA_OUTPUT,
+ spec->volume_offset));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", pfx);
+ err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_MUTE, idx, name,
+ HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+#define create_controls(codec, pfx, nid, chs) \
+ create_controls_idx(codec, pfx, 0, nid, chs)
+
+static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+ if (spec->multiout.num_dacs > 4) {
+ printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
+ return 1;
+ } else {
+ snd_BUG_ON(spec->multiout.dac_nids != spec->dac_nids);
+ spec->dac_nids[spec->multiout.num_dacs] = nid;
+ spec->multiout.num_dacs++;
+ }
+ return 0;
+}
+
+static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
+ if (!spec->multiout.extra_out_nid[i]) {
+ spec->multiout.extra_out_nid[i] = nid;
+ return 0;
+ }
+ }
+ printk(KERN_WARNING "stac92xx: No space for extra DAC 0x%x\n", nid);
+ return 1;
+}
+
+/* Create output controls
+ * The mixer elements are named depending on the given type (AUTO_PIN_XXX_OUT)
+ */
+static int create_multi_out_ctls(struct hda_codec *codec, int num_outs,
+ const hda_nid_t *pins,
+ const hda_nid_t *dac_nids,
+ int type)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ static const char * const chname[4] = {
+ "Front", "Surround", NULL /*CLFE*/, "Side"
+ };
+ hda_nid_t nid;
+ int i, err;
+ unsigned int wid_caps;
+
+ for (i = 0; i < num_outs && i < ARRAY_SIZE(chname); i++) {
+ if (type == AUTO_PIN_HP_OUT && !spec->hp_detect) {
+ if (is_jack_detectable(codec, pins[i]))
+ spec->hp_detect = 1;
+ }
+ nid = dac_nids[i];
+ if (!nid)
+ continue;
+ if (type != AUTO_PIN_HP_OUT && i == 2) {
+ /* Center/LFE */
+ err = create_controls(codec, "Center", nid, 1);
+ if (err < 0)
+ return err;
+ err = create_controls(codec, "LFE", nid, 2);
+ if (err < 0)
+ return err;
+
+ wid_caps = get_wcaps(codec, nid);
+
+ if (wid_caps & AC_WCAP_LR_SWAP) {
+ err = stac92xx_add_control(spec,
+ STAC_CTL_WIDGET_CLFE_SWITCH,
+ "Swap Center/LFE Playback Switch", nid);
+
+ if (err < 0)
+ return err;
+ }
+
+ } else {
+ const char *name;
+ int idx;
+ switch (type) {
+ case AUTO_PIN_HP_OUT:
+ name = "Headphone";
+ idx = i;
+ break;
+ case AUTO_PIN_SPEAKER_OUT:
+ if (num_outs <= 2) {
+ name = i ? "Bass Speaker" : "Speaker";
+ idx = 0;
+ break;
+ }
+ /* Fall through in case of multi speaker outs */
+ default:
+ name = chname[i];
+ idx = 0;
+ break;
+ }
+ err = create_controls_idx(codec, name, idx, nid, 3);
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
+ unsigned int dir_mask, unsigned int data);
+
+/* hook for controlling mic-mute LED GPIO */
+static int stac92xx_capture_sw_put_led(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ int err;
+ bool mute;
+
+ err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ if (err <= 0)
+ return err;
+ mute = !(ucontrol->value.integer.value[0] &&
+ ucontrol->value.integer.value[1]);
+ if (spec->mic_mute_led_on != mute) {
+ spec->mic_mute_led_on = mute;
+ if (mute)
+ spec->gpio_data |= spec->mic_mute_led_gpio;
+ else
+ spec->gpio_data &= ~spec->mic_mute_led_gpio;
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data);
+ }
+ return err;
+}
+
+static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol,
+ unsigned long sw, int idx)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+ int err;
+
+ err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx,
+ "Capture Volume", vol);
+ if (err < 0)
+ return err;
+
+ knew = add_control_temp(spec,
+ &stac92xx_control_templates[STAC_CTL_WIDGET_MUTE],
+ idx, "Capture Switch", sw);
+ if (!knew)
+ return -ENOMEM;
+ /* add a LED hook for some HP laptops */
+ if (spec->mic_mute_led_gpio)
+ knew->put = stac92xx_capture_sw_put_led;
+
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int err;
+ int idx;
+
+ err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins,
+ spec->multiout.dac_nids,
+ cfg->line_out_type);
+ if (err < 0)
+ return err;
+
+ if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) {
+ err = stac92xx_add_control(spec,
+ STAC_CTL_WIDGET_HP_SWITCH,
+ "Headphone as Line Out Switch",
+ cfg->hp_pins[cfg->hp_outs - 1]);
+ if (err < 0)
+ return err;
+ }
+
+ for (idx = 0; idx < cfg->num_inputs; idx++) {
+ if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
+ break;
+ nid = cfg->inputs[idx].pin;
+ err = stac92xx_add_jack_mode_control(codec, nid, idx);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+/* add playback controls for Speaker and HP outputs */
+static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int err;
+
+ err = create_multi_out_ctls(codec, cfg->hp_outs, cfg->hp_pins,
+ spec->hp_dacs, AUTO_PIN_HP_OUT);
+ if (err < 0)
+ return err;
+
+ err = create_multi_out_ctls(codec, cfg->speaker_outs, cfg->speaker_pins,
+ spec->speaker_dacs, AUTO_PIN_SPEAKER_OUT);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/* labels for mono mux outputs */
+static const char * const stac92xx_mono_labels[4] = {
+ "DAC0", "DAC1", "Mixer", "DAC2"
+};
+
+/* create mono mux for mono out on capable codecs */
+static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_input_mux *mono_mux = &spec->private_mono_mux;
+ int i, num_cons;
+ hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)];
+
+ num_cons = snd_hda_get_connections(codec,
+ spec->mono_nid,
+ con_lst,
+ HDA_MAX_NUM_INPUTS);
+ if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
+ return -EINVAL;
+
+ for (i = 0; i < num_cons; i++)
+ snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i,
+ NULL);
+
+ return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
+ "Mono Mux", spec->mono_nid);
+}
+
+/* create PC beep volume controls */
+static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
+ hda_nid_t nid)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
+ int err, type = STAC_CTL_WIDGET_MUTE_BEEP;
+
+ if (spec->anabeep_nid == nid)
+ type = STAC_CTL_WIDGET_MUTE;
+
+ /* check for mute support for the the amp */
+ if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
+ err = stac92xx_add_control(spec, type,
+ "Beep Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+
+ /* check to see if there is volume support for the amp */
+ if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) {
+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL,
+ "Beep Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+#define stac92xx_dig_beep_switch_info snd_ctl_boolean_mono_info
+
+static int stac92xx_dig_beep_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ ucontrol->value.integer.value[0] = codec->beep->enabled;
+ return 0;
+}
+
+static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
+}
+
+static const struct snd_kcontrol_new stac92xx_dig_beep_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = stac92xx_dig_beep_switch_info,
+ .get = stac92xx_dig_beep_switch_get,
+ .put = stac92xx_dig_beep_switch_put,
+};
+
+static int stac92xx_beep_switch_ctl(struct hda_codec *codec)
+{
+ return stac92xx_add_control_temp(codec->spec, &stac92xx_dig_beep_ctrl,
+ 0, "Beep Playback Switch", 0);
+}
+#endif
+
+static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i, j, err = 0;
+
+ for (i = 0; i < spec->num_muxes; i++) {
+ hda_nid_t nid;
+ unsigned int wcaps;
+ unsigned long val;
+
+ nid = spec->mux_nids[i];
+ wcaps = get_wcaps(codec, nid);
+ if (!(wcaps & AC_WCAP_OUT_AMP))
+ continue;
+
+ /* check whether already the same control was created as
+ * normal Capture Volume.
+ */
+ val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+ for (j = 0; j < spec->num_caps; j++) {
+ if (spec->capvols[j] == val)
+ break;
+ }
+ if (j < spec->num_caps)
+ continue;
+
+ err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, i,
+ "Mux Capture Volume", val);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+};
+
+static const char * const stac92xx_spdif_labels[3] = {
+ "Digital Playback", "Analog Mux 1", "Analog Mux 2",
+};
+
+static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_input_mux *spdif_mux = &spec->private_smux;
+ const char * const *labels = spec->spdif_labels;
+ int i, num_cons;
+ hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
+
+ num_cons = snd_hda_get_connections(codec,
+ spec->smux_nids[0],
+ con_lst,
+ HDA_MAX_NUM_INPUTS);
+ if (num_cons <= 0)
+ return -EINVAL;
+
+ if (!labels)
+ labels = stac92xx_spdif_labels;
+
+ for (i = 0; i < num_cons; i++)
+ snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL);
+
+ return 0;
+}
+
+/* labels for dmic mux inputs */
+static const char * const stac92xx_dmic_labels[5] = {
+ "Analog Inputs", "Digital Mic 1", "Digital Mic 2",
+ "Digital Mic 3", "Digital Mic 4"
+};
+
+static hda_nid_t get_connected_node(struct hda_codec *codec, hda_nid_t mux,
+ int idx)
+{
+ hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+ int nums;
+ nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+ if (idx >= 0 && idx < nums)
+ return conn[idx];
+ return 0;
+}
+
+/* look for NID recursively */
+#define get_connection_index(codec, mux, nid) \
+ snd_hda_get_conn_index(codec, mux, nid, 1)
+
+/* create a volume assigned to the given pin (only if supported) */
+/* return 1 if the volume control is created */
+static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
+ const char *label, int idx, int direction)
+{
+ unsigned int caps, nums;
+ char name[32];
+ int err;
+
+ if (direction == HDA_OUTPUT)
+ caps = AC_WCAP_OUT_AMP;
+ else
+ caps = AC_WCAP_IN_AMP;
+ if (!(get_wcaps(codec, nid) & caps))
+ return 0;
+ caps = query_amp_caps(codec, nid, direction);
+ nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ if (!nums)
+ return 0;
+ snprintf(name, sizeof(name), "%s Capture Volume", label);
+ err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
+ if (err < 0)
+ return err;
+ return 1;
+}
+
+/* create playback/capture controls for input pins on dmic capable codecs */
+static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->private_imux;
+ struct hda_input_mux *dimux = &spec->private_dimux;
+ int err, i;
+ unsigned int def_conf;
+
+ snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL);
+
+ for (i = 0; i < spec->num_dmics; i++) {
+ hda_nid_t nid;
+ int index, type_idx;
+ char label[32];
+
+ nid = spec->dmic_nids[i];
+ if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+ continue;
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+ continue;
+
+ index = get_connection_index(codec, spec->dmux_nids[0], nid);
+ if (index < 0)
+ continue;
+
+ snd_hda_get_pin_label(codec, nid, &spec->autocfg,
+ label, sizeof(label), NULL);
+ snd_hda_add_imux_item(dimux, label, index, &type_idx);
+ if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1)
+ snd_hda_add_imux_item(imux, label, index, &type_idx);
+
+ err = create_elem_capture_vol(codec, nid, label, type_idx,
+ HDA_INPUT);
+ if (err < 0)
+ return err;
+ if (!err) {
+ err = create_elem_capture_vol(codec, nid, label,
+ type_idx, HDA_OUTPUT);
+ if (err < 0)
+ return err;
+ if (!err) {
+ nid = get_connected_node(codec,
+ spec->dmux_nids[0], index);
+ if (nid)
+ err = create_elem_capture_vol(codec,
+ nid, label,
+ type_idx, HDA_INPUT);
+ if (err < 0)
+ return err;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock)
+{
+ unsigned int cfg;
+ unsigned int type;
+
+ if (!nid)
+ return 0;
+ cfg = snd_hda_codec_get_pincfg(codec, nid);
+ type = get_defcfg_device(cfg);
+ switch (snd_hda_get_input_pin_attr(cfg)) {
+ case INPUT_PIN_ATTR_INT:
+ if (*fixed)
+ return 1; /* already occupied */
+ if (type != AC_JACK_MIC_IN)
+ return 1; /* invalid type */
+ *fixed = nid;
+ break;
+ case INPUT_PIN_ATTR_UNUSED:
+ break;
+ case INPUT_PIN_ATTR_DOCK:
+ if (*dock)
+ return 1; /* already occupied */
+ if (type != AC_JACK_MIC_IN && type != AC_JACK_LINE_IN)
+ return 1; /* invalid type */
+ *dock = nid;
+ break;
+ default:
+ if (*ext)
+ return 1; /* already occupied */
+ if (type != AC_JACK_MIC_IN)
+ return 1; /* invalid type */
+ *ext = nid;
+ break;
+ }
+ return 0;
+}
+
+static int set_mic_route(struct hda_codec *codec,
+ struct sigmatel_mic_route *mic,
+ hda_nid_t pin)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ mic->pin = pin;
+ if (pin == 0)
+ return 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (pin == cfg->inputs[i].pin)
+ break;
+ }
+ if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) {
+ /* analog pin */
+ i = get_connection_index(codec, spec->mux_nids[0], pin);
+ if (i < 0)
+ return -1;
+ mic->mux_idx = i;
+ mic->dmux_idx = -1;
+ if (spec->dmux_nids)
+ mic->dmux_idx = get_connection_index(codec,
+ spec->dmux_nids[0],
+ spec->mux_nids[0]);
+ } else if (spec->dmux_nids) {
+ /* digital pin */
+ i = get_connection_index(codec, spec->dmux_nids[0], pin);
+ if (i < 0)
+ return -1;
+ mic->dmux_idx = i;
+ mic->mux_idx = -1;
+ if (spec->mux_nids)
+ mic->mux_idx = get_connection_index(codec,
+ spec->mux_nids[0],
+ spec->dmux_nids[0]);
+ }
+ return 0;
+}
+
+/* return non-zero if the device is for automatic mic switch */
+static int stac_check_auto_mic(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t fixed, ext, dock;
+ int i;
+
+ fixed = ext = dock = 0;
+ for (i = 0; i < cfg->num_inputs; i++)
+ if (check_mic_pin(codec, cfg->inputs[i].pin,
+ &fixed, &ext, &dock))
+ return 0;
+ for (i = 0; i < spec->num_dmics; i++)
+ if (check_mic_pin(codec, spec->dmic_nids[i],
+ &fixed, &ext, &dock))
+ return 0;
+ if (!fixed || (!ext && !dock))
+ return 0; /* no input to switch */
+ if (!is_jack_detectable(codec, ext))
+ return 0; /* no unsol support */
+ if (set_mic_route(codec, &spec->ext_mic, ext) ||
+ set_mic_route(codec, &spec->int_mic, fixed) ||
+ set_mic_route(codec, &spec->dock_mic, dock))
+ return 0; /* something is wrong */
+ return 1;
+}
+
+/* create playback/capture controls for input pins */
+static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct hda_input_mux *imux = &spec->private_imux;
+ int i, j;
+ const char *label;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ int index, err, type_idx;
+
+ index = -1;
+ for (j = 0; j < spec->num_muxes; j++) {
+ index = get_connection_index(codec, spec->mux_nids[j],
+ nid);
+ if (index >= 0)
+ break;
+ }
+ if (index < 0)
+ continue;
+
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ snd_hda_add_imux_item(imux, label, index, &type_idx);
+
+ err = create_elem_capture_vol(codec, nid,
+ label, type_idx,
+ HDA_INPUT);
+ if (err < 0)
+ return err;
+ }
+ spec->num_analog_muxes = imux->num_items;
+
+ if (imux->num_items) {
+ /*
+ * Set the current input for the muxes.
+ * The STAC9221 has two input muxes with identical source
+ * NID lists. Hopefully this won't get confused.
+ */
+ for (i = 0; i < spec->num_muxes; i++) {
+ snd_hda_codec_write_cache(codec, spec->mux_nids[i], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ imux->items[0].index);
+ }
+ }
+
+ return 0;
+}
+
+static void stac92xx_auto_init_multi_out(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->autocfg.line_outs; i++) {
+ hda_nid_t nid = spec->autocfg.line_out_pins[i];
+ stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
+ }
+}
+
+static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->autocfg.hp_outs; i++) {
+ hda_nid_t pin;
+ pin = spec->autocfg.hp_pins[i];
+ if (pin) /* connect to front */
+ stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+ }
+ for (i = 0; i < spec->autocfg.speaker_outs; i++) {
+ hda_nid_t pin;
+ pin = spec->autocfg.speaker_pins[i];
+ if (pin) /* connect to front */
+ stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN);
+ }
+}
+
+static int is_dual_headphones(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int i, valid_hps;
+
+ if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT ||
+ spec->autocfg.hp_outs <= 1)
+ return 0;
+ valid_hps = 0;
+ for (i = 0; i < spec->autocfg.hp_outs; i++) {
+ hda_nid_t nid = spec->autocfg.hp_pins[i];
+ unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid);
+ if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE)
+ continue;
+ valid_hps++;
+ }
+ return (valid_hps > 1);
+}
+
+
+static int stac92xx_parse_auto_config(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t dig_out = 0, dig_in = 0;
+ int hp_swap = 0;
+ int i, err;
+
+ if ((err = snd_hda_parse_pin_def_config(codec,
+ &spec->autocfg,
+ spec->dmic_nids)) < 0)
+ return err;
+ if (! spec->autocfg.line_outs)
+ return 0; /* can't find valid pin config */
+
+ /* If we have no real line-out pin and multiple hp-outs, HPs should
+ * be set up as multi-channel outputs.
+ */
+ if (is_dual_headphones(codec)) {
+ /* Copy hp_outs to line_outs, backup line_outs in
+ * speaker_outs so that the following routines can handle
+ * HP pins as primary outputs.
+ */
+ snd_printdd("stac92xx: Enabling multi-HPs workaround\n");
+ memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
+ sizeof(spec->autocfg.line_out_pins));
+ spec->autocfg.speaker_outs = spec->autocfg.line_outs;
+ memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
+ sizeof(spec->autocfg.hp_pins));
+ spec->autocfg.line_outs = spec->autocfg.hp_outs;
+ spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
+ spec->autocfg.hp_outs = 0;
+ hp_swap = 1;
+ }
+ if (spec->autocfg.mono_out_pin) {
+ int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) &
+ (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);
+ u32 caps = query_amp_caps(codec,
+ spec->autocfg.mono_out_pin, dir);
+ hda_nid_t conn_list[1];
+
+ /* get the mixer node and then the mono mux if it exists */
+ if (snd_hda_get_connections(codec,
+ spec->autocfg.mono_out_pin, conn_list, 1) &&
+ snd_hda_get_connections(codec, conn_list[0],
+ conn_list, 1) > 0) {
+
+ int wcaps = get_wcaps(codec, conn_list[0]);
+ int wid_type = get_wcaps_type(wcaps);
+ /* LR swap check, some stac925x have a mux that
+ * changes the DACs output path instead of the
+ * mono-mux path.
+ */
+ if (wid_type == AC_WID_AUD_SEL &&
+ !(wcaps & AC_WCAP_LR_SWAP))
+ spec->mono_nid = conn_list[0];
+ }
+ if (dir) {
+ hda_nid_t nid = spec->autocfg.mono_out_pin;
+
+ /* most mono outs have a least a mute/unmute switch */
+ dir = (dir & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT;
+ err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
+ "Mono Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir));
+ if (err < 0)
+ return err;
+ /* check for volume support for the amp */
+ if ((caps & AC_AMPCAP_NUM_STEPS)
+ >> AC_AMPCAP_NUM_STEPS_SHIFT) {
+ err = stac92xx_add_control(spec,
+ STAC_CTL_WIDGET_VOL,
+ "Mono Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir));
+ if (err < 0)
+ return err;
+ }
+ }
+
+ stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin,
+ AC_PINCTL_OUT_EN);
+ }
+
+ if (!spec->multiout.num_dacs) {
+ err = stac92xx_auto_fill_dac_nids(codec);
+ if (err < 0)
+ return err;
+ err = stac92xx_auto_create_multi_out_ctls(codec,
+ &spec->autocfg);
+ if (err < 0)
+ return err;
+ }
+
+ /* setup analog beep controls */
+ if (spec->anabeep_nid > 0) {
+ err = stac92xx_auto_create_beep_ctls(codec,
+ spec->anabeep_nid);
+ if (err < 0)
+ return err;
+ }
+
+ /* setup digital beep controls and input device */
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+ if (spec->digbeep_nid > 0) {
+ hda_nid_t nid = spec->digbeep_nid;
+ unsigned int caps;
+
+ err = stac92xx_auto_create_beep_ctls(codec, nid);
+ if (err < 0)
+ return err;
+ err = snd_hda_attach_beep_device(codec, nid);
+ if (err < 0)
+ return err;
+ if (codec->beep) {
+ /* IDT/STAC codecs have linear beep tone parameter */
+ codec->beep->linear_tone = spec->linear_tone_beep;
+ /* if no beep switch is available, make its own one */
+ caps = query_amp_caps(codec, nid, HDA_OUTPUT);
+ if (!(caps & AC_AMPCAP_MUTE)) {
+ err = stac92xx_beep_switch_ctl(codec);
+ if (err < 0)
+ return err;
+ }
+ }
+ }
+#endif
+
+ err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ /* All output parsing done, now restore the swapped hp pins */
+ if (hp_swap) {
+ memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
+ sizeof(spec->autocfg.hp_pins));
+ spec->autocfg.hp_outs = spec->autocfg.line_outs;
+ spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
+ spec->autocfg.line_outs = 0;
+ }
+
+ if (stac_check_auto_mic(codec)) {
+ spec->auto_mic = 1;
+ /* only one capture for auto-mic */
+ spec->num_adcs = 1;
+ spec->num_caps = 1;
+ spec->num_muxes = 1;
+ }
+
+ for (i = 0; i < spec->num_caps; i++) {
+ err = stac92xx_add_capvol_ctls(codec, spec->capvols[i],
+ spec->capsws[i], i);
+ if (err < 0)
+ return err;
+ }
+
+ err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ if (spec->mono_nid > 0) {
+ err = stac92xx_auto_create_mono_output_ctls(codec);
+ if (err < 0)
+ return err;
+ }
+ if (spec->num_dmics > 0 && !spec->dinput_mux)
+ if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
+ &spec->autocfg)) < 0)
+ return err;
+ if (spec->num_muxes > 0) {
+ err = stac92xx_auto_create_mux_input_ctls(codec);
+ if (err < 0)
+ return err;
+ }
+ if (spec->num_smuxes > 0) {
+ err = stac92xx_auto_create_spdif_mux_ctls(codec);
+ if (err < 0)
+ return err;
+ }
+
+ err = stac92xx_add_input_source(spec);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+ if (spec->multiout.max_channels > 2)
+ spec->surr_switch = 1;
+
+ /* find digital out and in converters */
+ for (i = codec->start_nid; i < codec->start_nid + codec->num_nodes; i++) {
+ unsigned int wid_caps = get_wcaps(codec, i);
+ if (wid_caps & AC_WCAP_DIGITAL) {
+ switch (get_wcaps_type(wid_caps)) {
+ case AC_WID_AUD_OUT:
+ if (!dig_out)
+ dig_out = i;
+ break;
+ case AC_WID_AUD_IN:
+ if (!dig_in)
+ dig_in = i;
+ break;
+ }
+ }
+ }
+ if (spec->autocfg.dig_outs)
+ spec->multiout.dig_out_nid = dig_out;
+ if (dig_in && spec->autocfg.dig_in_pin)
+ spec->dig_in_nid = dig_in;
+
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
+
+ spec->input_mux = &spec->private_imux;
+ if (!spec->dinput_mux)
+ spec->dinput_mux = &spec->private_dimux;
+ spec->sinput_mux = &spec->private_smux;
+ spec->mono_mux = &spec->private_mono_mux;
+ return 1;
+}
+
+/* add playback controls for HP output */
+static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t pin = cfg->hp_pins[0];
+
+ if (! pin)
+ return 0;
+
+ if (is_jack_detectable(codec, pin))
+ spec->hp_detect = 1;
+
+ return 0;
+}
+
+/* add playback controls for LFE output */
+static int stac9200_auto_create_lfe_ctls(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int err;
+ hda_nid_t lfe_pin = 0x0;
+ int i;
+
+ /*
+ * search speaker outs and line outs for a mono speaker pin
+ * with an amp. If one is found, add LFE controls
+ * for it.
+ */
+ for (i = 0; i < spec->autocfg.speaker_outs && lfe_pin == 0x0; i++) {
+ hda_nid_t pin = spec->autocfg.speaker_pins[i];
+ unsigned int wcaps = get_wcaps(codec, pin);
+ wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
+ if (wcaps == AC_WCAP_OUT_AMP)
+ /* found a mono speaker with an amp, must be lfe */
+ lfe_pin = pin;
+ }
+
+ /* if speaker_outs is 0, then speakers may be in line_outs */
+ if (lfe_pin == 0 && spec->autocfg.speaker_outs == 0) {
+ for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
+ hda_nid_t pin = spec->autocfg.line_out_pins[i];
+ unsigned int defcfg;
+ defcfg = snd_hda_codec_get_pincfg(codec, pin);
+ if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) {
+ unsigned int wcaps = get_wcaps(codec, pin);
+ wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
+ if (wcaps == AC_WCAP_OUT_AMP)
+ /* found a mono speaker with an amp,
+ must be lfe */
+ lfe_pin = pin;
+ }
+ }
+ }
+
+ if (lfe_pin) {
+ err = create_controls(codec, "LFE", lfe_pin, 1);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int stac9200_parse_auto_config(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int err;
+
+ if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
+ return err;
+
+ if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
+ return err;
+
+ if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0)
+ return err;
+
+ if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0)
+ return err;
+
+ if (spec->num_muxes > 0) {
+ err = stac92xx_auto_create_mux_input_ctls(codec);
+ if (err < 0)
+ return err;
+ }
+
+ err = stac92xx_add_input_source(spec);
+ if (err < 0)
+ return err;
+
+ if (spec->autocfg.dig_outs)
+ spec->multiout.dig_out_nid = 0x05;
+ if (spec->autocfg.dig_in_pin)
+ spec->dig_in_nid = 0x04;
+
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
+
+ spec->input_mux = &spec->private_imux;
+ spec->dinput_mux = &spec->private_dimux;
+
+ return 1;
+}
+
+/*
+ * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a
+ * funky external mute control using GPIO pins.
+ */
+
+static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
+ unsigned int dir_mask, unsigned int data)
+{
+ unsigned int gpiostate, gpiomask, gpiodir;
+
+ snd_printdd("%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data);
+
+ gpiostate = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+ gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask);
+
+ gpiomask = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_MASK, 0);
+ gpiomask |= mask;
+
+ gpiodir = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DIRECTION, 0);
+ gpiodir |= dir_mask;
+
+ /* Configure GPIOx as CMOS */
+ snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0);
+
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_MASK, gpiomask);
+ snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */
+
+ msleep(1);
+
+ snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
+}
+
+static int stac_add_event(struct hda_codec *codec, hda_nid_t nid,
+ unsigned char type, int data)
+{
+ struct hda_jack_tbl *event;
+
+ event = snd_hda_jack_tbl_new(codec, nid);
+ if (!event)
+ return -ENOMEM;
+ event->action = type;
+ event->private_data = data;
+
+ return 0;
+}
+
+static void handle_unsol_event(struct hda_codec *codec,
+ struct hda_jack_tbl *event);
+
+/* check if given nid is a valid pin and no other events are assigned
+ * to it. If OK, assign the event, set the unsol flag, and returns 1.
+ * Otherwise, returns zero.
+ */
+static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int type)
+{
+ struct hda_jack_tbl *event;
+
+ if (!is_jack_detectable(codec, nid))
+ return 0;
+ event = snd_hda_jack_tbl_new(codec, nid);
+ if (!event)
+ return -ENOMEM;
+ if (event->action && event->action != type)
+ return 0;
+ event->action = type;
+ event->callback = handle_unsol_event;
+ snd_hda_jack_detect_enable(codec, nid, 0);
+ return 1;
+}
+
+static int is_nid_out_jack_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
+{
+ int i;
+ for (i = 0; i < cfg->hp_outs; i++)
+ if (cfg->hp_pins[i] == nid)
+ return 1; /* nid is a HP-Out */
+ for (i = 0; i < cfg->line_outs; i++)
+ if (cfg->line_out_pins[i] == nid)
+ return 1; /* nid is a line-Out */
+ return 0; /* nid is not a HP-Out */
+};
+
+static void stac92xx_power_down(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ /* power down inactive DACs */
+ const hda_nid_t *dac;
+ for (dac = spec->dac_list; *dac; dac++)
+ if (!check_all_dac_nids(spec, *dac))
+ snd_hda_codec_write(codec, *dac, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+}
+
+static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
+ int enable);
+
+static inline int get_int_hint(struct hda_codec *codec, const char *key,
+ int *valp)
+{
+ const char *p;
+ p = snd_hda_get_hint(codec, key);
+ if (p) {
+ unsigned long val;
+ if (!strict_strtoul(p, 0, &val)) {
+ *valp = val;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* override some hints from the hwdep entry */
+static void stac_store_hints(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int val;
+
+ val = snd_hda_get_bool_hint(codec, "hp_detect");
+ if (val >= 0)
+ spec->hp_detect = val;
+ if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) {
+ spec->eapd_mask = spec->gpio_dir = spec->gpio_data =
+ spec->gpio_mask;
+ }
+ if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir))
+ spec->gpio_mask &= spec->gpio_mask;
+ if (get_int_hint(codec, "gpio_data", &spec->gpio_data))
+ spec->gpio_dir &= spec->gpio_mask;
+ if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask))
+ spec->eapd_mask &= spec->gpio_mask;
+ if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute))
+ spec->gpio_mute &= spec->gpio_mask;
+ val = snd_hda_get_bool_hint(codec, "eapd_switch");
+ if (val >= 0)
+ spec->eapd_switch = val;
+}
+
+static void stac_issue_unsol_events(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins)
+{
+ while (num_pins--)
+ stac_issue_unsol_event(codec, *pins++);
+}
+
+/* fake event to set up pins */
+static void stac_fake_hp_events(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (spec->autocfg.hp_outs)
+ stac_issue_unsol_events(codec, spec->autocfg.hp_outs,
+ spec->autocfg.hp_pins);
+ if (spec->autocfg.line_outs &&
+ spec->autocfg.line_out_pins[0] != spec->autocfg.hp_pins[0])
+ stac_issue_unsol_events(codec, spec->autocfg.line_outs,
+ spec->autocfg.line_out_pins);
+}
+
+static int stac92xx_init(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int gpio;
+ int i;
+
+ if (spec->init)
+ snd_hda_sequence_write(codec, spec->init);
+
+ /* power down adcs initially */
+ if (spec->powerdown_adcs)
+ for (i = 0; i < spec->num_adcs; i++)
+ snd_hda_codec_write(codec,
+ spec->adc_nids[i], 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+
+ /* override some hints */
+ stac_store_hints(codec);
+
+ /* set up GPIO */
+ gpio = spec->gpio_data;
+ /* turn on EAPD statically when spec->eapd_switch isn't set.
+ * otherwise, unsol event will turn it on/off dynamically
+ */
+ if (!spec->eapd_switch)
+ gpio |= spec->eapd_mask;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, gpio);
+
+ /* set up pins */
+ if (spec->hp_detect) {
+ /* Enable unsolicited responses on the HP widget */
+ for (i = 0; i < cfg->hp_outs; i++) {
+ hda_nid_t nid = cfg->hp_pins[i];
+ enable_pin_detect(codec, nid, STAC_HP_EVENT);
+ }
+ if (cfg->line_out_type == AUTO_PIN_LINE_OUT &&
+ cfg->speaker_outs > 0) {
+ /* enable pin-detect for line-outs as well */
+ for (i = 0; i < cfg->line_outs; i++) {
+ hda_nid_t nid = cfg->line_out_pins[i];
+ enable_pin_detect(codec, nid, STAC_LO_EVENT);
+ }
+ }
+
+ /* force to enable the first line-out; the others are set up
+ * in unsol_event
+ */
+ stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
+ AC_PINCTL_OUT_EN);
+ /* fake event to set up pins */
+ stac_fake_hp_events(codec);
+ } else {
+ stac92xx_auto_init_multi_out(codec);
+ stac92xx_auto_init_hp_out(codec);
+ for (i = 0; i < cfg->hp_outs; i++)
+ stac_toggle_power_map(codec, cfg->hp_pins[i], 1);
+ }
+ if (spec->auto_mic) {
+ /* initialize connection to analog input */
+ if (spec->dmux_nids)
+ snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
+ AC_VERB_SET_CONNECT_SEL, 0);
+ if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
+ stac_issue_unsol_event(codec, spec->ext_mic.pin);
+ if (enable_pin_detect(codec, spec->dock_mic.pin,
+ STAC_MIC_EVENT))
+ stac_issue_unsol_event(codec, spec->dock_mic.pin);
+ }
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ int type = cfg->inputs[i].type;
+ unsigned int pinctl, conf;
+ if (type == AUTO_PIN_MIC) {
+ /* for mic pins, force to initialize */
+ pinctl = snd_hda_get_default_vref(codec, nid);
+ pinctl |= AC_PINCTL_IN_EN;
+ stac92xx_auto_set_pinctl(codec, nid, pinctl);
+ } else {
+ pinctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ /* if PINCTL already set then skip */
+ /* Also, if both INPUT and OUTPUT are set,
+ * it must be a BIOS bug; need to override, too
+ */
+ if (!(pinctl & AC_PINCTL_IN_EN) ||
+ (pinctl & AC_PINCTL_OUT_EN)) {
+ pinctl &= ~AC_PINCTL_OUT_EN;
+ pinctl |= AC_PINCTL_IN_EN;
+ stac92xx_auto_set_pinctl(codec, nid, pinctl);
+ }
+ }
+ conf = snd_hda_codec_get_pincfg(codec, nid);
+ if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
+ if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT))
+ stac_issue_unsol_event(codec, nid);
+ }
+ }
+ for (i = 0; i < spec->num_dmics; i++)
+ stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
+ AC_PINCTL_IN_EN);
+ if (cfg->dig_out_pins[0])
+ stac92xx_auto_set_pinctl(codec, cfg->dig_out_pins[0],
+ AC_PINCTL_OUT_EN);
+ if (cfg->dig_in_pin)
+ stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
+ AC_PINCTL_IN_EN);
+ for (i = 0; i < spec->num_pwrs; i++) {
+ hda_nid_t nid = spec->pwr_nids[i];
+ unsigned int pinctl, def_conf;
+
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ def_conf = get_defcfg_connect(def_conf);
+ if (def_conf == AC_JACK_PORT_NONE) {
+ /* power off unused ports */
+ stac_toggle_power_map(codec, nid, 0);
+ continue;
+ }
+ if (def_conf == AC_JACK_PORT_FIXED) {
+ /* no need for jack detection for fixed pins */
+ stac_toggle_power_map(codec, nid, 1);
+ continue;
+ }
+ /* power on when no jack detection is available */
+ /* or when the VREF is used for controlling LED */
+ if (!spec->hp_detect ||
+ spec->vref_mute_led_nid == nid ||
+ !is_jack_detectable(codec, nid)) {
+ stac_toggle_power_map(codec, nid, 1);
+ continue;
+ }
+
+ if (is_nid_out_jack_pin(cfg, nid))
+ continue; /* already has an unsol event */
+
+ pinctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ /* outputs are only ports capable of power management
+ * any attempts on powering down a input port cause the
+ * referenced VREF to act quirky.
+ */
+ if (pinctl & AC_PINCTL_IN_EN) {
+ stac_toggle_power_map(codec, nid, 1);
+ continue;
+ }
+ if (enable_pin_detect(codec, nid, STAC_PWR_EVENT)) {
+ stac_issue_unsol_event(codec, nid);
+ continue;
+ }
+ /* none of the above, turn the port OFF */
+ stac_toggle_power_map(codec, nid, 0);
+ }
+
+ /* sync mute LED */
+ if (spec->gpio_led) {
+ if (spec->vmaster_mute.hook)
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+ else /* the very first init call doesn't have vmaster yet */
+ stac92xx_update_led_status(codec, false);
+ }
-static const struct hda_pintbl dell_9205_m44_pin_configs[] = {
- { 0x0a, 0x0421101f },
- { 0x0b, 0x04a11020 },
- { 0x0c, 0x400003fa },
- { 0x0d, 0x90170310 },
- { 0x0e, 0x400003fb },
- { 0x0f, 0x400003fc },
- { 0x14, 0x400003fd },
- { 0x16, 0x400003f9 },
- { 0x17, 0x90a60330 },
- { 0x18, 0x400003ff },
- { 0x21, 0x01441340 },
- { 0x22, 0x40c003fe },
- {}
-};
+ /* sync the power-map */
+ if (spec->num_pwrs)
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_IDT_SET_POWER_MAP,
+ spec->power_map_bits);
+ if (spec->dac_list)
+ stac92xx_power_down(codec);
+ return 0;
+}
-static void stac9205_fixup_ref(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+static void stac92xx_free_kctls(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- snd_hda_apply_pincfgs(codec, ref9205_pin_configs);
- /* SPDIF-In enabled */
- spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0;
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
}
+ snd_array_free(&spec->kctls);
}
-static void stac9205_fixup_dell_m43(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+static void stac92xx_shutup_pins(struct hda_codec *codec)
{
- struct sigmatel_spec *spec = codec->spec;
- struct hda_jack_tbl *jack;
+ unsigned int i, def_conf;
+
+ if (codec->bus->shutdown)
+ return;
+ for (i = 0; i < codec->init_pins.used; i++) {
+ struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ def_conf = snd_hda_codec_get_pincfg(codec, pin->nid);
+ if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
+ snd_hda_set_pin_ctl(codec, pin->nid, 0);
+ }
+}
- if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs);
+static void stac92xx_shutup(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
- /* Enable unsol response for GPIO4/Dock HP connection */
- snd_hda_codec_write_cache(codec, codec->afg, 0,
- AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
- snd_hda_jack_detect_enable_callback(codec, codec->afg,
- STAC_VREF_EVENT,
- stac_vref_event);
- jack = snd_hda_jack_tbl_get(codec, codec->afg);
- if (jack)
- jack->private_data = 0x01;
+ stac92xx_shutup_pins(codec);
- spec->gpio_dir = 0x0b;
- spec->eapd_mask = 0x01;
- spec->gpio_mask = 0x1b;
- spec->gpio_mute = 0x10;
- /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
- * GPIO3 Low = DRM
- */
- spec->gpio_data = 0x01;
- }
+ if (spec->eapd_mask)
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data &
+ ~spec->eapd_mask);
}
-static void stac9205_fixup_eapd(struct hda_codec *codec,
- const struct hda_fixup *fix, int action)
+static void stac92xx_free(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
- if (action == HDA_FIXUP_ACT_PRE_PROBE)
- spec->eapd_switch = 0;
+ if (! spec)
+ return;
+
+ kfree(spec);
+ snd_hda_detach_beep_device(codec);
}
-static const struct hda_fixup stac9205_fixups[] = {
- [STAC_9205_REF] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac9205_fixup_ref,
- },
- [STAC_9205_DELL_M42] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell_9205_m42_pin_configs,
- },
- [STAC_9205_DELL_M43] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac9205_fixup_dell_m43,
- },
- [STAC_9205_DELL_M44] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = dell_9205_m44_pin_configs,
- },
- [STAC_9205_EAPD] = {
- .type = HDA_FIXUP_FUNC,
- .v.func = stac9205_fixup_eapd,
- },
- {}
-};
+static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int flag)
+{
+ unsigned int old_ctl, pin_ctl;
-static const struct hda_model_fixup stac9205_models[] = {
- { .id = STAC_9205_REF, .name = "ref" },
- { .id = STAC_9205_DELL_M42, .name = "dell-m42" },
- { .id = STAC_9205_DELL_M43, .name = "dell-m43" },
- { .id = STAC_9205_DELL_M44, .name = "dell-m44" },
- { .id = STAC_9205_EAPD, .name = "eapd" },
- {}
-};
+ pin_ctl = snd_hda_codec_read(codec, nid,
+ 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
-static const struct snd_pci_quirk stac9205_fixup_tbl[] = {
- /* SigmaTel reference board */
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
- "DFI LanParty", STAC_9205_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xfb30,
- "SigmaTel", STAC_9205_REF),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
- "DFI LanParty", STAC_9205_REF),
- /* Dell */
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
- "unknown Dell", STAC_9205_DELL_M42),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
- "unknown Dell", STAC_9205_DELL_M42),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
- "unknown Dell", STAC_9205_DELL_M42),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
- "unknown Dell", STAC_9205_DELL_M42),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff,
- "Dell Precision M4300", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204,
- "unknown Dell", STAC_9205_DELL_M42),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021b,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021c,
- "Dell Precision", STAC_9205_DELL_M43),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
- "Dell Inspiron", STAC_9205_DELL_M44),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
- "Dell Vostro 1500", STAC_9205_DELL_M42),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0229,
- "Dell Vostro 1700", STAC_9205_DELL_M42),
- /* Gateway */
- SND_PCI_QUIRK(0x107b, 0x0560, "Gateway T6834c", STAC_9205_EAPD),
- SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD),
- {} /* terminator */
-};
+ if (pin_ctl & AC_PINCTL_IN_EN) {
+ /*
+ * we need to check the current set-up direction of
+ * shared input pins since they can be switched via
+ * "xxx as Output" mixer switch
+ */
+ struct sigmatel_spec *spec = codec->spec;
+ if (nid == spec->line_switch || nid == spec->mic_switch)
+ return;
+ }
+
+ old_ctl = pin_ctl;
+ /* if setting pin direction bits, clear the current
+ direction bits first */
+ if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))
+ pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+
+ pin_ctl |= flag;
+ if (old_ctl != pin_ctl)
+ snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl);
+}
+
+static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
+ unsigned int flag)
+{
+ unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
+ 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
+ if (pin_ctl & flag)
+ snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl & ~flag);
+}
+
+static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (!nid)
+ return 0;
+ return snd_hda_jack_detect(codec, nid);
+}
-static int stac_parse_auto_config(struct hda_codec *codec)
+static void stac92xx_line_out_detect(struct hda_codec *codec,
+ int presence)
{
struct sigmatel_spec *spec = codec->spec;
- int err;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
- err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
- if (err < 0)
- return err;
+ if (cfg->speaker_outs == 0)
+ return;
- /* add hooks */
- spec->gen.pcm_playback_hook = stac_playback_pcm_hook;
- spec->gen.pcm_capture_hook = stac_capture_pcm_hook;
+ for (i = 0; i < cfg->line_outs; i++) {
+ if (presence)
+ break;
+ presence = get_pin_presence(codec, cfg->line_out_pins[i]);
+ if (presence) {
+ unsigned int pinctl;
+ pinctl = snd_hda_codec_read(codec,
+ cfg->line_out_pins[i], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ if (pinctl & AC_PINCTL_IN_EN)
+ presence = 0; /* mic- or line-input */
+ }
+ }
- spec->gen.automute_hook = stac_update_outputs;
- spec->gen.hp_automute_hook = stac_hp_automute;
- spec->gen.line_automute_hook = stac_line_automute;
- spec->gen.mic_autoswitch_hook = stac_mic_autoswitch;
+ if (presence) {
+ /* disable speakers */
+ for (i = 0; i < cfg->speaker_outs; i++)
+ stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
+ AC_PINCTL_OUT_EN);
+ if (spec->eapd_mask && spec->eapd_switch)
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data &
+ ~spec->eapd_mask);
+ } else {
+ /* enable speakers */
+ for (i = 0; i < cfg->speaker_outs; i++)
+ stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
+ AC_PINCTL_OUT_EN);
+ if (spec->eapd_mask && spec->eapd_switch)
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data |
+ spec->eapd_mask);
+ }
+}
- err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
- if (err < 0)
- return err;
+/* return non-zero if the hp-pin of the given array index isn't
+ * a jack-detection target
+ */
+static int no_hp_sensing(struct sigmatel_spec *spec, int i)
+{
+ struct auto_pin_cfg *cfg = &spec->autocfg;
- /* minimum value is actually mute */
- spec->gen.vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
+ /* ignore sensing of shared line and mic jacks */
+ if (cfg->hp_pins[i] == spec->line_switch)
+ return 1;
+ if (cfg->hp_pins[i] == spec->mic_switch)
+ return 1;
+ /* ignore if the pin is set as line-out */
+ if (cfg->hp_pins[i] == spec->hp_switch)
+ return 1;
+ return 0;
+}
- /* setup analog beep controls */
- if (spec->anabeep_nid > 0) {
- err = stac_auto_create_beep_ctls(codec,
- spec->anabeep_nid);
- if (err < 0)
- return err;
- }
+static void stac92xx_hp_detect(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, presence;
- /* setup digital beep controls and input device */
-#ifdef CONFIG_SND_HDA_INPUT_BEEP
- if (spec->digbeep_nid > 0) {
- hda_nid_t nid = spec->digbeep_nid;
- unsigned int caps;
+ presence = 0;
+ if (spec->gpio_mute)
+ presence = !(snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute);
- err = stac_auto_create_beep_ctls(codec, nid);
- if (err < 0)
- return err;
- err = snd_hda_attach_beep_device(codec, nid);
- if (err < 0)
- return err;
- if (codec->beep) {
- /* IDT/STAC codecs have linear beep tone parameter */
- codec->beep->linear_tone = spec->linear_tone_beep;
- /* if no beep switch is available, make its own one */
- caps = query_amp_caps(codec, nid, HDA_OUTPUT);
- if (!(caps & AC_AMPCAP_MUTE)) {
- err = stac_beep_switch_ctl(codec);
- if (err < 0)
- return err;
- }
+ for (i = 0; i < cfg->hp_outs; i++) {
+ if (presence)
+ break;
+ if (no_hp_sensing(spec, i))
+ continue;
+ presence = get_pin_presence(codec, cfg->hp_pins[i]);
+ if (presence) {
+ unsigned int pinctl;
+ pinctl = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ if (pinctl & AC_PINCTL_IN_EN)
+ presence = 0; /* mic- or line-input */
}
}
-#endif
-
- if (spec->gpio_led)
- spec->gen.vmaster_mute.hook = stac_vmaster_hook;
- if (spec->aloopback_ctl &&
- snd_hda_get_bool_hint(codec, "loopback") == 1) {
- if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl))
- return -ENOMEM;
+ if (presence) {
+ /* disable lineouts */
+ if (spec->hp_switch)
+ stac92xx_reset_pinctl(codec, spec->hp_switch,
+ AC_PINCTL_OUT_EN);
+ for (i = 0; i < cfg->line_outs; i++)
+ stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
+ AC_PINCTL_OUT_EN);
+ } else {
+ /* enable lineouts */
+ if (spec->hp_switch)
+ stac92xx_set_pinctl(codec, spec->hp_switch,
+ AC_PINCTL_OUT_EN);
+ for (i = 0; i < cfg->line_outs; i++)
+ stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
+ AC_PINCTL_OUT_EN);
+ }
+ stac92xx_line_out_detect(codec, presence);
+ /* toggle hp outs */
+ for (i = 0; i < cfg->hp_outs; i++) {
+ unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN;
+ if (no_hp_sensing(spec, i))
+ continue;
+ if (1 /*presence*/)
+ stac92xx_set_pinctl(codec, cfg->hp_pins[i], val);
+#if 0 /* FIXME */
+/* Resetting the pinctl like below may lead to (a sort of) regressions
+ * on some devices since they use the HP pin actually for line/speaker
+ * outs although the default pin config shows a different pin (that is
+ * wrong and useless).
+ *
+ * So, it's basically a problem of default pin configs, likely a BIOS issue.
+ * But, disabling the code below just works around it, and I'm too tired of
+ * bug reports with such devices...
+ */
+ else
+ stac92xx_reset_pinctl(codec, cfg->hp_pins[i], val);
+#endif /* FIXME */
}
+}
- if (spec->have_spdif_mux) {
- err = stac_create_spdif_mux_ctls(codec);
- if (err < 0)
- return err;
+static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
+ int enable)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int idx, val;
+
+ for (idx = 0; idx < spec->num_pwrs; idx++) {
+ if (spec->pwr_nids[idx] == nid)
+ break;
}
+ if (idx >= spec->num_pwrs)
+ return;
+
+ idx = 1 << idx;
- stac_init_power_map(codec);
+ val = spec->power_map_bits;
+ if (enable)
+ val &= ~idx;
+ else
+ val |= idx;
- return 0;
+ /* power down unused output ports */
+ if (val != spec->power_map_bits) {
+ spec->power_map_bits = val;
+ snd_hda_codec_write(codec, codec->afg, 0,
+ AC_VERB_IDT_SET_POWER_MAP, val);
+ }
}
+static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
+{
+ stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid));
+}
-static int stac_init(struct hda_codec *codec)
+/* get the pin connection (fixed, none, etc) */
+static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
{
struct sigmatel_spec *spec = codec->spec;
- unsigned int gpio;
- int i;
+ unsigned int cfg;
- /* override some hints */
- stac_store_hints(codec);
+ cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
+ return get_defcfg_connect(cfg);
+}
- /* set up GPIO */
- gpio = spec->gpio_data;
- /* turn on EAPD statically when spec->eapd_switch isn't set.
- * otherwise, unsol event will turn it on/off dynamically
- */
- if (!spec->eapd_switch)
- gpio |= spec->eapd_mask;
- stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, gpio);
+static int stac92xx_connected_ports(struct hda_codec *codec,
+ const hda_nid_t *nids, int num_nids)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int idx, num;
+ unsigned int def_conf;
+
+ for (num = 0; num < num_nids; num++) {
+ for (idx = 0; idx < spec->num_pins; idx++)
+ if (spec->pin_nids[idx] == nids[num])
+ break;
+ if (idx >= spec->num_pins)
+ break;
+ def_conf = stac_get_defcfg_connect(codec, idx);
+ if (def_conf == AC_JACK_PORT_NONE)
+ break;
+ }
+ return num;
+}
- snd_hda_gen_init(codec);
+static void stac92xx_mic_detect(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ struct sigmatel_mic_route *mic;
- /* sync the power-map */
- if (spec->num_pwrs)
- snd_hda_codec_write(codec, codec->afg, 0,
- AC_VERB_IDT_SET_POWER_MAP,
- spec->power_map_bits);
+ if (get_pin_presence(codec, spec->ext_mic.pin))
+ mic = &spec->ext_mic;
+ else if (get_pin_presence(codec, spec->dock_mic.pin))
+ mic = &spec->dock_mic;
+ else
+ mic = &spec->int_mic;
+ if (mic->dmux_idx >= 0)
+ snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mic->dmux_idx);
+ if (mic->mux_idx >= 0)
+ snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mic->mux_idx);
+}
- /* power down inactive ADCs */
- if (spec->powerdown_adcs) {
- for (i = 0; i < spec->gen.num_all_adcs; i++) {
- if (spec->active_adcs & (1 << i))
- continue;
- snd_hda_codec_write(codec, spec->gen.all_adcs[i], 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
+static void handle_unsol_event(struct hda_codec *codec,
+ struct hda_jack_tbl *event)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int data;
+
+ switch (event->action) {
+ case STAC_HP_EVENT:
+ case STAC_LO_EVENT:
+ stac92xx_hp_detect(codec);
+ break;
+ case STAC_MIC_EVENT:
+ stac92xx_mic_detect(codec);
+ break;
+ }
+
+ switch (event->action) {
+ case STAC_HP_EVENT:
+ case STAC_LO_EVENT:
+ case STAC_MIC_EVENT:
+ case STAC_INSERT_EVENT:
+ case STAC_PWR_EVENT:
+ if (spec->num_pwrs > 0)
+ stac92xx_pin_sense(codec, event->nid);
+
+ switch (codec->subsystem_id) {
+ case 0x103c308f:
+ if (event->nid == 0xb) {
+ int pin = AC_PINCTL_IN_EN;
+
+ if (get_pin_presence(codec, 0xa)
+ && get_pin_presence(codec, 0xb))
+ pin |= AC_PINCTL_VREF_80;
+ if (!get_pin_presence(codec, 0xb))
+ pin |= AC_PINCTL_VREF_80;
+
+ /* toggle VREF state based on mic + hp pin
+ * status
+ */
+ stac92xx_auto_set_pinctl(codec, 0x0a, pin);
+ }
}
+ break;
+ case STAC_VREF_EVENT:
+ data = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+ /* toggle VREF state based on GPIOx status */
+ snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
+ !!(data & (1 << event->private_data)));
+ break;
}
+}
- return 0;
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct hda_jack_tbl *event = snd_hda_jack_tbl_get(codec, nid);
+ if (!event)
+ return;
+ handle_unsol_event(codec, event);
}
-static void stac_shutup(struct hda_codec *codec)
+static int hp_blike_system(u32 subsystem_id);
+
+static void set_hp_led_gpio(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
+ unsigned int gpio;
- snd_hda_shutup_pins(codec);
+ if (spec->gpio_led)
+ return;
- if (spec->eapd_mask)
- stac_gpio_set(codec, spec->gpio_mask,
- spec->gpio_dir, spec->gpio_data &
- ~spec->eapd_mask);
+ gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
+ gpio &= AC_GPIO_IO_COUNT;
+ if (gpio > 3)
+ spec->gpio_led = 0x08; /* GPIO 3 */
+ else
+ spec->gpio_led = 0x01; /* GPIO 0 */
}
-static void stac_free(struct hda_codec *codec)
+/*
+ * This method searches for the mute LED GPIO configuration
+ * provided as OEM string in SMBIOS. The format of that string
+ * is HP_Mute_LED_P_G or HP_Mute_LED_P
+ * where P can be 0 or 1 and defines mute LED GPIO control state (low/high)
+ * that corresponds to the NOT muted state of the master volume
+ * and G is the index of the GPIO to use as the mute LED control (0..9)
+ * If _G portion is missing it is assigned based on the codec ID
+ *
+ * So, HP B-series like systems may have HP_Mute_LED_0 (current models)
+ * or HP_Mute_LED_0_3 (future models) OEM SMBIOS strings
+ *
+ *
+ * The dv-series laptops don't seem to have the HP_Mute_LED* strings in
+ * SMBIOS - at least the ones I have seen do not have them - which include
+ * my own system (HP Pavilion dv6-1110ax) and my cousin's
+ * HP Pavilion dv9500t CTO.
+ * Need more information on whether it is true across the entire series.
+ * -- kunal
+ */
+static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
{
struct sigmatel_spec *spec = codec->spec;
+ const struct dmi_device *dev = NULL;
- if (!spec)
- return;
+ if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
+ get_int_hint(codec, "gpio_led_polarity",
+ &spec->gpio_led_polarity);
+ return 1;
+ }
+ if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
+ NULL, dev))) {
+ if (sscanf(dev->name, "HP_Mute_LED_%d_%x",
+ &spec->gpio_led_polarity,
+ &spec->gpio_led) == 2) {
+ unsigned int max_gpio;
+ max_gpio = snd_hda_param_read(codec, codec->afg,
+ AC_PAR_GPIO_CAP);
+ max_gpio &= AC_GPIO_IO_COUNT;
+ if (spec->gpio_led < max_gpio)
+ spec->gpio_led = 1 << spec->gpio_led;
+ else
+ spec->vref_mute_led_nid = spec->gpio_led;
+ return 1;
+ }
+ if (sscanf(dev->name, "HP_Mute_LED_%d",
+ &spec->gpio_led_polarity) == 1) {
+ set_hp_led_gpio(codec);
+ return 1;
+ }
+ /* BIOS bug: unfilled OEM string */
+ if (strstr(dev->name, "HP_Mute_LED_P_G")) {
+ set_hp_led_gpio(codec);
+ switch (codec->subsystem_id) {
+ case 0x103c148a:
+ spec->gpio_led_polarity = 0;
+ break;
+ default:
+ spec->gpio_led_polarity = 1;
+ break;
+ }
+ return 1;
+ }
+ }
- snd_hda_gen_spec_free(&spec->gen);
- kfree(spec);
- snd_hda_detach_beep_device(codec);
+ /*
+ * Fallback case - if we don't find the DMI strings,
+ * we statically set the GPIO - if not a B-series system
+ * and default polarity is provided
+ */
+ if (!hp_blike_system(codec->subsystem_id) &&
+ (default_polarity == 0 || default_polarity == 1)) {
+ set_hp_led_gpio(codec);
+ spec->gpio_led_polarity = default_polarity;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int hp_blike_system(u32 subsystem_id)
+{
+ switch (subsystem_id) {
+ case 0x103c1520:
+ case 0x103c1521:
+ case 0x103c1523:
+ case 0x103c1524:
+ case 0x103c1525:
+ case 0x103c1722:
+ case 0x103c1723:
+ case 0x103c1724:
+ case 0x103c1725:
+ case 0x103c1726:
+ case 0x103c1727:
+ case 0x103c1728:
+ case 0x103c1729:
+ case 0x103c172a:
+ case 0x103c172b:
+ case 0x103c307e:
+ case 0x103c307f:
+ case 0x103c3080:
+ case 0x103c3081:
+ case 0x103c7007:
+ case 0x103c7008:
+ return 1;
+ }
+ return 0;
}
#ifdef CONFIG_PROC_FS
@@ -3694,22 +5076,24 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer,
#endif
#ifdef CONFIG_PM
-static int stac_resume(struct hda_codec *codec)
+static int stac92xx_resume(struct hda_codec *codec)
{
- codec->patch_ops.init(codec);
+ stac92xx_init(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
+ /* fake event to set up pins again to override cached values */
+ stac_fake_hp_events(codec);
return 0;
}
-static int stac_suspend(struct hda_codec *codec)
+static int stac92xx_suspend(struct hda_codec *codec)
{
- stac_shutup(codec);
+ stac92xx_shutup(codec);
return 0;
}
-static void stac_set_power_state(struct hda_codec *codec, hda_nid_t fg,
- unsigned int power_state)
+static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state)
{
unsigned int afg_power_state = power_state;
struct sigmatel_spec *spec = codec->spec;
@@ -3726,37 +5110,67 @@ static void stac_set_power_state(struct hda_codec *codec, hda_nid_t fg,
}
snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
afg_power_state);
- snd_hda_codec_set_power_to_all(codec, fg, power_state);
+ snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
}
#else
-#define stac_suspend NULL
-#define stac_resume NULL
-#define stac_set_power_state NULL
+#define stac92xx_suspend NULL
+#define stac92xx_resume NULL
+#define stac92xx_set_power_state NULL
#endif /* CONFIG_PM */
-static const struct hda_codec_ops stac_patch_ops = {
- .build_controls = snd_hda_gen_build_controls,
- .build_pcms = snd_hda_gen_build_pcms,
- .init = stac_init,
- .free = stac_free,
+/* update mute-LED accoring to the master switch */
+static void stac92xx_update_led_status(struct hda_codec *codec, int enabled)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int muted = !enabled;
+
+ if (!spec->gpio_led)
+ return;
+
+ /* LED state is inverted on these systems */
+ if (spec->gpio_led_polarity)
+ muted = !muted;
+
+ if (!spec->vref_mute_led_nid) {
+ if (muted)
+ spec->gpio_data |= spec->gpio_led;
+ else
+ spec->gpio_data &= ~spec->gpio_led;
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir, spec->gpio_data);
+ } else {
+ spec->vref_led = muted ? AC_PINCTL_VREF_50 : AC_PINCTL_VREF_GRD;
+ stac_vrefout_set(codec, spec->vref_mute_led_nid,
+ spec->vref_led);
+ }
+}
+
+static const struct hda_codec_ops stac92xx_patch_ops = {
+ .build_controls = stac92xx_build_controls,
+ .build_pcms = stac92xx_build_pcms,
+ .init = stac92xx_init,
+ .free = stac92xx_free,
.unsol_event = snd_hda_jack_unsol_event,
#ifdef CONFIG_PM
- .suspend = stac_suspend,
- .resume = stac_resume,
+ .suspend = stac92xx_suspend,
+ .resume = stac92xx_resume,
#endif
- .reboot_notify = stac_shutup,
+ .reboot_notify = stac92xx_shutup,
};
-static int alloc_stac_spec(struct hda_codec *codec)
+static int alloc_stac_spec(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pin_nids)
{
struct sigmatel_spec *spec;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
- snd_hda_gen_spec_init(&spec->gen);
codec->spec = spec;
codec->no_trigger_sense = 1; /* seems common with STAC/IDT codecs */
+ spec->num_pins = num_pins;
+ spec->pin_nids = pin_nids;
+ snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
return 0;
}
@@ -3765,29 +5179,59 @@ static int patch_stac9200(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
+ err = alloc_stac_spec(codec, ARRAY_SIZE(stac9200_pin_nids),
+ stac9200_pin_nids);
if (err < 0)
return err;
spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->gen.own_eapd_ctl = 1;
-
- codec->patch_ops = stac_patch_ops;
-
- snd_hda_add_verbs(codec, stac9200_eapd_init);
+ spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
+ stac9200_models,
+ stac9200_cfg_tbl);
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
+ else
+ stac92xx_set_config_regs(codec,
+ stac9200_brd_tbl[spec->board_config]);
+
+ spec->multiout.max_channels = 2;
+ spec->multiout.num_dacs = 1;
+ spec->multiout.dac_nids = stac9200_dac_nids;
+ spec->adc_nids = stac9200_adc_nids;
+ spec->mux_nids = stac9200_mux_nids;
+ spec->num_muxes = 1;
+ spec->num_dmics = 0;
+ spec->num_adcs = 1;
+ spec->num_pwrs = 0;
+
+ if (spec->board_config == STAC_9200_M4 ||
+ spec->board_config == STAC_9200_M4_2 ||
+ spec->board_config == STAC_9200_OQO)
+ spec->init = stac9200_eapd_init;
+ else
+ spec->init = stac9200_core_init;
+ spec->mixer = stac9200_mixer;
- snd_hda_pick_fixup(codec, stac9200_models, stac9200_fixup_tbl,
- stac9200_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ if (spec->board_config == STAC_9200_PANASONIC) {
+ spec->gpio_mask = spec->gpio_dir = 0x09;
+ spec->gpio_data = 0x00;
+ }
- err = stac_parse_auto_config(codec);
+ err = stac9200_parse_auto_config(codec);
if (err < 0) {
- stac_free(codec);
+ stac92xx_free(codec);
return err;
}
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ /* CF-74 has no headphone detection, and the driver should *NOT*
+ * do detection and HP/speaker toggle because the hardware does it.
+ */
+ if (spec->board_config == STAC_9200_PANASONIC)
+ spec->hp_detect = 0;
+
+ codec->patch_ops = stac92xx_patch_ops;
return 0;
}
@@ -3797,29 +5241,79 @@ static int patch_stac925x(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
+ err = alloc_stac_spec(codec, ARRAY_SIZE(stac925x_pin_nids),
+ stac925x_pin_nids);
if (err < 0)
return err;
spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->gen.own_eapd_ctl = 1;
- codec->patch_ops = stac_patch_ops;
-
- snd_hda_add_verbs(codec, stac925x_core_init);
-
- snd_hda_pick_fixup(codec, stac925x_models, stac925x_fixup_tbl,
- stac925x_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ /* Check first for codec ID */
+ spec->board_config = snd_hda_check_board_codec_sid_config(codec,
+ STAC_925x_MODELS,
+ stac925x_models,
+ stac925x_codec_id_cfg_tbl);
+
+ /* Now checks for PCI ID, if codec ID is not found */
+ if (spec->board_config < 0)
+ spec->board_config = snd_hda_check_board_config(codec,
+ STAC_925x_MODELS,
+ stac925x_models,
+ stac925x_cfg_tbl);
+ again:
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
+ else
+ stac92xx_set_config_regs(codec,
+ stac925x_brd_tbl[spec->board_config]);
+
+ spec->multiout.max_channels = 2;
+ spec->multiout.num_dacs = 1;
+ spec->multiout.dac_nids = stac925x_dac_nids;
+ spec->adc_nids = stac925x_adc_nids;
+ spec->mux_nids = stac925x_mux_nids;
+ spec->num_muxes = 1;
+ spec->num_adcs = 1;
+ spec->num_pwrs = 0;
+ switch (codec->vendor_id) {
+ case 0x83847632: /* STAC9202 */
+ case 0x83847633: /* STAC9202D */
+ case 0x83847636: /* STAC9251 */
+ case 0x83847637: /* STAC9251D */
+ spec->num_dmics = STAC925X_NUM_DMICS;
+ spec->dmic_nids = stac925x_dmic_nids;
+ spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids);
+ spec->dmux_nids = stac925x_dmux_nids;
+ break;
+ default:
+ spec->num_dmics = 0;
+ break;
+ }
- err = stac_parse_auto_config(codec);
+ spec->init = stac925x_core_init;
+ spec->mixer = stac925x_mixer;
+ spec->num_caps = 1;
+ spec->capvols = stac925x_capvols;
+ spec->capsws = stac925x_capsws;
+
+ err = stac92xx_parse_auto_config(codec);
+ if (!err) {
+ if (spec->board_config < 0) {
+ printk(KERN_WARNING "hda_codec: No auto-config is "
+ "available, default to model=ref\n");
+ spec->board_config = STAC_925x_REF;
+ goto again;
+ }
+ err = -EINVAL;
+ }
if (err < 0) {
- stac_free(codec);
+ stac92xx_free(codec);
return err;
}
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ codec->patch_ops = stac92xx_patch_ops;
return 0;
}
@@ -3827,79 +5321,357 @@ static int patch_stac925x(struct hda_codec *codec)
static int patch_stac92hd73xx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
+ hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
int err;
int num_dacs;
- err = alloc_stac_spec(codec);
+ err = alloc_stac_spec(codec, ARRAY_SIZE(stac92hd73xx_pin_nids),
+ stac92hd73xx_pin_nids);
if (err < 0)
return err;
spec = codec->spec;
spec->linear_tone_beep = 0;
- spec->gen.mixer_nid = 0x1d;
- spec->have_spdif_mux = 1;
+ codec->slave_dig_outs = stac92hd73xx_slave_dig_outs;
+ spec->board_config = snd_hda_check_board_config(codec,
+ STAC_92HD73XX_MODELS,
+ stac92hd73xx_models,
+ stac92hd73xx_cfg_tbl);
+ /* check codec subsystem id if not found */
+ if (spec->board_config < 0)
+ spec->board_config =
+ snd_hda_check_board_codec_sid_config(codec,
+ STAC_92HD73XX_MODELS, stac92hd73xx_models,
+ stac92hd73xx_codec_id_cfg_tbl);
+again:
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
+ else
+ stac92xx_set_config_regs(codec,
+ stac92hd73xx_brd_tbl[spec->board_config]);
+
+ num_dacs = snd_hda_get_connections(codec, 0x0a,
+ conn, STAC92HD73_DAC_COUNT + 2) - 1;
- num_dacs = snd_hda_get_num_conns(codec, 0x0a) - 1;
if (num_dacs < 3 || num_dacs > 5) {
printk(KERN_WARNING "hda_codec: Could not determine "
"number of channels defaulting to DAC count\n");
- num_dacs = 5;
+ num_dacs = STAC92HD73_DAC_COUNT;
}
-
+ spec->init = stac92hd73xx_core_init;
switch (num_dacs) {
case 0x3: /* 6 Channel */
- spec->aloopback_ctl = &stac92hd73xx_6ch_loopback;
+ spec->aloopback_ctl = stac92hd73xx_6ch_loopback;
break;
case 0x4: /* 8 Channel */
- spec->aloopback_ctl = &stac92hd73xx_8ch_loopback;
+ spec->aloopback_ctl = stac92hd73xx_8ch_loopback;
break;
case 0x5: /* 10 Channel */
- spec->aloopback_ctl = &stac92hd73xx_10ch_loopback;
+ spec->aloopback_ctl = stac92hd73xx_10ch_loopback;
break;
}
+ spec->multiout.dac_nids = spec->dac_nids;
spec->aloopback_mask = 0x01;
spec->aloopback_shift = 8;
spec->digbeep_nid = 0x1c;
+ spec->mux_nids = stac92hd73xx_mux_nids;
+ spec->adc_nids = stac92hd73xx_adc_nids;
+ spec->dmic_nids = stac92hd73xx_dmic_nids;
+ spec->dmux_nids = stac92hd73xx_dmux_nids;
+ spec->smux_nids = stac92hd73xx_smux_nids;
+
+ spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
+ spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
+ spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
+
+ spec->num_caps = STAC92HD73XX_NUM_CAPS;
+ spec->capvols = stac92hd73xx_capvols;
+ spec->capsws = stac92hd73xx_capsws;
+
+ switch (spec->board_config) {
+ case STAC_DELL_EQ:
+ spec->init = dell_eq_core_init;
+ /* fallthru */
+ case STAC_DELL_M6_AMIC:
+ case STAC_DELL_M6_DMIC:
+ case STAC_DELL_M6_BOTH:
+ spec->num_smuxes = 0;
+ spec->eapd_switch = 0;
- /* GPIO0 High = Enable EAPD */
- spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
- spec->gpio_data = 0x01;
-
- spec->eapd_switch = 1;
+ switch (spec->board_config) {
+ case STAC_DELL_M6_AMIC: /* Analog Mics */
+ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
+ spec->num_dmics = 0;
+ break;
+ case STAC_DELL_M6_DMIC: /* Digital Mics */
+ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
+ spec->num_dmics = 1;
+ break;
+ case STAC_DELL_M6_BOTH: /* Both */
+ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
+ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
+ spec->num_dmics = 1;
+ break;
+ }
+ break;
+ case STAC_ALIENWARE_M17X:
+ spec->num_dmics = STAC92HD73XX_NUM_DMICS;
+ spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
+ spec->eapd_switch = 0;
+ break;
+ default:
+ spec->num_dmics = STAC92HD73XX_NUM_DMICS;
+ spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
+ spec->eapd_switch = 1;
+ break;
+ }
+ if (spec->board_config != STAC_92HD73XX_REF) {
+ /* GPIO0 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->gpio_data = 0x01;
+ }
spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
spec->pwr_nids = stac92hd73xx_pwr_nids;
- spec->gen.own_eapd_ctl = 1;
- spec->gen.power_down_unused = 1;
-
- codec->patch_ops = stac_patch_ops;
-
- snd_hda_pick_fixup(codec, stac92hd73xx_models, stac92hd73xx_fixup_tbl,
- stac92hd73xx_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ err = stac92xx_parse_auto_config(codec);
- if (!spec->volknob_init)
- snd_hda_add_verbs(codec, stac92hd73xx_core_init);
+ if (!err) {
+ if (spec->board_config < 0) {
+ printk(KERN_WARNING "hda_codec: No auto-config is "
+ "available, default to model=ref\n");
+ spec->board_config = STAC_92HD73XX_REF;
+ goto again;
+ }
+ err = -EINVAL;
+ }
- err = stac_parse_auto_config(codec);
if (err < 0) {
- stac_free(codec);
+ stac92xx_free(codec);
return err;
}
+ if (spec->board_config == STAC_92HD73XX_NO_JD)
+ spec->hp_detect = 0;
+
+ codec->patch_ops = stac92xx_patch_ops;
+
codec->proc_widget_hook = stac92hd7x_proc_hook;
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ return 0;
+}
+
+static int hp_bnb2011_with_dock(struct hda_codec *codec)
+{
+ if (codec->vendor_id != 0x111d7605 &&
+ codec->vendor_id != 0x111d76d1)
+ return 0;
+
+ switch (codec->subsystem_id) {
+ case 0x103c1618:
+ case 0x103c1619:
+ case 0x103c161a:
+ case 0x103c161b:
+ case 0x103c161c:
+ case 0x103c161d:
+ case 0x103c161e:
+ case 0x103c161f:
+
+ case 0x103c162a:
+ case 0x103c162b:
+
+ case 0x103c1630:
+ case 0x103c1631:
+
+ case 0x103c1633:
+ case 0x103c1634:
+ case 0x103c1635:
+
+ case 0x103c3587:
+ case 0x103c3588:
+ case 0x103c3589:
+ case 0x103c358a:
+ case 0x103c3667:
+ case 0x103c3668:
+ case 0x103c3669:
+
+ return 1;
+ }
return 0;
}
-static void stac_setup_gpio(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec = codec->spec;
+static void stac92hd8x_add_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ int i;
+
+ spec->auto_pin_nids[spec->auto_pin_cnt] = nid;
+ spec->auto_pin_cnt++;
+
+ if (get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
+ get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) {
+ for (i = 0; i < ARRAY_SIZE(stac92hd83xxx_dmic_nids); i++) {
+ if (nid == stac92hd83xxx_dmic_nids[i]) {
+ spec->auto_dmic_nids[spec->auto_dmic_cnt] = nid;
+ spec->auto_dmic_cnt++;
+ }
+ }
+ }
+}
+
+static void stac92hd8x_add_adc(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ spec->auto_adc_nids[spec->auto_adc_cnt] = nid;
+ spec->auto_adc_cnt++;
+}
+
+static void stac92hd8x_add_mux(struct hda_codec *codec, hda_nid_t nid)
+{
+ int i, j;
+ struct sigmatel_spec *spec = codec->spec;
+
+ for (i = 0; i < spec->auto_adc_cnt; i++) {
+ if (get_connection_index(codec,
+ spec->auto_adc_nids[i], nid) >= 0) {
+ /* mux and volume for adc_nids[i] */
+ if (!spec->auto_mux_nids[i]) {
+ spec->auto_mux_nids[i] = nid;
+ /* 92hd codecs capture volume is in mux */
+ spec->auto_capvols[i] = HDA_COMPOSE_AMP_VAL(nid,
+ 3, 0, HDA_OUTPUT);
+ }
+ for (j = 0; j < spec->auto_dmic_cnt; j++) {
+ if (get_connection_index(codec, nid,
+ spec->auto_dmic_nids[j]) >= 0) {
+ /* dmux for adc_nids[i] */
+ if (!spec->auto_dmux_nids[i])
+ spec->auto_dmux_nids[i] = nid;
+ break;
+ }
+ }
+ break;
+ }
+ }
+}
+
+static void stac92hd8x_fill_auto_spec(struct hda_codec *codec)
+{
+ hda_nid_t nid, end_nid;
+ unsigned int wid_caps, wid_type;
+ struct sigmatel_spec *spec = codec->spec;
+
+ end_nid = codec->start_nid + codec->num_nodes;
+
+ for (nid = codec->start_nid; nid < end_nid; nid++) {
+ wid_caps = get_wcaps(codec, nid);
+ wid_type = get_wcaps_type(wid_caps);
+
+ if (wid_type == AC_WID_PIN)
+ stac92hd8x_add_pin(codec, nid);
+
+ if (wid_type == AC_WID_AUD_IN && !(wid_caps & AC_WCAP_DIGITAL))
+ stac92hd8x_add_adc(codec, nid);
+ }
+
+ for (nid = codec->start_nid; nid < end_nid; nid++) {
+ wid_caps = get_wcaps(codec, nid);
+ wid_type = get_wcaps_type(wid_caps);
+
+ if (wid_type == AC_WID_AUD_SEL)
+ stac92hd8x_add_mux(codec, nid);
+ }
+
+ spec->pin_nids = spec->auto_pin_nids;
+ spec->num_pins = spec->auto_pin_cnt;
+ spec->adc_nids = spec->auto_adc_nids;
+ spec->num_adcs = spec->auto_adc_cnt;
+ spec->capvols = spec->auto_capvols;
+ spec->capsws = spec->auto_capvols;
+ spec->num_caps = spec->auto_adc_cnt;
+ spec->mux_nids = spec->auto_mux_nids;
+ spec->num_muxes = spec->auto_adc_cnt;
+ spec->dmux_nids = spec->auto_dmux_nids;
+ spec->num_dmuxes = spec->auto_adc_cnt;
+ spec->dmic_nids = spec->auto_dmic_nids;
+ spec->num_dmics = spec->auto_dmic_cnt;
+}
+
+static int patch_stac92hd83xxx(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec;
+ int default_polarity = -1; /* no default cfg */
+ int err;
+
+ err = alloc_stac_spec(codec, 0, NULL); /* pins filled later */
+ if (err < 0)
+ return err;
+
+ if (hp_bnb2011_with_dock(codec)) {
+ snd_hda_codec_set_pincfg(codec, 0xa, 0x2101201f);
+ snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e);
+ }
+
+ codec->epss = 0; /* longer delay needed for D3 */
+ stac92hd8x_fill_auto_spec(codec);
+
+ spec = codec->spec;
+ spec->linear_tone_beep = 0;
+ codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
+ spec->digbeep_nid = 0x21;
+ spec->pwr_nids = stac92hd83xxx_pwr_nids;
+ spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
+ spec->multiout.dac_nids = spec->dac_nids;
+ spec->init = stac92hd83xxx_core_init;
+
+ spec->board_config = snd_hda_check_board_config(codec,
+ STAC_92HD83XXX_MODELS,
+ stac92hd83xxx_models,
+ stac92hd83xxx_cfg_tbl);
+ /* check codec subsystem id if not found */
+ if (spec->board_config < 0)
+ spec->board_config =
+ snd_hda_check_board_codec_sid_config(codec,
+ STAC_92HD83XXX_MODELS, stac92hd83xxx_models,
+ stac92hd83xxx_codec_id_cfg_tbl);
+again:
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
+ else
+ stac92xx_set_config_regs(codec,
+ stac92hd83xxx_brd_tbl[spec->board_config]);
+
+ codec->patch_ops = stac92xx_patch_ops;
+
+ switch (spec->board_config) {
+ case STAC_HP_ZEPHYR:
+ spec->init = stac92hd83xxx_hp_zephyr_init;
+ break;
+ case STAC_92HD83XXX_HP_LED:
+ default_polarity = 0;
+ break;
+ case STAC_92HD83XXX_HP_INV_LED:
+ default_polarity = 1;
+ break;
+ case STAC_92HD83XXX_HP_MIC_LED:
+ spec->mic_mute_led_gpio = 0x08; /* GPIO3 */
+ break;
+ case STAC_92HD83XXX_HEADSET_JACK:
+ spec->headset_jack = 1;
+ break;
+ }
+
+ if (find_mute_led_cfg(codec, default_polarity))
+ snd_printd("mute LED gpio %d polarity %d\n",
+ spec->gpio_led,
+ spec->gpio_led_polarity);
if (spec->gpio_led) {
if (!spec->vref_mute_led_nid) {
@@ -3908,7 +5680,7 @@ static void stac_setup_gpio(struct hda_codec *codec)
spec->gpio_data |= spec->gpio_led;
} else {
codec->patch_ops.set_power_state =
- stac_set_power_state;
+ stac92xx_set_power_state;
}
}
@@ -3917,91 +5689,99 @@ static void stac_setup_gpio(struct hda_codec *codec)
spec->gpio_dir |= spec->mic_mute_led_gpio;
spec->mic_mute_led_on = true;
spec->gpio_data |= spec->mic_mute_led_gpio;
-
- spec->gen.cap_sync_hook = stac_capture_led_hook;
}
-}
-
-static int patch_stac92hd83xxx(struct hda_codec *codec)
-{
- struct sigmatel_spec *spec;
- int err;
- err = alloc_stac_spec(codec);
- if (err < 0)
- return err;
-
- codec->epss = 0; /* longer delay needed for D3 */
-
- spec = codec->spec;
- spec->linear_tone_beep = 0;
- spec->gen.own_eapd_ctl = 1;
- spec->gen.power_down_unused = 1;
- spec->gen.mixer_nid = 0x1b;
-
- spec->digbeep_nid = 0x21;
- spec->pwr_nids = stac92hd83xxx_pwr_nids;
- spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
- spec->default_polarity = -1; /* no default cfg */
-
- codec->patch_ops = stac_patch_ops;
-
- snd_hda_add_verbs(codec, stac92hd83xxx_core_init);
-
- snd_hda_pick_fixup(codec, stac92hd83xxx_models, stac92hd83xxx_fixup_tbl,
- stac92hd83xxx_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- stac_setup_gpio(codec);
+ err = stac92xx_parse_auto_config(codec);
+ if (!err) {
+ if (spec->board_config < 0) {
+ printk(KERN_WARNING "hda_codec: No auto-config is "
+ "available, default to model=ref\n");
+ spec->board_config = STAC_92HD83XXX_REF;
+ goto again;
+ }
+ err = -EINVAL;
+ }
- err = stac_parse_auto_config(codec);
if (err < 0) {
- stac_free(codec);
+ stac92xx_free(codec);
return err;
}
codec->proc_widget_hook = stac92hd_proc_hook;
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-
return 0;
}
-static const hda_nid_t stac92hd95_pwr_nids[] = {
- 0x0a, 0x0b, 0x0c, 0x0d
-};
-
-static int patch_stac92hd95(struct hda_codec *codec)
+static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
+ hda_nid_t dig0pin)
{
- struct sigmatel_spec *spec;
- int err;
+ struct sigmatel_spec *spec = codec->spec;
+ int idx;
- err = alloc_stac_spec(codec);
- if (err < 0)
- return err;
+ for (idx = 0; idx < spec->num_pins; idx++)
+ if (spec->pin_nids[idx] == dig0pin)
+ break;
+ if ((idx + 2) >= spec->num_pins)
+ return 0;
- codec->epss = 0; /* longer delay needed for D3 */
+ /* dig1pin case */
+ if (stac_get_defcfg_connect(codec, idx + 1) != AC_JACK_PORT_NONE)
+ return 2;
- spec = codec->spec;
- spec->linear_tone_beep = 0;
- spec->gen.own_eapd_ctl = 1;
- spec->gen.power_down_unused = 1;
+ /* dig0pin + dig2pin case */
+ if (stac_get_defcfg_connect(codec, idx + 2) != AC_JACK_PORT_NONE)
+ return 2;
+ if (stac_get_defcfg_connect(codec, idx) != AC_JACK_PORT_NONE)
+ return 1;
+ else
+ return 0;
+}
+
+/* HP dv7 bass switch - GPIO5 */
+#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info
+static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20);
+ return 0;
+}
+
+static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int gpio_data;
- spec->digbeep_nid = 0x19;
- spec->pwr_nids = stac92hd95_pwr_nids;
- spec->num_pwrs = ARRAY_SIZE(stac92hd95_pwr_nids);
- spec->default_polarity = -1; /* no default cfg */
+ gpio_data = (spec->gpio_data & ~0x20) |
+ (ucontrol->value.integer.value[0] ? 0x20 : 0);
+ if (gpio_data == spec->gpio_data)
+ return 0;
+ spec->gpio_data = gpio_data;
+ stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
+ return 1;
+}
- codec->patch_ops = stac_patch_ops;
+static const struct snd_kcontrol_new stac_hp_bass_sw_ctrl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = stac_hp_bass_gpio_info,
+ .get = stac_hp_bass_gpio_get,
+ .put = stac_hp_bass_gpio_put,
+};
- err = stac_parse_auto_config(codec);
- if (err < 0) {
- stac_free(codec);
- return err;
- }
+static int stac_add_hp_bass_switch(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
- codec->proc_widget_hook = stac92hd_proc_hook;
+ if (!stac_control_new(spec, &stac_hp_bass_sw_ctrl,
+ "Bass Speaker Playback Switch", 0))
+ return -ENOMEM;
+ spec->gpio_mask |= 0x20;
+ spec->gpio_dir |= 0x20;
+ spec->gpio_data |= 0x20;
return 0;
}
@@ -4009,32 +5789,82 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
const struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
+ unsigned int pin_cfg;
int err;
- err = alloc_stac_spec(codec);
+ err = alloc_stac_spec(codec, STAC92HD71BXX_NUM_PINS,
+ stac92hd71bxx_pin_nids_4port);
if (err < 0)
return err;
spec = codec->spec;
spec->linear_tone_beep = 0;
- spec->gen.own_eapd_ctl = 1;
- spec->gen.power_down_unused = 1;
- spec->gen.mixer_nid = 0x17;
- spec->have_spdif_mux = 1;
+ codec->patch_ops = stac92xx_patch_ops;
+ switch (codec->vendor_id) {
+ case 0x111d76b6:
+ case 0x111d76b7:
+ break;
+ case 0x111d7603:
+ case 0x111d7608:
+ /* On 92HD75Bx 0x27 isn't a pin nid */
+ spec->num_pins--;
+ /* fallthrough */
+ default:
+ spec->pin_nids = stac92hd71bxx_pin_nids_6port;
+ }
+ spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
+ spec->board_config = snd_hda_check_board_config(codec,
+ STAC_92HD71BXX_MODELS,
+ stac92hd71bxx_models,
+ stac92hd71bxx_cfg_tbl);
+again:
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
+ else
+ stac92xx_set_config_regs(codec,
+ stac92hd71bxx_brd_tbl[spec->board_config]);
+
+ if (spec->board_config != STAC_92HD71BXX_REF) {
+ /* GPIO0 = EAPD */
+ spec->gpio_mask = 0x01;
+ spec->gpio_dir = 0x01;
+ spec->gpio_data = 0x01;
+ }
- codec->patch_ops = stac_patch_ops;
+ spec->dmic_nids = stac92hd71bxx_dmic_nids;
+ spec->dmux_nids = stac92hd71bxx_dmux_nids;
- /* GPIO0 = EAPD */
- spec->gpio_mask = 0x01;
- spec->gpio_dir = 0x01;
- spec->gpio_data = 0x01;
+ spec->num_caps = STAC92HD71BXX_NUM_CAPS;
+ spec->capvols = stac92hd71bxx_capvols;
+ spec->capsws = stac92hd71bxx_capsws;
switch (codec->vendor_id) {
case 0x111d76b6: /* 4 Port without Analog Mixer */
case 0x111d76b7:
unmute_init++;
+ /* fallthru */
+ case 0x111d76b4: /* 6 Port without Analog Mixer */
+ case 0x111d76b5:
+ codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
+ spec->num_dmics = stac92xx_connected_ports(codec,
+ stac92hd71bxx_dmic_nids,
+ STAC92HD71BXX_NUM_DMICS);
break;
case 0x111d7608: /* 5 Port with Analog Mixer */
+ switch (spec->board_config) {
+ case STAC_HP_M4:
+ /* Enable VREF power saving on GPIO1 detect */
+ err = stac_add_event(codec, codec->afg,
+ STAC_VREF_EVENT, 0x02);
+ if (err < 0)
+ return err;
+ snd_hda_codec_write_cache(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
+ snd_hda_jack_detect_enable(codec, codec->afg, 0);
+ spec->gpio_mask |= 0x02;
+ break;
+ }
if ((codec->revision_id & 0xf) == 0 ||
(codec->revision_id & 0xf) == 1)
spec->stream_delay = 40; /* 40 milliseconds */
@@ -4043,44 +5873,157 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
unmute_init++;
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
+ spec->dmic_nids = stac92hd71bxx_dmic_5port_nids;
+ spec->num_dmics = stac92xx_connected_ports(codec,
+ stac92hd71bxx_dmic_5port_nids,
+ STAC92HD71BXX_NUM_DMICS - 1);
break;
case 0x111d7603: /* 6 Port with Analog Mixer */
if ((codec->revision_id & 0xf) == 1)
spec->stream_delay = 40; /* 40 milliseconds */
+ /* fallthru */
+ default:
+ codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
+ spec->num_dmics = stac92xx_connected_ports(codec,
+ stac92hd71bxx_dmic_nids,
+ STAC92HD71BXX_NUM_DMICS);
break;
}
if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB)
- snd_hda_add_verbs(codec, stac92hd71bxx_core_init);
+ spec->init = stac92hd71bxx_core_init;
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
snd_hda_sequence_write_cache(codec, unmute_init);
- spec->aloopback_ctl = &stac92hd71bxx_loopback;
+ spec->aloopback_ctl = stac92hd71bxx_loopback;
spec->aloopback_mask = 0x50;
spec->aloopback_shift = 0;
spec->powerdown_adcs = 1;
spec->digbeep_nid = 0x26;
- spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
+ spec->mux_nids = stac92hd71bxx_mux_nids;
+ spec->adc_nids = stac92hd71bxx_adc_nids;
+ spec->smux_nids = stac92hd71bxx_smux_nids;
spec->pwr_nids = stac92hd71bxx_pwr_nids;
- snd_hda_pick_fixup(codec, stac92hd71bxx_models, stac92hd71bxx_fixup_tbl,
- stac92hd71bxx_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
+ spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
+ spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
+ spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e);
+
+ snd_printdd("Found board config: %d\n", spec->board_config);
+
+ switch (spec->board_config) {
+ case STAC_HP_M4:
+ /* enable internal microphone */
+ snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040);
+ stac92xx_auto_set_pinctl(codec, 0x0e,
+ AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
+ /* fallthru */
+ case STAC_DELL_M4_2:
+ spec->num_dmics = 0;
+ spec->num_smuxes = 0;
+ spec->num_dmuxes = 0;
+ break;
+ case STAC_DELL_M4_1:
+ case STAC_DELL_M4_3:
+ spec->num_dmics = 1;
+ spec->num_smuxes = 0;
+ spec->num_dmuxes = 1;
+ break;
+ case STAC_HP_DV4_1222NR:
+ spec->num_dmics = 1;
+ /* I don't know if it needs 1 or 2 smuxes - will wait for
+ * bug reports to fix if needed
+ */
+ spec->num_smuxes = 1;
+ spec->num_dmuxes = 1;
+ /* fallthrough */
+ case STAC_HP_DV4:
+ spec->gpio_led = 0x01;
+ /* fallthrough */
+ case STAC_HP_DV5:
+ snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
+ stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN);
+ /* HP dv6 gives the headphone pin as a line-out. Thus we
+ * need to set hp_detect flag here to force to enable HP
+ * detection.
+ */
+ spec->hp_detect = 1;
+ break;
+ case STAC_HP_HDX:
+ spec->num_dmics = 1;
+ spec->num_dmuxes = 1;
+ spec->num_smuxes = 1;
+ spec->gpio_led = 0x08;
+ break;
+ }
+
+ if (hp_blike_system(codec->subsystem_id)) {
+ pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f);
+ if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT ||
+ get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER ||
+ get_defcfg_device(pin_cfg) == AC_JACK_HP_OUT) {
+ /* It was changed in the BIOS to just satisfy MS DTM.
+ * Lets turn it back into slaved HP
+ */
+ pin_cfg = (pin_cfg & (~AC_DEFCFG_DEVICE))
+ | (AC_JACK_HP_OUT <<
+ AC_DEFCFG_DEVICE_SHIFT);
+ pin_cfg = (pin_cfg & (~(AC_DEFCFG_DEF_ASSOC
+ | AC_DEFCFG_SEQUENCE)))
+ | 0x1f;
+ snd_hda_codec_set_pincfg(codec, 0x0f, pin_cfg);
+ }
+ }
+
+ if (find_mute_led_cfg(codec, 1))
+ snd_printd("mute LED gpio %d polarity %d\n",
+ spec->gpio_led,
+ spec->gpio_led_polarity);
+
+ if (spec->gpio_led) {
+ if (!spec->vref_mute_led_nid) {
+ spec->gpio_mask |= spec->gpio_led;
+ spec->gpio_dir |= spec->gpio_led;
+ spec->gpio_data |= spec->gpio_led;
+ } else {
+ codec->patch_ops.set_power_state =
+ stac92xx_set_power_state;
+ }
+ }
+
+ spec->multiout.dac_nids = spec->dac_nids;
- stac_setup_gpio(codec);
+ err = stac92xx_parse_auto_config(codec);
+ if (!err) {
+ if (spec->board_config < 0) {
+ printk(KERN_WARNING "hda_codec: No auto-config is "
+ "available, default to model=ref\n");
+ spec->board_config = STAC_92HD71BXX_REF;
+ goto again;
+ }
+ err = -EINVAL;
+ }
- err = stac_parse_auto_config(codec);
if (err < 0) {
- stac_free(codec);
+ stac92xx_free(codec);
return err;
}
- codec->proc_widget_hook = stac92hd7x_proc_hook;
+ /* enable bass on HP dv7 */
+ if (spec->board_config == STAC_HP_DV4 ||
+ spec->board_config == STAC_HP_DV5) {
+ unsigned int cap;
+ cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP);
+ cap &= AC_GPIO_IO_COUNT;
+ if (cap >= 6)
+ stac_add_hp_bass_switch(codec);
+ }
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ codec->proc_widget_hook = stac92hd7x_proc_hook;
return 0;
}
@@ -4090,86 +6033,217 @@ static int patch_stac922x(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
+ err = alloc_stac_spec(codec, ARRAY_SIZE(stac922x_pin_nids),
+ stac922x_pin_nids);
if (err < 0)
return err;
spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->gen.own_eapd_ctl = 1;
+ spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
+ stac922x_models,
+ stac922x_cfg_tbl);
+ if (spec->board_config == STAC_INTEL_MAC_AUTO) {
+ spec->gpio_mask = spec->gpio_dir = 0x03;
+ spec->gpio_data = 0x03;
+ /* Intel Macs have all same PCI SSID, so we need to check
+ * codec SSID to distinguish the exact models
+ */
+ printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id);
+ switch (codec->subsystem_id) {
+
+ case 0x106b0800:
+ spec->board_config = STAC_INTEL_MAC_V1;
+ break;
+ case 0x106b0600:
+ case 0x106b0700:
+ spec->board_config = STAC_INTEL_MAC_V2;
+ break;
+ case 0x106b0e00:
+ case 0x106b0f00:
+ case 0x106b1600:
+ case 0x106b1700:
+ case 0x106b0200:
+ case 0x106b1e00:
+ spec->board_config = STAC_INTEL_MAC_V3;
+ break;
+ case 0x106b1a00:
+ case 0x00000100:
+ spec->board_config = STAC_INTEL_MAC_V4;
+ break;
+ case 0x106b0a00:
+ case 0x106b2200:
+ spec->board_config = STAC_INTEL_MAC_V5;
+ break;
+ default:
+ spec->board_config = STAC_INTEL_MAC_V3;
+ break;
+ }
+ }
- codec->patch_ops = stac_patch_ops;
+ again:
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
+ else
+ stac92xx_set_config_regs(codec,
+ stac922x_brd_tbl[spec->board_config]);
- snd_hda_add_verbs(codec, stac922x_core_init);
+ spec->adc_nids = stac922x_adc_nids;
+ spec->mux_nids = stac922x_mux_nids;
+ spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids);
+ spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids);
+ spec->num_dmics = 0;
+ spec->num_pwrs = 0;
- /* Fix Mux capture level; max to 2 */
- snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
- (0 << AC_AMPCAP_OFFSET_SHIFT) |
- (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
- (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
- (0 << AC_AMPCAP_MUTE_SHIFT));
+ spec->init = stac922x_core_init;
- snd_hda_pick_fixup(codec, stac922x_models, stac922x_fixup_tbl,
- stac922x_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ spec->num_caps = STAC922X_NUM_CAPS;
+ spec->capvols = stac922x_capvols;
+ spec->capsws = stac922x_capsws;
- err = stac_parse_auto_config(codec);
+ spec->multiout.dac_nids = spec->dac_nids;
+
+ err = stac92xx_parse_auto_config(codec);
+ if (!err) {
+ if (spec->board_config < 0) {
+ printk(KERN_WARNING "hda_codec: No auto-config is "
+ "available, default to model=ref\n");
+ spec->board_config = STAC_D945_REF;
+ goto again;
+ }
+ err = -EINVAL;
+ }
if (err < 0) {
- stac_free(codec);
+ stac92xx_free(codec);
return err;
}
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ codec->patch_ops = stac92xx_patch_ops;
+
+ /* Fix Mux capture level; max to 2 */
+ snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
+ (0 << AC_AMPCAP_OFFSET_SHIFT) |
+ (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (0 << AC_AMPCAP_MUTE_SHIFT));
return 0;
}
-static const char * const stac927x_spdif_labels[] = {
- "Digital Playback", "ADAT", "Analog Mux 1",
- "Analog Mux 2", "Analog Mux 3", NULL
-};
-
static int patch_stac927x(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
+ err = alloc_stac_spec(codec, ARRAY_SIZE(stac927x_pin_nids),
+ stac927x_pin_nids);
if (err < 0)
return err;
spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->gen.own_eapd_ctl = 1;
- spec->have_spdif_mux = 1;
- spec->spdif_labels = stac927x_spdif_labels;
+ codec->slave_dig_outs = stac927x_slave_dig_outs;
+ spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
+ stac927x_models,
+ stac927x_cfg_tbl);
+ again:
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
+ else
+ stac92xx_set_config_regs(codec,
+ stac927x_brd_tbl[spec->board_config]);
spec->digbeep_nid = 0x23;
+ spec->adc_nids = stac927x_adc_nids;
+ spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);
+ spec->mux_nids = stac927x_mux_nids;
+ spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids);
+ spec->smux_nids = stac927x_smux_nids;
+ spec->num_smuxes = ARRAY_SIZE(stac927x_smux_nids);
+ spec->spdif_labels = stac927x_spdif_labels;
+ spec->dac_list = stac927x_dac_nids;
+ spec->multiout.dac_nids = spec->dac_nids;
- /* GPIO0 High = Enable EAPD */
- spec->eapd_mask = spec->gpio_mask = 0x01;
- spec->gpio_dir = spec->gpio_data = 0x01;
+ if (spec->board_config != STAC_D965_REF) {
+ /* GPIO0 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = 0x01;
+ spec->gpio_dir = spec->gpio_data = 0x01;
+ }
- spec->aloopback_ctl = &stac927x_loopback;
- spec->aloopback_mask = 0x40;
- spec->aloopback_shift = 0;
- spec->eapd_switch = 1;
+ switch (spec->board_config) {
+ case STAC_D965_3ST:
+ case STAC_D965_5ST:
+ /* GPIO0 High = Enable EAPD */
+ spec->num_dmics = 0;
+ spec->init = d965_core_init;
+ break;
+ case STAC_DELL_BIOS:
+ switch (codec->subsystem_id) {
+ case 0x10280209:
+ case 0x1028022e:
+ /* correct the device field to SPDIF out */
+ snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070);
+ break;
+ }
+ /* configure the analog microphone on some laptops */
+ snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130);
+ /* correct the front output jack as a hp out */
+ snd_hda_codec_set_pincfg(codec, 0x0f, 0x0227011f);
+ /* correct the front input jack as a mic */
+ snd_hda_codec_set_pincfg(codec, 0x0e, 0x02a79130);
+ /* fallthru */
+ case STAC_DELL_3ST:
+ if (codec->subsystem_id != 0x1028022f) {
+ /* GPIO2 High = Enable EAPD */
+ spec->eapd_mask = spec->gpio_mask = 0x04;
+ spec->gpio_dir = spec->gpio_data = 0x04;
+ }
+ spec->dmic_nids = stac927x_dmic_nids;
+ spec->num_dmics = STAC927X_NUM_DMICS;
- codec->patch_ops = stac_patch_ops;
+ spec->init = dell_3st_core_init;
+ spec->dmux_nids = stac927x_dmux_nids;
+ spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
+ break;
+ case STAC_927X_VOLKNOB:
+ spec->num_dmics = 0;
+ spec->init = stac927x_volknob_core_init;
+ break;
+ default:
+ spec->num_dmics = 0;
+ spec->init = stac927x_core_init;
+ break;
+ }
- snd_hda_pick_fixup(codec, stac927x_models, stac927x_fixup_tbl,
- stac927x_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ spec->num_caps = STAC927X_NUM_CAPS;
+ spec->capvols = stac927x_capvols;
+ spec->capsws = stac927x_capsws;
- if (!spec->volknob_init)
- snd_hda_add_verbs(codec, stac927x_core_init);
+ spec->num_pwrs = 0;
+ spec->aloopback_ctl = stac927x_loopback;
+ spec->aloopback_mask = 0x40;
+ spec->aloopback_shift = 0;
+ spec->eapd_switch = 1;
- err = stac_parse_auto_config(codec);
+ err = stac92xx_parse_auto_config(codec);
+ if (!err) {
+ if (spec->board_config < 0) {
+ printk(KERN_WARNING "hda_codec: No auto-config is "
+ "available, default to model=ref\n");
+ spec->board_config = STAC_D965_REF;
+ goto again;
+ }
+ err = -EINVAL;
+ }
if (err < 0) {
- stac_free(codec);
+ stac92xx_free(codec);
return err;
}
+ codec->patch_ops = stac92xx_patch_ops;
+
codec->proc_widget_hook = stac927x_proc_hook;
/*
@@ -4184,7 +6258,9 @@ static int patch_stac927x(struct hda_codec *codec)
*/
codec->bus->needs_damn_long_delay = 1;
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ /* no jack detecion for ref-no-jd model */
+ if (spec->board_config == STAC_D965_REF_NO_JD)
+ spec->hp_detect = 0;
return 0;
}
@@ -4194,45 +6270,102 @@ static int patch_stac9205(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
+ err = alloc_stac_spec(codec, ARRAY_SIZE(stac9205_pin_nids),
+ stac9205_pin_nids);
if (err < 0)
return err;
spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->gen.own_eapd_ctl = 1;
- spec->have_spdif_mux = 1;
+ spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
+ stac9205_models,
+ stac9205_cfg_tbl);
+ again:
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
+ else
+ stac92xx_set_config_regs(codec,
+ stac9205_brd_tbl[spec->board_config]);
spec->digbeep_nid = 0x23;
-
- snd_hda_add_verbs(codec, stac9205_core_init);
- spec->aloopback_ctl = &stac9205_loopback;
+ spec->adc_nids = stac9205_adc_nids;
+ spec->num_adcs = ARRAY_SIZE(stac9205_adc_nids);
+ spec->mux_nids = stac9205_mux_nids;
+ spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids);
+ spec->smux_nids = stac9205_smux_nids;
+ spec->num_smuxes = ARRAY_SIZE(stac9205_smux_nids);
+ spec->dmic_nids = stac9205_dmic_nids;
+ spec->num_dmics = STAC9205_NUM_DMICS;
+ spec->dmux_nids = stac9205_dmux_nids;
+ spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids);
+ spec->num_pwrs = 0;
+
+ spec->init = stac9205_core_init;
+ spec->aloopback_ctl = stac9205_loopback;
+
+ spec->num_caps = STAC9205_NUM_CAPS;
+ spec->capvols = stac9205_capvols;
+ spec->capsws = stac9205_capsws;
spec->aloopback_mask = 0x40;
spec->aloopback_shift = 0;
-
- /* GPIO0 High = EAPD */
- spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
- spec->gpio_data = 0x01;
-
/* Turn on/off EAPD per HP plugging */
- spec->eapd_switch = 1;
+ if (spec->board_config != STAC_9205_EAPD)
+ spec->eapd_switch = 1;
+ spec->multiout.dac_nids = spec->dac_nids;
+
+ switch (spec->board_config){
+ case STAC_9205_DELL_M43:
+ /* Enable SPDIF in/out */
+ snd_hda_codec_set_pincfg(codec, 0x1f, 0x01441030);
+ snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030);
- codec->patch_ops = stac_patch_ops;
+ /* Enable unsol response for GPIO4/Dock HP connection */
+ err = stac_add_event(codec, codec->afg, STAC_VREF_EVENT, 0x01);
+ if (err < 0)
+ return err;
+ snd_hda_codec_write_cache(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
+ snd_hda_jack_detect_enable(codec, codec->afg, 0);
- snd_hda_pick_fixup(codec, stac9205_models, stac9205_fixup_tbl,
- stac9205_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+ spec->gpio_dir = 0x0b;
+ spec->eapd_mask = 0x01;
+ spec->gpio_mask = 0x1b;
+ spec->gpio_mute = 0x10;
+ /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute,
+ * GPIO3 Low = DRM
+ */
+ spec->gpio_data = 0x01;
+ break;
+ case STAC_9205_REF:
+ /* SPDIF-In enabled */
+ break;
+ default:
+ /* GPIO0 High = EAPD */
+ spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
+ spec->gpio_data = 0x01;
+ break;
+ }
- err = stac_parse_auto_config(codec);
+ err = stac92xx_parse_auto_config(codec);
+ if (!err) {
+ if (spec->board_config < 0) {
+ printk(KERN_WARNING "hda_codec: No auto-config is "
+ "available, default to model=ref\n");
+ spec->board_config = STAC_9205_REF;
+ goto again;
+ }
+ err = -EINVAL;
+ }
if (err < 0) {
- stac_free(codec);
+ stac92xx_free(codec);
return err;
}
- codec->proc_widget_hook = stac9205_proc_hook;
+ codec->patch_ops = stac92xx_patch_ops;
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+ codec->proc_widget_hook = stac9205_proc_hook;
return 0;
}
@@ -4247,32 +6380,40 @@ static const struct hda_verb stac9872_core_init[] = {
{}
};
-static const struct hda_pintbl stac9872_vaio_pin_configs[] = {
- { 0x0a, 0x03211020 },
- { 0x0b, 0x411111f0 },
- { 0x0c, 0x411111f0 },
- { 0x0d, 0x03a15030 },
- { 0x0e, 0x411111f0 },
- { 0x0f, 0x90170110 },
- { 0x11, 0x411111f0 },
- { 0x13, 0x411111f0 },
- { 0x14, 0x90a7013e },
- {}
+static const hda_nid_t stac9872_pin_nids[] = {
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x11, 0x13, 0x14,
};
-static const struct hda_model_fixup stac9872_models[] = {
- { .id = STAC_9872_VAIO, .name = "vaio" },
- {}
+static const hda_nid_t stac9872_adc_nids[] = {
+ 0x8 /*,0x6*/
};
-static const struct hda_fixup stac9872_fixups[] = {
- [STAC_9872_VAIO] = {
- .type = HDA_FIXUP_PINS,
- .v.pins = stac9872_vaio_pin_configs,
- },
+static const hda_nid_t stac9872_mux_nids[] = {
+ 0x15
+};
+
+static const unsigned long stac9872_capvols[] = {
+ HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
+};
+#define stac9872_capsws stac9872_capvols
+
+static const unsigned int stac9872_vaio_pin_configs[9] = {
+ 0x03211020, 0x411111f0, 0x411111f0, 0x03a15030,
+ 0x411111f0, 0x90170110, 0x411111f0, 0x411111f0,
+ 0x90a7013e
+};
+
+static const char * const stac9872_models[STAC_9872_MODELS] = {
+ [STAC_9872_AUTO] = "auto",
+ [STAC_9872_VAIO] = "vaio",
};
-static const struct snd_pci_quirk stac9872_fixup_tbl[] = {
+static const unsigned int *stac9872_brd_tbl[STAC_9872_MODELS] = {
+ [STAC_9872_VAIO] = stac9872_vaio_pin_configs,
+};
+
+static const struct snd_pci_quirk stac9872_cfg_tbl[] = {
SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0,
"Sony VAIO F/S", STAC_9872_VAIO),
{} /* terminator */
@@ -4283,30 +6424,41 @@ static int patch_stac9872(struct hda_codec *codec)
struct sigmatel_spec *spec;
int err;
- err = alloc_stac_spec(codec);
+ err = alloc_stac_spec(codec, ARRAY_SIZE(stac9872_pin_nids),
+ stac9872_pin_nids);
if (err < 0)
return err;
spec = codec->spec;
spec->linear_tone_beep = 1;
- spec->gen.own_eapd_ctl = 1;
-
- codec->patch_ops = stac_patch_ops;
-
- snd_hda_add_verbs(codec, stac9872_core_init);
- snd_hda_pick_fixup(codec, stac9872_models, stac9872_fixup_tbl,
- stac9872_fixups);
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
-
- err = stac_parse_auto_config(codec);
+ spec->board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
+ stac9872_models,
+ stac9872_cfg_tbl);
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+ codec->chip_name);
+ else
+ stac92xx_set_config_regs(codec,
+ stac9872_brd_tbl[spec->board_config]);
+
+ spec->multiout.dac_nids = spec->dac_nids;
+ spec->num_adcs = ARRAY_SIZE(stac9872_adc_nids);
+ spec->adc_nids = stac9872_adc_nids;
+ spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids);
+ spec->mux_nids = stac9872_mux_nids;
+ spec->init = stac9872_core_init;
+ spec->num_caps = 1;
+ spec->capvols = stac9872_capvols;
+ spec->capsws = stac9872_capsws;
+
+ err = stac92xx_parse_auto_config(codec);
if (err < 0) {
- stac_free(codec);
+ stac92xx_free(codec);
return -EINVAL;
}
-
- snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
-
+ spec->input_mux = &spec->private_imux;
+ codec->patch_ops = stac92xx_patch_ops;
return 0;
}
@@ -4377,7 +6529,6 @@ static const struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
{ .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
{ .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
- { .id = 0x111d7695, .name = "92HD95", .patch = patch_stac92hd95 },
{ .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
{ .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
{ .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
diff --git a/trunk/sound/pci/hda/patch_via.c b/trunk/sound/pci/hda/patch_via.c
index c35338a8771d..09bb64996d72 100644
--- a/trunk/sound/pci/hda/patch_via.c
+++ b/trunk/sound/pci/hda/patch_via.c
@@ -56,7 +56,6 @@
#include "hda_local.h"
#include "hda_auto_parser.h"
#include "hda_jack.h"
-#include "hda_generic.h"
/* Pin Widget NID */
#define VT1708_HP_PIN_NID 0x20
@@ -87,6 +86,39 @@ enum VIA_HDA_CODEC {
(spec)->codec_type == VT1812 ||\
(spec)->codec_type == VT1802)
+#define MAX_NID_PATH_DEPTH 5
+
+/* output-path: DAC -> ... -> pin
+ * idx[] contains the source index number of the next widget;
+ * e.g. idx[0] is the index of the DAC selected by path[1] widget
+ * multi[] indicates whether it's a selector widget with multi-connectors
+ * (i.e. the connection selection is mandatory)
+ * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
+ */
+struct nid_path {
+ int depth;
+ hda_nid_t path[MAX_NID_PATH_DEPTH];
+ unsigned char idx[MAX_NID_PATH_DEPTH];
+ unsigned char multi[MAX_NID_PATH_DEPTH];
+ unsigned int vol_ctl;
+ unsigned int mute_ctl;
+};
+
+/* input-path */
+struct via_input {
+ hda_nid_t pin; /* input-pin or aa-mix */
+ int adc_idx; /* ADC index to be used */
+ int mux_idx; /* MUX index (if any) */
+ const char *label; /* input-source label */
+};
+
+#define VIA_MAX_ADCS 3
+
+enum {
+ STREAM_MULTI_OUT = (1 << 0),
+ STREAM_INDEP_HP = (1 << 1),
+};
+
struct via_spec {
struct hda_gen_spec gen;
@@ -97,7 +129,77 @@ struct via_spec {
const struct hda_verb *init_verbs[5];
unsigned int num_iverbs;
+ char stream_name_analog[32];
+ char stream_name_hp[32];
+ const struct hda_pcm_stream *stream_analog_playback;
+ const struct hda_pcm_stream *stream_analog_capture;
+
+ char stream_name_digital[32];
+ const struct hda_pcm_stream *stream_digital_playback;
+ const struct hda_pcm_stream *stream_digital_capture;
+
+ /* playback */
+ struct hda_multi_out multiout;
+ hda_nid_t slave_dig_outs[2];
+ hda_nid_t hp_dac_nid;
+ hda_nid_t speaker_dac_nid;
+ int hp_indep_shared; /* indep HP-DAC is shared with side ch */
+ int opened_streams; /* STREAM_* bits */
+ int active_streams; /* STREAM_* bits */
+ int aamix_mode; /* loopback is enabled for output-path? */
+
+ /* Output-paths:
+ * There are different output-paths depending on the setup.
+ * out_path, hp_path and speaker_path are primary paths. If both
+ * direct DAC and aa-loopback routes are available, these contain
+ * the former paths. Meanwhile *_mix_path contain the paths with
+ * loopback mixer. (Since the loopback is only for front channel,
+ * no out_mix_path for surround channels.)
+ * The HP output has another path, hp_indep_path, which is used in
+ * the independent-HP mode.
+ */
+ struct nid_path out_path[HDA_SIDE + 1];
+ struct nid_path out_mix_path;
+ struct nid_path hp_path;
+ struct nid_path hp_mix_path;
+ struct nid_path hp_indep_path;
+ struct nid_path speaker_path;
+ struct nid_path speaker_mix_path;
+
+ /* capture */
+ unsigned int num_adc_nids;
+ hda_nid_t adc_nids[VIA_MAX_ADCS];
+ hda_nid_t mux_nids[VIA_MAX_ADCS];
+ hda_nid_t aa_mix_nid;
+ hda_nid_t dig_in_nid;
+
+ /* capture source */
+ bool dyn_adc_switch;
+ int num_inputs;
+ struct via_input inputs[AUTO_CFG_MAX_INS + 1];
+ unsigned int cur_mux[VIA_MAX_ADCS];
+
+ /* dynamic DAC switching */
+ unsigned int cur_dac_stream_tag;
+ unsigned int cur_dac_format;
+ unsigned int cur_hp_stream_tag;
+ unsigned int cur_hp_format;
+
+ /* dynamic ADC switching */
+ hda_nid_t cur_adc;
+ unsigned int cur_adc_stream_tag;
+ unsigned int cur_adc_format;
+
+ /* PCM information */
+ struct hda_pcm pcm_rec[3];
+
+ /* dynamic controls, init_verbs and input_mux */
+ struct auto_pin_cfg autocfg;
+ struct snd_array kctls;
+ hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+
/* HP mode source */
+ unsigned int hp_independent_mode;
unsigned int dmic_enabled;
unsigned int no_pin_power_ctl;
enum VIA_HDA_CODEC codec_type;
@@ -105,22 +207,36 @@ struct via_spec {
/* analog low-power control */
bool alc_mode;
+ /* smart51 setup */
+ unsigned int smart51_nums;
+ hda_nid_t smart51_pins[2];
+ int smart51_idxs[2];
+ const char *smart51_labels[2];
+ unsigned int smart51_enabled;
+
/* work to check hp jack state */
+ struct hda_codec *codec;
+ struct delayed_work vt1708_hp_work;
int hp_work_active;
int vt1708_jack_detect;
+ int vt1708_hp_present;
void (*set_widgets_power_state)(struct hda_codec *codec);
unsigned int dac_stream_tag[4];
+
+ struct hda_loopback_check loopback;
+ int num_loopbacks;
+ struct hda_amp_list loopback_list[8];
+
+ /* bind capture-volume */
+ struct hda_bind_ctls *bind_cap_vol;
+ struct hda_bind_ctls *bind_cap_sw;
+
+ struct mutex config_mutex;
};
static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
-static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream,
- int action);
-static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl);
-
-static struct via_spec *via_new_spec(struct hda_codec *codec)
+static struct via_spec * via_new_spec(struct hda_codec *codec)
{
struct via_spec *spec;
@@ -128,15 +244,15 @@ static struct via_spec *via_new_spec(struct hda_codec *codec)
if (spec == NULL)
return NULL;
+ snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
+ mutex_init(&spec->config_mutex);
codec->spec = spec;
- snd_hda_gen_spec_init(&spec->gen);
+ spec->codec = codec;
spec->codec_type = get_codec_type(codec);
/* VT1708BCE & VT1708S are almost same */
if (spec->codec_type == VT1708BCE)
spec->codec_type = VT1708S;
- spec->no_pin_power_ctl = 1;
- spec->gen.indep_hp = 1;
- spec->gen.pcm_playback_hook = via_playback_pcm_hook;
+ snd_hda_gen_init(&spec->gen);
return spec;
}
@@ -192,6 +308,16 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
return codec_type;
};
+#define VIA_JACK_EVENT 0x20
+#define VIA_HP_EVENT 0x01
+#define VIA_LINE_EVENT 0x03
+
+enum {
+ VIA_CTL_WIDGET_VOL,
+ VIA_CTL_WIDGET_MUTE,
+ VIA_CTL_WIDGET_ANALOG_MUTE,
+};
+
static void analog_low_current_mode(struct hda_codec *codec);
static bool is_aa_path_mute(struct hda_codec *codec);
@@ -199,34 +325,31 @@ static bool is_aa_path_mute(struct hda_codec *codec);
(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
!is_aa_path_mute(codec))
-static void vt1708_stop_hp_work(struct hda_codec *codec)
+static void vt1708_stop_hp_work(struct via_spec *spec)
{
- struct via_spec *spec = codec->spec;
- if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
+ if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
return;
if (spec->hp_work_active) {
- snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
- cancel_delayed_work_sync(&codec->jackpoll_work);
- spec->hp_work_active = false;
- codec->jackpoll_interval = 0;
+ snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 1);
+ cancel_delayed_work_sync(&spec->vt1708_hp_work);
+ spec->hp_work_active = 0;
}
}
-static void vt1708_update_hp_work(struct hda_codec *codec)
+static void vt1708_update_hp_work(struct via_spec *spec)
{
- struct via_spec *spec = codec->spec;
- if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
+ if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
return;
- if (spec->vt1708_jack_detect) {
+ if (spec->vt1708_jack_detect &&
+ (spec->active_streams || hp_detect_with_aa(spec->codec))) {
if (!spec->hp_work_active) {
- codec->jackpoll_interval = msecs_to_jiffies(100);
- snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
- queue_delayed_work(codec->bus->workq,
- &codec->jackpoll_work, 0);
- spec->hp_work_active = true;
+ snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 0);
+ schedule_delayed_work(&spec->vt1708_hp_work,
+ msecs_to_jiffies(100));
+ spec->hp_work_active = 1;
}
- } else if (!hp_detect_with_aa(codec))
- vt1708_stop_hp_work(codec);
+ } else if (!hp_detect_with_aa(spec->codec))
+ vt1708_stop_hp_work(spec);
}
static void set_widgets_power_state(struct hda_codec *codec)
@@ -236,10 +359,361 @@ static void set_widgets_power_state(struct hda_codec *codec)
spec->set_widgets_power_state(codec);
}
+static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ set_widgets_power_state(codec);
+ analog_low_current_mode(snd_kcontrol_chip(kcontrol));
+ vt1708_update_hp_work(codec->spec);
+ return change;
+}
+
+/* modify .put = snd_hda_mixer_amp_switch_put */
+#define ANALOG_INPUT_MUTE \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = NULL, \
+ .index = 0, \
+ .info = snd_hda_mixer_amp_switch_info, \
+ .get = snd_hda_mixer_amp_switch_get, \
+ .put = analog_input_switch_put, \
+ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
+
+static const struct snd_kcontrol_new via_control_templates[] = {
+ HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+ HDA_CODEC_MUTE(NULL, 0, 0, 0),
+ ANALOG_INPUT_MUTE,
+};
+
+
+/* add dynamic controls */
+static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
+ const struct snd_kcontrol_new *tmpl,
+ const char *name)
+{
+ struct snd_kcontrol_new *knew;
+
+ knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return NULL;
+ *knew = *tmpl;
+ if (!name)
+ name = tmpl->name;
+ if (name) {
+ knew->name = kstrdup(name, GFP_KERNEL);
+ if (!knew->name)
+ return NULL;
+ }
+ return knew;
+}
+
+static int __via_add_control(struct via_spec *spec, int type, const char *name,
+ int idx, unsigned long val)
+{
+ struct snd_kcontrol_new *knew;
+
+ knew = __via_clone_ctl(spec, &via_control_templates[type], name);
+ if (!knew)
+ return -ENOMEM;
+ knew->index = idx;
+ if (get_amp_nid_(val))
+ knew->subdevice = HDA_SUBDEV_AMP_FLAG;
+ knew->private_value = val;
+ return 0;
+}
+
+#define via_add_control(spec, type, name, val) \
+ __via_add_control(spec, type, name, 0, val)
+
+#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
+
+static void via_free_kctls(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+
+ if (spec->kctls.list) {
+ struct snd_kcontrol_new *kctl = spec->kctls.list;
+ int i;
+ for (i = 0; i < spec->kctls.used; i++)
+ kfree(kctl[i].name);
+ }
+ snd_array_free(&spec->kctls);
+}
+
+/* create input playback/capture controls for the given pin */
+static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
+ int type_idx, int idx, int mix_nid)
+{
+ char name[32];
+ int err;
+
+ sprintf(name, "%s Playback Volume", ctlname);
+ err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
+ HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", ctlname);
+ err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
+ HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+#define get_connection_index(codec, mux, nid) \
+ snd_hda_get_conn_index(codec, mux, nid, 0)
+
+static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
+ unsigned int mask)
+{
+ unsigned int caps;
+ if (!nid)
+ return false;
+ caps = get_wcaps(codec, nid);
+ if (dir == HDA_INPUT)
+ caps &= AC_WCAP_IN_AMP;
+ else
+ caps &= AC_WCAP_OUT_AMP;
+ if (!caps)
+ return false;
+ if (query_amp_caps(codec, nid, dir) & mask)
+ return true;
+ return false;
+}
+
+#define have_mute(codec, nid, dir) \
+ check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
+
+/* enable/disable the output-route mixers */
+static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
+ hda_nid_t mix_nid, int idx, bool enable)
+{
+ int i, num, val;
+
+ if (!path)
+ return;
+ num = snd_hda_get_num_conns(codec, mix_nid);
+ for (i = 0; i < num; i++) {
+ if (i == idx)
+ val = AMP_IN_UNMUTE(i);
+ else
+ val = AMP_IN_MUTE(i);
+ snd_hda_codec_write(codec, mix_nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
+ }
+}
+
+/* enable/disable the output-route */
+static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
+ bool enable, bool force)
+{
+ struct via_spec *spec = codec->spec;
+ int i;
+ for (i = 0; i < path->depth; i++) {
+ hda_nid_t src, dst;
+ int idx = path->idx[i];
+ src = path->path[i];
+ if (i < path->depth - 1)
+ dst = path->path[i + 1];
+ else
+ dst = 0;
+ if (enable && path->multi[i])
+ snd_hda_codec_write(codec, dst, 0,
+ AC_VERB_SET_CONNECT_SEL, idx);
+ if (!force && (dst == spec->aa_mix_nid))
+ continue;
+ if (have_mute(codec, dst, HDA_INPUT))
+ activate_output_mix(codec, path, dst, idx, enable);
+ if (!force && (src == path->vol_ctl || src == path->mute_ctl))
+ continue;
+ if (have_mute(codec, src, HDA_OUTPUT)) {
+ int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
+ snd_hda_codec_write(codec, src, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
+ }
+ }
+}
+
+/* set the given pin as output */
+static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
+ int pin_type)
+{
+ if (!pin)
+ return;
+ snd_hda_set_pin_ctl(codec, pin, pin_type);
+ if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+}
+
+static void via_auto_init_output(struct hda_codec *codec,
+ struct nid_path *path, int pin_type)
+{
+ unsigned int caps;
+ hda_nid_t pin;
+
+ if (!path->depth)
+ return;
+ pin = path->path[path->depth - 1];
+
+ init_output_pin(codec, pin, pin_type);
+ if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+ caps = query_amp_caps(codec, pin, HDA_OUTPUT);
+ else
+ caps = 0;
+ if (caps & AC_AMPCAP_MUTE) {
+ unsigned int val;
+ val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+ snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE | val);
+ }
+ activate_output_path(codec, path, true, true); /* force on */
+}
+
+static void via_auto_init_multi_out(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ struct nid_path *path;
+ int i;
+
+ for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
+ path = &spec->out_path[i];
+ if (!i && spec->aamix_mode && spec->out_mix_path.depth)
+ path = &spec->out_mix_path;
+ via_auto_init_output(codec, path, PIN_OUT);
+ }
+}
+
+/* deactivate the inactive headphone-paths */
+static void deactivate_hp_paths(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int shared = spec->hp_indep_shared;
+
+ if (spec->hp_independent_mode) {
+ activate_output_path(codec, &spec->hp_path, false, false);
+ activate_output_path(codec, &spec->hp_mix_path, false, false);
+ if (shared)
+ activate_output_path(codec, &spec->out_path[shared],
+ false, false);
+ } else if (spec->aamix_mode || !spec->hp_path.depth) {
+ activate_output_path(codec, &spec->hp_indep_path, false, false);
+ activate_output_path(codec, &spec->hp_path, false, false);
+ } else {
+ activate_output_path(codec, &spec->hp_indep_path, false, false);
+ activate_output_path(codec, &spec->hp_mix_path, false, false);
+ }
+}
+
+static void via_auto_init_hp_out(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+
+ if (!spec->hp_path.depth) {
+ via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
+ return;
+ }
+ deactivate_hp_paths(codec);
+ if (spec->hp_independent_mode)
+ via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP);
+ else if (spec->aamix_mode)
+ via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
+ else
+ via_auto_init_output(codec, &spec->hp_path, PIN_HP);
+}
+
+static void via_auto_init_speaker_out(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+
+ if (!spec->autocfg.speaker_outs)
+ return;
+ if (!spec->speaker_path.depth) {
+ via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
+ return;
+ }
+ if (!spec->aamix_mode) {
+ activate_output_path(codec, &spec->speaker_mix_path,
+ false, false);
+ via_auto_init_output(codec, &spec->speaker_path, PIN_OUT);
+ } else {
+ activate_output_path(codec, &spec->speaker_path, false, false);
+ via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
+ }
+}
+
+static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
+static void via_hp_automute(struct hda_codec *codec);
+
+static void via_auto_init_analog_input(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t conn[HDA_MAX_CONNECTIONS];
+ unsigned int ctl;
+ int i, num_conns;
+
+ /* init ADCs */
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ hda_nid_t nid = spec->adc_nids[i];
+ if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP) ||
+ !(query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE))
+ continue;
+ snd_hda_codec_write(codec, spec->adc_nids[i], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+ }
+
+ /* init pins */
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ if (spec->smart51_enabled && is_smart51_pins(codec, nid))
+ ctl = PIN_OUT;
+ else {
+ ctl = PIN_IN;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC)
+ ctl |= snd_hda_get_default_vref(codec, nid);
+ }
+ snd_hda_set_pin_ctl(codec, nid, ctl);
+ }
+
+ /* init input-src */
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
+ /* secondary ADCs must have the unique MUX */
+ if (i > 0 && !spec->mux_nids[i])
+ break;
+ if (spec->mux_nids[adc_idx]) {
+ int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
+ snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mux_idx);
+ }
+ if (spec->dyn_adc_switch)
+ break; /* only one input-src */
+ }
+
+ /* init aa-mixer */
+ if (!spec->aa_mix_nid)
+ return;
+ num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
+ ARRAY_SIZE(conn));
+ for (i = 0; i < num_conns; i++) {
+ unsigned int caps = get_wcaps(codec, conn[i]);
+ if (get_wcaps_type(caps) == AC_WID_PIN)
+ snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_MUTE(i));
+ }
+}
+
static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
unsigned int parm)
{
- if (snd_hda_check_power_state(codec, nid, parm))
+ if (snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0) == parm)
return;
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
}
@@ -249,8 +723,8 @@ static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid,
{
struct via_spec *spec = codec->spec;
unsigned int format;
-
- if (snd_hda_check_power_state(codec, nid, parm))
+ if (snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0) == parm)
return;
format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
if (format && (spec->dac_stream_tag[index] != format))
@@ -266,23 +740,6 @@ static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid,
}
}
-static bool smart51_enabled(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- return spec->gen.ext_channel_count > 2;
-}
-
-static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
-{
- struct via_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->gen.multi_ios; i++)
- if (spec->gen.multi_io[i].pin == pin)
- return true;
- return false;
-}
-
static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
unsigned int *affected_parm)
{
@@ -297,7 +754,7 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
no_presence |= spec->no_pin_power_ctl;
if (!no_presence)
present = snd_hda_jack_detect(codec, nid);
- if ((smart51_enabled(codec) && is_smart51_pins(codec, nid))
+ if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
|| ((no_presence || present)
&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
*affected_parm = AC_PWRST_D0; /* if it's connected */
@@ -338,187 +795,1802 @@ static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = {
- {
+static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Dynamic Power-Control",
.info = via_pin_power_ctl_info,
.get = via_pin_power_ctl_get,
.put = via_pin_power_ctl_put,
- },
- {} /* terminator */
};
-/* check AA path's mute status */
-static bool is_aa_path_mute(struct hda_codec *codec)
+static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static const char * const texts[] = { "OFF", "ON" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item >= 2)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- const struct hda_amp_list *p;
- int ch, v;
- p = spec->gen.loopback.amplist;
- if (!p)
- return true;
- for (; p->nid; p++) {
- for (ch = 0; ch < 2; ch++) {
- v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
- p->idx);
- if (!(v & HDA_AMP_MUTE) && v > 0)
- return false;
- }
+ ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
+ return 0;
+}
+
+/* adjust spec->multiout setup according to the current flags */
+static void setup_playback_multi_pcm(struct via_spec *spec)
+{
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
+ spec->multiout.hp_nid = 0;
+ if (!spec->hp_independent_mode) {
+ if (!spec->hp_indep_shared)
+ spec->multiout.hp_nid = spec->hp_dac_nid;
+ } else {
+ if (spec->hp_indep_shared)
+ spec->multiout.num_dacs = cfg->line_outs - 1;
}
- return true;
}
-/* enter/exit analog low-current mode */
-static void __analog_low_current_mode(struct hda_codec *codec, bool force)
+/* update DAC setups according to indep-HP switch;
+ * this function is called only when indep-HP is modified
+ */
+static void switch_indep_hp_dacs(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- bool enable;
- unsigned int verb, parm;
+ int shared = spec->hp_indep_shared;
+ hda_nid_t shared_dac, hp_dac;
- if (spec->no_pin_power_ctl)
- enable = false;
- else
- enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
- if (enable == spec->alc_mode && !force)
+ if (!spec->opened_streams)
return;
- spec->alc_mode = enable;
- /* decide low current mode's verb & parameter */
- switch (spec->codec_type) {
- case VT1708B_8CH:
- case VT1708B_4CH:
- verb = 0xf70;
- parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
- break;
- case VT1708S:
- case VT1718S:
- case VT1716S:
- verb = 0xf73;
- parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
- break;
- case VT1702:
- verb = 0xf73;
- parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
- break;
- case VT2002P:
- case VT1812:
- case VT1802:
- verb = 0xf93;
- parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
- break;
- case VT1705CF:
- case VT1808:
- verb = 0xf82;
- parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
- break;
- default:
- return; /* other codecs are not supported */
+ shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
+ hp_dac = spec->hp_dac_nid;
+ if (spec->hp_independent_mode) {
+ /* switch to indep-HP mode */
+ if (spec->active_streams & STREAM_MULTI_OUT) {
+ __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
+ __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
+ }
+ if (spec->active_streams & STREAM_INDEP_HP)
+ snd_hda_codec_setup_stream(codec, hp_dac,
+ spec->cur_hp_stream_tag, 0,
+ spec->cur_hp_format);
+ } else {
+ /* back to HP or shared-DAC */
+ if (spec->active_streams & STREAM_INDEP_HP)
+ __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
+ if (spec->active_streams & STREAM_MULTI_OUT) {
+ hda_nid_t dac;
+ int ch;
+ if (shared_dac) { /* reset mutli-ch DAC */
+ dac = shared_dac;
+ ch = shared * 2;
+ } else { /* reset HP DAC */
+ dac = hp_dac;
+ ch = 0;
+ }
+ snd_hda_codec_setup_stream(codec, dac,
+ spec->cur_dac_stream_tag, ch,
+ spec->cur_dac_format);
+ }
+ }
+ setup_playback_multi_pcm(spec);
+}
+
+static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ int cur, shared;
+
+ mutex_lock(&spec->config_mutex);
+ cur = !!ucontrol->value.enumerated.item[0];
+ if (spec->hp_independent_mode == cur) {
+ mutex_unlock(&spec->config_mutex);
+ return 0;
+ }
+ spec->hp_independent_mode = cur;
+ shared = spec->hp_indep_shared;
+ deactivate_hp_paths(codec);
+ if (cur)
+ activate_output_path(codec, &spec->hp_indep_path, true, false);
+ else {
+ if (shared)
+ activate_output_path(codec, &spec->out_path[shared],
+ true, false);
+ if (spec->aamix_mode || !spec->hp_path.depth)
+ activate_output_path(codec, &spec->hp_mix_path,
+ true, false);
+ else
+ activate_output_path(codec, &spec->hp_path,
+ true, false);
+ }
+
+ switch_indep_hp_dacs(codec);
+ mutex_unlock(&spec->config_mutex);
+
+ /* update jack power state */
+ set_widgets_power_state(codec);
+ via_hp_automute(codec);
+ return 1;
+}
+
+static const struct snd_kcontrol_new via_hp_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Independent HP",
+ .info = via_independent_hp_info,
+ .get = via_independent_hp_get,
+ .put = via_independent_hp_put,
+};
+
+static int via_hp_build(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+ hda_nid_t nid;
+
+ nid = spec->autocfg.hp_pins[0];
+ knew = via_clone_control(spec, &via_hp_mixer);
+ if (knew == NULL)
+ return -ENOMEM;
+
+ knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
+
+ return 0;
+}
+
+static void notify_aa_path_ctls(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->smart51_nums; i++) {
+ struct snd_kcontrol *ctl;
+ struct snd_ctl_elem_id id;
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
+ ctl = snd_hda_find_mixer_ctl(codec, id.name);
+ if (ctl)
+ snd_ctl_notify(codec->bus->card,
+ SNDRV_CTL_EVENT_MASK_VALUE,
+ &ctl->id);
+ }
+}
+
+static void mute_aa_path(struct hda_codec *codec, int mute)
+{
+ struct via_spec *spec = codec->spec;
+ int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
+ int i;
+
+ /* check AA path's mute status */
+ for (i = 0; i < spec->smart51_nums; i++) {
+ if (spec->smart51_idxs[i] < 0)
+ continue;
+ snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
+ HDA_INPUT, spec->smart51_idxs[i],
+ HDA_AMP_MUTE, val);
+ }
+}
+
+static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct via_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->smart51_nums; i++)
+ if (spec->smart51_pins[i] == pin)
+ return true;
+ return false;
+}
+
+static int via_smart51_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+
+ *ucontrol->value.integer.value = spec->smart51_enabled;
+ return 0;
+}
+
+static int via_smart51_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ int out_in = *ucontrol->value.integer.value
+ ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
+ int i;
+
+ for (i = 0; i < spec->smart51_nums; i++) {
+ hda_nid_t nid = spec->smart51_pins[i];
+ unsigned int parm;
+
+ parm = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+ parm |= out_in;
+ snd_hda_set_pin_ctl(codec, nid, parm);
+ if (out_in == AC_PINCTL_OUT_EN) {
+ mute_aa_path(codec, 1);
+ notify_aa_path_ctls(codec);
+ }
+ }
+ spec->smart51_enabled = *ucontrol->value.integer.value;
+ set_widgets_power_state(codec);
+ return 1;
+}
+
+static const struct snd_kcontrol_new via_smart51_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Smart 5.1",
+ .count = 1,
+ .info = snd_ctl_boolean_mono_info,
+ .get = via_smart51_get,
+ .put = via_smart51_put,
+};
+
+static int via_smart51_build(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+
+ if (!spec->smart51_nums)
+ return 0;
+ if (!via_clone_control(spec, &via_smart51_mixer))
+ return -ENOMEM;
+ return 0;
+}
+
+/* check AA path's mute status */
+static bool is_aa_path_mute(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ const struct hda_amp_list *p;
+ int i, ch, v;
+
+ for (i = 0; i < spec->num_loopbacks; i++) {
+ p = &spec->loopback_list[i];
+ for (ch = 0; ch < 2; ch++) {
+ v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
+ p->idx);
+ if (!(v & HDA_AMP_MUTE) && v > 0)
+ return false;
+ }
+ }
+ return true;
+}
+
+/* enter/exit analog low-current mode */
+static void __analog_low_current_mode(struct hda_codec *codec, bool force)
+{
+ struct via_spec *spec = codec->spec;
+ bool enable;
+ unsigned int verb, parm;
+
+ if (spec->no_pin_power_ctl)
+ enable = false;
+ else
+ enable = is_aa_path_mute(codec) && !spec->opened_streams;
+ if (enable == spec->alc_mode && !force)
+ return;
+ spec->alc_mode = enable;
+
+ /* decide low current mode's verb & parameter */
+ switch (spec->codec_type) {
+ case VT1708B_8CH:
+ case VT1708B_4CH:
+ verb = 0xf70;
+ parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
+ break;
+ case VT1708S:
+ case VT1718S:
+ case VT1716S:
+ verb = 0xf73;
+ parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
+ break;
+ case VT1702:
+ verb = 0xf73;
+ parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
+ break;
+ case VT2002P:
+ case VT1812:
+ case VT1802:
+ verb = 0xf93;
+ parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
+ break;
+ case VT1705CF:
+ case VT1808:
+ verb = 0xf82;
+ parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
+ break;
+ default:
+ return; /* other codecs are not supported */
+ }
+ /* send verb */
+ snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
+}
+
+static void analog_low_current_mode(struct hda_codec *codec)
+{
+ return __analog_low_current_mode(codec, false);
+}
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static const struct hda_verb vt1708_init_verbs[] = {
+ /* power down jack detect function */
+ {0x1, 0xf81, 0x1},
+ { }
+};
+
+static void set_stream_open(struct hda_codec *codec, int bit, bool active)
+{
+ struct via_spec *spec = codec->spec;
+
+ if (active)
+ spec->opened_streams |= bit;
+ else
+ spec->opened_streams &= ~bit;
+ analog_low_current_mode(codec);
+}
+
+static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ int err;
+
+ spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+ set_stream_open(codec, STREAM_MULTI_OUT, true);
+ err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
+ if (err < 0) {
+ set_stream_open(codec, STREAM_MULTI_OUT, false);
+ return err;
+ }
+ return 0;
+}
+
+static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ set_stream_open(codec, STREAM_MULTI_OUT, false);
+ return 0;
+}
+
+static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+
+ if (snd_BUG_ON(!spec->hp_dac_nid))
+ return -EINVAL;
+ set_stream_open(codec, STREAM_INDEP_HP, true);
+ return 0;
+}
+
+static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ set_stream_open(codec, STREAM_INDEP_HP, false);
+ return 0;
+}
+
+static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+
+ mutex_lock(&spec->config_mutex);
+ setup_playback_multi_pcm(spec);
+ snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+ format, substream);
+ /* remember for dynamic DAC switch with indep-HP */
+ spec->active_streams |= STREAM_MULTI_OUT;
+ spec->cur_dac_stream_tag = stream_tag;
+ spec->cur_dac_format = format;
+ mutex_unlock(&spec->config_mutex);
+ vt1708_update_hp_work(spec);
+ return 0;
+}
+
+static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+
+ mutex_lock(&spec->config_mutex);
+ if (spec->hp_independent_mode)
+ snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
+ stream_tag, 0, format);
+ spec->active_streams |= STREAM_INDEP_HP;
+ spec->cur_hp_stream_tag = stream_tag;
+ spec->cur_hp_format = format;
+ mutex_unlock(&spec->config_mutex);
+ vt1708_update_hp_work(spec);
+ return 0;
+}
+
+static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+
+ mutex_lock(&spec->config_mutex);
+ snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+ spec->active_streams &= ~STREAM_MULTI_OUT;
+ mutex_unlock(&spec->config_mutex);
+ vt1708_update_hp_work(spec);
+ return 0;
+}
+
+static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+
+ mutex_lock(&spec->config_mutex);
+ if (spec->hp_independent_mode)
+ snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
+ spec->active_streams &= ~STREAM_INDEP_HP;
+ mutex_unlock(&spec->config_mutex);
+ vt1708_update_hp_work(spec);
+ return 0;
+}
+
+/*
+ * Digital out
+ */
+static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+ return 0;
+}
+
+/*
+ * Analog capture
+ */
+static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+ stream_tag, 0, format);
+ return 0;
+}
+
+static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
+ return 0;
+}
+
+/* analog capture with dynamic ADC switching */
+static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+ int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
+
+ mutex_lock(&spec->config_mutex);
+ spec->cur_adc = spec->adc_nids[adc_idx];
+ spec->cur_adc_stream_tag = stream_tag;
+ spec->cur_adc_format = format;
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+ mutex_unlock(&spec->config_mutex);
+ return 0;
+}
+
+static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct via_spec *spec = codec->spec;
+
+ mutex_lock(&spec->config_mutex);
+ snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+ spec->cur_adc = 0;
+ mutex_unlock(&spec->config_mutex);
+ return 0;
+}
+
+/* re-setup the stream if running; called from input-src put */
+static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
+{
+ struct via_spec *spec = codec->spec;
+ int adc_idx = spec->inputs[cur].adc_idx;
+ hda_nid_t adc = spec->adc_nids[adc_idx];
+ bool ret = false;
+
+ mutex_lock(&spec->config_mutex);
+ if (spec->cur_adc && spec->cur_adc != adc) {
+ /* stream is running, let's swap the current ADC */
+ __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+ spec->cur_adc = adc;
+ snd_hda_codec_setup_stream(codec, adc,
+ spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+ ret = true;
+ }
+ mutex_unlock(&spec->config_mutex);
+ return ret;
+}
+
+static const struct hda_pcm_stream via_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .open = via_playback_multi_pcm_open,
+ .close = via_playback_multi_pcm_close,
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream via_pcm_hp_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .open = via_playback_hp_pcm_open,
+ .close = via_playback_hp_pcm_close,
+ .prepare = via_playback_hp_pcm_prepare,
+ .cleanup = via_playback_hp_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ /* NID is set in via_build_pcms */
+ /* We got noisy outputs on the right channel on VT1708 when
+ * 24bit samples are used. Until any workaround is found,
+ * disable the 24bit format, so far.
+ */
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .ops = {
+ .open = via_playback_multi_pcm_open,
+ .close = via_playback_multi_pcm_close,
+ .prepare = via_playback_multi_pcm_prepare,
+ .cleanup = via_playback_multi_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream via_pcm_analog_capture = {
+ .substreams = 1, /* will be changed in via_build_pcms() */
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .prepare = via_capture_pcm_prepare,
+ .cleanup = via_capture_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .prepare = via_dyn_adc_capture_pcm_prepare,
+ .cleanup = via_dyn_adc_capture_pcm_cleanup,
+ },
+};
+
+static const struct hda_pcm_stream via_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .open = via_dig_playback_pcm_open,
+ .close = via_dig_playback_pcm_close,
+ .prepare = via_dig_playback_pcm_prepare,
+ .cleanup = via_dig_playback_pcm_cleanup
+ },
+};
+
+static const struct hda_pcm_stream via_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+/*
+ * slave controls for virtual master
+ */
+static const char * const via_slave_pfxs[] = {
+ "Front", "Surround", "Center", "LFE", "Side",
+ "Headphone", "Speaker", "Bass Speaker",
+ NULL,
+};
+
+static int via_build_controls(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ struct snd_kcontrol *kctl;
+ int err, i;
+
+ spec->no_pin_power_ctl = 1;
+ if (spec->set_widgets_power_state)
+ if (!via_clone_control(spec, &via_pin_power_ctl_enum))
+ return -ENOMEM;
+
+ for (i = 0; i < spec->num_mixers; i++) {
+ err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+ if (err < 0)
+ 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);
+ if (err < 0)
+ return err;
+ err = snd_hda_create_spdif_share_sw(codec,
+ &spec->multiout);
+ if (err < 0)
+ return err;
+ spec->multiout.share_spdif = 1;
+ }
+ if (spec->dig_in_nid) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+ if (err < 0)
+ return err;
+ }
+
+ /* if we have no master control, let's create it */
+ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
+ unsigned int vmaster_tlv[4];
+ snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
+ HDA_OUTPUT, vmaster_tlv);
+ err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+ vmaster_tlv, via_slave_pfxs,
+ "Playback Volume");
+ if (err < 0)
+ return err;
+ }
+ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
+ err = snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, via_slave_pfxs,
+ "Playback Switch");
+ if (err < 0)
+ return err;
+ }
+
+ /* assign Capture Source enums to NID */
+ kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
+ for (i = 0; kctl && i < kctl->count; i++) {
+ if (!spec->mux_nids[i])
+ continue;
+ err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
+ if (err < 0)
+ return err;
+ }
+
+ via_free_kctls(codec); /* no longer needed */
+
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int via_build_pcms(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+
+ codec->num_pcms = 0;
+ codec->pcm_info = info;
+
+ if (spec->multiout.num_dacs || spec->num_adc_nids) {
+ snprintf(spec->stream_name_analog,
+ sizeof(spec->stream_name_analog),
+ "%s Analog", codec->chip_name);
+ info->name = spec->stream_name_analog;
+
+ if (spec->multiout.num_dacs) {
+ if (!spec->stream_analog_playback)
+ spec->stream_analog_playback =
+ &via_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ *spec->stream_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->multiout.dac_nids[0];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+ if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT
+ && spec->autocfg.line_outs == 2)
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap =
+ snd_pcm_2_1_chmaps;
+ }
+
+ if (!spec->stream_analog_capture) {
+ if (spec->dyn_adc_switch)
+ spec->stream_analog_capture =
+ &via_pcm_dyn_adc_analog_capture;
+ else
+ spec->stream_analog_capture =
+ &via_pcm_analog_capture;
+ }
+ if (spec->num_adc_nids) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ *spec->stream_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+ spec->adc_nids[0];
+ if (!spec->dyn_adc_switch)
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
+ spec->num_adc_nids;
+ }
+ codec->num_pcms++;
+ info++;
+ }
+
+ if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+ snprintf(spec->stream_name_digital,
+ sizeof(spec->stream_name_digital),
+ "%s Digital", codec->chip_name);
+ info->name = spec->stream_name_digital;
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->multiout.dig_out_nid) {
+ if (!spec->stream_digital_playback)
+ spec->stream_digital_playback =
+ &via_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ *spec->stream_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->multiout.dig_out_nid;
+ }
+ if (spec->dig_in_nid) {
+ if (!spec->stream_digital_capture)
+ spec->stream_digital_capture =
+ &via_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ *spec->stream_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+ spec->dig_in_nid;
+ }
+ codec->num_pcms++;
+ info++;
+ }
+
+ if (spec->hp_dac_nid) {
+ snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
+ "%s HP", codec->chip_name);
+ info->name = spec->stream_name_hp;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->hp_dac_nid;
+ codec->num_pcms++;
+ info++;
+ }
+ return 0;
+}
+
+static void via_free(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+
+ if (!spec)
+ return;
+
+ via_free_kctls(codec);
+ vt1708_stop_hp_work(spec);
+ kfree(spec->bind_cap_vol);
+ kfree(spec->bind_cap_sw);
+ snd_hda_gen_free(&spec->gen);
+ kfree(spec);
+}
+
+/* mute/unmute outputs */
+static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
+ hda_nid_t *pins, bool mute)
+{
+ int i;
+ for (i = 0; i < num_pins; i++) {
+ unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ if (parm & AC_PINCTL_IN_EN)
+ continue;
+ if (mute)
+ parm &= ~AC_PINCTL_OUT_EN;
+ else
+ parm |= AC_PINCTL_OUT_EN;
+ snd_hda_set_pin_ctl(codec, pins[i], parm);
+ }
+}
+
+/* mute internal speaker if line-out is plugged */
+static void via_line_automute(struct hda_codec *codec, int present)
+{
+ struct via_spec *spec = codec->spec;
+
+ if (!spec->autocfg.speaker_outs)
+ return;
+ if (!present)
+ present = snd_hda_jack_detect(codec,
+ spec->autocfg.line_out_pins[0]);
+ toggle_output_mutes(codec, spec->autocfg.speaker_outs,
+ spec->autocfg.speaker_pins,
+ present);
+}
+
+/* mute internal speaker if HP is plugged */
+static void via_hp_automute(struct hda_codec *codec)
+{
+ int present = 0;
+ int nums;
+ struct via_spec *spec = codec->spec;
+
+ if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] &&
+ (spec->codec_type != VT1708 || spec->vt1708_jack_detect) &&
+ is_jack_detectable(codec, spec->autocfg.hp_pins[0]))
+ present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
+
+ if (spec->smart51_enabled)
+ nums = spec->autocfg.line_outs + spec->smart51_nums;
+ else
+ nums = spec->autocfg.line_outs;
+ toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
+
+ via_line_automute(codec, present);
+}
+
+#ifdef CONFIG_PM
+static int via_suspend(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ vt1708_stop_hp_work(spec);
+
+ if (spec->codec_type == VT1802) {
+ /* Fix pop noise on headphones */
+ int i;
+ for (i = 0; i < spec->autocfg.hp_outs; i++)
+ snd_hda_set_pin_ctl(codec, spec->autocfg.hp_pins[i], 0);
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct via_spec *spec = codec->spec;
+ return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
+}
+#endif
+
+/*
+ */
+
+static int via_init(struct hda_codec *codec);
+
+static const struct hda_codec_ops via_patch_ops = {
+ .build_controls = via_build_controls,
+ .build_pcms = via_build_pcms,
+ .init = via_init,
+ .free = via_free,
+ .unsol_event = snd_hda_jack_unsol_event,
+#ifdef CONFIG_PM
+ .suspend = via_suspend,
+ .check_power_status = via_check_power_status,
+#endif
+};
+
+static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
+{
+ struct via_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->multiout.num_dacs; i++) {
+ if (spec->multiout.dac_nids[i] == dac)
+ return false;
+ }
+ if (spec->hp_dac_nid == dac)
+ return false;
+ return true;
+}
+
+static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t target_dac, int with_aa_mix,
+ struct nid_path *path, int depth)
+{
+ struct via_spec *spec = codec->spec;
+ hda_nid_t conn[8];
+ int i, nums;
+
+ if (nid == spec->aa_mix_nid) {
+ if (!with_aa_mix)
+ return false;
+ with_aa_mix = 2; /* mark aa-mix is included */
+ }
+
+ nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
+ for (i = 0; i < nums; i++) {
+ if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
+ continue;
+ if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
+ /* aa-mix is requested but not included? */
+ if (!(spec->aa_mix_nid && with_aa_mix == 1))
+ goto found;
+ }
+ }
+ if (depth >= MAX_NID_PATH_DEPTH)
+ return false;
+ for (i = 0; i < nums; i++) {
+ unsigned int type;
+ type = get_wcaps_type(get_wcaps(codec, conn[i]));
+ if (type == AC_WID_AUD_OUT)
+ continue;
+ if (__parse_output_path(codec, conn[i], target_dac,
+ with_aa_mix, path, depth + 1))
+ goto found;
+ }
+ return false;
+
+ found:
+ path->path[path->depth] = conn[i];
+ path->idx[path->depth] = i;
+ if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
+ path->multi[path->depth] = 1;
+ path->depth++;
+ return true;
+}
+
+static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t target_dac, int with_aa_mix,
+ struct nid_path *path)
+{
+ if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
+ path->path[path->depth] = nid;
+ path->depth++;
+ snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
+ path->depth, path->path[0], path->path[1],
+ path->path[2], path->path[3], path->path[4]);
+ return true;
+ }
+ return false;
+}
+
+static int via_auto_fill_dac_nids(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+ hda_nid_t nid;
+
+ spec->multiout.num_dacs = 0;
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ for (i = 0; i < cfg->line_outs; i++) {
+ hda_nid_t dac = 0;
+ nid = cfg->line_out_pins[i];
+ if (!nid)
+ continue;
+ if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
+ dac = spec->out_path[i].path[0];
+ if (!i && parse_output_path(codec, nid, dac, 1,
+ &spec->out_mix_path))
+ dac = spec->out_mix_path.path[0];
+ if (dac)
+ spec->private_dac_nids[spec->multiout.num_dacs++] = dac;
+ }
+ if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
+ spec->out_path[0] = spec->out_mix_path;
+ spec->out_mix_path.depth = 0;
+ }
+ return 0;
+}
+
+static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
+ int chs, bool check_dac, struct nid_path *path)
+{
+ struct via_spec *spec = codec->spec;
+ char name[32];
+ hda_nid_t dac, pin, sel, nid;
+ int err;
+
+ dac = check_dac ? path->path[0] : 0;
+ pin = path->path[path->depth - 1];
+ sel = path->depth > 1 ? path->path[1] : 0;
+
+ if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
+ nid = dac;
+ else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
+ nid = pin;
+ else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
+ nid = sel;
+ else
+ nid = 0;
+ if (nid) {
+ sprintf(name, "%s Playback Volume", pfx);
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ path->vol_ctl = nid;
+ }
+
+ if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
+ nid = dac;
+ else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
+ nid = pin;
+ else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
+ nid = sel;
+ else
+ nid = 0;
+ if (nid) {
+ sprintf(name, "%s Playback Switch", pfx);
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ path->mute_ctl = nid;
+ }
+ return 0;
+}
+
+static void mangle_smart51(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct auto_pin_cfg_item *ins = cfg->inputs;
+ int i, j, nums, attr;
+ int pins[AUTO_CFG_MAX_INS];
+
+ for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
+ nums = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ unsigned int def;
+ if (ins[i].type > AUTO_PIN_LINE_IN)
+ continue;
+ def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
+ if (snd_hda_get_input_pin_attr(def) != attr)
+ continue;
+ for (j = 0; j < nums; j++)
+ if (ins[pins[j]].type < ins[i].type) {
+ memmove(pins + j + 1, pins + j,
+ (nums - j) * sizeof(int));
+ break;
+ }
+ pins[j] = i;
+ nums++;
+ }
+ if (cfg->line_outs + nums < 3)
+ continue;
+ for (i = 0; i < nums; i++) {
+ hda_nid_t pin = ins[pins[i]].pin;
+ spec->smart51_pins[spec->smart51_nums++] = pin;
+ cfg->line_out_pins[cfg->line_outs++] = pin;
+ if (cfg->line_outs == 3)
+ break;
+ }
+ return;
+ }
+}
+
+static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
+{
+ dst->vol_ctl = src->vol_ctl;
+ dst->mute_ctl = src->mute_ctl;
+}
+
+/* add playback controls from the parsed DAC table */
+static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct nid_path *path;
+ static const char * const chname[4] = {
+ "Front", "Surround", NULL /* "CLFE" */, "Side"
+ };
+ int i, idx, err;
+ int old_line_outs;
+
+ /* check smart51 */
+ old_line_outs = cfg->line_outs;
+ if (cfg->line_outs == 1)
+ mangle_smart51(codec);
+
+ err = via_auto_fill_dac_nids(codec);
+ if (err < 0)
+ return err;
+
+ if (spec->multiout.num_dacs < 3) {
+ spec->smart51_nums = 0;
+ cfg->line_outs = old_line_outs;
+ }
+ for (i = 0; i < cfg->line_outs; i++) {
+ hda_nid_t pin, dac;
+ pin = cfg->line_out_pins[i];
+ dac = spec->multiout.dac_nids[i];
+ if (!pin || !dac)
+ continue;
+ path = spec->out_path + i;
+ if (i == HDA_CLFE) {
+ err = create_ch_ctls(codec, "Center", 1, true, path);
+ if (err < 0)
+ return err;
+ err = create_ch_ctls(codec, "LFE", 2, true, path);
+ if (err < 0)
+ return err;
+ } else {
+ const char *pfx = chname[i];
+ if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ cfg->line_outs <= 2)
+ pfx = i ? "Bass Speaker" : "Speaker";
+ err = create_ch_ctls(codec, pfx, 3, true, path);
+ if (err < 0)
+ return err;
+ }
+ if (path != spec->out_path + i)
+ copy_path_mixer_ctls(&spec->out_path[i], path);
+ if (path == spec->out_path && spec->out_mix_path.depth)
+ copy_path_mixer_ctls(&spec->out_mix_path, path);
+ }
+
+ idx = get_connection_index(codec, spec->aa_mix_nid,
+ spec->multiout.dac_nids[0]);
+ if (idx >= 0) {
+ /* add control to mixer */
+ const char *name;
+ name = spec->out_mix_path.depth ?
+ "PCM Loopback Playback Volume" : "PCM Playback Volume";
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
+ idx, HDA_INPUT));
+ if (err < 0)
+ return err;
+ name = spec->out_mix_path.depth ?
+ "PCM Loopback Playback Switch" : "PCM Playback Switch";
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
+ idx, HDA_INPUT));
+ if (err < 0)
+ return err;
+ }
+
+ cfg->line_outs = old_line_outs;
+
+ return 0;
+}
+
+static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct via_spec *spec = codec->spec;
+ struct nid_path *path;
+ bool check_dac;
+ int i, err;
+
+ if (!pin)
+ return 0;
+
+ if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
+ for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
+ if (i < spec->multiout.num_dacs &&
+ parse_output_path(codec, pin,
+ spec->multiout.dac_nids[i], 0,
+ &spec->hp_indep_path)) {
+ spec->hp_indep_shared = i;
+ break;
+ }
+ }
+ }
+ if (spec->hp_indep_path.depth) {
+ spec->hp_dac_nid = spec->hp_indep_path.path[0];
+ if (!spec->hp_indep_shared)
+ spec->hp_path = spec->hp_indep_path;
+ }
+ /* optionally check front-path w/o AA-mix */
+ if (!spec->hp_path.depth)
+ parse_output_path(codec, pin,
+ spec->multiout.dac_nids[HDA_FRONT], 0,
+ &spec->hp_path);
+
+ if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
+ 1, &spec->hp_mix_path) && !spec->hp_path.depth)
+ return 0;
+
+ if (spec->hp_path.depth) {
+ path = &spec->hp_path;
+ check_dac = true;
+ } else {
+ path = &spec->hp_mix_path;
+ check_dac = false;
+ }
+ err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
+ if (err < 0)
+ return err;
+ if (check_dac)
+ copy_path_mixer_ctls(&spec->hp_mix_path, path);
+ else
+ copy_path_mixer_ctls(&spec->hp_path, path);
+ if (spec->hp_indep_path.depth)
+ copy_path_mixer_ctls(&spec->hp_indep_path, path);
+ return 0;
+}
+
+static int via_auto_create_speaker_ctls(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ struct nid_path *path;
+ bool check_dac;
+ hda_nid_t pin, dac = 0;
+ int err;
+
+ pin = spec->autocfg.speaker_pins[0];
+ if (!spec->autocfg.speaker_outs || !pin)
+ return 0;
+
+ if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
+ dac = spec->speaker_path.path[0];
+ if (!dac)
+ parse_output_path(codec, pin,
+ spec->multiout.dac_nids[HDA_FRONT], 0,
+ &spec->speaker_path);
+ if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
+ 1, &spec->speaker_mix_path) && !dac)
+ return 0;
+
+ /* no AA-path for front? */
+ if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
+ dac = 0;
+
+ spec->speaker_dac_nid = dac;
+ spec->multiout.extra_out_nid[0] = dac;
+ if (dac) {
+ path = &spec->speaker_path;
+ check_dac = true;
+ } else {
+ path = &spec->speaker_mix_path;
+ check_dac = false;
+ }
+ err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
+ if (err < 0)
+ return err;
+ if (check_dac)
+ copy_path_mixer_ctls(&spec->speaker_mix_path, path);
+ else
+ copy_path_mixer_ctls(&spec->speaker_path, path);
+ return 0;
+}
+
+#define via_aamix_ctl_info via_pin_power_ctl_info
+
+static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ ucontrol->value.enumerated.item[0] = spec->aamix_mode;
+ return 0;
+}
+
+static void update_aamix_paths(struct hda_codec *codec, int do_mix,
+ struct nid_path *nomix, struct nid_path *mix)
+{
+ if (do_mix) {
+ activate_output_path(codec, nomix, false, false);
+ activate_output_path(codec, mix, true, false);
+ } else {
+ activate_output_path(codec, mix, false, false);
+ activate_output_path(codec, nomix, true, false);
+ }
+}
+
+static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ unsigned int val = ucontrol->value.enumerated.item[0];
+
+ if (val == spec->aamix_mode)
+ return 0;
+ spec->aamix_mode = val;
+ /* update front path */
+ update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
+ /* update HP path */
+ if (!spec->hp_independent_mode) {
+ update_aamix_paths(codec, val, &spec->hp_path,
+ &spec->hp_mix_path);
+ }
+ /* update speaker path */
+ update_aamix_paths(codec, val, &spec->speaker_path,
+ &spec->speaker_mix_path);
+ return 1;
+}
+
+static const struct snd_kcontrol_new via_aamix_ctl_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Loopback Mixing",
+ .info = via_aamix_ctl_info,
+ .get = via_aamix_ctl_get,
+ .put = via_aamix_ctl_put,
+};
+
+static int via_auto_create_loopback_switch(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+
+ if (!spec->aa_mix_nid)
+ return 0; /* no loopback switching available */
+ if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth ||
+ spec->speaker_path.depth))
+ return 0; /* no loopback switching available */
+ if (!via_clone_control(spec, &via_aamix_ctl_enum))
+ return -ENOMEM;
+ return 0;
+}
+
+/* look for ADCs */
+static int via_fill_adcs(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ hda_nid_t nid = codec->start_nid;
+ int i;
+
+ for (i = 0; i < codec->num_nodes; i++, nid++) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
+ continue;
+ if (wcaps & AC_WCAP_DIGITAL)
+ continue;
+ if (!(wcaps & AC_WCAP_CONN_LIST))
+ continue;
+ if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
+ return -ENOMEM;
+ spec->adc_nids[spec->num_adc_nids++] = nid;
+ }
+ return 0;
+}
+
+/* input-src control */
+static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = spec->num_inputs;
+ if (uinfo->value.enumerated.item >= spec->num_inputs)
+ uinfo->value.enumerated.item = spec->num_inputs - 1;
+ strcpy(uinfo->value.enumerated.name,
+ spec->inputs[uinfo->value.enumerated.item].label);
+ return 0;
+}
+
+static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+ ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
+ return 0;
+}
+
+static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct via_spec *spec = codec->spec;
+ unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ hda_nid_t mux;
+ int cur;
+
+ cur = ucontrol->value.enumerated.item[0];
+ if (cur < 0 || cur >= spec->num_inputs)
+ return -EINVAL;
+ if (spec->cur_mux[idx] == cur)
+ return 0;
+ spec->cur_mux[idx] = cur;
+ if (spec->dyn_adc_switch) {
+ int adc_idx = spec->inputs[cur].adc_idx;
+ mux = spec->mux_nids[adc_idx];
+ via_dyn_adc_pcm_resetup(codec, cur);
+ } else {
+ mux = spec->mux_nids[idx];
+ if (snd_BUG_ON(!mux))
+ return -EINVAL;
+ }
+
+ if (mux) {
+ /* switch to D0 beofre change index */
+ update_power_state(codec, mux, AC_PWRST_D0);
+ snd_hda_codec_write(codec, mux, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ spec->inputs[cur].mux_idx);
+ }
+
+ /* update jack power state */
+ set_widgets_power_state(codec);
+ return 0;
+}
+
+static const struct snd_kcontrol_new via_input_src_ctl = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .info = via_mux_enum_info,
+ .get = via_mux_enum_get,
+ .put = via_mux_enum_put,
+};
+
+static int create_input_src_ctls(struct hda_codec *codec, int count)
+{
+ struct via_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+
+ if (spec->num_inputs <= 1 || !count)
+ return 0; /* no need for single src */
+
+ knew = via_clone_control(spec, &via_input_src_ctl);
+ if (!knew)
+ return -ENOMEM;
+ knew->count = count;
+ return 0;
+}
+
+/* add the powersave loopback-list entry */
+static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
+{
+ struct hda_amp_list *list;
+
+ if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
+ return;
+ list = spec->loopback_list + spec->num_loopbacks;
+ list->nid = mix;
+ list->dir = HDA_INPUT;
+ list->idx = idx;
+ spec->num_loopbacks++;
+ spec->loopback.amplist = spec->loopback_list;
+}
+
+static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
+ hda_nid_t dst)
+{
+ return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
+}
+
+/* add the input-route to the given pin */
+static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct via_spec *spec = codec->spec;
+ int c, idx;
+
+ spec->inputs[spec->num_inputs].adc_idx = -1;
+ spec->inputs[spec->num_inputs].pin = pin;
+ for (c = 0; c < spec->num_adc_nids; c++) {
+ if (spec->mux_nids[c]) {
+ idx = get_connection_index(codec, spec->mux_nids[c],
+ pin);
+ if (idx < 0)
+ continue;
+ spec->inputs[spec->num_inputs].mux_idx = idx;
+ } else {
+ if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
+ continue;
+ }
+ spec->inputs[spec->num_inputs].adc_idx = c;
+ /* Can primary ADC satisfy all inputs? */
+ if (!spec->dyn_adc_switch &&
+ spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
+ snd_printd(KERN_INFO
+ "via: dynamic ADC switching enabled\n");
+ spec->dyn_adc_switch = 1;
+ }
+ return true;
}
- /* send verb */
- snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
+ return false;
}
-static void analog_low_current_mode(struct hda_codec *codec)
-{
- return __analog_low_current_mode(codec, false);
-}
+static int get_mux_nids(struct hda_codec *codec);
-static int via_build_controls(struct hda_codec *codec)
+/* parse input-routes; fill ADCs, MUXs and input-src entries */
+static int parse_analog_inputs(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- int err, i;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, err;
- err = snd_hda_gen_build_controls(codec);
+ err = via_fill_adcs(codec);
+ if (err < 0)
+ return err;
+ err = get_mux_nids(codec);
if (err < 0)
return err;
- if (spec->set_widgets_power_state)
- spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
-
- for (i = 0; i < spec->num_mixers; i++) {
- err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
- if (err < 0)
- return err;
+ /* fill all input-routes */
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (add_input_route(codec, cfg->inputs[i].pin))
+ spec->inputs[spec->num_inputs++].label =
+ hda_get_autocfg_input_label(codec, cfg, i);
}
+ /* check for internal loopback recording */
+ if (spec->aa_mix_nid &&
+ add_input_route(codec, spec->aa_mix_nid))
+ spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
+
return 0;
}
-static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream,
- int action)
+/* create analog-loopback volume/switch controls */
+static int create_loopback_ctls(struct hda_codec *codec)
{
- analog_low_current_mode(codec);
- vt1708_update_hp_work(codec);
+ struct via_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ const char *prev_label = NULL;
+ int type_idx = 0;
+ int i, j, err, idx;
+
+ if (!spec->aa_mix_nid)
+ return 0;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin = cfg->inputs[i].pin;
+ const char *label = hda_get_autocfg_input_label(codec, cfg, i);
+
+ if (prev_label && !strcmp(label, prev_label))
+ type_idx++;
+ else
+ type_idx = 0;
+ prev_label = label;
+ idx = get_connection_index(codec, spec->aa_mix_nid, pin);
+ if (idx >= 0) {
+ err = via_new_analog_input(spec, label, type_idx,
+ idx, spec->aa_mix_nid);
+ if (err < 0)
+ return err;
+ add_loopback_list(spec, spec->aa_mix_nid, idx);
+ }
+
+ /* remember the label for smart51 control */
+ for (j = 0; j < spec->smart51_nums; j++) {
+ if (spec->smart51_pins[j] == pin) {
+ spec->smart51_idxs[j] = idx;
+ spec->smart51_labels[j] = label;
+ break;
+ }
+ }
+ }
+ return 0;
}
-static void via_free(struct hda_codec *codec)
+/* create mic-boost controls (if present) */
+static int create_mic_boost_ctls(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ const char *prev_label = NULL;
+ int type_idx = 0;
+ int i, err;
- if (!spec)
- return;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin = cfg->inputs[i].pin;
+ unsigned int caps;
+ const char *label;
+ char name[32];
- vt1708_stop_hp_work(codec);
- snd_hda_gen_spec_free(&spec->gen);
- kfree(spec);
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ continue;
+ caps = query_amp_caps(codec, pin, HDA_INPUT);
+ if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
+ continue;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ if (prev_label && !strcmp(label, prev_label))
+ type_idx++;
+ else
+ type_idx = 0;
+ prev_label = label;
+ snprintf(name, sizeof(name), "%s Boost Volume", label);
+ err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
+ if (err < 0)
+ return err;
+ }
+ return 0;
}
-#ifdef CONFIG_PM
-static int via_suspend(struct hda_codec *codec)
+/* create capture and input-src controls for multiple streams */
+static int create_multi_adc_ctls(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- vt1708_stop_hp_work(codec);
+ int i, err;
- if (spec->codec_type == VT1802) {
- /* Fix pop noise on headphones */
- int i;
- for (i = 0; i < spec->gen.autocfg.hp_outs; i++)
- snd_hda_set_pin_ctl(codec, spec->gen.autocfg.hp_pins[i], 0);
+ /* create capture mixer elements */
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ hda_nid_t adc = spec->adc_nids[i];
+ err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Capture Volume", i,
+ HDA_COMPOSE_AMP_VAL(adc, 3, 0,
+ HDA_INPUT));
+ if (err < 0)
+ return err;
+ err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Capture Switch", i,
+ HDA_COMPOSE_AMP_VAL(adc, 3, 0,
+ HDA_INPUT));
+ if (err < 0)
+ return err;
}
+ /* input-source control */
+ for (i = 0; i < spec->num_adc_nids; i++)
+ if (!spec->mux_nids[i])
+ break;
+ err = create_input_src_ctls(codec, i);
+ if (err < 0)
+ return err;
return 0;
}
-#endif
-#ifdef CONFIG_PM
-static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+/* bind capture volume/switch */
+static struct snd_kcontrol_new via_bind_cap_vol_ctl =
+ HDA_BIND_VOL("Capture Volume", 0);
+static struct snd_kcontrol_new via_bind_cap_sw_ctl =
+ HDA_BIND_SW("Capture Switch", 0);
+
+static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
+ struct hda_ctl_ops *ops)
{
- struct via_spec *spec = codec->spec;
- set_widgets_power_state(codec);
- analog_low_current_mode(codec);
- vt1708_update_hp_work(codec);
- return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
+ struct hda_bind_ctls *ctl;
+ int i;
+
+ ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
+ if (!ctl)
+ return -ENOMEM;
+ ctl->ops = ops;
+ for (i = 0; i < spec->num_adc_nids; i++)
+ ctl->values[i] =
+ HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
+ *ctl_ret = ctl;
+ return 0;
}
-#endif
-/*
- */
+/* create capture and input-src controls for dynamic ADC-switch case */
+static int create_dyn_adc_ctls(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+ int err;
-static int via_init(struct hda_codec *codec);
+ /* set up the bind capture ctls */
+ err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
+ if (err < 0)
+ return err;
+ err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
+ if (err < 0)
+ return err;
-static const struct hda_codec_ops via_patch_ops = {
- .build_controls = via_build_controls,
- .build_pcms = snd_hda_gen_build_pcms,
- .init = via_init,
- .free = via_free,
- .unsol_event = snd_hda_jack_unsol_event,
-#ifdef CONFIG_PM
- .suspend = via_suspend,
- .check_power_status = via_check_power_status,
-#endif
-};
+ /* create capture mixer elements */
+ knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = (long)spec->bind_cap_vol;
+ knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = (long)spec->bind_cap_sw;
+
+ /* input-source control */
+ err = create_input_src_ctls(codec, 1);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/* parse and create capture-related stuff */
+static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ err = parse_analog_inputs(codec);
+ if (err < 0)
+ return err;
+ if (spec->dyn_adc_switch)
+ err = create_dyn_adc_ctls(codec);
+ else
+ err = create_multi_adc_ctls(codec);
+ if (err < 0)
+ return err;
+ err = create_loopback_ctls(codec);
+ if (err < 0)
+ return err;
+ err = create_mic_boost_ctls(codec);
+ if (err < 0)
+ return err;
+ return 0;
+}
-static const struct hda_verb vt1708_init_verbs[] = {
- /* power down jack detect function */
- {0x1, 0xf81, 0x1},
- { }
-};
static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int def_conf;
@@ -561,32 +2633,102 @@ static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
if (spec->vt1708_jack_detect == val)
return 0;
spec->vt1708_jack_detect = val;
- vt1708_update_hp_work(codec);
+ if (spec->vt1708_jack_detect &&
+ snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) {
+ mute_aa_path(codec, 1);
+ notify_aa_path_ctls(codec);
+ }
+ via_hp_automute(codec);
+ vt1708_update_hp_work(spec);
return 1;
}
-static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = {
- {
+static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Jack Detect",
.count = 1,
.info = snd_ctl_boolean_mono_info,
.get = vt1708_jack_detect_get,
.put = vt1708_jack_detect_put,
- },
- {} /* terminator */
};
-static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
+static void fill_dig_outs(struct hda_codec *codec);
+static void fill_dig_in(struct hda_codec *codec);
+
+static int via_parse_auto_config(struct hda_codec *codec)
{
- set_widgets_power_state(codec);
- snd_hda_gen_hp_automute(codec, tbl);
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ if (err < 0)
+ return err;
+ if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
+ return -EINVAL;
+
+ err = via_auto_create_multi_out_ctls(codec);
+ if (err < 0)
+ return err;
+ err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
+ if (err < 0)
+ return err;
+ err = via_auto_create_speaker_ctls(codec);
+ if (err < 0)
+ return err;
+ err = via_auto_create_loopback_switch(codec);
+ if (err < 0)
+ return err;
+ err = via_auto_create_analog_input_ctls(codec);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ fill_dig_outs(codec);
+ fill_dig_in(codec);
+
+ if (spec->kctls.list)
+ spec->mixers[spec->num_mixers++] = spec->kctls.list;
+
+
+ if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
+ err = via_hp_build(codec);
+ if (err < 0)
+ return err;
+ }
+
+ err = via_smart51_build(codec);
+ if (err < 0)
+ return err;
+
+ /* assign slave outs */
+ if (spec->slave_dig_outs[0])
+ codec->slave_dig_outs = spec->slave_dig_outs;
+
+ return 1;
+}
+
+static void via_auto_init_dig_outs(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ if (spec->multiout.dig_out_nid)
+ init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
+ if (spec->slave_dig_outs[0])
+ init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
}
-static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
+static void via_auto_init_dig_in(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ if (!spec->dig_in_nid)
+ return;
+ snd_hda_set_pin_ctl(codec, spec->autocfg.dig_in_pin, PIN_IN);
+}
+
+static void via_jack_output_event(struct hda_codec *codec, struct hda_jack_tbl *tbl)
{
set_widgets_power_state(codec);
- snd_hda_gen_line_automute(codec, tbl);
+ via_hp_automute(codec);
}
static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl)
@@ -594,55 +2736,41 @@ static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_t
set_widgets_power_state(codec);
}
-#define VIA_JACK_EVENT (HDA_GEN_LAST_EVENT + 1)
-
-static void via_set_jack_unsol_events(struct hda_codec *codec)
+/* initialize the unsolicited events */
+static void via_auto_init_unsol_event(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->gen.autocfg;
- hda_nid_t pin;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int ev;
int i;
+ hda_jack_callback cb;
+
+ if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
+ snd_hda_jack_detect_enable_callback(codec, cfg->hp_pins[0],
+ VIA_HP_EVENT | VIA_JACK_EVENT,
+ via_jack_output_event);
- spec->gen.hp_automute_hook = via_hp_automute;
if (cfg->speaker_pins[0])
- spec->gen.line_automute_hook = via_line_automute;
+ ev = VIA_LINE_EVENT;
+ else
+ ev = 0;
+ cb = ev ? via_jack_output_event : via_jack_powerstate_event;
for (i = 0; i < cfg->line_outs; i++) {
- pin = cfg->line_out_pins[i];
- if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
- is_jack_detectable(codec, pin))
- snd_hda_jack_detect_enable_callback(codec, pin,
- VIA_JACK_EVENT,
- via_jack_powerstate_event);
+ if (cfg->line_out_pins[i] &&
+ is_jack_detectable(codec, cfg->line_out_pins[i]))
+ snd_hda_jack_detect_enable_callback(codec, cfg->line_out_pins[i],
+ ev | VIA_JACK_EVENT, cb);
}
for (i = 0; i < cfg->num_inputs; i++) {
- pin = cfg->line_out_pins[i];
- if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
- is_jack_detectable(codec, pin))
- snd_hda_jack_detect_enable_callback(codec, pin,
+ if (is_jack_detectable(codec, cfg->inputs[i].pin))
+ snd_hda_jack_detect_enable_callback(codec, cfg->inputs[i].pin,
VIA_JACK_EVENT,
via_jack_powerstate_event);
}
}
-static int via_parse_auto_config(struct hda_codec *codec)
-{
- struct via_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
- if (err < 0)
- return err;
-
- err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
- if (err < 0)
- return err;
-
- via_set_jack_unsol_events(codec);
- return 0;
-}
-
static int via_init(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
@@ -655,47 +2783,63 @@ static int via_init(struct hda_codec *codec)
set_widgets_power_state(codec);
__analog_low_current_mode(codec, true);
- snd_hda_gen_init(codec);
+ via_auto_init_multi_out(codec);
+ via_auto_init_hp_out(codec);
+ via_auto_init_speaker_out(codec);
+ via_auto_init_analog_input(codec);
+ via_auto_init_dig_outs(codec);
+ via_auto_init_dig_in(codec);
+
+ via_auto_init_unsol_event(codec);
- vt1708_update_hp_work(codec);
+ via_hp_automute(codec);
+ vt1708_update_hp_work(spec);
return 0;
}
-static int vt1708_build_controls(struct hda_codec *codec)
+static void vt1708_update_hp_jack_state(struct work_struct *work)
{
- /* In order not to create "Phantom Jack" controls,
- temporary enable jackpoll */
- int err;
- int old_interval = codec->jackpoll_interval;
- codec->jackpoll_interval = msecs_to_jiffies(100);
- err = via_build_controls(codec);
- codec->jackpoll_interval = old_interval;
- return err;
+ struct via_spec *spec = container_of(work, struct via_spec,
+ vt1708_hp_work.work);
+ if (spec->codec_type != VT1708)
+ return;
+ snd_hda_jack_set_dirty_all(spec->codec);
+ /* if jack state toggled */
+ if (spec->vt1708_hp_present
+ != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
+ spec->vt1708_hp_present ^= 1;
+ via_hp_automute(spec->codec);
+ }
+ if (spec->vt1708_jack_detect)
+ schedule_delayed_work(&spec->vt1708_hp_work,
+ msecs_to_jiffies(100));
}
-static int vt1708_build_pcms(struct hda_codec *codec)
+static int get_mux_nids(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- int i, err;
-
- err = snd_hda_gen_build_pcms(codec);
- if (err < 0 || codec->vendor_id != 0x11061708)
- return err;
-
- /* We got noisy outputs on the right channel on VT1708 when
- * 24bit samples are used. Until any workaround is found,
- * disable the 24bit format, so far.
- */
- for (i = 0; i < codec->num_pcms; i++) {
- struct hda_pcm *info = &spec->gen.pcm_rec[i];
- if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
- info->pcm_type != HDA_PCM_TYPE_AUDIO)
- continue;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
- SNDRV_PCM_FMTBIT_S16_LE;
+ hda_nid_t nid, conn[8];
+ unsigned int type;
+ int i, n;
+
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ nid = spec->adc_nids[i];
+ while (nid) {
+ type = get_wcaps_type(get_wcaps(codec, nid));
+ if (type == AC_WID_PIN)
+ break;
+ n = snd_hda_get_connections(codec, nid, conn,
+ ARRAY_SIZE(conn));
+ if (n <= 0)
+ break;
+ if (n > 1) {
+ spec->mux_nids[i] = nid;
+ break;
+ }
+ nid = conn[0];
+ }
}
-
return 0;
}
@@ -709,15 +2853,7 @@ static int patch_vt1708(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->gen.mixer_nid = 0x17;
-
- /* set jackpoll_interval while parsing the codec */
- codec->jackpoll_interval = msecs_to_jiffies(100);
- spec->vt1708_jack_detect = 1;
-
- /* don't support the input jack switching due to lack of unsol event */
- /* (it may work with polling, though, but it needs testing) */
- spec->gen.suppress_auto_mic = 1;
+ spec->aa_mix_nid = 0x17;
/* Add HP and CD pin config connect bit re-config action */
vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
@@ -731,17 +2867,18 @@ static int patch_vt1708(struct hda_codec *codec)
}
/* add jack detect on/off control */
- spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl;
+ if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
+ return -ENOMEM;
+
+ /* disable 32bit format on VT1708 */
+ if (codec->vendor_id == 0x11061708)
+ spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
codec->patch_ops = via_patch_ops;
- codec->patch_ops.build_controls = vt1708_build_controls;
- codec->patch_ops.build_pcms = vt1708_build_pcms;
-
- /* clear jackpoll_interval again; it's set dynamically */
- codec->jackpoll_interval = 0;
+ INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
return 0;
}
@@ -755,7 +2892,7 @@ static int patch_vt1709(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->gen.mixer_nid = 0x18;
+ spec->aa_mix_nid = 0x18;
err = via_parse_auto_config(codec);
if (err < 0) {
@@ -799,7 +2936,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
/* PW0 (19h), SW1 (18h), AOW1 (11h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x19, &parm);
- if (smart51_enabled(codec))
+ if (spec->smart51_enabled)
set_pin_power_state(codec, 0x1b, &parm);
update_power_state(codec, 0x18, parm);
update_power_state(codec, 0x11, parm);
@@ -808,7 +2945,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
if (is_8ch) {
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x22, &parm);
- if (smart51_enabled(codec))
+ if (spec->smart51_enabled)
set_pin_power_state(codec, 0x1a, &parm);
update_power_state(codec, 0x26, parm);
update_power_state(codec, 0x24, parm);
@@ -816,7 +2953,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
/* PW7(23h), SW2(27h), AOW2(25h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x23, &parm);
- if (smart51_enabled(codec))
+ if (spec->smart51_enabled)
set_pin_power_state(codec, 0x1a, &parm);
update_power_state(codec, 0x27, parm);
update_power_state(codec, 0x25, parm);
@@ -836,7 +2973,7 @@ static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
if (is_8ch) {
update_power_state(codec, 0x25, parm);
update_power_state(codec, 0x27, parm);
- } else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled)
+ } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
update_power_state(codec, 0x25, parm);
}
@@ -854,7 +2991,7 @@ static int patch_vt1708B(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->gen.mixer_nid = 0x16;
+ spec->aa_mix_nid = 0x16;
/* automatic parse from the BIOS config */
err = via_parse_auto_config(codec);
@@ -879,6 +3016,58 @@ static const struct hda_verb vt1708S_init_verbs[] = {
{ }
};
+/* fill out digital output widgets; one for master and one for slave outputs */
+static void fill_dig_outs(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->autocfg.dig_outs; i++) {
+ hda_nid_t nid;
+ int conn;
+
+ nid = spec->autocfg.dig_out_pins[i];
+ if (!nid)
+ continue;
+ conn = snd_hda_get_connections(codec, nid, &nid, 1);
+ if (conn < 1)
+ continue;
+ if (!spec->multiout.dig_out_nid)
+ spec->multiout.dig_out_nid = nid;
+ else {
+ spec->slave_dig_outs[0] = nid;
+ break; /* at most two dig outs */
+ }
+ }
+}
+
+static void fill_dig_in(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ hda_nid_t dig_nid;
+ int i, err;
+
+ if (!spec->autocfg.dig_in_pin)
+ return;
+
+ dig_nid = codec->start_nid;
+ for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
+ unsigned int wcaps = get_wcaps(codec, dig_nid);
+ if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
+ continue;
+ if (!(wcaps & AC_WCAP_DIGITAL))
+ continue;
+ if (!(wcaps & AC_WCAP_CONN_LIST))
+ continue;
+ err = get_connection_index(codec, dig_nid,
+ spec->autocfg.dig_in_pin);
+ if (err >= 0) {
+ spec->dig_in_nid = dig_nid;
+ break;
+ }
+ }
+}
+
static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
int offset, int num_steps, int step_size)
{
@@ -899,10 +3088,21 @@ static int patch_vt1708S(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->gen.mixer_nid = 0x16;
+ spec->aa_mix_nid = 0x16;
override_mic_boost(codec, 0x1a, 0, 3, 40);
override_mic_boost(codec, 0x1e, 0, 3, 40);
+ /* automatic parse from the BIOS config */
+ err = via_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
+ return err;
+ }
+
+ spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
+
+ codec->patch_ops = via_patch_ops;
+
/* correct names for VT1708BCE */
if (get_codec_type(codec) == VT1708BCE) {
kfree(codec->chip_name);
@@ -919,18 +3119,6 @@ static int patch_vt1708S(struct hda_codec *codec)
sizeof(codec->bus->card->mixername),
"%s %s", codec->vendor_name, codec->chip_name);
}
-
- /* automatic parse from the BIOS config */
- err = via_parse_auto_config(codec);
- if (err < 0) {
- via_free(codec);
- return err;
- }
-
- spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
-
- codec->patch_ops = via_patch_ops;
-
spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
return 0;
}
@@ -985,7 +3173,7 @@ static int patch_vt1702(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->gen.mixer_nid = 0x1a;
+ spec->aa_mix_nid = 0x1a;
/* limit AA path volume to 0 dB */
snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
@@ -1052,17 +3240,17 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
/* PW2 (26h), AOW2 (ah) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x26, &parm);
- if (smart51_enabled(codec))
+ if (spec->smart51_enabled)
set_pin_power_state(codec, 0x2b, &parm);
update_power_state(codec, 0xa, parm);
/* PW0 (24h), AOW0 (8h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x24, &parm);
- if (!spec->gen.indep_hp_enabled) /* check for redirected HP */
+ if (!spec->hp_independent_mode) /* check for redirected HP */
set_pin_power_state(codec, 0x28, &parm);
update_power_state(codec, 0x8, parm);
- if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
+ if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3)
parm = parm2;
update_power_state(codec, 0xb, parm);
/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
@@ -1071,11 +3259,11 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
/* PW1 (25h), AOW1 (9h) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x25, &parm);
- if (smart51_enabled(codec))
+ if (spec->smart51_enabled)
set_pin_power_state(codec, 0x2a, &parm);
update_power_state(codec, 0x9, parm);
- if (spec->gen.indep_hp_enabled) {
+ if (spec->hp_independent_mode) {
/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x28, &parm);
@@ -1095,9 +3283,9 @@ static int add_secret_dac_path(struct hda_codec *codec)
hda_nid_t conn[8];
hda_nid_t nid;
- if (!spec->gen.mixer_nid)
+ if (!spec->aa_mix_nid)
return 0;
- nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
+ nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
ARRAY_SIZE(conn) - 1);
for (i = 0; i < nums; i++) {
if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
@@ -1112,7 +3300,7 @@ static int add_secret_dac_path(struct hda_codec *codec)
!(caps & AC_WCAP_DIGITAL)) {
conn[nums++] = nid;
return snd_hda_override_conn_list(codec,
- spec->gen.mixer_nid,
+ spec->aa_mix_nid,
nums, conn);
}
}
@@ -1130,7 +3318,7 @@ static int patch_vt1718S(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->gen.mixer_nid = 0x21;
+ spec->aa_mix_nid = 0x21;
override_mic_boost(codec, 0x2b, 0, 3, 40);
override_mic_boost(codec, 0x29, 0, 3, 40);
add_secret_dac_path(codec);
@@ -1261,7 +3449,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x19, &parm);
/* Smart 5.1 PW2(1bh) */
- if (smart51_enabled(codec))
+ if (spec->smart51_enabled)
set_pin_power_state(codec, 0x1b, &parm);
update_power_state(codec, 0x18, parm);
update_power_state(codec, 0x11, parm);
@@ -1270,12 +3458,12 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x23, &parm);
/* Smart 5.1 PW1(1ah) */
- if (smart51_enabled(codec))
+ if (spec->smart51_enabled)
set_pin_power_state(codec, 0x1a, &parm);
update_power_state(codec, 0x27, parm);
/* Smart 5.1 PW5(1eh) */
- if (smart51_enabled(codec))
+ if (spec->smart51_enabled)
set_pin_power_state(codec, 0x1e, &parm);
update_power_state(codec, 0x25, parm);
@@ -1287,7 +3475,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
mono_out = 0;
else {
present = snd_hda_jack_detect(codec, 0x1d);
- if (!spec->gen.indep_hp_enabled && present)
+ if (!spec->hp_independent_mode && present)
mono_out = 0;
else
mono_out = 1;
@@ -1302,7 +3490,7 @@ static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
set_pin_power_state(codec, 0x1c, &parm);
set_pin_power_state(codec, 0x1d, &parm);
/* HP Independent Mode, power on AOW3 */
- if (spec->gen.indep_hp_enabled)
+ if (spec->hp_independent_mode)
update_power_state(codec, 0x25, parm);
/* force to D0 for internal Speaker */
@@ -1321,7 +3509,7 @@ static int patch_vt1716S(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->gen.mixer_nid = 0x16;
+ spec->aa_mix_nid = 0x16;
override_mic_boost(codec, 0x1a, 0, 3, 40);
override_mic_boost(codec, 0x1e, 0, 3, 40);
@@ -1334,7 +3522,9 @@ static int patch_vt1716S(struct hda_codec *codec)
spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
- spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer;
+ spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
+ spec->num_mixers++;
+
spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
codec->patch_ops = via_patch_ops;
@@ -1419,7 +3609,7 @@ static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
update_power_state(codec, 0x35, parm);
}
- if (spec->gen.indep_hp_enabled)
+ if (spec->hp_independent_mode)
update_power_state(codec, 0x9, AC_PWRST_D0);
/* Class-D */
@@ -1517,7 +3707,7 @@ static int patch_vt2002P(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->gen.mixer_nid = 0x21;
+ spec->aa_mix_nid = 0x21;
override_mic_boost(codec, 0x2b, 0, 3, 40);
override_mic_boost(codec, 0x29, 0, 3, 40);
if (spec->codec_type == VT1802)
@@ -1588,7 +3778,7 @@ static void set_widgets_power_state_vt1812(struct hda_codec *codec)
set_pin_power_state(codec, 0x25, &parm);
update_power_state(codec, 0x15, parm);
update_power_state(codec, 0x35, parm);
- if (spec->gen.indep_hp_enabled)
+ if (spec->hp_independent_mode)
update_power_state(codec, 0x9, AC_PWRST_D0);
/* Internal Speaker */
@@ -1641,7 +3831,7 @@ static int patch_vt1812(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->gen.mixer_nid = 0x21;
+ spec->aa_mix_nid = 0x21;
override_mic_boost(codec, 0x2b, 0, 3, 40);
override_mic_boost(codec, 0x29, 0, 3, 40);
add_secret_dac_path(codec);
@@ -1711,7 +3901,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec)
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x26, &parm);
update_power_state(codec, 0x36, parm);
- if (smart51_enabled(codec)) {
+ if (spec->smart51_enabled) {
/* PW7(2bh), MW7(3bh), MUX7(1Bh) */
set_pin_power_state(codec, 0x2b, &parm);
update_power_state(codec, 0x3b, parm);
@@ -1723,7 +3913,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec)
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x25, &parm);
update_power_state(codec, 0x35, parm);
- if (smart51_enabled(codec)) {
+ if (spec->smart51_enabled) {
/* PW6(2ah), MW6(3ah), MUX6(1ah) */
set_pin_power_state(codec, 0x2a, &parm);
update_power_state(codec, 0x3a, parm);
@@ -1736,7 +3926,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec)
set_pin_power_state(codec, 0x28, &parm);
update_power_state(codec, 0x38, parm);
update_power_state(codec, 0x18, parm);
- if (spec->gen.indep_hp_enabled)
+ if (spec->hp_independent_mode)
update_conv_power_state(codec, 0xb, parm, 3);
parm2 = parm; /* for pin 0x0b */
@@ -1744,7 +3934,7 @@ static void set_widgets_power_state_vt3476(struct hda_codec *codec)
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x24, &parm);
update_power_state(codec, 0x34, parm);
- if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
+ if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3)
parm = parm2;
update_conv_power_state(codec, 0x8, parm, 0);
/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
@@ -1761,7 +3951,7 @@ static int patch_vt3476(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
- spec->gen.mixer_nid = 0x3f;
+ spec->aa_mix_nid = 0x3f;
add_secret_dac_path(codec);
/* automatic parse from the BIOS config */
diff --git a/trunk/sound/pci/ice1712/wm8766.c b/trunk/sound/pci/ice1712/wm8766.c
index e473f8a88f9c..8072adeecf68 100644
--- a/trunk/sound/pci/ice1712/wm8766.c
+++ b/trunk/sound/pci/ice1712/wm8766.c
@@ -31,7 +31,7 @@
static void snd_wm8766_write(struct snd_wm8766 *wm, u16 addr, u16 data)
{
- if (addr < WM8766_REG_COUNT)
+ if (addr < WM8766_REG_RESET)
wm->regs[addr] = data;
wm->ops.write(wm, addr, data);
}
diff --git a/trunk/sound/pci/intel8x0.c b/trunk/sound/pci/intel8x0.c
index b8fe40531b9c..3b9be752f3e2 100644
--- a/trunk/sound/pci/intel8x0.c
+++ b/trunk/sound/pci/intel8x0.c
@@ -3266,13 +3266,11 @@ static int check_default_spdif_aclink(struct pci_dev *pci)
w = snd_pci_quirk_lookup(pci, spdif_aclink_defaults);
if (w) {
if (w->value)
- snd_printdd(KERN_INFO
- "intel8x0: Using SPDIF over AC-Link for %s\n",
- snd_pci_quirk_name(w));
+ snd_printdd(KERN_INFO "intel8x0: Using SPDIF over "
+ "AC-Link for %s\n", w->name);
else
- snd_printdd(KERN_INFO
- "intel8x0: Using integrated SPDIF DMA for %s\n",
- snd_pci_quirk_name(w));
+ snd_printdd(KERN_INFO "intel8x0: Using integrated "
+ "SPDIF DMA for %s\n", w->name);
return w->value;
}
return 0;
diff --git a/trunk/sound/pci/maestro3.c b/trunk/sound/pci/maestro3.c
index c76ac1411210..9387533f70dc 100644
--- a/trunk/sound/pci/maestro3.c
+++ b/trunk/sound/pci/maestro3.c
@@ -2586,9 +2586,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
else {
quirk = snd_pci_quirk_lookup(pci, m3_amp_quirk_list);
if (quirk) {
- snd_printdd(KERN_INFO
- "maestro3: set amp-gpio for '%s'\n",
- snd_pci_quirk_name(quirk));
+ snd_printdd(KERN_INFO "maestro3: set amp-gpio "
+ "for '%s'\n", quirk->name);
chip->amp_gpio = quirk->value;
} else if (chip->allegro_flag)
chip->amp_gpio = GPO_EXT_AMP_ALLEGRO;
@@ -2598,9 +2597,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
quirk = snd_pci_quirk_lookup(pci, m3_irda_quirk_list);
if (quirk) {
- snd_printdd(KERN_INFO
- "maestro3: enabled irda workaround for '%s'\n",
- snd_pci_quirk_name(quirk));
+ snd_printdd(KERN_INFO "maestro3: enabled irda workaround "
+ "for '%s'\n", quirk->name);
chip->irda_workaround = 1;
}
quirk = snd_pci_quirk_lookup(pci, m3_hv_quirk_list);
diff --git a/trunk/sound/pci/nm256/nm256.c b/trunk/sound/pci/nm256/nm256.c
index 6febedb05936..563a193e36a3 100644
--- a/trunk/sound/pci/nm256/nm256.c
+++ b/trunk/sound/pci/nm256/nm256.c
@@ -1660,8 +1660,7 @@ static int snd_nm256_probe(struct pci_dev *pci,
q = snd_pci_quirk_lookup(pci, nm256_quirks);
if (q) {
- snd_printdd(KERN_INFO "nm256: Enabled quirk for %s.\n",
- snd_pci_quirk_name(q));
+ snd_printdd(KERN_INFO "nm256: Enabled quirk for %s.\n", q->name);
switch (q->value) {
case NM_BLACKLISTED:
printk(KERN_INFO "nm256: The device is blacklisted. "
diff --git a/trunk/sound/pci/pcxhr/pcxhr_core.c b/trunk/sound/pci/pcxhr/pcxhr_core.c
index 37b431b9b69d..b33db1e006e7 100644
--- a/trunk/sound/pci/pcxhr/pcxhr_core.c
+++ b/trunk/sound/pci/pcxhr/pcxhr_core.c
@@ -1012,12 +1012,13 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err,
enum pcxhr_async_err_src err_src, int pipe,
int is_capture)
{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
static char* err_src_name[] = {
[PCXHR_ERR_PIPE] = "Pipe",
[PCXHR_ERR_STREAM] = "Stream",
[PCXHR_ERR_AUDIO] = "Audio"
};
-
+#endif
if (err & 0xfff)
err &= 0xfff;
else
diff --git a/trunk/sound/pci/rme32.c b/trunk/sound/pci/rme32.c
index 0ecd4100713e..2450663e1a18 100644
--- a/trunk/sound/pci/rme32.c
+++ b/trunk/sound/pci/rme32.c
@@ -1017,7 +1017,7 @@ static int snd_rme32_capture_close(struct snd_pcm_substream *substream)
spin_lock_irq(&rme32->lock);
rme32->capture_substream = NULL;
rme32->capture_periodsize = 0;
- spin_unlock_irq(&rme32->lock);
+ spin_unlock(&rme32->lock);
return 0;
}
diff --git a/trunk/sound/pci/rme9652/hdsp.c b/trunk/sound/pci/rme9652/hdsp.c
index 94084cdb130c..4fae81f21efb 100644
--- a/trunk/sound/pci/rme9652/hdsp.c
+++ b/trunk/sound/pci/rme9652/hdsp.c
@@ -154,13 +154,10 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin");
#define HDSP_BIGENDIAN_MODE 0x200
#define HDSP_RD_MULTIPLE 0x400
#define HDSP_9652_ENABLE_MIXER 0x800
-#define HDSP_S200 0x800
-#define HDSP_S300 (0x100 | HDSP_S200) /* dummy, purpose of 0x100 unknown */
-#define HDSP_CYCLIC_MODE 0x1000
#define HDSP_TDO 0x10000000
-#define HDSP_S_PROGRAM (HDSP_CYCLIC_MODE|HDSP_PROGRAM|HDSP_CONFIG_MODE_0)
-#define HDSP_S_LOAD (HDSP_CYCLIC_MODE|HDSP_PROGRAM|HDSP_CONFIG_MODE_1)
+#define HDSP_S_PROGRAM (HDSP_PROGRAM|HDSP_CONFIG_MODE_0)
+#define HDSP_S_LOAD (HDSP_PROGRAM|HDSP_CONFIG_MODE_1)
/* Control Register bits */
@@ -674,23 +671,13 @@ static unsigned int hdsp_read(struct hdsp *hdsp, int reg)
static int hdsp_check_for_iobox (struct hdsp *hdsp)
{
- int i;
-
if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0;
- for (i = 0; i < 500; i++) {
- if (0 == (hdsp_read(hdsp, HDSP_statusRegister) &
- HDSP_ConfigError)) {
- if (i) {
- snd_printd("Hammerfall-DSP: IO box found after %d ms\n",
- (20 * i));
- }
- return 0;
- }
- msleep(20);
+ if (hdsp_read (hdsp, HDSP_statusRegister) & HDSP_ConfigError) {
+ snd_printk("Hammerfall-DSP: no IO box connected!\n");
+ hdsp->state &= ~HDSP_FirmwareLoaded;
+ return -EIO;
}
- snd_printk(KERN_ERR "Hammerfall-DSP: no IO box connected!\n");
- hdsp->state &= ~HDSP_FirmwareLoaded;
- return -EIO;
+ return 0;
}
static int hdsp_wait_for_iobox(struct hdsp *hdsp, unsigned int loops,
@@ -741,7 +728,6 @@ static int snd_hdsp_load_firmware_from_cache(struct hdsp *hdsp) {
if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) {
snd_printk ("Hammerfall-DSP: timeout waiting for download preparation\n");
- hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200);
return -EIO;
}
@@ -751,15 +737,17 @@ static int snd_hdsp_load_firmware_from_cache(struct hdsp *hdsp) {
hdsp_write(hdsp, HDSP_fifoData, cache[i]);
if (hdsp_fifo_wait (hdsp, 127, HDSP_LONG_WAIT)) {
snd_printk ("Hammerfall-DSP: timeout during firmware loading\n");
- hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200);
return -EIO;
}
}
- hdsp_fifo_wait(hdsp, 3, HDSP_LONG_WAIT);
- hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200);
-
ssleep(3);
+
+ if (hdsp_fifo_wait (hdsp, 0, HDSP_LONG_WAIT)) {
+ snd_printk ("Hammerfall-DSP: timeout at end of firmware loading\n");
+ return -EIO;
+ }
+
#ifdef SNDRV_BIG_ENDIAN
hdsp->control2_register = HDSP_BIGENDIAN_MODE;
#else
@@ -785,51 +773,24 @@ static int hdsp_get_iobox_version (struct hdsp *hdsp)
{
if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) {
- hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
- hdsp_write(hdsp, HDSP_fifoData, 0);
-
- if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) {
- hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300);
- hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
- }
+ hdsp_write (hdsp, HDSP_control2Reg, HDSP_PROGRAM);
+ hdsp_write (hdsp, HDSP_fifoData, 0);
+ if (hdsp_fifo_wait (hdsp, 0, HDSP_SHORT_WAIT) < 0)
+ return -EIO;
- hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200 | HDSP_PROGRAM);
+ hdsp_write (hdsp, HDSP_control2Reg, HDSP_S_LOAD);
hdsp_write (hdsp, HDSP_fifoData, 0);
- if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) {
- hdsp->io_type = Multiface;
- snd_printk("Hammerfall-DSP: Multiface found\n");
- return 0;
- }
- hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
- hdsp_write(hdsp, HDSP_fifoData, 0);
- if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) == 0) {
+ if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT)) {
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_VERSION_BIT);
+ hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
+ if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT))
+ hdsp->io_type = RPM;
+ else
+ hdsp->io_type = Multiface;
+ } else {
hdsp->io_type = Digiface;
- snd_printk("Hammerfall-DSP: Digiface found\n");
- return 0;
}
-
- hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300);
- hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
- hdsp_write(hdsp, HDSP_fifoData, 0);
- if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) == 0) {
- hdsp->io_type = Multiface;
- snd_printk("Hammerfall-DSP: Multiface found\n");
- return 0;
- }
-
- hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300);
- hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD);
- hdsp_write(hdsp, HDSP_fifoData, 0);
- if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) {
- hdsp->io_type = Multiface;
- snd_printk("Hammerfall-DSP: Multiface found\n");
- return 0;
- }
-
- hdsp->io_type = RPM;
- snd_printk("Hammerfall-DSP: RPM found\n");
- return 0;
} else {
/* firmware was already loaded, get iobox type */
if (hdsp_read(hdsp, HDSP_status2Register) & HDSP_version2)
@@ -1713,50 +1674,127 @@ static int snd_hdsp_put_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change;
}
-#define HDSP_TOGGLE_SETTING(xname, xindex) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .private_value = xindex, \
- .info = snd_hdsp_info_toggle_setting, \
- .get = snd_hdsp_get_toggle_setting, \
- .put = snd_hdsp_put_toggle_setting \
-}
+#define HDSP_SPDIF_OUT(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_hdsp_info_spdif_bits, \
+ .get = snd_hdsp_get_spdif_out, .put = snd_hdsp_put_spdif_out }
-static int hdsp_toggle_setting(struct hdsp *hdsp, u32 regmask)
+static int hdsp_spdif_out(struct hdsp *hdsp)
{
- return (hdsp->control_register & regmask) ? 1 : 0;
+ return (hdsp->control_register & HDSP_SPDIFOpticalOut) ? 1 : 0;
}
-static int hdsp_set_toggle_setting(struct hdsp *hdsp, u32 regmask, int out)
+static int hdsp_set_spdif_output(struct hdsp *hdsp, int out)
{
if (out)
- hdsp->control_register |= regmask;
+ hdsp->control_register |= HDSP_SPDIFOpticalOut;
else
- hdsp->control_register &= ~regmask;
+ hdsp->control_register &= ~HDSP_SPDIFOpticalOut;
hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+#define snd_hdsp_info_spdif_bits snd_ctl_boolean_mono_info
+
+static int snd_hdsp_get_spdif_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = hdsp_spdif_out(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_spdif_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_spdif_out(hdsp);
+ hdsp_set_spdif_output(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_SPDIF_PROFESSIONAL(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_hdsp_info_spdif_bits, \
+ .get = snd_hdsp_get_spdif_professional, .put = snd_hdsp_put_spdif_professional }
+
+static int hdsp_spdif_professional(struct hdsp *hdsp)
+{
+ return (hdsp->control_register & HDSP_SPDIFProfessional) ? 1 : 0;
+}
+static int hdsp_set_spdif_professional(struct hdsp *hdsp, int val)
+{
+ if (val)
+ hdsp->control_register |= HDSP_SPDIFProfessional;
+ else
+ hdsp->control_register &= ~HDSP_SPDIFProfessional;
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
return 0;
}
-#define snd_hdsp_info_toggle_setting snd_ctl_boolean_mono_info
+static int snd_hdsp_get_spdif_professional(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = hdsp_spdif_professional(hdsp);
+ return 0;
+}
-static int snd_hdsp_get_toggle_setting(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_hdsp_put_spdif_professional(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- u32 regmask = kcontrol->private_value;
+ int change;
+ unsigned int val;
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdsp->lock);
- ucontrol->value.integer.value[0] = hdsp_toggle_setting(hdsp, regmask);
+ change = (int)val != hdsp_spdif_professional(hdsp);
+ hdsp_set_spdif_professional(hdsp, val);
spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_SPDIF_EMPHASIS(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_hdsp_info_spdif_bits, \
+ .get = snd_hdsp_get_spdif_emphasis, .put = snd_hdsp_put_spdif_emphasis }
+
+static int hdsp_spdif_emphasis(struct hdsp *hdsp)
+{
+ return (hdsp->control_register & HDSP_SPDIFEmphasis) ? 1 : 0;
+}
+
+static int hdsp_set_spdif_emphasis(struct hdsp *hdsp, int val)
+{
+ if (val)
+ hdsp->control_register |= HDSP_SPDIFEmphasis;
+ else
+ hdsp->control_register &= ~HDSP_SPDIFEmphasis;
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_get_spdif_emphasis(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = hdsp_spdif_emphasis(hdsp);
return 0;
}
-static int snd_hdsp_put_toggle_setting(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int snd_hdsp_put_spdif_emphasis(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- u32 regmask = kcontrol->private_value;
int change;
unsigned int val;
@@ -1764,9 +1802,52 @@ static int snd_hdsp_put_toggle_setting(struct snd_kcontrol *kcontrol,
return -EBUSY;
val = ucontrol->value.integer.value[0] & 1;
spin_lock_irq(&hdsp->lock);
- change = (int) val != hdsp_toggle_setting(hdsp, regmask);
- if (change)
- hdsp_set_toggle_setting(hdsp, regmask, val);
+ change = (int)val != hdsp_spdif_emphasis(hdsp);
+ hdsp_set_spdif_emphasis(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+#define HDSP_SPDIF_NON_AUDIO(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+ .info = snd_hdsp_info_spdif_bits, \
+ .get = snd_hdsp_get_spdif_nonaudio, .put = snd_hdsp_put_spdif_nonaudio }
+
+static int hdsp_spdif_nonaudio(struct hdsp *hdsp)
+{
+ return (hdsp->control_register & HDSP_SPDIFNonAudio) ? 1 : 0;
+}
+
+static int hdsp_set_spdif_nonaudio(struct hdsp *hdsp, int val)
+{
+ if (val)
+ hdsp->control_register |= HDSP_SPDIFNonAudio;
+ else
+ hdsp->control_register &= ~HDSP_SPDIFNonAudio;
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+static int snd_hdsp_get_spdif_nonaudio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = hdsp_spdif_nonaudio(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_spdif_nonaudio(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_spdif_nonaudio(hdsp);
+ hdsp_set_spdif_nonaudio(hdsp, val);
spin_unlock_irq(&hdsp->lock);
return change;
}
@@ -2370,6 +2451,114 @@ static int snd_hdsp_put_phone_gain(struct snd_kcontrol *kcontrol, struct snd_ctl
return change;
}
+#define HDSP_XLR_BREAKOUT_CABLE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_xlr_breakout_cable, \
+ .get = snd_hdsp_get_xlr_breakout_cable, \
+ .put = snd_hdsp_put_xlr_breakout_cable \
+}
+
+static int hdsp_xlr_breakout_cable(struct hdsp *hdsp)
+{
+ if (hdsp->control_register & HDSP_XLRBreakoutCable)
+ return 1;
+ return 0;
+}
+
+static int hdsp_set_xlr_breakout_cable(struct hdsp *hdsp, int mode)
+{
+ if (mode)
+ hdsp->control_register |= HDSP_XLRBreakoutCable;
+ else
+ hdsp->control_register &= ~HDSP_XLRBreakoutCable;
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+#define snd_hdsp_info_xlr_breakout_cable snd_ctl_boolean_mono_info
+
+static int snd_hdsp_get_xlr_breakout_cable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_xlr_breakout_cable(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_xlr_breakout_cable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_xlr_breakout_cable(hdsp);
+ hdsp_set_xlr_breakout_cable(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
+/* (De)activates old RME Analog Extension Board
+ These are connected to the internal ADAT connector
+ Switching this on desactivates external ADAT
+*/
+#define HDSP_AEB(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_aeb, \
+ .get = snd_hdsp_get_aeb, \
+ .put = snd_hdsp_put_aeb \
+}
+
+static int hdsp_aeb(struct hdsp *hdsp)
+{
+ if (hdsp->control_register & HDSP_AnalogExtensionBoard)
+ return 1;
+ return 0;
+}
+
+static int hdsp_set_aeb(struct hdsp *hdsp, int mode)
+{
+ if (mode)
+ hdsp->control_register |= HDSP_AnalogExtensionBoard;
+ else
+ hdsp->control_register &= ~HDSP_AnalogExtensionBoard;
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+#define snd_hdsp_info_aeb snd_ctl_boolean_mono_info
+
+static int snd_hdsp_get_aeb(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdsp_aeb(hdsp);
+ return 0;
+}
+
+static int snd_hdsp_put_aeb(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_aeb(hdsp);
+ hdsp_set_aeb(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
#define HDSP_PREF_SYNC_REF(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
@@ -2558,6 +2747,58 @@ static int snd_hdsp_get_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_c
return 0;
}
+#define HDSP_LINE_OUT(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = xindex, \
+ .info = snd_hdsp_info_line_out, \
+ .get = snd_hdsp_get_line_out, \
+ .put = snd_hdsp_put_line_out \
+}
+
+static int hdsp_line_out(struct hdsp *hdsp)
+{
+ return (hdsp->control_register & HDSP_LineOut) ? 1 : 0;
+}
+
+static int hdsp_set_line_output(struct hdsp *hdsp, int out)
+{
+ if (out)
+ hdsp->control_register |= HDSP_LineOut;
+ else
+ hdsp->control_register &= ~HDSP_LineOut;
+ hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register);
+ return 0;
+}
+
+#define snd_hdsp_info_line_out snd_ctl_boolean_mono_info
+
+static int snd_hdsp_get_line_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+
+ spin_lock_irq(&hdsp->lock);
+ ucontrol->value.integer.value[0] = hdsp_line_out(hdsp);
+ spin_unlock_irq(&hdsp->lock);
+ return 0;
+}
+
+static int snd_hdsp_put_line_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
+ int change;
+ unsigned int val;
+
+ if (!snd_hdsp_use_is_exclusive(hdsp))
+ return -EBUSY;
+ val = ucontrol->value.integer.value[0] & 1;
+ spin_lock_irq(&hdsp->lock);
+ change = (int)val != hdsp_line_out(hdsp);
+ hdsp_set_line_output(hdsp, val);
+ spin_unlock_irq(&hdsp->lock);
+ return change;
+}
+
#define HDSP_PRECISE_POINTER(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, \
.name = xname, \
@@ -2949,7 +3190,7 @@ static struct snd_kcontrol_new snd_hdsp_9632_controls[] = {
HDSP_DA_GAIN("DA Gain", 0),
HDSP_AD_GAIN("AD Gain", 0),
HDSP_PHONE_GAIN("Phones Gain", 0),
-HDSP_TOGGLE_SETTING("XLR Breakout Cable", HDSP_XLRBreakoutCable),
+HDSP_XLR_BREAKOUT_CABLE("XLR Breakout Cable", 0),
HDSP_DDS_OFFSET("DDS Sample Rate Offset", 0)
};
@@ -2991,10 +3232,10 @@ static struct snd_kcontrol_new snd_hdsp_controls[] = {
},
HDSP_MIXER("Mixer", 0),
HDSP_SPDIF_IN("IEC958 Input Connector", 0),
-HDSP_TOGGLE_SETTING("IEC958 Output also on ADAT1", HDSP_SPDIFOpticalOut),
-HDSP_TOGGLE_SETTING("IEC958 Professional Bit", HDSP_SPDIFProfessional),
-HDSP_TOGGLE_SETTING("IEC958 Emphasis Bit", HDSP_SPDIFEmphasis),
-HDSP_TOGGLE_SETTING("IEC958 Non-audio Bit", HDSP_SPDIFNonAudio),
+HDSP_SPDIF_OUT("IEC958 Output also on ADAT1", 0),
+HDSP_SPDIF_PROFESSIONAL("IEC958 Professional Bit", 0),
+HDSP_SPDIF_EMPHASIS("IEC958 Emphasis Bit", 0),
+HDSP_SPDIF_NON_AUDIO("IEC958 Non-audio Bit", 0),
/* 'Sample Clock Source' complies with the alsa control naming scheme */
HDSP_CLOCK_SOURCE("Sample Clock Source", 0),
{
@@ -3014,7 +3255,7 @@ HDSP_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
HDSP_WC_SYNC_CHECK("Word Clock Lock Status", 0),
HDSP_SPDIF_SYNC_CHECK("SPDIF Lock Status", 0),
HDSP_ADATSYNC_SYNC_CHECK("ADAT Sync Lock Status", 0),
-HDSP_TOGGLE_SETTING("Line Out", HDSP_LineOut),
+HDSP_LINE_OUT("Line Out", 0),
HDSP_PRECISE_POINTER("Precise Pointer", 0),
HDSP_USE_MIDI_TASKLET("Use Midi Tasklet", 0),
};
@@ -3331,9 +3572,7 @@ static struct snd_kcontrol_new snd_hdsp_rpm_controls[] = {
HDSP_MIXER("Mixer", 0)
};
-static struct snd_kcontrol_new snd_hdsp_96xx_aeb =
- HDSP_TOGGLE_SETTING("Analog Extension Board",
- HDSP_AnalogExtensionBoard);
+static struct snd_kcontrol_new snd_hdsp_96xx_aeb = HDSP_AEB("Analog Extension Board", 0);
static struct snd_kcontrol_new snd_hdsp_adat_sync_check = HDSP_ADAT_SYNC_CHECK;
static int snd_hdsp_create_controls(struct snd_card *card, struct hdsp *hdsp)
@@ -3756,9 +3995,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
}
snd_iprintf(buffer, "Phones Gain : %s\n", tmp);
- snd_iprintf(buffer, "XLR Breakout Cable : %s\n",
- hdsp_toggle_setting(hdsp, HDSP_XLRBreakoutCable) ?
- "yes" : "no");
+ snd_iprintf(buffer, "XLR Breakout Cable : %s\n", hdsp_xlr_breakout_cable(hdsp) ? "yes" : "no");
if (hdsp->control_register & HDSP_AnalogExtensionBoard)
snd_iprintf(buffer, "AEB : on (ADAT1 internal)\n");
@@ -4789,38 +5026,29 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
for (i = 0; i < ((hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632) ? 3 : 1); ++i)
info.adat_sync_check[i] = (unsigned char)hdsp_adat_sync_check(hdsp, i);
info.spdif_in = (unsigned char)hdsp_spdif_in(hdsp);
- info.spdif_out = (unsigned char)hdsp_toggle_setting(hdsp,
- HDSP_SPDIFOpticalOut);
- info.spdif_professional = (unsigned char)
- hdsp_toggle_setting(hdsp, HDSP_SPDIFProfessional);
- info.spdif_emphasis = (unsigned char)
- hdsp_toggle_setting(hdsp, HDSP_SPDIFEmphasis);
- info.spdif_nonaudio = (unsigned char)
- hdsp_toggle_setting(hdsp, HDSP_SPDIFNonAudio);
+ info.spdif_out = (unsigned char)hdsp_spdif_out(hdsp);
+ info.spdif_professional = (unsigned char)hdsp_spdif_professional(hdsp);
+ info.spdif_emphasis = (unsigned char)hdsp_spdif_emphasis(hdsp);
+ info.spdif_nonaudio = (unsigned char)hdsp_spdif_nonaudio(hdsp);
info.spdif_sample_rate = hdsp_spdif_sample_rate(hdsp);
info.system_sample_rate = hdsp->system_sample_rate;
info.autosync_sample_rate = hdsp_external_sample_rate(hdsp);
info.system_clock_mode = (unsigned char)hdsp_system_clock_mode(hdsp);
info.clock_source = (unsigned char)hdsp_clock_source(hdsp);
info.autosync_ref = (unsigned char)hdsp_autosync_ref(hdsp);
- info.line_out = (unsigned char)
- hdsp_toggle_setting(hdsp, HDSP_LineOut);
+ info.line_out = (unsigned char)hdsp_line_out(hdsp);
if (hdsp->io_type == H9632) {
info.da_gain = (unsigned char)hdsp_da_gain(hdsp);
info.ad_gain = (unsigned char)hdsp_ad_gain(hdsp);
info.phone_gain = (unsigned char)hdsp_phone_gain(hdsp);
- info.xlr_breakout_cable =
- (unsigned char)hdsp_toggle_setting(hdsp,
- HDSP_XLRBreakoutCable);
+ info.xlr_breakout_cable = (unsigned char)hdsp_xlr_breakout_cable(hdsp);
} else if (hdsp->io_type == RPM) {
info.da_gain = (unsigned char) hdsp_rpm_input12(hdsp);
info.ad_gain = (unsigned char) hdsp_rpm_input34(hdsp);
}
if (hdsp->io_type == H9632 || hdsp->io_type == H9652)
- info.analog_extension_board =
- (unsigned char)hdsp_toggle_setting(hdsp,
- HDSP_AnalogExtensionBoard);
+ info.analog_extension_board = (unsigned char)hdsp_aeb(hdsp);
spin_unlock_irqrestore(&hdsp->lock, flags);
if (copy_to_user(argp, &info, sizeof(info)))
return -EFAULT;
diff --git a/trunk/sound/pci/via82xx.c b/trunk/sound/pci/via82xx.c
index d756a3562706..6442f611a07b 100644
--- a/trunk/sound/pci/via82xx.c
+++ b/trunk/sound/pci/via82xx.c
@@ -2517,7 +2517,7 @@ static int check_dxs_list(struct pci_dev *pci, int revision)
w = snd_pci_quirk_lookup(pci, dxs_whitelist);
if (w) {
snd_printdd(KERN_INFO "via82xx: DXS white list for %s found\n",
- snd_pci_quirk_name(w));
+ w->name);
return w->value;
}
diff --git a/trunk/sound/soc/codecs/Kconfig b/trunk/sound/soc/codecs/Kconfig
index 615c47889092..3a847828932a 100644
--- a/trunk/sound/soc/codecs/Kconfig
+++ b/trunk/sound/soc/codecs/Kconfig
@@ -98,7 +98,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8782
select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8900 if I2C
- select SND_SOC_WM8903 if I2C && GENERIC_HARDIRQS
+ select SND_SOC_WM8903 if I2C
select SND_SOC_WM8904 if I2C
select SND_SOC_WM8940 if I2C
select SND_SOC_WM8955 if I2C
diff --git a/trunk/sound/soc/codecs/arizona.c b/trunk/sound/soc/codecs/arizona.c
index d824c984c8a4..ac948a671ea6 100644
--- a/trunk/sound/soc/codecs/arizona.c
+++ b/trunk/sound/soc/codecs/arizona.c
@@ -335,6 +335,23 @@ EXPORT_SYMBOL_GPL(arizona_ng_hold);
int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
+ unsigned int reg;
+
+ if (w->shift % 2)
+ reg = ARIZONA_ADC_DIGITAL_VOLUME_1L + ((w->shift / 2) * 8);
+ else
+ reg = ARIZONA_ADC_DIGITAL_VOLUME_1R + ((w->shift / 2) * 8);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(w->codec, reg, ARIZONA_IN1L_MUTE, 0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(w->codec, reg, ARIZONA_IN1L_MUTE,
+ ARIZONA_IN1L_MUTE);
+ break;
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(arizona_in_ev);
diff --git a/trunk/sound/soc/codecs/wm5102.c b/trunk/sound/soc/codecs/wm5102.c
index 5e85b645f2eb..ab69c83626cd 100644
--- a/trunk/sound/soc/codecs/wm5102.c
+++ b/trunk/sound/soc/codecs/wm5102.c
@@ -636,19 +636,6 @@ SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
- ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
-SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R,
- ARIZONA_IN1R_MUTE_SHIFT, 1, 1),
-SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
- ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
-SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R,
- ARIZONA_IN2R_MUTE_SHIFT, 1, 1),
-SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
- ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
-SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R,
- ARIZONA_IN3R_MUTE_SHIFT, 1, 1),
-
SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
diff --git a/trunk/sound/soc/codecs/wm5110.c b/trunk/sound/soc/codecs/wm5110.c
index 23199372d518..a1631320b448 100644
--- a/trunk/sound/soc/codecs/wm5110.c
+++ b/trunk/sound/soc/codecs/wm5110.c
@@ -80,23 +80,6 @@ SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
- ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
-SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R,
- ARIZONA_IN1R_MUTE_SHIFT, 1, 1),
-SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
- ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
-SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R,
- ARIZONA_IN2R_MUTE_SHIFT, 1, 1),
-SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
- ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
-SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R,
- ARIZONA_IN3R_MUTE_SHIFT, 1, 1),
-SOC_SINGLE("IN4L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L,
- ARIZONA_IN4L_MUTE_SHIFT, 1, 1),
-SOC_SINGLE("IN4R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4R,
- ARIZONA_IN4R_MUTE_SHIFT, 1, 1),
-
SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
diff --git a/trunk/sound/usb/caiaq/device.c b/trunk/sound/usb/caiaq/device.c
index e4d6dbb0342d..c828f8189c25 100644
--- a/trunk/sound/usb/caiaq/device.c
+++ b/trunk/sound/usb/caiaq/device.c
@@ -48,10 +48,10 @@ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
"{Native Instruments, Audio 8 DJ},"
"{Native Instruments, Traktor Audio 2},"
"{Native Instruments, Session I/O},"
- "{Native Instruments, GuitarRig mobile},"
- "{Native Instruments, Traktor Kontrol X1},"
- "{Native Instruments, Traktor Kontrol S4},"
- "{Native Instruments, Maschine Controller}}");
+ "{Native Instruments, GuitarRig mobile}"
+ "{Native Instruments, Traktor Kontrol X1}"
+ "{Native Instruments, Traktor Kontrol S4}"
+ "{Native Instruments, Maschine Controller}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
diff --git a/trunk/sound/usb/card.c b/trunk/sound/usb/card.c
index 803953a9bff3..ccf95cfe186f 100644
--- a/trunk/sound/usb/card.c
+++ b/trunk/sound/usb/card.c
@@ -646,7 +646,7 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
as->substream[0].need_setup_ep =
as->substream[1].need_setup_ep = true;
}
- }
+ }
} else {
/*
* otherwise we keep the rest of the system in the dark
diff --git a/trunk/sound/usb/mixer.c b/trunk/sound/usb/mixer.c
index 638e7f738018..e90daf8cdaa8 100644
--- a/trunk/sound/usb/mixer.c
+++ b/trunk/sound/usb/mixer.c
@@ -807,7 +807,6 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
{
switch (cval->mixer->chip->usb_id) {
case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
- case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
if (strcmp(kctl->id.name, "Effect Duration") == 0) {
cval->min = 0x0000;
cval->max = 0xffff;
diff --git a/trunk/sound/usb/mixer_maps.c b/trunk/sound/usb/mixer_maps.c
index cc2dd1f0decb..0e2ed3d05c45 100644
--- a/trunk/sound/usb/mixer_maps.c
+++ b/trunk/sound/usb/mixer_maps.c
@@ -379,10 +379,6 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.id = USB_ID(0x0763, 0x2030),
.selector_map = c400_selectors,
},
- {
- .id = USB_ID(0x0763, 0x2031),
- .selector_map = c400_selectors,
- },
{
.id = USB_ID(0x08bb, 0x2702),
.map = linex_map,
diff --git a/trunk/sound/usb/mixer_quirks.c b/trunk/sound/usb/mixer_quirks.c
index 497d2741d119..15520de1df56 100644
--- a/trunk/sound/usb/mixer_quirks.c
+++ b/trunk/sound/usb/mixer_quirks.c
@@ -637,7 +637,7 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
}
/* M-Audio FastTrack Ultra quirks */
-/* FTU Effect switch (also used by C400/C600) */
+/* FTU Effect switch (also used by C400) */
struct snd_ftu_eff_switch_priv_val {
struct usb_mixer_interface *mixer;
int cached_value;
@@ -1029,45 +1029,32 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
}
}
-/* M-Audio Fast Track C400/C600 */
-/* C400/C600 volume controls, this control needs a volume quirk, see mixer.c */
+/* M-Audio Fast Track C400 */
+/* C400 volume controls, this control needs a volume quirk, see mixer.c */
static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer)
{
char name[64];
unsigned int cmask, offset;
int out, chan, err;
- int num_outs = 0;
- int num_ins = 0;
const unsigned int id = 0x40;
const int val_type = USB_MIXER_S16;
const int control = 1;
- switch (mixer->chip->usb_id) {
- case USB_ID(0x0763, 0x2030):
- num_outs = 6;
- num_ins = 4;
- break;
- case USB_ID(0x0763, 0x2031):
- num_outs = 8;
- num_ins = 6;
- break;
- }
-
- for (chan = 0; chan < num_outs + num_ins; chan++) {
- for (out = 0; out < num_outs; out++) {
- if (chan < num_outs) {
+ for (chan = 0; chan < 10; chan++) {
+ for (out = 0; out < 6; out++) {
+ if (chan < 6) {
snprintf(name, sizeof(name),
"PCM%d-Out%d Playback Volume",
chan + 1, out + 1);
} else {
snprintf(name, sizeof(name),
"In%d-Out%d Playback Volume",
- chan - num_outs + 1, out + 1);
+ chan - 5, out + 1);
}
cmask = (out == 0) ? 0 : 1 << (out - 1);
- offset = chan * num_outs;
+ offset = chan * 6;
err = snd_create_std_mono_ctl_offset(mixer, id, control,
cmask, val_type, offset, name,
&snd_usb_mixer_vol_tlv);
@@ -1123,33 +1110,20 @@ static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer)
char name[64];
unsigned int cmask;
int chan, err;
- int num_outs = 0;
- int num_ins = 0;
const unsigned int id = 0x42;
const int val_type = USB_MIXER_S16;
const int control = 1;
- switch (mixer->chip->usb_id) {
- case USB_ID(0x0763, 0x2030):
- num_outs = 6;
- num_ins = 4;
- break;
- case USB_ID(0x0763, 0x2031):
- num_outs = 8;
- num_ins = 6;
- break;
- }
-
- for (chan = 0; chan < num_outs + num_ins; chan++) {
- if (chan < num_outs) {
+ for (chan = 0; chan < 10; chan++) {
+ if (chan < 6) {
snprintf(name, sizeof(name),
"Effect Send DOut%d",
chan + 1);
} else {
snprintf(name, sizeof(name),
"Effect Send AIn%d",
- chan - num_outs + 1);
+ chan - 5);
}
cmask = (chan == 0) ? 0 : 1 << (chan - 1);
@@ -1168,33 +1142,20 @@ static int snd_c400_create_effect_ret_vol_ctls(struct usb_mixer_interface *mixer
char name[64];
unsigned int cmask;
int chan, err;
- int num_outs = 0;
- int offset = 0;
const unsigned int id = 0x40;
const int val_type = USB_MIXER_S16;
const int control = 1;
+ const int chan_id[6] = { 0, 7, 2, 9, 4, 0xb };
+ const unsigned int offset = 0x3c;
+ /* { 0x3c, 0x43, 0x3e, 0x45, 0x40, 0x47 } */
- switch (mixer->chip->usb_id) {
- case USB_ID(0x0763, 0x2030):
- num_outs = 6;
- offset = 0x3c;
- /* { 0x3c, 0x43, 0x3e, 0x45, 0x40, 0x47 } */
- break;
- case USB_ID(0x0763, 0x2031):
- num_outs = 8;
- offset = 0x70;
- /* { 0x70, 0x79, 0x72, 0x7b, 0x74, 0x7d, 0x76, 0x7f } */
- break;
- }
-
- for (chan = 0; chan < num_outs; chan++) {
+ for (chan = 0; chan < 6; chan++) {
snprintf(name, sizeof(name),
"Effect Return %d",
chan + 1);
- cmask = (chan == 0) ? 0 :
- 1 << (chan + (chan % 2) * num_outs - 1);
+ cmask = (chan_id[chan] == 0) ? 0 : 1 << (chan_id[chan] - 1);
err = snd_create_std_mono_ctl_offset(mixer, id, control,
cmask, val_type, offset, name,
&snd_usb_mixer_vol_tlv);
@@ -1338,7 +1299,6 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
break;
case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
- case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C400 */
err = snd_c400_create_mixer(mixer);
break;
diff --git a/trunk/sound/usb/pcm.c b/trunk/sound/usb/pcm.c
index f94397b42aa5..d82e378d37cb 100644
--- a/trunk/sound/usb/pcm.c
+++ b/trunk/sound/usb/pcm.c
@@ -59,12 +59,7 @@ snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
/* Approximation based on number of samples per USB frame (ms),
some truncation for 44.1 but the estimate is good enough */
- est_delay = frame_diff * rate / 1000;
- if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
- est_delay = subs->last_delay - est_delay;
- else
- est_delay = subs->last_delay + est_delay;
-
+ est_delay = subs->last_delay - (frame_diff * rate / 1000);
if (est_delay < 0)
est_delay = 0;
return est_delay;
@@ -83,7 +78,8 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream
return SNDRV_PCM_POS_XRUN;
spin_lock(&subs->lock);
hwptr_done = subs->hwptr_done;
- substream->runtime->delay = snd_usb_pcm_delay(subs,
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ substream->runtime->delay = snd_usb_pcm_delay(subs,
substream->runtime->rate);
spin_unlock(&subs->lock);
return hwptr_done / (substream->runtime->frame_bits >> 3);
@@ -367,7 +363,6 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
switch (subs->stream->chip->usb_id) {
case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
- case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
if (is_playback) {
implicit_fb = 1;
ep = 0x81;
@@ -1162,10 +1157,6 @@ static void retire_capture_urb(struct snd_usb_substream *subs,
int i, period_elapsed = 0;
unsigned long flags;
unsigned char *cp;
- int current_frame_number;
-
- /* read frame number here, update pointer in critical section */
- current_frame_number = usb_get_current_frame_number(subs->dev);
stride = runtime->frame_bits >> 3;
@@ -1180,7 +1171,9 @@ static void retire_capture_urb(struct snd_usb_substream *subs,
if (!subs->txfr_quirk)
bytes = frames * stride;
if (bytes % (runtime->sample_bits >> 3) != 0) {
+#ifdef CONFIG_SND_DEBUG_VERBOSE
int oldbytes = bytes;
+#endif
bytes = frames * stride;
snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
oldbytes, bytes);
@@ -1197,15 +1190,6 @@ static void retire_capture_urb(struct snd_usb_substream *subs,
subs->transfer_done -= runtime->period_size;
period_elapsed = 1;
}
- /* capture delay is by construction limited to one URB,
- * reset delays here
- */
- runtime->delay = subs->last_delay = 0;
-
- /* realign last_frame_number */
- subs->last_frame_number = current_frame_number;
- subs->last_frame_number &= 0xFF; /* keep 8 LSBs */
-
spin_unlock_irqrestore(&subs->lock, flags);
/* copy a data chunk */
if (oldptr + bytes > runtime->buffer_size * stride) {
diff --git a/trunk/sound/usb/quirks-table.h b/trunk/sound/usb/quirks-table.h
index c39f898b15d2..64d25a7a4d59 100644
--- a/trunk/sound/usb/quirks-table.h
+++ b/trunk/sound/usb/quirks-table.h
@@ -1750,7 +1750,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "Roland", */
/* .product_name = "A-PRO", */
- .ifnum = 0,
+ .ifnum = 1,
.type = QUIRK_MIDI_FIXED_ENDPOINT,
.data = & (const struct snd_usb_midi_endpoint_info) {
.out_cables = 0x0003,
@@ -2325,77 +2325,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
}
}
},
-{
- USB_DEVICE_VENDOR_SPEC(0x0763, 0x2031),
- .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
- /* .vendor_name = "M-Audio", */
- /* .product_name = "Fast Track C600", */
- .ifnum = QUIRK_ANY_INTERFACE,
- .type = QUIRK_COMPOSITE,
- .data = &(const struct snd_usb_audio_quirk[]) {
- {
- .ifnum = 1,
- .type = QUIRK_AUDIO_STANDARD_MIXER,
- },
- /* Playback */
- {
- .ifnum = 2,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
- .formats = SNDRV_PCM_FMTBIT_S24_3LE,
- .channels = 8,
- .iface = 2,
- .altsetting = 1,
- .altset_idx = 1,
- .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
- .endpoint = 0x01,
- .ep_attr = 0x09,
- .rates = SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_88200 |
- SNDRV_PCM_RATE_96000,
- .rate_min = 44100,
- .rate_max = 96000,
- .nr_rates = 4,
- .rate_table = (unsigned int[]) {
- 44100, 48000, 88200, 96000
- },
- .clock = 0x80,
- }
- },
- /* Capture */
- {
- .ifnum = 3,
- .type = QUIRK_AUDIO_FIXED_ENDPOINT,
- .data = &(const struct audioformat) {
- .formats = SNDRV_PCM_FMTBIT_S24_3LE,
- .channels = 6,
- .iface = 3,
- .altsetting = 1,
- .altset_idx = 1,
- .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE,
- .endpoint = 0x81,
- .ep_attr = 0x05,
- .rates = SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_88200 |
- SNDRV_PCM_RATE_96000,
- .rate_min = 44100,
- .rate_max = 96000,
- .nr_rates = 4,
- .rate_table = (unsigned int[]) {
- 44100, 48000, 88200, 96000
- },
- .clock = 0x80,
- }
- },
- /* MIDI */
- {
- .ifnum = -1 /* Interface = 4 */
- }
- }
- }
-},
{
USB_DEVICE_VENDOR_SPEC(0x0763, 0x2080),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
diff --git a/trunk/sound/usb/quirks.c b/trunk/sound/usb/quirks.c
index 7d7ad0b0620e..2c971858d6b7 100644
--- a/trunk/sound/usb/quirks.c
+++ b/trunk/sound/usb/quirks.c
@@ -863,14 +863,13 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
ep->skip_packets = 4;
/*
- * M-Audio Fast Track C400/C600 - when packets are not skipped, real
- * world latency varies by approx. +/- 50 frames (at 96KHz) each time
- * the stream is (re)started. When skipping packets 16 at endpoint
- * start up, the real world latency is stable within +/- 1 frame (also
+ * M-Audio Fast Track C400 - when packets are not skipped, real world
+ * latency varies by approx. +/- 50 frames (at 96KHz) each time the
+ * stream is (re)started. When skipping packets 16 at endpoint start
+ * up, the real world latency is stable within +/- 1 frame (also
* across power cycles).
*/
- if ((ep->chip->usb_id == USB_ID(0x0763, 0x2030) ||
- ep->chip->usb_id == USB_ID(0x0763, 0x2031)) &&
+ if (ep->chip->usb_id == USB_ID(0x0763, 0x2030) &&
ep->type == SND_USB_ENDPOINT_TYPE_DATA)
ep->skip_packets = 16;
}