]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - sound/pci/hda/patch_ca0132.c
Merge tag 'stable/for-linus-3.11-rc0-tag-two' of git://git.kernel.org/pub/scm/linux...
[karo-tx-linux.git] / sound / pci / hda / patch_ca0132.c
index 0792b5725f9c5e501886556ce68f1c383aef1460..90ff7a3f72dff8de71a4b1fa0a338d00e8579006 100644 (file)
@@ -131,6 +131,13 @@ enum {
 /* Effects values size*/
 #define EFFECT_VALS_MAX_COUNT 12
 
+/* Latency introduced by DSP blocks in milliseconds. */
+#define DSP_CAPTURE_INIT_LATENCY        0
+#define DSP_CRYSTAL_VOICE_LATENCY       124
+#define DSP_PLAYBACK_INIT_LATENCY       13
+#define DSP_PLAY_ENHANCEMENT_LATENCY    30
+#define DSP_SPEAKER_OUT_LATENCY         7
+
 struct ct_effect {
        char name[44];
        hda_nid_t nid;
@@ -741,6 +748,9 @@ struct ca0132_spec {
        long voicefx_val;
        long cur_mic_boost;
 
+       struct hda_codec *codec;
+       struct delayed_work unsol_hp_work;
+
 #ifdef ENABLE_TUNING_CONTROLS
        long cur_ctl_vals[TUNING_CTLS_COUNT];
 #endif
@@ -2740,6 +2750,31 @@ static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
        return 0;
 }
 
+static unsigned int ca0132_playback_pcm_delay(struct hda_pcm_stream *info,
+                       struct hda_codec *codec,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+       unsigned int latency = DSP_PLAYBACK_INIT_LATENCY;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       if (spec->dsp_state != DSP_DOWNLOADED)
+               return 0;
+
+       /* Add latency if playback enhancement and either effect is enabled. */
+       if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) {
+               if ((spec->effects_switch[SURROUND - EFFECT_START_NID]) ||
+                   (spec->effects_switch[DIALOG_PLUS - EFFECT_START_NID]))
+                       latency += DSP_PLAY_ENHANCEMENT_LATENCY;
+       }
+
+       /* Applying Speaker EQ adds latency as well. */
+       if (spec->cur_out_type == SPEAKER_OUT)
+               latency += DSP_SPEAKER_OUT_LATENCY;
+
+       return (latency * runtime->rate) / 1000;
+}
+
 /*
  * Digital out
  */
@@ -2808,6 +2843,23 @@ static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
        return 0;
 }
 
+static unsigned int ca0132_capture_pcm_delay(struct hda_pcm_stream *info,
+                       struct hda_codec *codec,
+                       struct snd_pcm_substream *substream)
+{
+       struct ca0132_spec *spec = codec->spec;
+       unsigned int latency = DSP_CAPTURE_INIT_LATENCY;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       if (spec->dsp_state != DSP_DOWNLOADED)
+               return 0;
+
+       if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID])
+               latency += DSP_CRYSTAL_VOICE_LATENCY;
+
+       return (latency * runtime->rate) / 1000;
+}
+
 /*
  * Controls stuffs.
  */
@@ -3227,6 +3279,14 @@ exit:
        return err < 0 ? err : 0;
 }
 
+static void ca0132_unsol_hp_delayed(struct work_struct *work)
+{
+       struct ca0132_spec *spec = container_of(
+               to_delayed_work(work), struct ca0132_spec, unsol_hp_work);
+       ca0132_select_out(spec->codec);
+       snd_hda_jack_report_sync(spec->codec);
+}
+
 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);
@@ -3991,7 +4051,8 @@ static struct hda_pcm_stream ca0132_pcm_analog_playback = {
        .channels_max = 6,
        .ops = {
                .prepare = ca0132_playback_pcm_prepare,
-               .cleanup = ca0132_playback_pcm_cleanup
+               .cleanup = ca0132_playback_pcm_cleanup,
+               .get_delay = ca0132_playback_pcm_delay,
        },
 };
 
@@ -4001,7 +4062,8 @@ static struct hda_pcm_stream ca0132_pcm_analog_capture = {
        .channels_max = 2,
        .ops = {
                .prepare = ca0132_capture_pcm_prepare,
-               .cleanup = ca0132_capture_pcm_cleanup
+               .cleanup = ca0132_capture_pcm_cleanup,
+               .get_delay = ca0132_capture_pcm_delay,
        },
 };
 
@@ -4399,8 +4461,7 @@ static void ca0132_process_dsp_response(struct hda_codec *codec)
 
 static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res)
 {
-       snd_printdd(KERN_INFO "ca0132_unsol_event: 0x%x\n", res);
-
+       struct ca0132_spec *spec = codec->spec;
 
        if (((res >> AC_UNSOL_RES_TAG_SHIFT) & 0x3f) == UNSOL_TAG_DSP) {
                ca0132_process_dsp_response(codec);
@@ -4412,8 +4473,13 @@ static void ca0132_unsol_event(struct hda_codec *codec, unsigned int res)
 
                switch (res) {
                case UNSOL_TAG_HP:
-                       ca0132_select_out(codec);
-                       snd_hda_jack_report_sync(codec);
+                       /* Delay enabling the HP amp, to let the mic-detection
+                        * state machine run.
+                        */
+                       cancel_delayed_work_sync(&spec->unsol_hp_work);
+                       queue_delayed_work(codec->bus->workq,
+                                          &spec->unsol_hp_work,
+                                          msecs_to_jiffies(500));
                        break;
                case UNSOL_TAG_AMIC1:
                        ca0132_select_mic(codec);
@@ -4588,6 +4654,7 @@ static void ca0132_free(struct hda_codec *codec)
 {
        struct ca0132_spec *spec = codec->spec;
 
+       cancel_delayed_work_sync(&spec->unsol_hp_work);
        snd_hda_power_up(codec);
        snd_hda_sequence_write(codec, spec->base_exit_verbs);
        ca0132_exit_chip(codec);
@@ -4653,6 +4720,7 @@ static int patch_ca0132(struct hda_codec *codec)
        if (!spec)
                return -ENOMEM;
        codec->spec = spec;
+       spec->codec = codec;
 
        spec->num_mixers = 1;
        spec->mixers[0] = ca0132_mixer;
@@ -4663,6 +4731,8 @@ static int patch_ca0132(struct hda_codec *codec)
        spec->init_verbs[1] = ca0132_init_verbs1;
        spec->num_init_verbs = 2;
 
+       INIT_DELAYED_WORK(&spec->unsol_hp_work, ca0132_unsol_hp_delayed);
+
        ca0132_init_chip(codec);
 
        ca0132_config(codec);