Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2011 16:25:15 +0000 (09:25 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2011 16:25:15 +0000 (09:25 -0700)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (22 commits)
  ALSA: hda - Cirrus Logic CS421x support
  ALSA: Make pcm.h self-contained
  ALSA: hda - Allow codec-specific set_power_state ops
  ALSA: hda - Add post_suspend patch ops
  ALSA: hda - Make CONFIG_SND_HDA_POWER_SAVE depending on CONFIG_PM
  ALSA: hda - Make sure mute led reflects master mute state
  ALSA: hda - Fix invalid mute led state on resume of IDT codecs
  ASoC: Revert "ASoC: SAMSUNG: Add I2S0 internal dma driver"
  ALSA: hda - Add support of the 4 internal speakers on certain HP laptops
  ALSA: Make snd_pcm_debug_name usable outside pcm_lib
  ALSA: hda - Fix DAC filling for multi-connection pins in Realtek parser
  ASoC: dapm - Add methods to retrieve snd_card and soc_card from dapm context.
  ASoC: SAMSUNG: Add I2S0 internal dma driver
  ASoC: SAMSUNG: Modify I2S driver to support idma
  ASoC: davinci: add missing break statement
  ASoC: davinci: fix codec start and stop functions
  ASoC: dapm - add DAPM macro for external enum widgets
  ASoC: Acknowledge WM8962 interrupts before acting on them
  ASoC: sgtl5000: guide user when regulator support is needed
  ASoC: sgtl5000: refactor registering internal ldo
  ...

21 files changed:
include/sound/pcm.h
include/sound/pcm_params.h
include/sound/soc-dapm.h
sound/core/pcm_lib.c
sound/pci/asihpi/asihpi.c
sound/pci/hda/Kconfig
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_local.h
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c
sound/soc/codecs/sgtl5000.c
sound/soc/codecs/wm8962.c
sound/soc/davinci/davinci-vcif.c
sound/soc/samsung/i2s.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c

index e1bad11..57e71fa 100644 (file)
@@ -507,6 +507,18 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream);
 void snd_pcm_vma_notify_data(void *client, void *data);
 int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area);
 
+
+#ifdef CONFIG_SND_DEBUG
+void snd_pcm_debug_name(struct snd_pcm_substream *substream,
+                          char *name, size_t len);
+#else
+static inline void
+snd_pcm_debug_name(struct snd_pcm_substream *substream, char *buf, size_t size)
+{
+       *buf = 0;
+}
+#endif
+
 /*
  *  PCM library
  */
@@ -749,17 +761,18 @@ static inline const struct snd_interval *hw_param_interval_c(const struct snd_pc
        return &params->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
 }
 
-#define params_access(p) ((__force snd_pcm_access_t)snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS)))
-#define params_format(p) ((__force snd_pcm_format_t)snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT)))
-#define params_subformat(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_SUBFORMAT))
-#define params_channels(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min
-#define params_rate(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_RATE)->min
-#define params_period_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min
-#define params_period_bytes(p) ((params_period_size(p)*snd_pcm_format_physical_width(params_format(p))*params_channels(p))/8)
-#define params_periods(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_PERIODS)->min
-#define params_buffer_size(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min
-#define params_buffer_bytes(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min
-
+#define params_channels(p) \
+       (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min)
+#define params_rate(p) \
+       (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_RATE)->min)
+#define params_period_size(p) \
+       (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_PERIOD_SIZE)->min)
+#define params_periods(p) \
+       (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_PERIODS)->min)
+#define params_buffer_size(p) \
+       (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_BUFFER_SIZE)->min)
+#define params_buffer_bytes(p) \
+       (hw_param_interval_c((p), SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min)
 
 int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v);
 void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c);
index 85cf1cf..f494f1e 100644 (file)
@@ -337,5 +337,19 @@ static inline unsigned int sub(unsigned int a, unsigned int b)
        return 0;
 }
 
-#endif /* __SOUND_PCM_PARAMS_H */
+#define params_access(p) ((__force snd_pcm_access_t)\
+               snd_mask_min(hw_param_mask_c((p), SNDRV_PCM_HW_PARAM_ACCESS)))
+#define params_format(p) ((__force snd_pcm_format_t)\
+               snd_mask_min(hw_param_mask_c((p), SNDRV_PCM_HW_PARAM_FORMAT)))
+#define params_subformat(p) \
+       snd_mask_min(hw_param_mask_c((p), SNDRV_PCM_HW_PARAM_SUBFORMAT))
 
+static inline unsigned int
+params_period_bytes(const struct snd_pcm_hw_params *p)
+{
+       return (params_period_size(p) *
+               snd_pcm_format_physical_width(params_format(p)) *
+               params_channels(p)) / 8;
+}
+
+#endif /* __SOUND_PCM_PARAMS_H */
index e09505c..e0583b7 100644 (file)
        .get = snd_soc_dapm_get_enum_virt, \
        .put = snd_soc_dapm_put_enum_virt, \
        .private_value = (unsigned long)&xenum }
+#define SOC_DAPM_ENUM_EXT(xname, xenum, xget, xput) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_enum_double, \
+       .get = xget, \
+       .put = xput, \
+       .private_value = (unsigned long)&xenum }
 #define SOC_DAPM_VALUE_ENUM(xname, xenum) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_enum_double, \
index f134130..86d0caf 100644 (file)
@@ -128,7 +128,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
        }
 }
 
-static void pcm_debug_name(struct snd_pcm_substream *substream,
+#ifdef CONFIG_SND_DEBUG
+void snd_pcm_debug_name(struct snd_pcm_substream *substream,
                           char *name, size_t len)
 {
        snprintf(name, len, "pcmC%dD%d%c:%d",
@@ -137,6 +138,8 @@ static void pcm_debug_name(struct snd_pcm_substream *substream,
                 substream->stream ? 'c' : 'p',
                 substream->number);
 }
+EXPORT_SYMBOL(snd_pcm_debug_name);
+#endif
 
 #define XRUN_DEBUG_BASIC       (1<<0)
 #define XRUN_DEBUG_STACK       (1<<1)  /* dump also stack */
@@ -168,7 +171,7 @@ static void xrun(struct snd_pcm_substream *substream)
        snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
        if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
                char name[16];
-               pcm_debug_name(substream, name, sizeof(name));
+               snd_pcm_debug_name(substream, name, sizeof(name));
                snd_printd(KERN_DEBUG "XRUN: %s\n", name);
                dump_stack_on_xrun(substream);
        }
@@ -243,7 +246,7 @@ static void xrun_log_show(struct snd_pcm_substream *substream)
                return;
        if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
                return;
-       pcm_debug_name(substream, name, sizeof(name));
+       snd_pcm_debug_name(substream, name, sizeof(name));
        for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) {
                entry = &log->entries[idx];
                if (entry->period_size == 0)
@@ -319,7 +322,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
        if (pos >= runtime->buffer_size) {
                if (printk_ratelimit()) {
                        char name[16];
-                       pcm_debug_name(substream, name, sizeof(name));
+                       snd_pcm_debug_name(substream, name, sizeof(name));
                        xrun_log_show(substream);
                        snd_printd(KERN_ERR  "BUG: %s, pos = %ld, "
                                   "buffer size = %ld, period size = %ld\n",
@@ -364,7 +367,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
        if (xrun_debug(substream, in_interrupt ?
                        XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
                char name[16];
-               pcm_debug_name(substream, name, sizeof(name));
+               snd_pcm_debug_name(substream, name, sizeof(name));
                snd_printd("%s_update: %s: pos=%u/%u/%u, "
                           "hwptr=%ld/%ld/%ld/%ld\n",
                           in_interrupt ? "period" : "hwptr",
index b941d25..eae62eb 100644 (file)
 #include <sound/tlv.h>
 #include <sound/hwdep.h>
 
-
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>");
 MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx");
 
-#if defined CONFIG_SND_DEBUG
-/* copied from pcm_lib.c, hope later patch will make that version public
-and this copy can be removed */
-static inline void
-snd_pcm_debug_name(struct snd_pcm_substream *substream, char *buf, size_t size)
-{
-       snprintf(buf, size, "pcmC%dD%d%c:%d",
-                substream->pcm->card->number,
-                substream->pcm->device,
-                substream->stream ? 'c' : 'p',
-                substream->number);
-}
-#else
-static inline void
-snd_pcm_debug_name(struct snd_pcm_substream *substream, char *buf, size_t size)
-{
-       *buf = 0;
-}
-#endif
-
 #if defined CONFIG_SND_DEBUG_VERBOSE
 /**
  * snd_printddd - very verbose debug printk
index 7489b46..bb7e102 100644 (file)
@@ -243,6 +243,7 @@ config SND_HDA_GENERIC
 
 config SND_HDA_POWER_SAVE
        bool "Aggressive power-saving on HD-audio"
+       depends on PM
        help
          Say Y here to enable more aggressive power-saving mode on
          HD-audio driver.  The power-saving timeout can be configured
index 9c27a3a..3e7850c 100644 (file)
@@ -91,8 +91,10 @@ EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static void hda_power_work(struct work_struct *work);
 static void hda_keep_power_on(struct hda_codec *codec);
+#define hda_codec_is_power_on(codec)   ((codec)->power_on)
 #else
 static inline void hda_keep_power_on(struct hda_codec *codec) {}
+#define hda_codec_is_power_on(codec)   1
 #endif
 
 /**
@@ -1101,7 +1103,7 @@ void snd_hda_shutup_pins(struct hda_codec *codec)
 }
 EXPORT_SYMBOL_HDA(snd_hda_shutup_pins);
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 /* Restore the pin controls cleared previously via snd_hda_shutup_pins() */
 static void restore_shutup_pins(struct hda_codec *codec)
 {
@@ -1499,7 +1501,7 @@ static void purify_inactive_streams(struct hda_codec *codec)
        }
 }
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 /* clean up all streams; called from suspend */
 static void hda_cleanup_all_streams(struct hda_codec *codec)
 {
@@ -1838,7 +1840,7 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 /**
  * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
  * @codec: HD-audio codec
@@ -1868,7 +1870,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
        }
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
-#endif /* SND_HDA_NEEDS_RESUME */
+#endif /* CONFIG_PM */
 
 static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
                             unsigned int ofs)
@@ -3082,7 +3084,7 @@ 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 SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 /*
  * command cache
  */
@@ -3199,53 +3201,32 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
                                          seq->param);
 }
 EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
-#endif /* SND_HDA_NEEDS_RESUME */
+#endif /* CONFIG_PM */
 
-/*
- * set power state of the codec
- */
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
-                               unsigned int power_state)
+void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
+                                   unsigned int power_state,
+                                   bool eapd_workaround)
 {
-       hda_nid_t nid;
+       hda_nid_t nid = codec->start_nid;
        int i;
 
-       /* this delay seems necessary to avoid click noise at power-down */
-       if (power_state == AC_PWRST_D3)
-               msleep(100);
-       snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
-                           power_state);
-       /* partial workaround for "azx_get_response timeout" */
-       if (power_state == AC_PWRST_D0 &&
-           (codec->vendor_id & 0xffff0000) == 0x14f10000)
-               msleep(10);
-
-       nid = codec->start_nid;
        for (i = 0; i < codec->num_nodes; i++, nid++) {
                unsigned int wcaps = get_wcaps(codec, nid);
-               if (wcaps & AC_WCAP_POWER) {
-                       unsigned int wid_type = get_wcaps_type(wcaps);
-                       if (power_state == AC_PWRST_D3 &&
-                           wid_type == AC_WID_PIN) {
-                               unsigned int pincap;
-                               /*
-                                * don't power down the widget if it controls
-                                * eapd and EAPD_BTLENABLE is set.
-                                */
-                               pincap = snd_hda_query_pin_caps(codec, nid);
-                               if (pincap & AC_PINCAP_EAPD) {
-                                       int eapd = snd_hda_codec_read(codec,
-                                               nid, 0,
+               if (!(wcaps & AC_WCAP_POWER))
+                       continue;
+               /* 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);
-                                       eapd &= 0x02;
-                                       if (eapd)
-                                               continue;
-                               }
-                       }
-                       snd_hda_codec_write(codec, nid, 0,
-                                           AC_VERB_SET_POWER_STATE,
-                                           power_state);
+                       if (eapd & 0x02)
+                               continue;
                }
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
+                                   power_state);
        }
 
        if (power_state == AC_PWRST_D0) {
@@ -3262,6 +3243,26 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
                } while (time_after_eq(end_time, jiffies));
        }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);
+
+/*
+ * set power state of the codec
+ */
+static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+                               unsigned int power_state)
+{
+       if (codec->patch_ops.set_power_state) {
+               codec->patch_ops.set_power_state(codec, fg, power_state);
+               return;
+       }
+
+       /* this delay seems necessary to avoid click noise at power-down */
+       if (power_state == AC_PWRST_D3)
+               msleep(100);
+       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, true);
+}
 
 #ifdef CONFIG_SND_HDA_HWDEP
 /* execute additional init verbs */
@@ -3274,7 +3275,7 @@ static void hda_exec_init_verbs(struct hda_codec *codec)
 static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
 #endif
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 /*
  * call suspend and power-down; used both from PM and power-save
  */
@@ -3315,7 +3316,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
                snd_hda_codec_resume_cache(codec);
        }
 }
-#endif /* SND_HDA_NEEDS_RESUME */
+#endif /* CONFIG_PM */
 
 
 /**
@@ -4071,9 +4072,6 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
 EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
-                               unsigned int power_state);
-
 static void hda_power_work(struct work_struct *work)
 {
        struct hda_codec *codec =
@@ -4376,11 +4374,8 @@ void snd_hda_bus_reboot_notify(struct hda_bus *bus)
        if (!bus)
                return;
        list_for_each_entry(codec, &bus->codec_list, list) {
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-               if (!codec->power_on)
-                       continue;
-#endif
-               if (codec->patch_ops.reboot_notify)
+               if (hda_codec_is_power_on(codec) &&
+                   codec->patch_ops.reboot_notify)
                        codec->patch_ops.reboot_notify(codec);
        }
 }
@@ -5079,11 +5074,10 @@ int snd_hda_suspend(struct hda_bus *bus)
        struct hda_codec *codec;
 
        list_for_each_entry(codec, &bus->codec_list, list) {
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-               if (!codec->power_on)
-                       continue;
-#endif
-               hda_call_codec_suspend(codec);
+               if (hda_codec_is_power_on(codec))
+                       hda_call_codec_suspend(codec);
+               if (codec->patch_ops.post_suspend)
+                       codec->patch_ops.post_suspend(codec);
        }
        return 0;
 }
@@ -5103,6 +5097,8 @@ int snd_hda_resume(struct hda_bus *bus)
        struct hda_codec *codec;
 
        list_for_each_entry(codec, &bus->codec_list, list) {
+               if (codec->patch_ops.pre_resume)
+                       codec->patch_ops.pre_resume(codec);
                if (snd_hda_codec_needs_resume(codec))
                        hda_call_codec_resume(codec);
        }
index f465e07..755f2b0 100644 (file)
 #include <sound/pcm.h>
 #include <sound/hwdep.h>
 
-#if defined(CONFIG_PM) || defined(CONFIG_SND_HDA_POWER_SAVE)
-#define SND_HDA_NEEDS_RESUME   /* resume control code is required */
-#endif
-
 /*
  * nodes
  */
@@ -704,8 +700,12 @@ struct hda_codec_ops {
        int (*init)(struct hda_codec *codec);
        void (*free)(struct hda_codec *codec);
        void (*unsol_event)(struct hda_codec *codec, unsigned int res);
-#ifdef SND_HDA_NEEDS_RESUME
+       void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg,
+                               unsigned int power_state);
+#ifdef CONFIG_PM
        int (*suspend)(struct hda_codec *codec, pm_message_t state);
+       int (*post_suspend)(struct hda_codec *codec);
+       int (*pre_resume)(struct hda_codec *codec);
        int (*resume)(struct hda_codec *codec);
 #endif
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -927,7 +927,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 SND_HDA_NEEDS_RESUME
+#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,
@@ -1008,6 +1008,9 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
  */
 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,
+                                   bool eapd_workaround);
 
 /*
  * power management
index 88b277e..2e7ac31 100644 (file)
@@ -131,7 +131,7 @@ 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);
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 void snd_hda_codec_resume_amp(struct hda_codec *codec);
 #endif
 
index 1362c8b..8648917 100644 (file)
@@ -563,7 +563,7 @@ static void ad198x_free(struct hda_codec *codec)
        snd_hda_detach_beep_device(codec);
 }
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
 {
        ad198x_shutup(codec);
@@ -579,7 +579,7 @@ static const struct hda_codec_ops ad198x_patch_ops = {
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        .check_power_status = ad198x_check_power_status,
 #endif
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
        .suspend = ad198x_suspend,
 #endif
        .reboot_notify = ad198x_shutup,
index 7f93739..47d6ffc 100644 (file)
@@ -25,6 +25,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include <sound/tlv.h>
 
 /*
  */
@@ -61,9 +62,15 @@ struct cs_spec {
 
        unsigned int hp_detect:1;
        unsigned int mic_detect:1;
+       /* CS421x */
+       unsigned int spdif_detect:1;
+       unsigned int sense_b:1;
+       hda_nid_t vendor_nid;
+       struct hda_input_mux input_mux;
+       unsigned int last_input;
 };
 
-/* available models */
+/* available models with CS420x */
 enum {
        CS420X_MBP53,
        CS420X_MBP55,
@@ -72,6 +79,12 @@ enum {
        CS420X_MODELS
 };
 
+/* CS421x boards */
+enum {
+       CS421X_CDB4210,
+       CS421X_MODELS
+};
+
 /* Vendor-specific processing widget */
 #define CS420X_VENDOR_NID      0x11
 #define CS_DIG_OUT1_PIN_NID    0x10
@@ -111,21 +124,42 @@ enum {
 /* 0x0009 - 0x0014 -> 12 test regs */
 /* 0x0015 - visibility reg */
 
+/*
+ * Cirrus Logic CS4210
+ *
+ * 1 DAC => HP(sense) / Speakers,
+ * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
+ * 1 SPDIF OUT => SPDIF Trasmitter(sense)
+*/
+#define CS4210_DAC_NID         0x02
+#define CS4210_ADC_NID         0x03
+#define CS421X_VENDOR_NID      0x0B
+#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 SPDIF_EVENT            0x04
 
 static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
 {
-       snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+       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, CS420X_VENDOR_NID, 0,
+       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)
 {
-       snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+       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, CS420X_VENDOR_NID, 0,
+       snd_hda_codec_write(codec, spec->vendor_nid, 0,
                            AC_VERB_SET_PROC_COEF, coef);
 }
 
@@ -347,15 +381,12 @@ static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
        nid = codec->start_nid;
        for (i = 0; i < codec->num_nodes; i++, nid++) {
                unsigned int type;
-               int idx;
                type = get_wcaps_type(get_wcaps(codec, nid));
                if (type != AC_WID_AUD_IN)
                        continue;
-               idx = snd_hda_get_conn_index(codec, nid, pin, 0);
-               if (idx >= 0) {
-                       *idxp = idx;
+               *idxp = snd_hda_get_conn_index(codec, nid, pin, false);
+               if (*idxp >= 0)
                        return nid;
-               }
        }
        return 0;
 }
@@ -835,6 +866,8 @@ static int build_digital_input(struct hda_codec *codec)
 
 /*
  * auto-mute and auto-mic switching
+ * CS421x auto-output redirecting
+ * HP/SPK/SPDIF
  */
 
 static void cs_automute(struct hda_codec *codec)
@@ -842,9 +875,25 @@ static void cs_automute(struct hda_codec *codec)
        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;
 
+       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];
@@ -854,11 +903,19 @@ static void cs_automute(struct hda_codec *codec)
                if (hp_present)
                        break;
        }
+
+       /* mute speakers if spdif or hp jack is plugged in */
        for (i = 0; i < cfg->speaker_outs; i++) {
                nid = cfg->speaker_pins[i];
                snd_hda_codec_write(codec, nid, 0,
                                    AC_VERB_SET_PIN_WIDGET_CONTROL,
                                    hp_present ? 0 : PIN_OUT);
+               /* detect on spdif is specific to CS421x */
+               if (spec->vendor_nid == CS421X_VENDOR_NID) {
+                       snd_hda_codec_write(codec, nid, 0,
+                                       AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                       spdif_present ? 0 : PIN_OUT);
+               }
        }
        if (spec->board_config == CS420X_MBP53 ||
            spec->board_config == CS420X_MBP55 ||
@@ -867,21 +924,62 @@ static void cs_automute(struct hda_codec *codec)
                snd_hda_codec_write(codec, 0x01, 0,
                                    AC_VERB_SET_GPIO_DATA, gpio);
        }
+
+       /* specific to CS421x */
+       if (spec->vendor_nid == CS421X_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_codec_write(codec, nid, 0,
+                               AC_VERB_SET_PIN_WIDGET_CONTROL,
+                               (spdif_present && spec->sense_b) ? 0 : PIN_HP);
+               }
+
+               /* SPDIF TX on/off */
+               if (cfg->dig_outs) {
+                       nid = cfg->dig_out_pins[0];
+                       snd_hda_codec_write(codec, nid, 0,
+                               AC_VERB_SET_PIN_WIDGET_CONTROL,
+                               spdif_present ? PIN_OUT : 0);
+
+               }
+               /* Update board GPIOs if neccessary ... */
+       }
 }
 
+/*
+ * Auto-input redirect for CS421x
+ * Switch max 3 inputs of a single ADC (nid 3)
+*/
+
 static void cs_automic(struct hda_codec *codec)
 {
        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);
-       if (present)
-               change_cur_input(codec, spec->automic_idx, 0);
-       else
-               change_cur_input(codec, !spec->automic_idx, 0);
+
+       /* specific to CS421x, single ADC */
+       if (spec->vendor_nid == CS421X_VENDOR_NID) {
+               if (present) {
+                       spec->last_input = spec->cur_input;
+                       spec->cur_input = spec->automic_idx;
+               } else  {
+                       spec->cur_input = spec->last_input;
+               }
+
+               snd_hda_codec_write_cache(codec, spec->cur_adc, 0,
+                                       AC_VERB_SET_CONNECT_SEL,
+                                       spec->adc_idx[spec->cur_input]);
+       } else {
+               if (present)
+                       change_cur_input(codec, spec->automic_idx, 0);
+               else
+                       change_cur_input(codec, !spec->automic_idx, 0);
+       }
 }
 
 /*
@@ -911,23 +1009,28 @@ static void init_output(struct hda_codec *codec)
        for (i = 0; i < cfg->line_outs; i++)
                snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
                                    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+       /* HP */
        for (i = 0; i < cfg->hp_outs; i++) {
                hda_nid_t nid = cfg->hp_pins[i];
                snd_hda_codec_write(codec, nid, 0,
                                    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
                if (!cfg->speaker_outs)
                        continue;
-               if (is_jack_detectable(codec, nid)) {
+               if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
                        snd_hda_codec_write(codec, nid, 0,
                                            AC_VERB_SET_UNSOLICITED_ENABLE,
                                            AC_USRSP_EN | HP_EVENT);
                        spec->hp_detect = 1;
                }
        }
+
+       /* Speaker */
        for (i = 0; i < cfg->speaker_outs; i++)
                snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
                                    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-       if (spec->hp_detect)
+
+       /* SPDIF is enabled on presence detect for CS421x */
+       if (spec->hp_detect || spec->spdif_detect)
                cs_automute(codec);
 }
 
@@ -961,19 +1064,31 @@ static void init_input(struct hda_codec *codec)
                                            AC_VERB_SET_UNSOLICITED_ENABLE,
                                            AC_USRSP_EN | MIC_EVENT);
        }
-       change_cur_input(codec, spec->cur_input, 1);
-       if (spec->mic_detect)
-               cs_automic(codec);
-
-       coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */
-       if (is_active_pin(codec, CS_DMIC2_PIN_NID))
-               coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */
-       if (is_active_pin(codec, CS_DMIC1_PIN_NID))
-               coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0 
-                                * No effect if SPDIF_OUT2 is selected in 
-                                * IDX_SPDIF_CTL.
-                                 */
-       cs_vendor_coef_set(codec, IDX_ADC_CFG, coef);
+       /* specific to CS421x */
+       if (spec->vendor_nid == CS421X_VENDOR_NID) {
+               if (spec->mic_detect)
+                       cs_automic(codec);
+               else  {
+                       spec->cur_adc = spec->adc_nid[spec->cur_input];
+                       snd_hda_codec_write(codec, spec->cur_adc, 0,
+                                       AC_VERB_SET_CONNECT_SEL,
+                                       spec->adc_idx[spec->cur_input]);
+               }
+       } else {
+               change_cur_input(codec, spec->cur_input, 1);
+               if (spec->mic_detect)
+                       cs_automic(codec);
+
+               coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */
+               if (is_active_pin(codec, CS_DMIC2_PIN_NID))
+                       coef |= 0x0500; /* DMIC2 2 chan on, GPIO1 off */
+               if (is_active_pin(codec, CS_DMIC1_PIN_NID))
+                       coef |= 0x1800; /* DMIC1 2 chan on, GPIO0 off
+                                        * No effect if SPDIF_OUT2 is
+                                        * selected in IDX_SPDIF_CTL.
+                                       */
+               cs_vendor_coef_set(codec, IDX_ADC_CFG, coef);
+       }
 }
 
 static const struct hda_verb cs_coef_init_verbs[] = {
@@ -1221,16 +1336,16 @@ static const struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
        [CS420X_IMAC27] = imac27_pincfgs,
 };
 
-static void fix_pincfg(struct hda_codec *codec, int model)
+static void fix_pincfg(struct hda_codec *codec, int model,
+                      const struct cs_pincfg **pin_configs)
 {
-       const struct cs_pincfg *cfg = cs_pincfgs[model];
+       const struct cs_pincfg *cfg = pin_configs[model];
        if (!cfg)
                return;
        for (; cfg->nid; cfg++)
                snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
 }
 
-
 static int patch_cs420x(struct hda_codec *codec)
 {
        struct cs_spec *spec;
@@ -1241,11 +1356,13 @@ static int patch_cs420x(struct hda_codec *codec)
                return -ENOMEM;
        codec->spec = spec;
 
+       spec->vendor_nid = CS420X_VENDOR_NID;
+
        spec->board_config =
                snd_hda_check_board_config(codec, CS420X_MODELS,
                                           cs420x_models, cs420x_cfg_tbl);
        if (spec->board_config >= 0)
-               fix_pincfg(codec, spec->board_config);
+               fix_pincfg(codec, spec->board_config, cs_pincfgs);
 
        switch (spec->board_config) {
        case CS420X_IMAC27:
@@ -1272,6 +1389,562 @@ static int patch_cs420x(struct hda_codec *codec)
        return err;
 }
 
+/*
+ * Cirrus Logic CS4210
+ *
+ * 1 DAC => HP(sense) / Speakers,
+ * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
+ * 1 SPDIF OUT => SPDIF Trasmitter(sense)
+*/
+
+/* CS4210 board names */
+static const char *cs421x_models[CS421X_MODELS] = {
+       [CS421X_CDB4210] = "cdb4210",
+};
+
+static const struct snd_pci_quirk cs421x_cfg_tbl[] = {
+       /* Test Intel board + CDB2410  */
+       SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210),
+       {} /* terminator */
+};
+
+/* CS4210 board pinconfigs */
+/* Default CS4210 (CDB4210)*/
+static const struct cs_pincfg cdb4210_pincfgs[] = {
+       { 0x05, 0x0321401f },
+       { 0x06, 0x90170010 },
+       { 0x07, 0x03813031 },
+       { 0x08, 0xb7a70037 },
+       { 0x09, 0xb7a6003e },
+       { 0x0a, 0x034510f0 },
+       {} /* terminator */
+};
+
+static const struct cs_pincfg *cs421x_pincfgs[CS421X_MODELS] = {
+       [CS421X_CDB4210] = cdb4210_pincfgs,
+};
+
+static const struct hda_verb cs421x_coef_init_verbs[] = {
+       {0x0B, AC_VERB_SET_PROC_STATE, 1},
+       {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG},
+       /*
+           Disable Coefficient Index Auto-Increment(DAI)=1,
+           PDREF=0
+       */
+       {0x0B, AC_VERB_SET_PROC_COEF, 0x0001 },
+
+       {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG},
+       /* ADC SZCMode = Digital Soft Ramp */
+       {0x0B, AC_VERB_SET_PROC_COEF, 0x0002 },
+
+       {0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG},
+       {0x0B, AC_VERB_SET_PROC_COEF,
+        (0x0002 /* DAC SZCMode = Digital Soft Ramp */
+         | 0x0004 /* Mute DAC on FIFO error */
+         | 0x0008 /* Enable DAC High Pass Filter */
+         )},
+       {} /* terminator */
+};
+
+/* Errata: CS4210 rev A1 Silicon
+ *
+ * http://www.cirrus.com/en/pubs/errata/
+ *
+ * Description:
+ * 1. Performance degredation is present in the ADC.
+ * 2. Speaker output is not completely muted upon HP detect.
+ * 3. Noise is present when clipping occurs on the amplified
+ *    speaker outputs.
+ *
+ * Workaround:
+ * The following verb sequence written to the registers during
+ * initialization will correct the issues listed above.
+ */
+
+static const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = {
+       {0x0B, AC_VERB_SET_PROC_STATE, 0x01},  /* VPW: processing on */
+
+       {0x0B, AC_VERB_SET_COEF_INDEX, 0x0006},
+       {0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */
+
+       {0x0B, AC_VERB_SET_COEF_INDEX, 0x000A},
+       {0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */
+
+       {0x0B, AC_VERB_SET_COEF_INDEX, 0x0011},
+       {0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */
+
+       {0x0B, AC_VERB_SET_COEF_INDEX, 0x001A},
+       {0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */
+
+       {0x0B, AC_VERB_SET_COEF_INDEX, 0x001B},
+       {0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */
+
+       {} /* terminator */
+};
+
+/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */
+static const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0);
+
+static int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 3;
+       return 0;
+}
+
+static int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] =
+               cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003;
+       return 0;
+}
+
+static int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       unsigned int vol = ucontrol->value.integer.value[0];
+       unsigned int coef =
+               cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL);
+       unsigned int original_coef = coef;
+
+       coef &= ~0x0003;
+       coef |= (vol & 0x0003);
+       if (original_coef == coef)
+               return 0;
+       else {
+               cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef);
+               return 1;
+       }
+}
+
+static const struct snd_kcontrol_new cs421x_speaker_bost_ctl = {
+
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                       SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+       .name = "Speaker Boost Playback Volume",
+       .info = cs421x_boost_vol_info,
+       .get = cs421x_boost_vol_get,
+       .put = cs421x_boost_vol_put,
+       .tlv = { .p = cs421x_speaker_boost_db_scale },
+};
+
+static void cs421x_pinmux_init(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       unsigned int def_conf, coef;
+
+       /* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */
+       coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
+
+       if (spec->gpio_mask)
+               coef |= 0x0008; /* B1,B2 are GPIOs */
+       else
+               coef &= ~0x0008;
+
+       if (spec->sense_b)
+               coef |= 0x0010; /* B2 is SENSE_B, not inverted  */
+       else
+               coef &= ~0x0010;
+
+       cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
+
+       if ((spec->gpio_mask || spec->sense_b) &&
+           is_active_pin(codec, CS421X_DMIC_PIN_NID)) {
+
+               /*
+                   GPIO or SENSE_B forced - disconnect the DMIC pin.
+               */
+               def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID);
+               def_conf &= ~AC_DEFCFG_PORT_CONN;
+               def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT);
+               snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf);
+       }
+}
+
+static void init_cs421x_digital(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+       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_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_UNSOLICITED_ENABLE,
+                                   AC_USRSP_EN | SPDIF_EVENT);
+                       spec->spdif_detect = 1;
+               }
+       }
+}
+
+static int cs421x_init(struct hda_codec *codec)
+{
+       struct cs_spec *spec = codec->spec;
+
+       snd_hda_sequence_write(codec, cs421x_coef_init_verbs);
+       snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes);
+
+       cs421x_pinmux_init(codec);
+
+       if (spec->gpio_mask) {
+               snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
+                                   spec->gpio_mask);
+               snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
+                                   spec->gpio_dir);
+               snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+                                   spec->gpio_data);
+       }
+
+       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);
+}
+
+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_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 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 = "HP/Speakers";
+
+       fix_volume_caps(codec, dac);
+       if (!spec->vmaster_sw) {
+               err = add_vmaster(codec, dac);
+               if (err < 0)
+                       return err;
+       }
+
+       err = add_mute(codec, name, 0,
+                       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, 0,
+                       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;
+
+       if (cfg->speaker_outs) {
+               err = snd_hda_ctl_add(codec, 0,
+                       snd_ctl_new1(&cs421x_speaker_bost_ctl, codec));
+               if (err < 0)
+                       return err;
+       }
+       return err;
+}
+
+static int cs421x_build_controls(struct hda_codec *codec)
+{
+       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;
+       return cs421x_init(codec);
+}
+
+static void cs421x_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       switch ((res >> 26) & 0x3f) {
+       case HP_EVENT:
+       case SPDIF_EVENT:
+               cs_automute(codec);
+               break;
+
+       case MIC_EVENT:
+               cs_automic(codec);
+               break;
+       }
+}
+
+static int parse_cs421x_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->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;
+       int err;
+
+       err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+       if (err < 0)
+               return err;
+       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;
+       return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+       Manage PDREF, when transitioning to D3hot
+       (DAC,ADC) -> D3, PDREF=1, AFG->D3
+*/
+static int cs421x_suspend(struct hda_codec *codec, pm_message_t state)
+{
+       unsigned int coef;
+
+       snd_hda_shutup_pins(codec);
+
+       snd_hda_codec_write(codec, CS4210_DAC_NID, 0,
+                           AC_VERB_SET_POWER_STATE,  AC_PWRST_D3);
+       snd_hda_codec_write(codec, CS4210_ADC_NID, 0,
+                           AC_VERB_SET_POWER_STATE,  AC_PWRST_D3);
+
+       coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
+       coef |= 0x0004; /* PDREF */
+       cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
+
+       return 0;
+}
+#endif
+
+static struct hda_codec_ops cs4210_patch_ops = {
+       .build_controls = cs421x_build_controls,
+       .build_pcms = cs_build_pcms,
+       .init = cs421x_init,
+       .free = cs_free,
+       .unsol_event = cs421x_unsol_event,
+#ifdef CONFIG_PM
+       .suspend = cs421x_suspend,
+#endif
+};
+
+static int patch_cs421x(struct hda_codec *codec)
+{
+       struct cs_spec *spec;
+       int err;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       codec->spec = spec;
+
+       spec->vendor_nid = CS421X_VENDOR_NID;
+
+       spec->board_config =
+               snd_hda_check_board_config(codec, CS421X_MODELS,
+                                          cs421x_models, cs421x_cfg_tbl);
+       if (spec->board_config >= 0)
+               fix_pincfg(codec, spec->board_config, cs421x_pincfgs);
+       /*
+           Setup GPIO/SENSE for each board (if used)
+       */
+       switch (spec->board_config) {
+       case CS421X_CDB4210:
+               snd_printd("CS4210 board: %s\n",
+                       cs421x_models[spec->board_config]);
+/*             spec->gpio_mask = 3;
+               spec->gpio_dir = 3;
+               spec->gpio_data = 3;
+*/
+               spec->sense_b = 1;
+
+               break;
+       }
+
+       /*
+           Update the GPIO/DMIC/SENSE_B pinmux before the configuration
+           is auto-parsed. If GPIO or SENSE_B is forced, DMIC input
+           is disabled.
+       */
+       cs421x_pinmux_init(codec);
+
+       err = cs421x_parse_auto_config(codec);
+       if (err < 0)
+               goto error;
+
+       codec->patch_ops = cs4210_patch_ops;
+
+       return 0;
+
+ error:
+       kfree(codec->spec);
+       codec->spec = NULL;
+       return err;
+}
+
 
 /*
  * patch entries
@@ -1279,11 +1952,13 @@ static int patch_cs420x(struct hda_codec *codec)
 static const struct hda_codec_preset snd_hda_preset_cirrus[] = {
        { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
        { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
+       { .id = 0x10134210, .name = "CS4210", .patch = patch_cs421x },
        {} /* terminator */
 };
 
 MODULE_ALIAS("snd-hda-codec-id:10134206");
 MODULE_ALIAS("snd-hda-codec-id:10134207");
+MODULE_ALIAS("snd-hda-codec-id:10134210");
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
index 884f67b..502fc94 100644 (file)
@@ -446,6 +446,19 @@ static int conexant_init_jacks(struct hda_codec *codec)
        return 0;
 }
 
+static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg,
+                              unsigned int power_state)
+{
+       if (power_state == AC_PWRST_D3)
+               msleep(100);
+       snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+                           power_state);
+       /* 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, true);
+}
+
 static int conexant_init(struct hda_codec *codec)
 {
        struct conexant_spec *spec = codec->spec;
@@ -588,6 +601,7 @@ static const struct hda_codec_ops conexant_patch_ops = {
        .build_pcms = conexant_build_pcms,
        .init = conexant_init,
        .free = conexant_free,
+       .set_power_state = conexant_set_power,
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        .suspend = conexant_suspend,
 #endif
index 52ce075..694327a 100644 (file)
@@ -2386,7 +2386,7 @@ static int alc_suspend(struct hda_codec *codec, pm_message_t state)
 }
 #endif
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 static int alc_resume(struct hda_codec *codec)
 {
        msleep(150); /* to avoid pop noise */
@@ -2406,7 +2406,7 @@ static const struct hda_codec_ops alc_patch_ops = {
        .init = alc_init,
        .free = alc_free,
        .unsol_event = alc_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
        .resume = alc_resume,
 #endif
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -2801,7 +2801,8 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec)
        int i;
 
  again:
-       spec->multiout.num_dacs = 0;
+       /* set num_dacs once to full for alc_auto_look_for_dac() */
+       spec->multiout.num_dacs = cfg->line_outs;
        spec->multiout.hp_nid = 0;
        spec->multiout.extra_out_nid[0] = 0;
        memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
@@ -2834,6 +2835,8 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec)
                }
        }
 
+       /* 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++;
@@ -4410,7 +4413,7 @@ static void alc269_shutup(struct hda_codec *codec)
        }
 }
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 static int alc269_resume(struct hda_codec *codec)
 {
        if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
@@ -4433,7 +4436,7 @@ static int alc269_resume(struct hda_codec *codec)
        hda_call_check_power_status(codec, 0x01);
        return 0;
 }
-#endif /* SND_HDA_NEEDS_RESUME */
+#endif /* CONFIG_PM */
 
 static void alc269_fixup_hweq(struct hda_codec *codec,
                               const struct alc_fixup *fix, int action)
@@ -4725,7 +4728,7 @@ static int patch_alc269(struct hda_codec *codec)
        spec->vmaster_nid = 0x02;
 
        codec->patch_ops = alc_patch_ops;
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
        codec->patch_ops.resume = alc269_resume;
 #endif
        if (board_config == ALC_MODEL_AUTO)
index 56425a5..fcf4c71 100644 (file)
@@ -95,6 +95,7 @@ enum {
        STAC_92HD83XXX_PWR_REF,
        STAC_DELL_S14,
        STAC_92HD83XXX_HP,
+       STAC_92HD83XXX_HP_cNB11_INTQUAD,
        STAC_HP_DV7_4000,
        STAC_92HD83XXX_MODELS
 };
@@ -1636,10 +1637,17 @@ static const unsigned int hp_dv7_4000_pin_configs[10] = {
        0x40f000f0, 0x40f000f0,
 };
 
+static const unsigned int hp_cNB11_intquad_pin_configs[10] = {
+       0x40f000f0, 0x0221101f, 0x02a11020, 0x92170110,
+       0x40f000f0, 0x92170110, 0x40f000f0, 0xd5a30130,
+       0x40f000f0, 0x40f000f0,
+};
+
 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_92HD83XXX_HP_cNB11_INTQUAD] = hp_cNB11_intquad_pin_configs,
        [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs,
 };
 
@@ -1649,6 +1657,7 @@ static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
        [STAC_92HD83XXX_PWR_REF] = "mic-ref",
        [STAC_DELL_S14] = "dell-s14",
        [STAC_92HD83XXX_HP] = "hp",
+       [STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad",
        [STAC_HP_DV7_4000] = "hp-dv7-4000",
 };
 
@@ -1661,7 +1670,47 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba,
                      "unknown Dell", STAC_DELL_S14),
        SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600,
-                     "HP", STAC_92HD83XXX_HP),
+                         "HP", STAC_92HD83XXX_HP),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1656,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1657,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1658,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1659,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165A,
+                         "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, 0x3388,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355B,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355C,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355D,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355E,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x355F,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3560,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358B,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358C,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x358D,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3591,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3592,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
+       SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3593,
+                         "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
        {} /* terminator */
 };
 
@@ -4885,7 +4934,18 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer,
 #define stac927x_proc_hook     NULL
 #endif
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
+static int stac92xx_pre_resume(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+
+       /* sync mute LED */
+       if (spec->gpio_led)
+               stac_gpio_set(codec, spec->gpio_mask,
+                               spec->gpio_dir, spec->gpio_data);
+       return 0;
+}
+
 static int stac92xx_resume(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -4901,29 +4961,19 @@ static int stac92xx_resume(struct hda_codec *codec)
                        stac_issue_unsol_event(codec,
                                               spec->autocfg.line_out_pins[0]);
        }
-       /* sync mute LED */
-       if (spec->gpio_led)
-               hda_call_check_power_status(codec, 0x01);
        return 0;
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
 /*
- * using power check for controlling mute led of HP notebooks
- * check for mute state only on Speakers (nid = 0x10)
- *
- * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise
- * the LED is NOT working properly !
- *
- * Changed name to reflect that it now works for any designated
- * model, not just HP HDX.
+ * For this feature CONFIG_SND_HDA_POWER_SAVE is needed
+ * as mute LED state is updated in check_power_status hook
  */
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static int stac92xx_hp_check_power_status(struct hda_codec *codec,
-                                             hda_nid_t nid)
+static int stac92xx_update_led_status(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
-       int i, muted = 1;
+       int i, num_ext_dacs, muted = 1;
+       hda_nid_t nid;
 
        for (i = 0; i < spec->multiout.num_dacs; i++) {
                nid = spec->multiout.dac_nids[i];
@@ -4933,6 +4983,22 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
                        break;
                }
        }
+       if (muted && spec->multiout.hp_nid)
+               if (!(snd_hda_codec_amp_read(codec,
+                               spec->multiout.hp_nid, 0, HDA_OUTPUT, 0) &
+                                       HDA_AMP_MUTE)) {
+                       muted = 0; /* HP is not muted */
+               }
+       num_ext_dacs = ARRAY_SIZE(spec->multiout.extra_out_nid);
+       for (i = 0; muted && i < num_ext_dacs; i++) {
+               nid = spec->multiout.extra_out_nid[i];
+               if (nid == 0)
+                       break;
+               if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
+                     HDA_AMP_MUTE)) {
+                       muted = 0; /* extra output is not muted */
+               }
+       }
        if (muted)
                spec->gpio_data &= ~spec->gpio_led; /* orange */
        else
@@ -4946,6 +5012,17 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
        stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
        return 0;
 }
+
+/*
+ * use power check for controlling mute led of HP notebooks
+ */
+static int stac92xx_check_power_status(struct hda_codec *codec,
+                                             hda_nid_t nid)
+{
+       stac92xx_update_led_status(codec);
+
+       return 0;
+}
 #endif
 
 static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
@@ -4953,7 +5030,7 @@ static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
        stac92xx_shutup(codec);
        return 0;
 }
-#endif
+#endif /* CONFIG_PM */
 
 static const struct hda_codec_ops stac92xx_patch_ops = {
        .build_controls = stac92xx_build_controls,
@@ -4961,9 +5038,10 @@ static const struct hda_codec_ops stac92xx_patch_ops = {
        .init = stac92xx_init,
        .free = stac92xx_free,
        .unsol_event = stac92xx_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
        .suspend = stac92xx_suspend,
        .resume = stac92xx_resume,
+       .pre_resume = stac92xx_pre_resume,
 #endif
        .reboot_notify = stac92xx_shutup,
 };
@@ -5482,7 +5560,7 @@ again:
                spec->gpio_data |= spec->gpio_led;
                /* register check_power_status callback. */
                codec->patch_ops.check_power_status =
-                       stac92xx_hp_check_power_status;
+                       stac92xx_check_power_status;
        }
 #endif 
 
@@ -5810,7 +5888,7 @@ again:
                spec->gpio_data |= spec->gpio_led;
                /* register check_power_status callback. */
                codec->patch_ops.check_power_status =
-                       stac92xx_hp_check_power_status;
+                       stac92xx_check_power_status;
        }
 #endif 
 
index f38160b..84d8798 100644 (file)
@@ -1708,7 +1708,7 @@ static void via_unsol_event(struct hda_codec *codec,
                via_gpio_control(codec);
 }
 
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
 static int via_suspend(struct hda_codec *codec, pm_message_t state)
 {
        struct via_spec *spec = codec->spec;
@@ -1736,7 +1736,7 @@ static const struct hda_codec_ops via_patch_ops = {
        .init = via_init,
        .free = via_free,
        .unsol_event = via_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
+#ifdef CONFIG_PM
        .suspend = via_suspend,
 #endif
 #ifdef CONFIG_SND_HDA_POWER_SAVE
index ff29380..76258f2 100644 (file)
@@ -907,6 +907,7 @@ static int ldo_regulator_register(struct snd_soc_codec *codec,
                                struct regulator_init_data *init_data,
                                int voltage)
 {
+       dev_err(codec->dev, "this setup needs regulator support in the kernel\n");
        return -EINVAL;
 }
 
@@ -1218,6 +1219,34 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
        return 0;
 }
 
+static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec)
+{
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       /* set internal ldo to 1.2v */
+       ret = ldo_regulator_register(codec, &ldo_init_data, LDO_VOLTAGE);
+       if (ret) {
+               dev_err(codec->dev,
+                       "Failed to register vddd internal supplies: %d\n", ret);
+               return ret;
+       }
+
+       sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
+
+       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
+                       sgtl5000->supplies);
+
+       if (ret) {
+               ldo_regulator_remove(codec);
+               dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+               return ret;
+       }
+
+       dev_info(codec->dev, "Using internal LDO instead of VDDD\n");
+       return 0;
+}
+
 static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
 {
        u16 reg;
@@ -1235,30 +1264,9 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
        if (!ret)
                external_vddd = 1;
        else {
-               /* set internal ldo to 1.2v */
-               int voltage = LDO_VOLTAGE;
-
-               ret = ldo_regulator_register(codec, &ldo_init_data, voltage);
-               if (ret) {
-                       dev_err(codec->dev,
-                       "Failed to register vddd internal supplies: %d\n",
-                               ret);
-                       return ret;
-               }
-
-               sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
-
-               ret = regulator_bulk_get(codec->dev,
-                               ARRAY_SIZE(sgtl5000->supplies),
-                               sgtl5000->supplies);
-
-               if (ret) {
-                       ldo_regulator_remove(codec);
-                       dev_err(codec->dev,
-                               "Failed to request supplies: %d\n", ret);
-
+               ret = sgtl5000_replace_vddd_with_ldo(codec);
+               if (ret)
                        return ret;
-               }
        }
 
        ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
@@ -1287,7 +1295,6 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
         * roll back to use internal LDO
         */
        if (external_vddd && rev >= 0x11) {
-               int voltage = LDO_VOLTAGE;
                /* disable all regulator first */
                regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
                                        sgtl5000->supplies);
@@ -1295,23 +1302,10 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
                regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
                                        sgtl5000->supplies);
 
-               ret = ldo_regulator_register(codec, &ldo_init_data, voltage);
+               ret = sgtl5000_replace_vddd_with_ldo(codec);
                if (ret)
                        return ret;
 
-               sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
-
-               ret = regulator_bulk_get(codec->dev,
-                               ARRAY_SIZE(sgtl5000->supplies),
-                               sgtl5000->supplies);
-               if (ret) {
-                       ldo_regulator_remove(codec);
-                       dev_err(codec->dev,
-                               "Failed to request supplies: %d\n", ret);
-
-                       return ret;
-               }
-
                ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
                                                sgtl5000->supplies);
                if (ret)
index 8499c56..60d740e 100644 (file)
@@ -3409,6 +3409,9 @@ static irqreturn_t wm8962_irq(int irq, void *data)
        active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2);
        active &= ~mask;
 
+       /* Acknowledge the interrupts */
+       snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active);
+
        if (active & WM8962_FLL_LOCK_EINT) {
                dev_dbg(codec->dev, "FLL locked\n");
                complete(&wm8962->fll_lock);
@@ -3433,9 +3436,6 @@ static irqreturn_t wm8962_irq(int irq, void *data)
                                      msecs_to_jiffies(250));
        }
 
-       /* Acknowledge the interrupts */
-       snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active);
-
        return IRQ_HANDLED;
 }
 
index 9259f1f..1f11525 100644 (file)
@@ -62,9 +62,9 @@ static void davinci_vcif_start(struct snd_pcm_substream *substream)
        w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1);
+               MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0);
        else
-               MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1);
+               MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0);
 
        writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
 }
@@ -80,9 +80,9 @@ static void davinci_vcif_stop(struct snd_pcm_substream *substream)
        /* Reset transmitter/receiver and sample rate/frame sync generators */
        w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0);
+               MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1);
        else
-               MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0);
+               MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1);
 
        writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
 }
@@ -159,6 +159,7 @@ static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd,
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                davinci_vcif_start(substream);
+               break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
index 1568eea..c086b78 100644 (file)
@@ -21,6 +21,7 @@
 #include <plat/audio.h>
 
 #include "dma.h"
+#include "idma.h"
 #include "i2s.h"
 #include "i2s-regs.h"
 
@@ -60,6 +61,7 @@ struct i2s_dai {
        /* DMA parameters */
        struct s3c_dma_params dma_playback;
        struct s3c_dma_params dma_capture;
+       struct s3c_dma_params idma_playback;
        u32     quirks;
        u32     suspend_i2smod;
        u32     suspend_i2scon;
@@ -877,6 +879,10 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
        if (i2s->quirks & QUIRK_NEED_RSTCLR)
                writel(CON_RSTCLR, i2s->addr + I2SCON);
 
+       if (i2s->quirks & QUIRK_SEC_DAI)
+               idma_reg_addr_init((void *)i2s->addr,
+                                       i2s->sec_dai->idma_playback.dma_addr);
+
 probe_exit:
        /* Reset any constraint on RFS and BFS */
        i2s->rfs = 0;
@@ -1077,6 +1083,7 @@ static __devinit int samsung_i2s_probe(struct platform_device *pdev)
                sec_dai->dma_playback.dma_size = 4;
                sec_dai->base = regs_base;
                sec_dai->quirks = quirks;
+               sec_dai->idma_playback.dma_addr = i2s_cfg->idma_addr;
                sec_dai->pri_dai = pri_dai;
                pri_dai->sec_dai = sec_dai;
        }
index e44267f..83ad8ca 100644 (file)
@@ -577,6 +577,7 @@ int snd_soc_suspend(struct device *dev)
                        case SND_SOC_BIAS_OFF:
                                codec->driver->suspend(codec, PMSG_SUSPEND);
                                codec->suspended = 1;
+                               codec->cache_sync = 1;
                                break;
                        default:
                                dev_dbg(codec->dev, "CODEC is on over suspend\n");
@@ -1140,7 +1141,7 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
                        }
                }
                cpu_dai->probed = 1;
-               /* mark cpu_dai as probed and add to card cpu_dai list */
+               /* mark cpu_dai as probed and add to card dai list */
                list_add(&cpu_dai->card_list, &card->dai_dev_list);
        }
 
@@ -1171,7 +1172,7 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
                        }
                }
 
-               /* mark cpu_dai as probed and add to card cpu_dai list */
+               /* mark codec_dai as probed and add to card dai list */
                codec_dai->probed = 1;
                list_add(&codec_dai->card_list, &card->dai_dev_list);
        }
index fbfcda0..7e15914 100644 (file)
@@ -124,6 +124,36 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
        return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
 }
 
+/* get snd_card from DAPM context */
+static inline struct snd_card *dapm_get_snd_card(
+       struct snd_soc_dapm_context *dapm)
+{
+       if (dapm->codec)
+               return dapm->codec->card->snd_card;
+       else if (dapm->platform)
+               return dapm->platform->card->snd_card;
+       else
+               BUG();
+
+       /* unreachable */
+       return NULL;
+}
+
+/* get soc_card from DAPM context */
+static inline struct snd_soc_card *dapm_get_soc_card(
+               struct snd_soc_dapm_context *dapm)
+{
+       if (dapm->codec)
+               return dapm->codec->card;
+       else if (dapm->platform)
+               return dapm->platform->card;
+       else
+               BUG();
+
+       /* unreachable */
+       return NULL;
+}
+
 static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg)
 {
        if (w->codec)