diff options
Diffstat (limited to 'sound/pci/hda/patch_conexant.c')
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 114 |
1 files changed, 111 insertions, 3 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index b20e1cede00b..75de40aaab0a 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -25,6 +25,8 @@ #include <linux/slab.h> #include <linux/pci.h> #include <sound/core.h> +#include <sound/jack.h> + #include "hda_codec.h" #include "hda_local.h" @@ -37,8 +39,21 @@ #define CONEXANT_HP_EVENT 0x37 #define CONEXANT_MIC_EVENT 0x38 +/* Conexant 5051 specific */ + +#define CXT5051_SPDIF_OUT 0x1C +#define CXT5051_PORTB_EVENT 0x38 +#define CXT5051_PORTC_EVENT 0x39 +struct conexant_jack { + + hda_nid_t nid; + int type; + struct snd_jack *jack; + +}; + struct conexant_spec { struct snd_kcontrol_new *mixers[5]; @@ -83,6 +98,9 @@ struct conexant_spec { unsigned int spdif_route; + /* jack detection */ + struct snd_array jacks; + /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; struct hda_input_mux private_imux; @@ -329,6 +347,86 @@ static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol, &spec->cur_mux[adc_idx]); } +static int conexant_add_jack(struct hda_codec *codec, + hda_nid_t nid, int type) +{ + struct conexant_spec *spec; + struct conexant_jack *jack; + const char *name; + + spec = codec->spec; + snd_array_init(&spec->jacks, sizeof(*jack), 32); + jack = snd_array_new(&spec->jacks); + name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ; + + if (!jack) + return -ENOMEM; + + jack->nid = nid; + jack->type = type; + + return snd_jack_new(codec->bus->card, name, type, &jack->jack); +} + +static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid) +{ + struct conexant_spec *spec = codec->spec; + struct conexant_jack *jacks = spec->jacks.list; + + if (jacks) { + int i; + for (i = 0; i < spec->jacks.used; i++) { + if (jacks->nid == nid) { + unsigned int present; + present = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_SENSE, 0) & + AC_PINSENSE_PRESENCE; + + present = (present) ? jacks->type : 0 ; + + snd_jack_report(jacks->jack, + present); + } + jacks++; + } + } +} + +static int conexant_init_jacks(struct hda_codec *codec) +{ +#ifdef CONFIG_SND_JACK + struct conexant_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->num_init_verbs; i++) { + const struct hda_verb *hv; + + hv = spec->init_verbs[i]; + while (hv->nid) { + int err = 0; + switch (hv->param ^ AC_USRSP_EN) { + case CONEXANT_HP_EVENT: + err = conexant_add_jack(codec, hv->nid, + SND_JACK_HEADPHONE); + conexant_report_jack(codec, hv->nid); + break; + case CXT5051_PORTC_EVENT: + case CONEXANT_MIC_EVENT: + err = conexant_add_jack(codec, hv->nid, + SND_JACK_MICROPHONE); + conexant_report_jack(codec, hv->nid); + break; + } + if (err < 0) + return err; + ++hv; + } + } +#endif + return 0; + +} + static int conexant_init(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -341,6 +439,16 @@ static int conexant_init(struct hda_codec *codec) static void conexant_free(struct hda_codec *codec) { +#ifdef CONFIG_SND_JACK + struct conexant_spec *spec = codec->spec; + if (spec->jacks.list) { + struct conexant_jack *jacks = spec->jacks.list; + int i; + for (i = 0; i < spec->jacks.used; i++) + snd_device_free(codec->bus->card, &jacks[i].jack); + snd_array_free(&spec->jacks); + } +#endif kfree(codec->spec); } @@ -1526,9 +1634,6 @@ static int patch_cxt5047(struct hda_codec *codec) /* Conexant 5051 specific */ static hda_nid_t cxt5051_dac_nids[1] = { 0x10 }; static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 }; -#define CXT5051_SPDIF_OUT 0x1C -#define CXT5051_PORTB_EVENT 0x38 -#define CXT5051_PORTC_EVENT 0x39 static struct hda_channel_mode cxt5051_modes[1] = { { 2, NULL }, @@ -1608,6 +1713,7 @@ static void cxt5051_hp_automute(struct hda_codec *codec) static void cxt5051_hp_unsol_event(struct hda_codec *codec, unsigned int res) { + int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20; switch (res >> 26) { case CONEXANT_HP_EVENT: cxt5051_hp_automute(codec); @@ -1619,6 +1725,7 @@ static void cxt5051_hp_unsol_event(struct hda_codec *codec, cxt5051_portc_automic(codec); break; } + conexant_report_jack(codec, nid); } static struct snd_kcontrol_new cxt5051_mixers[] = { @@ -1693,6 +1800,7 @@ static struct hda_verb cxt5051_init_verbs[] = { static int cxt5051_init(struct hda_codec *codec) { conexant_init(codec); + conexant_init_jacks(codec); if (codec->patch_ops.unsol_event) { cxt5051_hp_automute(codec); cxt5051_portb_automic(codec); |