ALSA: hda/ca0132: support for Alienware 15 Creative Sound Core3D-EX
authorGabriele Martino <g.martino@gmx.com>
Mon, 18 May 2015 19:15:13 +0000 (21:15 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 19 May 2015 04:58:26 +0000 (06:58 +0200)
This patch adds quirks detection to the Creative CA0132 codec, and the
quirk for Alienware 15 (2015).
Some quirks may need different pin configuration, so the relevant
compile-time configuration has been removed.
The pin configuration and related initialization verbs are generated at
runtime instead, in ca0132_config() and ca0132_prepare_verbs().

Signed-off-by: Gabriele Martino <g.martino@gmx.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/patch_ca0132.c

index 2de1a4222a7df7a829d1acebc90af6bd760e6f58..8dec790dae45a873d0fbd2c45630650aa4d620d2 100644 (file)
@@ -43,8 +43,6 @@
 #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)
@@ -703,8 +701,8 @@ struct ca0132_spec {
        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 */
+       const struct hda_verb *chip_init_verbs;
+       struct hda_verb *spec_init_verbs;
        struct auto_pin_cfg autocfg;
 
        /* Nodes configurations */
@@ -719,6 +717,8 @@ struct ca0132_spec {
        unsigned int num_inputs;
        hda_nid_t shared_mic_nid;
        hda_nid_t shared_out_nid;
+       hda_nid_t unsol_tag_hp;
+       hda_nid_t unsol_tag_amic1;
 
        /* chip access */
        struct mutex chipio_mutex; /* chip access mutex */
@@ -748,12 +748,26 @@ struct ca0132_spec {
 
        struct hda_codec *codec;
        struct delayed_work unsol_hp_work;
+       int quirk;
 
 #ifdef ENABLE_TUNING_CONTROLS
        long cur_ctl_vals[TUNING_CTLS_COUNT];
 #endif
 };
 
+/*
+ * CA0132 quirks table
+ */
+enum {
+       QUIRK_NONE,
+       QUIRK_ALIENWARE,
+};
+
+static const struct snd_pci_quirk ca0132_quirks[] = {
+       SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15", QUIRK_ALIENWARE),
+       {}
+};
+
 /*
  * CA0132 codec access
  */
@@ -3224,7 +3238,7 @@ static void ca0132_unsol_hp_delayed(struct work_struct *work)
        struct hda_jack_tbl *jack;
 
        ca0132_select_out(spec->codec);
-       jack = snd_hda_jack_tbl_get(spec->codec, UNSOL_TAG_HP);
+       jack = snd_hda_jack_tbl_get(spec->codec, spec->unsol_tag_hp);
        if (jack) {
                jack->block_report = 0;
                snd_hda_jack_report_sync(spec->codec);
@@ -4414,8 +4428,9 @@ static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb)
 
 static void ca0132_init_unsol(struct hda_codec *codec)
 {
-       snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_HP, hp_callback);
-       snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_AMIC1,
+       struct ca0132_spec *spec = codec->spec;
+       snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_hp, hp_callback);
+       snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_amic1,
                                            amic_callback);
        snd_hda_jack_detect_enable_callback(codec, UNSOL_TAG_DSP,
                                            ca0132_process_dsp_response);
@@ -4476,17 +4491,6 @@ static struct hda_verb ca0132_init_verbs0[] = {
        {}
 };
 
-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},*/
-       {}
-};
-
 static void ca0132_init_chip(struct hda_codec *codec)
 {
        struct ca0132_spec *spec = codec->spec;
@@ -4566,8 +4570,8 @@ 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]);
+       snd_hda_sequence_write(codec, spec->chip_init_verbs);
+       snd_hda_sequence_write(codec, spec->spec_init_verbs);
 
        ca0132_select_out(codec);
        ca0132_select_mic(codec);
@@ -4588,6 +4592,7 @@ static void ca0132_free(struct hda_codec *codec)
        snd_hda_sequence_write(codec, spec->base_exit_verbs);
        ca0132_exit_chip(codec);
        snd_hda_power_down(codec);
+       kfree(spec->spec_init_verbs);
        kfree(codec->spec);
 }
 
@@ -4614,18 +4619,25 @@ static void ca0132_config(struct hda_codec *codec)
 
        spec->num_outputs = 2;
        spec->out_pins[0] = 0x0b; /* speaker out */
-       spec->out_pins[1] = 0x10; /* headphone out */
+       if (spec->quirk == QUIRK_ALIENWARE) {
+               codec_dbg(codec, "ca0132_config: QUIRK_ALIENWARE applied.\n");
+               spec->out_pins[1] = 0x0f;
+       } else{
+               spec->out_pins[1] = 0x10; /* headphone out */
+       }
        spec->shared_out_nid = 0x2;
+       spec->unsol_tag_hp = spec->out_pins[1];
 
-       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->num_inputs = 3;
        spec->input_pins[0] = 0x12;
        spec->input_pins[1] = 0x11;
        spec->input_pins[2] = 0x13;
+       spec->shared_mic_nid = 0x7;
+       spec->unsol_tag_amic1 = spec->input_pins[0];
 
        /* SPDIF I/O */
        spec->dig_out = 0x05;
@@ -4638,10 +4650,56 @@ static void ca0132_config(struct hda_codec *codec)
        cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
 }
 
+static int ca0132_prepare_verbs(struct hda_codec *codec)
+{
+/* Verbs + terminator (an empty element) */
+#define NUM_SPEC_VERBS 4
+       struct ca0132_spec *spec = codec->spec;
+
+       spec->chip_init_verbs = ca0132_init_verbs0;
+       spec->spec_init_verbs = kzalloc(sizeof(struct hda_verb) * NUM_SPEC_VERBS, GFP_KERNEL);
+       if (!spec->spec_init_verbs)
+               return -ENOMEM;
+
+       /* HP jack autodetection */
+       spec->spec_init_verbs[0].nid = spec->unsol_tag_hp;
+       spec->spec_init_verbs[0].param = AC_VERB_SET_UNSOLICITED_ENABLE;
+       spec->spec_init_verbs[0].verb = AC_USRSP_EN | spec->unsol_tag_hp;
+
+       /* MIC1 jack autodetection */
+       spec->spec_init_verbs[1].nid = spec->unsol_tag_amic1;
+       spec->spec_init_verbs[1].param = AC_VERB_SET_UNSOLICITED_ENABLE;
+       spec->spec_init_verbs[1].verb = AC_USRSP_EN | spec->unsol_tag_amic1;
+
+       /* config EAPD */
+       spec->spec_init_verbs[2].nid = 0x0b;
+       spec->spec_init_verbs[2].param = 0x78D;
+       spec->spec_init_verbs[2].verb = 0x00;
+
+       /* Previously commented configuration */
+       /*
+       spec->spec_init_verbs[3].nid = 0x0b;
+       spec->spec_init_verbs[3].param = AC_VERB_SET_EAPD_BTLENABLE;
+       spec->spec_init_verbs[3].verb = 0x02;
+
+       spec->spec_init_verbs[4].nid = 0x10;
+       spec->spec_init_verbs[4].param = 0x78D;
+       spec->spec_init_verbs[4].verb = 0x02;
+
+       spec->spec_init_verbs[5].nid = 0x10;
+       spec->spec_init_verbs[5].param = AC_VERB_SET_EAPD_BTLENABLE;
+       spec->spec_init_verbs[5].verb = 0x02;
+       */
+
+       /* Terminator: spec->spec_init_verbs[NUM_SPEC_VERBS-1] */
+       return 0;
+}
+
 static int patch_ca0132(struct hda_codec *codec)
 {
        struct ca0132_spec *spec;
        int err;
+       const struct snd_pci_quirk *quirk;
 
        codec_dbg(codec, "patch_ca0132\n");
 
@@ -4651,15 +4709,19 @@ static int patch_ca0132(struct hda_codec *codec)
        codec->spec = spec;
        spec->codec = codec;
 
+       /* Detect codec quirk */
+       quirk = snd_pci_quirk_lookup(codec->bus->pci, ca0132_quirks);
+       if (quirk)
+               spec->quirk = quirk->value;
+       else
+               spec->quirk = QUIRK_NONE;
+
        spec->dsp_state = DSP_DOWNLOAD_INIT;
        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;
 
        INIT_DELAYED_WORK(&spec->unsol_hp_work, ca0132_unsol_hp_delayed);
 
@@ -4667,6 +4729,10 @@ static int patch_ca0132(struct hda_codec *codec)
 
        ca0132_config(codec);
 
+       err = ca0132_prepare_verbs(codec);
+       if (err < 0)
+               return err;
+
        err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
        if (err < 0)
                return err;