From c3b0c29c15dd832b9a959d200bd567a40a3adc11 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 6 Feb 2013 16:57:29 +0000 Subject: [PATCH] --- yaml --- r: 353487 b: refs/heads/master c: 43cd8bf1c8d8f6e897ed0f2c4bd50a4266b5c36e h: refs/heads/master i: 353485: d2af7af317f68006fa2145e227de8748206e4fb3 353483: b86b6d2c0d41d78b89cc2604a4ac849ed7d2b576 353479: ce7f183b6d5b6482d19b4e146f9fb993e5803273 353471: cb8f75778e2c9f8d01ec853d8450c995ff12b592 v: v3 --- [refs] | 2 +- .../DocBook/writing-an-alsa-driver.tmpl | 58 +- .../sound/alsa/ALSA-Configuration.txt | 5 +- .../sound/alsa/HD-Audio-Models.txt | 2 +- trunk/Documentation/sound/alsa/HD-Audio.txt | 126 +- .../sound/alsa/compress_offload.txt | 46 - trunk/include/sound/compress_driver.h | 8 - trunk/include/sound/core.h | 12 +- trunk/include/sound/memalloc.h | 2 +- trunk/include/uapi/sound/compress_offload.h | 31 +- trunk/sound/core/compress_offload.c | 114 +- trunk/sound/drivers/aloop.c | 5 +- trunk/sound/drivers/vx/vx_core.c | 3 +- trunk/sound/pci/Kconfig | 1 - trunk/sound/pci/ali5451/ali5451.c | 2 +- trunk/sound/pci/atiixp.c | 5 +- trunk/sound/pci/au88x0/au88x0_pcm.c | 23 - trunk/sound/pci/hda/Kconfig | 25 +- trunk/sound/pci/hda/ca0132_regs.h | 409 - trunk/sound/pci/hda/hda_auto_parser.c | 129 +- trunk/sound/pci/hda/hda_auto_parser.h | 81 +- trunk/sound/pci/hda/hda_codec.c | 664 +- trunk/sound/pci/hda/hda_codec.h | 93 +- trunk/sound/pci/hda/hda_generic.c | 5474 ++--------- trunk/sound/pci/hda/hda_generic.h | 303 - trunk/sound/pci/hda/hda_hwdep.c | 87 +- trunk/sound/pci/hda/hda_intel.c | 154 +- trunk/sound/pci/hda/hda_jack.c | 9 +- trunk/sound/pci/hda/hda_local.h | 90 +- trunk/sound/pci/hda/hda_proc.c | 35 +- trunk/sound/pci/hda/patch_analog.c | 1438 ++- trunk/sound/pci/hda/patch_ca0110.c | 490 +- trunk/sound/pci/hda/patch_ca0132.c | 4500 +-------- trunk/sound/pci/hda/patch_cirrus.c | 1328 ++- trunk/sound/pci/hda/patch_cmedia.c | 166 +- trunk/sound/pci/hda/patch_conexant.c | 1534 +++- trunk/sound/pci/hda/patch_hdmi.c | 122 +- trunk/sound/pci/hda/patch_realtek.c | 4614 ++++++++-- trunk/sound/pci/hda/patch_sigmatel.c | 8077 +++++++++++------ trunk/sound/pci/hda/patch_via.c | 2806 +++++- trunk/sound/pci/ice1712/wm8766.c | 2 +- trunk/sound/pci/intel8x0.c | 10 +- trunk/sound/pci/maestro3.c | 10 +- trunk/sound/pci/nm256/nm256.c | 3 +- trunk/sound/pci/pcxhr/pcxhr_core.c | 3 +- trunk/sound/pci/rme32.c | 2 +- trunk/sound/pci/rme9652/hdsp.c | 462 +- trunk/sound/pci/via82xx.c | 2 +- trunk/sound/soc/codecs/Kconfig | 2 +- trunk/sound/soc/codecs/arizona.c | 17 + trunk/sound/soc/codecs/wm5102.c | 13 - trunk/sound/soc/codecs/wm5110.c | 17 - trunk/sound/usb/caiaq/device.c | 8 +- trunk/sound/usb/card.c | 2 +- trunk/sound/usb/mixer.c | 1 - trunk/sound/usb/mixer_maps.c | 4 - trunk/sound/usb/mixer_quirks.c | 72 +- trunk/sound/usb/pcm.c | 26 +- trunk/sound/usb/quirks-table.h | 73 +- trunk/sound/usb/quirks.c | 11 +- 60 files changed, 17456 insertions(+), 16357 deletions(-) delete mode 100644 trunk/sound/pci/hda/ca0132_regs.h delete mode 100644 trunk/sound/pci/hda/hda_generic.h 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; }