Merge branch 'topic/hda-next' into topic/hda
This commit is contained in:
@ -30,12 +30,13 @@
|
||||
#include <linux/pci.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/jack.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
#include "hda_patch.h"
|
||||
#include "hda_beep.h"
|
||||
|
||||
#define NUM_CONTROL_ALLOC 32
|
||||
#define STAC_INSERT_EVENT 0x10
|
||||
#define STAC_PWR_EVENT 0x20
|
||||
#define STAC_HP_EVENT 0x30
|
||||
#define STAC_VREF_EVENT 0x40
|
||||
@ -129,6 +130,17 @@ enum {
|
||||
STAC_927X_MODELS
|
||||
};
|
||||
|
||||
struct sigmatel_event {
|
||||
hda_nid_t nid;
|
||||
int data;
|
||||
};
|
||||
|
||||
struct sigmatel_jack {
|
||||
hda_nid_t nid;
|
||||
int type;
|
||||
struct snd_jack *jack;
|
||||
};
|
||||
|
||||
struct sigmatel_spec {
|
||||
struct snd_kcontrol_new *mixers[4];
|
||||
unsigned int num_mixers;
|
||||
@ -161,6 +173,12 @@ struct sigmatel_spec {
|
||||
hda_nid_t *pwr_nids;
|
||||
hda_nid_t *dac_list;
|
||||
|
||||
/* jack detection */
|
||||
struct snd_array jacks;
|
||||
|
||||
/* events */
|
||||
struct snd_array events;
|
||||
|
||||
/* playback */
|
||||
struct hda_input_mux *mono_mux;
|
||||
struct hda_input_mux *amp_mux;
|
||||
@ -218,8 +236,7 @@ struct sigmatel_spec {
|
||||
|
||||
/* dynamic controls and input_mux */
|
||||
struct auto_pin_cfg autocfg;
|
||||
unsigned int num_kctl_alloc, num_kctl_used;
|
||||
struct snd_kcontrol_new *kctl_alloc;
|
||||
struct snd_array kctls;
|
||||
struct hda_input_mux private_dimux;
|
||||
struct hda_input_mux private_imux;
|
||||
struct hda_input_mux private_smux;
|
||||
@ -1233,6 +1250,8 @@ static const char *slave_sws[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static void stac92xx_free_kctls(struct hda_codec *codec);
|
||||
|
||||
static int stac92xx_build_controls(struct hda_codec *codec)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
@ -1250,7 +1269,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
|
||||
}
|
||||
if (spec->num_dmuxes > 0) {
|
||||
stac_dmux_mixer.count = spec->num_dmuxes;
|
||||
err = snd_ctl_add(codec->bus->card,
|
||||
err = snd_hda_ctl_add(codec,
|
||||
snd_ctl_new1(&stac_dmux_mixer, codec));
|
||||
if (err < 0)
|
||||
return err;
|
||||
@ -1305,6 +1324,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
|
||||
return err;
|
||||
}
|
||||
|
||||
stac92xx_free_kctls(codec); /* no longer needed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2453,13 +2473,15 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
int nid = cfg->hp_pins[cfg->hp_outs - 1];
|
||||
|
||||
spec->hp_switch = ucontrol->value.integer.value[0];
|
||||
|
||||
/* check to be sure that the ports are upto date with
|
||||
* switch changes
|
||||
*/
|
||||
codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
|
||||
codec->patch_ops.unsol_event(codec, (STAC_HP_EVENT | nid) << 26);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -2499,7 +2521,8 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
|
||||
* appropriately according to the pin direction
|
||||
*/
|
||||
if (spec->hp_detect)
|
||||
codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
|
||||
codec->patch_ops.unsol_event(codec,
|
||||
(STAC_HP_EVENT | nid) << 26);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -2592,28 +2615,16 @@ static int stac92xx_add_control_idx(struct sigmatel_spec *spec, int type,
|
||||
{
|
||||
struct snd_kcontrol_new *knew;
|
||||
|
||||
if (spec->num_kctl_used >= spec->num_kctl_alloc) {
|
||||
int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
|
||||
|
||||
knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
|
||||
if (! knew)
|
||||
return -ENOMEM;
|
||||
if (spec->kctl_alloc) {
|
||||
memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
|
||||
kfree(spec->kctl_alloc);
|
||||
}
|
||||
spec->kctl_alloc = knew;
|
||||
spec->num_kctl_alloc = num;
|
||||
}
|
||||
|
||||
knew = &spec->kctl_alloc[spec->num_kctl_used];
|
||||
snd_array_init(&spec->kctls, sizeof(*knew), 32);
|
||||
knew = snd_array_new(&spec->kctls);
|
||||
if (!knew)
|
||||
return -ENOMEM;
|
||||
*knew = stac92xx_control_templates[type];
|
||||
knew->index = idx;
|
||||
knew->name = kstrdup(name, GFP_KERNEL);
|
||||
if (! knew->name)
|
||||
return -ENOMEM;
|
||||
knew->private_value = val;
|
||||
spec->num_kctl_used++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3434,8 +3445,8 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
|
||||
if (dig_in && spec->autocfg.dig_in_pin)
|
||||
spec->dig_in_nid = dig_in;
|
||||
|
||||
if (spec->kctl_alloc)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
|
||||
if (spec->kctls.list)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctls.list;
|
||||
|
||||
spec->input_mux = &spec->private_imux;
|
||||
spec->dinput_mux = &spec->private_dimux;
|
||||
@ -3536,8 +3547,8 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
|
||||
if (spec->autocfg.dig_in_pin)
|
||||
spec->dig_in_nid = 0x04;
|
||||
|
||||
if (spec->kctl_alloc)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
|
||||
if (spec->kctls.list)
|
||||
spec->mixers[spec->num_mixers++] = spec->kctls.list;
|
||||
|
||||
spec->input_mux = &spec->private_imux;
|
||||
spec->dinput_mux = &spec->private_dimux;
|
||||
@ -3581,13 +3592,70 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
|
||||
AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
|
||||
}
|
||||
|
||||
static int stac92xx_add_jack(struct hda_codec *codec,
|
||||
hda_nid_t nid, int type)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
struct sigmatel_jack *jack;
|
||||
int def_conf = snd_hda_codec_read(codec, nid,
|
||||
0, AC_VERB_GET_CONFIG_DEFAULT, 0);
|
||||
int connectivity = get_defcfg_connect(def_conf);
|
||||
char name[32];
|
||||
|
||||
if (connectivity && connectivity != AC_JACK_PORT_FIXED)
|
||||
return 0;
|
||||
|
||||
snd_array_init(&spec->jacks, sizeof(*jack), 32);
|
||||
jack = snd_array_new(&spec->jacks);
|
||||
if (!jack)
|
||||
return -ENOMEM;
|
||||
jack->nid = nid;
|
||||
jack->type = type;
|
||||
|
||||
sprintf(name, "%s at %s %s Jack",
|
||||
snd_hda_get_jack_type(def_conf),
|
||||
snd_hda_get_jack_connectivity(def_conf),
|
||||
snd_hda_get_jack_location(def_conf));
|
||||
|
||||
return snd_jack_new(codec->bus->card, name, type, &jack->jack);
|
||||
}
|
||||
|
||||
static int stac92xx_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
|
||||
int data)
|
||||
{
|
||||
struct sigmatel_event *event;
|
||||
|
||||
snd_array_init(&spec->events, sizeof(*event), 32);
|
||||
event = snd_array_new(&spec->events);
|
||||
if (!event)
|
||||
return -ENOMEM;
|
||||
event->nid = nid;
|
||||
event->data = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stac92xx_event_data(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
struct sigmatel_event *events = spec->events.list;
|
||||
if (events) {
|
||||
int i;
|
||||
for (i = 0; i < spec->events.used; i++)
|
||||
if (events[i].nid == nid)
|
||||
return events[i].data;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
|
||||
unsigned int event)
|
||||
{
|
||||
if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)
|
||||
if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
|
||||
snd_hda_codec_write_cache(codec, nid, 0,
|
||||
AC_VERB_SET_UNSOLICITED_ENABLE,
|
||||
(AC_USRSP_EN | event));
|
||||
(AC_USRSP_EN | event | nid));
|
||||
}
|
||||
}
|
||||
|
||||
static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
|
||||
@ -3617,7 +3685,7 @@ static int stac92xx_init(struct hda_codec *codec)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||
int i;
|
||||
int i, err;
|
||||
|
||||
snd_hda_sequence_write(codec, spec->init);
|
||||
|
||||
@ -3630,21 +3698,36 @@ static int stac92xx_init(struct hda_codec *codec)
|
||||
/* set up pins */
|
||||
if (spec->hp_detect) {
|
||||
/* Enable unsolicited responses on the HP widget */
|
||||
for (i = 0; i < cfg->hp_outs; i++)
|
||||
enable_pin_detect(codec, cfg->hp_pins[i],
|
||||
STAC_HP_EVENT);
|
||||
for (i = 0; i < cfg->hp_outs; i++) {
|
||||
int type = SND_JACK_HEADPHONE;
|
||||
hda_nid_t nid = cfg->hp_pins[i];
|
||||
enable_pin_detect(codec, nid, STAC_HP_EVENT | nid);
|
||||
/* jack detection */
|
||||
if (cfg->hp_outs == i)
|
||||
type |= SND_JACK_LINEOUT;
|
||||
err = stac92xx_add_jack(codec, nid, type);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
}
|
||||
/* force to enable the first line-out; the others are set up
|
||||
* in unsol_event
|
||||
*/
|
||||
stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
|
||||
AC_PINCTL_OUT_EN);
|
||||
stac92xx_auto_init_hp_out(codec);
|
||||
AC_PINCTL_OUT_EN);
|
||||
/* fake event to set up pins */
|
||||
codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
|
||||
codec->patch_ops.unsol_event(codec,
|
||||
(STAC_HP_EVENT | spec->autocfg.hp_pins[0]) << 26);
|
||||
} else {
|
||||
stac92xx_auto_init_multi_out(codec);
|
||||
stac92xx_auto_init_hp_out(codec);
|
||||
}
|
||||
for (i = 0; i < cfg->line_outs; i++) {
|
||||
err = stac92xx_add_jack(codec,
|
||||
cfg->line_out_pins[i], SND_JACK_LINEOUT);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
for (i = 0; i < AUTO_PIN_LAST; i++) {
|
||||
hda_nid_t nid = cfg->input_pins[i];
|
||||
if (nid) {
|
||||
@ -3657,6 +3740,11 @@ static int stac92xx_init(struct hda_codec *codec)
|
||||
if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC)
|
||||
pinctl |= stac92xx_get_vref(codec, nid);
|
||||
stac92xx_auto_set_pinctl(codec, nid, pinctl);
|
||||
err = stac92xx_add_jack(codec, nid,
|
||||
SND_JACK_MICROPHONE);
|
||||
if (err < 0)
|
||||
return err;
|
||||
enable_pin_detect(codec, nid, STAC_INSERT_EVENT | nid);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < spec->num_dmics; i++)
|
||||
@ -3698,22 +3786,42 @@ static int stac92xx_init(struct hda_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stac92xx_free_jacks(struct hda_codec *codec)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
if (spec->jacks.list) {
|
||||
struct sigmatel_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);
|
||||
}
|
||||
|
||||
static void stac92xx_free_kctls(struct hda_codec *codec)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
|
||||
if (spec->kctls.list) {
|
||||
struct snd_kcontrol_new *kctl = spec->kctls.list;
|
||||
int i;
|
||||
for (i = 0; i < spec->kctls.used; i++)
|
||||
kfree(kctl[i].name);
|
||||
}
|
||||
snd_array_free(&spec->kctls);
|
||||
}
|
||||
|
||||
static void stac92xx_free(struct hda_codec *codec)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
int i;
|
||||
|
||||
if (! spec)
|
||||
return;
|
||||
|
||||
if (spec->kctl_alloc) {
|
||||
for (i = 0; i < spec->num_kctl_used; i++)
|
||||
kfree(spec->kctl_alloc[i].name);
|
||||
kfree(spec->kctl_alloc);
|
||||
}
|
||||
|
||||
if (spec->bios_pin_configs)
|
||||
kfree(spec->bios_pin_configs);
|
||||
stac92xx_free_jacks(codec);
|
||||
snd_array_free(&spec->events);
|
||||
|
||||
kfree(spec);
|
||||
snd_hda_detach_beep_device(codec);
|
||||
@ -3852,24 +3960,57 @@ static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
|
||||
|
||||
/* power down unused output ports */
|
||||
snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
|
||||
};
|
||||
}
|
||||
|
||||
static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
struct sigmatel_jack *jacks = spec->jacks.list;
|
||||
|
||||
if (jacks) {
|
||||
int i;
|
||||
for (i = 0; i < spec->jacks.used; i++) {
|
||||
if (jacks->nid == nid) {
|
||||
unsigned int pin_ctl =
|
||||
snd_hda_codec_read(codec, nid,
|
||||
0, AC_VERB_GET_PIN_WIDGET_CONTROL,
|
||||
0x00);
|
||||
int type = jacks->type;
|
||||
if (type == (SND_JACK_LINEOUT
|
||||
| SND_JACK_HEADPHONE))
|
||||
type = (pin_ctl & AC_PINCTL_HP_EN)
|
||||
? SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
|
||||
snd_jack_report(jacks->jack,
|
||||
get_hp_pin_presence(codec, nid)
|
||||
? type : 0);
|
||||
}
|
||||
jacks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
struct sigmatel_spec *spec = codec->spec;
|
||||
int idx = res >> 26 & 0x0f;
|
||||
int event = (res >> 26) & 0x70;
|
||||
int nid = res >> 26 & 0x0f;
|
||||
|
||||
switch ((res >> 26) & 0x70) {
|
||||
switch (event) {
|
||||
case STAC_HP_EVENT:
|
||||
stac92xx_hp_detect(codec, res);
|
||||
/* fallthru */
|
||||
case STAC_INSERT_EVENT:
|
||||
case STAC_PWR_EVENT:
|
||||
if (spec->num_pwrs > 0)
|
||||
stac92xx_pin_sense(codec, idx);
|
||||
if (nid) {
|
||||
if (spec->num_pwrs > 0)
|
||||
stac92xx_pin_sense(codec, nid);
|
||||
stac92xx_report_jack(codec, nid);
|
||||
}
|
||||
break;
|
||||
case STAC_VREF_EVENT: {
|
||||
int data = snd_hda_codec_read(codec, codec->afg, 0,
|
||||
AC_VERB_GET_GPIO_DATA, 0);
|
||||
int idx = stac92xx_event_data(codec, nid);
|
||||
/* toggle VREF state based on GPIOx status */
|
||||
snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
|
||||
!!(data & (1 << idx)));
|
||||
@ -4392,8 +4533,11 @@ again:
|
||||
snd_hda_codec_write(codec, codec->afg, 0,
|
||||
AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
|
||||
snd_hda_codec_write_cache(codec, codec->afg, 0,
|
||||
AC_VERB_SET_UNSOLICITED_ENABLE,
|
||||
(AC_USRSP_EN | STAC_VREF_EVENT | 0x01));
|
||||
AC_VERB_SET_UNSOLICITED_ENABLE,
|
||||
(AC_USRSP_EN | STAC_VREF_EVENT | codec->afg));
|
||||
err = stac92xx_add_event(spec, codec->afg, 0x02);
|
||||
if (err < 0)
|
||||
return err;
|
||||
spec->gpio_mask |= 0x02;
|
||||
break;
|
||||
}
|
||||
@ -4792,8 +4936,11 @@ static int patch_stac9205(struct hda_codec *codec)
|
||||
snd_hda_codec_write(codec, codec->afg, 0,
|
||||
AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
|
||||
snd_hda_codec_write_cache(codec, codec->afg, 0,
|
||||
AC_VERB_SET_UNSOLICITED_ENABLE,
|
||||
(AC_USRSP_EN | STAC_HP_EVENT));
|
||||
AC_VERB_SET_UNSOLICITED_ENABLE,
|
||||
(AC_USRSP_EN | STAC_VREF_EVENT | codec->afg));
|
||||
err = stac92xx_add_event(spec, codec->afg, 0x01);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
spec->gpio_dir = 0x0b;
|
||||
spec->eapd_mask = 0x01;
|
||||
|
Reference in New Issue
Block a user