Merge branch 'topic/hda' into for-next
This is a large chunk merge for 3.6 HD-audio things
This commit is contained in:
@@ -37,8 +37,8 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
|
||||
MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define FREQ_LO (76U * 16000)
|
||||
#define FREQ_HI (108U * 16000)
|
||||
#define FREQ_LO ((tea->tea5759 ? 760 : 875) * 1600U)
|
||||
#define FREQ_HI ((tea->tea5759 ? 910 : 1080) * 1600U)
|
||||
|
||||
/*
|
||||
* definitions
|
||||
@@ -120,9 +120,9 @@ static u32 snd_tea575x_read(struct snd_tea575x *tea)
|
||||
return data;
|
||||
}
|
||||
|
||||
static u32 snd_tea575x_get_freq(struct snd_tea575x *tea)
|
||||
static u32 snd_tea575x_val_to_freq(struct snd_tea575x *tea, u32 val)
|
||||
{
|
||||
u32 freq = snd_tea575x_read(tea) & TEA575X_BIT_FREQ_MASK;
|
||||
u32 freq = val & TEA575X_BIT_FREQ_MASK;
|
||||
|
||||
if (freq == 0)
|
||||
return freq;
|
||||
@@ -139,6 +139,11 @@ static u32 snd_tea575x_get_freq(struct snd_tea575x *tea)
|
||||
return clamp(freq * 16, FREQ_LO, FREQ_HI); /* from kHz */
|
||||
}
|
||||
|
||||
static u32 snd_tea575x_get_freq(struct snd_tea575x *tea)
|
||||
{
|
||||
return snd_tea575x_val_to_freq(tea, snd_tea575x_read(tea));
|
||||
}
|
||||
|
||||
static void snd_tea575x_set_freq(struct snd_tea575x *tea)
|
||||
{
|
||||
u32 freq = tea->freq;
|
||||
@@ -156,6 +161,7 @@ static void snd_tea575x_set_freq(struct snd_tea575x *tea)
|
||||
tea->val &= ~TEA575X_BIT_FREQ_MASK;
|
||||
tea->val |= freq & TEA575X_BIT_FREQ_MASK;
|
||||
snd_tea575x_write(tea, tea->val);
|
||||
tea->freq = snd_tea575x_val_to_freq(tea, tea->val);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -317,7 +323,6 @@ static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
}
|
||||
|
||||
static const struct v4l2_file_operations tea575x_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = video_ioctl2,
|
||||
.open = v4l2_fh_open,
|
||||
.release = v4l2_fh_release,
|
||||
@@ -337,7 +342,6 @@ static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
|
||||
};
|
||||
|
||||
static const struct video_device tea575x_radio = {
|
||||
.fops = &tea575x_fops,
|
||||
.ioctl_ops = &tea575x_ioctl_ops,
|
||||
.release = video_device_release_empty,
|
||||
};
|
||||
@@ -349,7 +353,7 @@ static const struct v4l2_ctrl_ops tea575x_ctrl_ops = {
|
||||
/*
|
||||
* initialize all the tea575x chips
|
||||
*/
|
||||
int snd_tea575x_init(struct snd_tea575x *tea)
|
||||
int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner)
|
||||
{
|
||||
int retval;
|
||||
|
||||
@@ -374,6 +378,9 @@ int snd_tea575x_init(struct snd_tea575x *tea)
|
||||
tea->vd.lock = &tea->mutex;
|
||||
tea->vd.v4l2_dev = tea->v4l2_dev;
|
||||
tea->vd.ctrl_handler = &tea->ctrl_handler;
|
||||
tea->fops = tea575x_fops;
|
||||
tea->fops.owner = owner;
|
||||
tea->vd.fops = &tea->fops;
|
||||
set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags);
|
||||
/* disable hw_freq_seek if we can't use it */
|
||||
if (tea->cannot_read_data)
|
||||
|
@@ -2769,7 +2769,7 @@ static int __devinit snd_es1968_create(struct snd_card *card,
|
||||
chip->tea.ops = &snd_es1968_tea_ops;
|
||||
strlcpy(chip->tea.card, "SF64-PCE2", sizeof(chip->tea.card));
|
||||
sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
|
||||
if (!snd_tea575x_init(&chip->tea))
|
||||
if (!snd_tea575x_init(&chip->tea, THIS_MODULE))
|
||||
printk(KERN_INFO "es1968: detected TEA575x radio\n");
|
||||
#endif
|
||||
|
||||
|
@@ -1254,7 +1254,7 @@ static int __devinit snd_fm801_create(struct snd_card *card,
|
||||
sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
|
||||
if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
|
||||
(tea575x_tuner & TUNER_TYPE_MASK) < 4) {
|
||||
if (snd_tea575x_init(&chip->tea)) {
|
||||
if (snd_tea575x_init(&chip->tea, THIS_MODULE)) {
|
||||
snd_printk(KERN_ERR "TEA575x radio not found\n");
|
||||
snd_fm801_free(chip);
|
||||
return -ENODEV;
|
||||
@@ -1263,7 +1263,7 @@ static int __devinit snd_fm801_create(struct snd_card *card,
|
||||
/* autodetect tuner connection */
|
||||
for (tea575x_tuner = 1; tea575x_tuner <= 3; tea575x_tuner++) {
|
||||
chip->tea575x_tuner = tea575x_tuner;
|
||||
if (!snd_tea575x_init(&chip->tea)) {
|
||||
if (!snd_tea575x_init(&chip->tea, THIS_MODULE)) {
|
||||
snd_printk(KERN_INFO "detected TEA575x radio type %s\n",
|
||||
get_tea575x_gpio(chip)->name);
|
||||
break;
|
||||
|
@@ -53,15 +53,14 @@ config SND_HDA_INPUT_BEEP
|
||||
driver. This interface is used to generate digital beeps.
|
||||
|
||||
config SND_HDA_INPUT_BEEP_MODE
|
||||
int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)"
|
||||
int "Digital beep registration mode (0=off, 1=on)"
|
||||
depends on SND_HDA_INPUT_BEEP=y
|
||||
default "1"
|
||||
range 0 2
|
||||
range 0 1
|
||||
help
|
||||
Set 0 to disable the digital beep interface for HD-audio by default.
|
||||
Set 1 to always enable the digital beep interface for HD-audio by
|
||||
default. Set 2 to control the beep device registration to input
|
||||
layer using a "Beep Switch" in mixer applications.
|
||||
default.
|
||||
|
||||
config SND_HDA_INPUT_JACK
|
||||
bool "Support jack plugging notification via input layer"
|
||||
|
@@ -162,50 +162,20 @@ static int snd_hda_do_attach(struct hda_beep *beep)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_hda_do_register(struct work_struct *work)
|
||||
{
|
||||
struct hda_beep *beep =
|
||||
container_of(work, struct hda_beep, register_work);
|
||||
|
||||
mutex_lock(&beep->mutex);
|
||||
if (beep->enabled && !beep->dev)
|
||||
snd_hda_do_attach(beep);
|
||||
mutex_unlock(&beep->mutex);
|
||||
}
|
||||
|
||||
static void snd_hda_do_unregister(struct work_struct *work)
|
||||
{
|
||||
struct hda_beep *beep =
|
||||
container_of(work, struct hda_beep, unregister_work.work);
|
||||
|
||||
mutex_lock(&beep->mutex);
|
||||
if (!beep->enabled && beep->dev)
|
||||
snd_hda_do_detach(beep);
|
||||
mutex_unlock(&beep->mutex);
|
||||
}
|
||||
|
||||
int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
|
||||
{
|
||||
struct hda_beep *beep = codec->beep;
|
||||
enable = !!enable;
|
||||
if (beep == NULL)
|
||||
if (!beep)
|
||||
return 0;
|
||||
enable = !!enable;
|
||||
if (beep->enabled != enable) {
|
||||
beep->enabled = enable;
|
||||
if (!enable) {
|
||||
cancel_work_sync(&beep->beep_work);
|
||||
/* turn off beep */
|
||||
snd_hda_codec_write(beep->codec, beep->nid, 0,
|
||||
AC_VERB_SET_BEEP_CONTROL, 0);
|
||||
}
|
||||
if (beep->mode == HDA_BEEP_MODE_SWREG) {
|
||||
if (enable) {
|
||||
cancel_delayed_work(&beep->unregister_work);
|
||||
schedule_work(&beep->register_work);
|
||||
} else {
|
||||
schedule_delayed_work(&beep->unregister_work,
|
||||
HZ);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -215,6 +185,7 @@ EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device);
|
||||
int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
|
||||
{
|
||||
struct hda_beep *beep;
|
||||
int err;
|
||||
|
||||
if (!snd_hda_get_bool_hint(codec, "beep"))
|
||||
return 0; /* disabled explicitly by hints */
|
||||
@@ -232,21 +203,16 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
|
||||
|
||||
beep->nid = nid;
|
||||
beep->codec = codec;
|
||||
beep->mode = codec->beep_mode;
|
||||
codec->beep = beep;
|
||||
|
||||
INIT_WORK(&beep->register_work, &snd_hda_do_register);
|
||||
INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister);
|
||||
INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
|
||||
mutex_init(&beep->mutex);
|
||||
|
||||
if (beep->mode == HDA_BEEP_MODE_ON) {
|
||||
int err = snd_hda_do_attach(beep);
|
||||
if (err < 0) {
|
||||
kfree(beep);
|
||||
codec->beep = NULL;
|
||||
return err;
|
||||
}
|
||||
err = snd_hda_do_attach(beep);
|
||||
if (err < 0) {
|
||||
kfree(beep);
|
||||
codec->beep = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -257,8 +223,6 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
|
||||
{
|
||||
struct hda_beep *beep = codec->beep;
|
||||
if (beep) {
|
||||
cancel_work_sync(&beep->register_work);
|
||||
cancel_delayed_work(&beep->unregister_work);
|
||||
if (beep->dev)
|
||||
snd_hda_do_detach(beep);
|
||||
codec->beep = NULL;
|
||||
@@ -266,3 +230,31 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
|
||||
|
||||
/* get/put callbacks for beep mute mixer switches */
|
||||
int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct hda_beep *beep = codec->beep;
|
||||
if (beep) {
|
||||
ucontrol->value.integer.value[0] =
|
||||
ucontrol->value.integer.value[1] =
|
||||
beep->enabled;
|
||||
return 0;
|
||||
}
|
||||
return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get_beep);
|
||||
|
||||
int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct hda_beep *beep = codec->beep;
|
||||
if (beep)
|
||||
snd_hda_enable_beep_device(codec,
|
||||
*ucontrol->value.integer.value);
|
||||
return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
|
||||
|
@@ -26,21 +26,16 @@
|
||||
|
||||
#define HDA_BEEP_MODE_OFF 0
|
||||
#define HDA_BEEP_MODE_ON 1
|
||||
#define HDA_BEEP_MODE_SWREG 2
|
||||
|
||||
/* beep information */
|
||||
struct hda_beep {
|
||||
struct input_dev *dev;
|
||||
struct hda_codec *codec;
|
||||
unsigned int mode;
|
||||
char phys[32];
|
||||
int tone;
|
||||
hda_nid_t nid;
|
||||
unsigned int enabled:1;
|
||||
unsigned int request_enable:1;
|
||||
unsigned int linear_tone:1; /* linear tone for IDT/STAC codec */
|
||||
struct work_struct register_work; /* registration work */
|
||||
struct delayed_work unregister_work; /* unregistration work */
|
||||
struct work_struct beep_work; /* scheduled task for beep event */
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
@@ -2676,25 +2676,6 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
|
||||
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
/**
|
||||
* snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch
|
||||
*
|
||||
* This function calls snd_hda_enable_beep_device(), which behaves differently
|
||||
* depending on beep_mode option.
|
||||
*/
|
||||
int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
long *valp = ucontrol->value.integer.value;
|
||||
|
||||
snd_hda_enable_beep_device(codec, *valp);
|
||||
return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
|
||||
#endif /* CONFIG_SND_HDA_INPUT_BEEP */
|
||||
|
||||
/*
|
||||
* bound volume controls
|
||||
*
|
||||
@@ -3508,23 +3489,53 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);
|
||||
|
||||
/*
|
||||
* supported power states check
|
||||
*/
|
||||
static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, hda_nid_t fg,
|
||||
unsigned int power_state)
|
||||
{
|
||||
int sup = snd_hda_param_read(codec, fg, AC_PAR_POWER_STATE);
|
||||
|
||||
if (sup < 0)
|
||||
return false;
|
||||
if (sup & power_state)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* set power state of the codec
|
||||
*/
|
||||
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
|
||||
unsigned int power_state)
|
||||
{
|
||||
int count;
|
||||
unsigned int 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);
|
||||
if (power_state == AC_PWRST_D3) {
|
||||
/* transition time less than 10ms for power down */
|
||||
bool epss = snd_hda_codec_get_supported_ps(codec, fg, AC_PWRST_EPSS);
|
||||
msleep(epss ? 10 : 100);
|
||||
}
|
||||
|
||||
/* repeat power states setting at most 10 times*/
|
||||
for (count = 0; count < 10; count++) {
|
||||
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);
|
||||
state = snd_hda_codec_read(codec, fg, 0,
|
||||
AC_VERB_GET_POWER_STATE, 0);
|
||||
if (!(state & AC_PWRST_ERROR))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_HDA_HWDEP
|
||||
@@ -4418,6 +4429,13 @@ static void __snd_hda_power_up(struct hda_codec *codec, bool wait_power_down)
|
||||
cancel_delayed_work_sync(&codec->power_work);
|
||||
|
||||
spin_lock(&codec->power_lock);
|
||||
/* If the power down delayed work was cancelled above before starting,
|
||||
* then there is no need to go through power up here.
|
||||
*/
|
||||
if (codec->power_on) {
|
||||
spin_unlock(&codec->power_lock);
|
||||
return;
|
||||
}
|
||||
trace_hda_power_up(codec);
|
||||
snd_hda_update_power_acct(codec);
|
||||
codec->power_on = 1;
|
||||
|
@@ -323,6 +323,9 @@ enum {
|
||||
#define AC_PWRST_D1 0x01
|
||||
#define AC_PWRST_D2 0x02
|
||||
#define AC_PWRST_D3 0x03
|
||||
#define AC_PWRST_ERROR (1<<8)
|
||||
#define AC_PWRST_CLK_STOP_OK (1<<9)
|
||||
#define AC_PWRST_SETTING_RESET (1<<10)
|
||||
|
||||
/* Processing capabilies */
|
||||
#define AC_PCAP_BENIGN (1<<0)
|
||||
|
@@ -72,7 +72,7 @@ static int enable_msi = -1;
|
||||
static char *patch[SNDRV_CARDS];
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
static int beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
|
||||
static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
|
||||
CONFIG_SND_HDA_INPUT_BEEP_MODE};
|
||||
#endif
|
||||
|
||||
@@ -103,9 +103,9 @@ module_param_array(patch, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
|
||||
#endif
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
module_param_array(beep_mode, int, NULL, 0444);
|
||||
module_param_array(beep_mode, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
|
||||
"(0=off, 1=on, 2=mute switch on/off) (default=1).");
|
||||
"(0=off, 1=on) (default=1).");
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
@@ -151,6 +151,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
|
||||
"{Intel, CPT},"
|
||||
"{Intel, PPT},"
|
||||
"{Intel, LPT},"
|
||||
"{Intel, HPT},"
|
||||
"{Intel, PBG},"
|
||||
"{Intel, SCH},"
|
||||
"{ATI, SB450},"
|
||||
@@ -535,6 +536,7 @@ enum {
|
||||
#define AZX_DCAPS_BUFSIZE (1 << 21) /* no buffer size alignment */
|
||||
#define AZX_DCAPS_ALIGN_BUFSIZE (1 << 22) /* buffer size alignment */
|
||||
#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */
|
||||
#define AZX_DCAPS_POSFIX_COMBO (1 << 24) /* Use COMBO as default */
|
||||
|
||||
/* quirks for ATI SB / AMD Hudson */
|
||||
#define AZX_DCAPS_PRESET_ATI_SB \
|
||||
@@ -2731,6 +2733,10 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
|
||||
snd_printd(SFX "Using LPIB position fix\n");
|
||||
return POS_FIX_LPIB;
|
||||
}
|
||||
if (chip->driver_caps & AZX_DCAPS_POSFIX_COMBO) {
|
||||
snd_printd(SFX "Using COMBO position fix\n");
|
||||
return POS_FIX_COMBO;
|
||||
}
|
||||
return POS_FIX_AUTO;
|
||||
}
|
||||
|
||||
@@ -3243,7 +3249,7 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
|
||||
/* CPT */
|
||||
{ PCI_DEVICE(0x8086, 0x1c20),
|
||||
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
|
||||
AZX_DCAPS_BUFSIZE },
|
||||
AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
|
||||
/* PBG */
|
||||
{ PCI_DEVICE(0x8086, 0x1d20),
|
||||
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
|
||||
@@ -3251,11 +3257,15 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
|
||||
/* Panther Point */
|
||||
{ PCI_DEVICE(0x8086, 0x1e20),
|
||||
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
|
||||
AZX_DCAPS_BUFSIZE},
|
||||
AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
|
||||
/* Lynx Point */
|
||||
{ PCI_DEVICE(0x8086, 0x8c20),
|
||||
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
|
||||
AZX_DCAPS_BUFSIZE},
|
||||
AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
|
||||
/* Haswell */
|
||||
{ PCI_DEVICE(0x8086, 0x0c0c),
|
||||
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
|
||||
AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
|
||||
/* SCH */
|
||||
{ PCI_DEVICE(0x8086, 0x811b),
|
||||
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
|
||||
@@ -3341,6 +3351,10 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
|
||||
/* VIA VT8251/VT8237A */
|
||||
{ PCI_DEVICE(0x1106, 0x3288),
|
||||
.driver_data = AZX_DRIVER_VIA | AZX_DCAPS_POSFIX_VIA },
|
||||
/* VIA GFX VT7122/VX900 */
|
||||
{ PCI_DEVICE(0x1106, 0x9170), .driver_data = AZX_DRIVER_GENERIC },
|
||||
/* VIA GFX VT6122/VX11 */
|
||||
{ PCI_DEVICE(0x1106, 0x9140), .driver_data = AZX_DRIVER_GENERIC },
|
||||
/* SIS966 */
|
||||
{ PCI_DEVICE(0x1039, 0x7502), .driver_data = AZX_DRIVER_SIS },
|
||||
/* ULI M5461 */
|
||||
|
@@ -127,10 +127,15 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec)
|
||||
static void jack_detect_update(struct hda_codec *codec,
|
||||
struct hda_jack_tbl *jack)
|
||||
{
|
||||
if (jack->jack_dirty || !jack->jack_detect) {
|
||||
if (!jack->jack_dirty)
|
||||
return;
|
||||
|
||||
if (jack->phantom_jack)
|
||||
jack->pin_sense = AC_PINSENSE_PRESENCE;
|
||||
else
|
||||
jack->pin_sense = read_pin_sense(codec, jack->nid);
|
||||
jack->jack_dirty = 0;
|
||||
}
|
||||
|
||||
jack->jack_dirty = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,8 +269,8 @@ static void hda_free_jack_priv(struct snd_jack *jack)
|
||||
* This assigns a jack-detection kctl to the given pin. The kcontrol
|
||||
* will have the given name and index.
|
||||
*/
|
||||
int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
|
||||
const char *name, int idx)
|
||||
static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
|
||||
const char *name, int idx, bool phantom_jack)
|
||||
{
|
||||
struct hda_jack_tbl *jack;
|
||||
struct snd_kcontrol *kctl;
|
||||
@@ -283,47 +288,81 @@ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
|
||||
if (err < 0)
|
||||
return err;
|
||||
jack->kctl = kctl;
|
||||
jack->phantom_jack = !!phantom_jack;
|
||||
|
||||
state = snd_hda_jack_detect(codec, nid);
|
||||
snd_kctl_jack_report(codec->bus->card, kctl, state);
|
||||
#ifdef CONFIG_SND_HDA_INPUT_JACK
|
||||
jack->type = get_input_jack_type(codec, nid);
|
||||
err = snd_jack_new(codec->bus->card, name, jack->type, &jack->jack);
|
||||
if (err < 0)
|
||||
return err;
|
||||
jack->jack->private_data = jack;
|
||||
jack->jack->private_free = hda_free_jack_priv;
|
||||
snd_jack_report(jack->jack, state ? jack->type : 0);
|
||||
if (!phantom_jack) {
|
||||
jack->type = get_input_jack_type(codec, nid);
|
||||
err = snd_jack_new(codec->bus->card, name, jack->type,
|
||||
&jack->jack);
|
||||
if (err < 0)
|
||||
return err;
|
||||
jack->jack->private_data = jack;
|
||||
jack->jack->private_free = hda_free_jack_priv;
|
||||
snd_jack_report(jack->jack, state ? jack->type : 0);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid,
|
||||
const char *name, int idx)
|
||||
{
|
||||
return __snd_hda_jack_add_kctl(codec, nid, name, idx, false);
|
||||
}
|
||||
EXPORT_SYMBOL_HDA(snd_hda_jack_add_kctl);
|
||||
|
||||
/* get the unique index number for the given kctl name */
|
||||
static int get_unique_index(struct hda_codec *codec, const char *name, int idx)
|
||||
{
|
||||
struct hda_jack_tbl *jack;
|
||||
int i, len = strlen(name);
|
||||
again:
|
||||
jack = codec->jacktbl.list;
|
||||
for (i = 0; i < codec->jacktbl.used; i++, jack++) {
|
||||
/* jack->kctl.id contains "XXX Jack" name string with index */
|
||||
if (jack->kctl &&
|
||||
!strncmp(name, jack->kctl->id.name, len) &&
|
||||
!strcmp(" Jack", jack->kctl->id.name + len) &&
|
||||
jack->kctl->id.index == idx) {
|
||||
idx++;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid,
|
||||
const struct auto_pin_cfg *cfg,
|
||||
char *lastname, int *lastidx)
|
||||
const struct auto_pin_cfg *cfg)
|
||||
{
|
||||
unsigned int def_conf, conn;
|
||||
char name[44];
|
||||
int idx, err;
|
||||
bool phantom_jack;
|
||||
|
||||
if (!nid)
|
||||
return 0;
|
||||
if (!is_jack_detectable(codec, nid))
|
||||
return 0;
|
||||
def_conf = snd_hda_codec_get_pincfg(codec, nid);
|
||||
conn = get_defcfg_connect(def_conf);
|
||||
if (conn != AC_JACK_PORT_COMPLEX)
|
||||
if (conn == AC_JACK_PORT_NONE)
|
||||
return 0;
|
||||
phantom_jack = (conn != AC_JACK_PORT_COMPLEX) ||
|
||||
!is_jack_detectable(codec, nid);
|
||||
|
||||
snd_hda_get_pin_label(codec, nid, cfg, name, sizeof(name), &idx);
|
||||
if (!strcmp(name, lastname) && idx == *lastidx)
|
||||
idx++;
|
||||
strncpy(lastname, name, 44);
|
||||
*lastidx = idx;
|
||||
err = snd_hda_jack_add_kctl(codec, nid, name, idx);
|
||||
if (phantom_jack)
|
||||
/* Example final name: "Internal Mic Phantom Jack" */
|
||||
strncat(name, " Phantom", sizeof(name) - strlen(name) - 1);
|
||||
idx = get_unique_index(codec, name, idx);
|
||||
err = __snd_hda_jack_add_kctl(codec, nid, name, idx, phantom_jack);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return snd_hda_jack_detect_enable(codec, nid, 0);
|
||||
|
||||
if (!phantom_jack)
|
||||
return snd_hda_jack_detect_enable(codec, nid, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,42 +372,41 @@ int snd_hda_jack_add_kctls(struct hda_codec *codec,
|
||||
const struct auto_pin_cfg *cfg)
|
||||
{
|
||||
const hda_nid_t *p;
|
||||
int i, err, lastidx = 0;
|
||||
char lastname[44] = "";
|
||||
int i, err;
|
||||
|
||||
for (i = 0, p = cfg->line_out_pins; i < cfg->line_outs; i++, p++) {
|
||||
err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx);
|
||||
err = add_jack_kctl(codec, *p, cfg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
for (i = 0, p = cfg->hp_pins; i < cfg->hp_outs; i++, p++) {
|
||||
if (*p == *cfg->line_out_pins) /* might be duplicated */
|
||||
break;
|
||||
err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx);
|
||||
err = add_jack_kctl(codec, *p, cfg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
for (i = 0, p = cfg->speaker_pins; i < cfg->speaker_outs; i++, p++) {
|
||||
if (*p == *cfg->line_out_pins) /* might be duplicated */
|
||||
break;
|
||||
err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx);
|
||||
err = add_jack_kctl(codec, *p, cfg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
for (i = 0; i < cfg->num_inputs; i++) {
|
||||
err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg, lastname, &lastidx);
|
||||
err = add_jack_kctl(codec, cfg->inputs[i].pin, cfg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
for (i = 0, p = cfg->dig_out_pins; i < cfg->dig_outs; i++, p++) {
|
||||
err = add_jack_kctl(codec, *p, cfg, lastname, &lastidx);
|
||||
err = add_jack_kctl(codec, *p, cfg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
err = add_jack_kctl(codec, cfg->dig_in_pin, cfg, lastname, &lastidx);
|
||||
err = add_jack_kctl(codec, cfg->dig_in_pin, cfg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = add_jack_kctl(codec, cfg->mono_out_pin, cfg, lastname, &lastidx);
|
||||
err = add_jack_kctl(codec, cfg->mono_out_pin, cfg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
|
@@ -23,6 +23,7 @@ struct hda_jack_tbl {
|
||||
unsigned int pin_sense; /* cached pin-sense value */
|
||||
unsigned int jack_detect:1; /* capable of jack-detection? */
|
||||
unsigned int jack_dirty:1; /* needs to update? */
|
||||
unsigned int phantom_jack:1; /* a fixed, always present port? */
|
||||
struct snd_kcontrol *kctl; /* assigned kctl for jack-detection */
|
||||
#ifdef CONFIG_SND_HDA_INPUT_JACK
|
||||
int type;
|
||||
|
@@ -89,7 +89,7 @@
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
|
||||
.subdevice = HDA_SUBDEV_AMP_FLAG, \
|
||||
.info = snd_hda_mixer_amp_switch_info, \
|
||||
.get = snd_hda_mixer_amp_switch_get, \
|
||||
.get = snd_hda_mixer_amp_switch_get_beep, \
|
||||
.put = snd_hda_mixer_amp_switch_put_beep, \
|
||||
.private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
|
||||
#else
|
||||
@@ -121,6 +121,8 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
|
||||
int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
#ifdef CONFIG_SND_HDA_INPUT_BEEP
|
||||
int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
#endif
|
||||
|
@@ -426,10 +426,10 @@ static void print_digital_conv(struct snd_info_buffer *buffer,
|
||||
|
||||
static const char *get_pwr_state(u32 state)
|
||||
{
|
||||
static const char * const buf[4] = {
|
||||
"D0", "D1", "D2", "D3"
|
||||
static const char * const buf[] = {
|
||||
"D0", "D1", "D2", "D3", "D3cold"
|
||||
};
|
||||
if (state < 4)
|
||||
if (state < ARRAY_SIZE(buf))
|
||||
return buf[state];
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@@ -451,14 +451,21 @@ static void print_power_state(struct snd_info_buffer *buffer,
|
||||
int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE);
|
||||
int pwr = snd_hda_codec_read(codec, nid, 0,
|
||||
AC_VERB_GET_POWER_STATE, 0);
|
||||
if (sup)
|
||||
if (sup != -1)
|
||||
snd_iprintf(buffer, " Power states: %s\n",
|
||||
bits_names(sup, names, ARRAY_SIZE(names)));
|
||||
|
||||
snd_iprintf(buffer, " Power: setting=%s, actual=%s\n",
|
||||
snd_iprintf(buffer, " Power: setting=%s, actual=%s",
|
||||
get_pwr_state(pwr & AC_PWRST_SETTING),
|
||||
get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
|
||||
AC_PWRST_ACTUAL_SHIFT));
|
||||
if (pwr & AC_PWRST_ERROR)
|
||||
snd_iprintf(buffer, ", Error");
|
||||
if (pwr & AC_PWRST_CLK_STOP_OK)
|
||||
snd_iprintf(buffer, ", Clock-stop-OK");
|
||||
if (pwr & AC_PWRST_SETTING_RESET)
|
||||
snd_iprintf(buffer, ", Setting-reset");
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
|
||||
static void print_unsol_cap(struct snd_info_buffer *buffer,
|
||||
|
@@ -85,7 +85,7 @@ struct hdmi_spec {
|
||||
* Non-generic ATI/NVIDIA specific
|
||||
*/
|
||||
struct hda_multi_out multiout;
|
||||
const struct hda_pcm_stream *pcm_playback;
|
||||
struct hda_pcm_stream pcm_playback;
|
||||
};
|
||||
|
||||
|
||||
@@ -787,7 +787,7 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
||||
int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
|
||||
|
||||
printk(KERN_INFO
|
||||
"HDMI CP event: CODEC=%d PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
|
||||
"HDMI CP event: CODEC=%d TAG=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
|
||||
codec->addr,
|
||||
tag,
|
||||
subtag,
|
||||
@@ -1277,6 +1277,22 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generic_hdmi_init_per_pins(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int pin_idx;
|
||||
|
||||
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
||||
struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
|
||||
struct hdmi_eld *eld = &per_pin->sink_eld;
|
||||
|
||||
per_pin->codec = codec;
|
||||
INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld);
|
||||
snd_hda_eld_proc_new(codec, eld, pin_idx);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generic_hdmi_init(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
@@ -1285,14 +1301,9 @@ static int generic_hdmi_init(struct hda_codec *codec)
|
||||
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
|
||||
struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
|
||||
hda_nid_t pin_nid = per_pin->pin_nid;
|
||||
struct hdmi_eld *eld = &per_pin->sink_eld;
|
||||
|
||||
hdmi_init_pin(codec, pin_nid);
|
||||
snd_hda_jack_detect_enable(codec, pin_nid, pin_nid);
|
||||
|
||||
per_pin->codec = codec;
|
||||
INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld);
|
||||
snd_hda_eld_proc_new(codec, eld, pin_idx);
|
||||
}
|
||||
snd_hda_jack_report_sync(codec);
|
||||
return 0;
|
||||
@@ -1338,6 +1349,7 @@ static int patch_generic_hdmi(struct hda_codec *codec)
|
||||
return -EINVAL;
|
||||
}
|
||||
codec->patch_ops = generic_hdmi_patch_ops;
|
||||
generic_hdmi_init_per_pins(codec);
|
||||
|
||||
init_channel_allocations();
|
||||
|
||||
@@ -1352,45 +1364,65 @@ static int simple_playback_build_pcms(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
struct hda_pcm *info = spec->pcm_rec;
|
||||
int i;
|
||||
unsigned int chans;
|
||||
struct hda_pcm_stream *pstr;
|
||||
|
||||
codec->num_pcms = spec->num_cvts;
|
||||
codec->num_pcms = 1;
|
||||
codec->pcm_info = info;
|
||||
|
||||
for (i = 0; i < codec->num_pcms; i++, info++) {
|
||||
unsigned int chans;
|
||||
struct hda_pcm_stream *pstr;
|
||||
chans = get_wcaps(codec, spec->cvts[0].cvt_nid);
|
||||
chans = get_wcaps_channels(chans);
|
||||
|
||||
chans = get_wcaps(codec, spec->cvts[i].cvt_nid);
|
||||
chans = get_wcaps_channels(chans);
|
||||
|
||||
info->name = get_hdmi_pcm_name(i);
|
||||
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
||||
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
|
||||
snd_BUG_ON(!spec->pcm_playback);
|
||||
*pstr = *spec->pcm_playback;
|
||||
pstr->nid = spec->cvts[i].cvt_nid;
|
||||
if (pstr->channels_max <= 2 && chans && chans <= 16)
|
||||
pstr->channels_max = chans;
|
||||
}
|
||||
info->name = get_hdmi_pcm_name(0);
|
||||
info->pcm_type = HDA_PCM_TYPE_HDMI;
|
||||
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
|
||||
*pstr = spec->pcm_playback;
|
||||
pstr->nid = spec->cvts[0].cvt_nid;
|
||||
if (pstr->channels_max <= 2 && chans && chans <= 16)
|
||||
pstr->channels_max = chans;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* unsolicited event for jack sensing */
|
||||
static void simple_hdmi_unsol_event(struct hda_codec *codec,
|
||||
unsigned int res)
|
||||
{
|
||||
snd_hda_jack_set_dirty_all(codec);
|
||||
snd_hda_jack_report_sync(codec);
|
||||
}
|
||||
|
||||
/* generic_hdmi_build_jack can be used for simple_hdmi, too,
|
||||
* as long as spec->pins[] is set correctly
|
||||
*/
|
||||
#define simple_hdmi_build_jack generic_hdmi_build_jack
|
||||
|
||||
static int simple_playback_build_controls(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < codec->num_pcms; i++) {
|
||||
err = snd_hda_create_spdif_out_ctls(codec,
|
||||
spec->cvts[i].cvt_nid,
|
||||
spec->cvts[i].cvt_nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
err = snd_hda_create_spdif_out_ctls(codec,
|
||||
spec->cvts[0].cvt_nid,
|
||||
spec->cvts[0].cvt_nid);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return simple_hdmi_build_jack(codec, 0);
|
||||
}
|
||||
|
||||
static int simple_playback_init(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
hda_nid_t pin = spec->pins[0].pin_nid;
|
||||
|
||||
snd_hda_codec_write(codec, pin, 0,
|
||||
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
|
||||
/* some codecs require to unmute the pin */
|
||||
if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
|
||||
snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
|
||||
AMP_OUT_UNMUTE);
|
||||
snd_hda_jack_detect_enable(codec, pin, pin);
|
||||
snd_hda_jack_report_sync(codec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1418,7 +1450,15 @@ static const hda_nid_t nvhdmi_con_nids_7x[4] = {
|
||||
0x6, 0x8, 0xa, 0xc,
|
||||
};
|
||||
|
||||
static const struct hda_verb nvhdmi_basic_init_7x[] = {
|
||||
static const struct hda_verb nvhdmi_basic_init_7x_2ch[] = {
|
||||
/* set audio protect on */
|
||||
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
|
||||
/* enable digital output on pin widget */
|
||||
{ 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
static const struct hda_verb nvhdmi_basic_init_7x_8ch[] = {
|
||||
/* set audio protect on */
|
||||
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
|
||||
/* enable digital output on pin widget */
|
||||
@@ -1446,9 +1486,15 @@ static const struct hda_verb nvhdmi_basic_init_7x[] = {
|
||||
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
|
||||
#endif
|
||||
|
||||
static int nvhdmi_7x_init(struct hda_codec *codec)
|
||||
static int nvhdmi_7x_init_2ch(struct hda_codec *codec)
|
||||
{
|
||||
snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
|
||||
snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvhdmi_7x_init_8ch(struct hda_codec *codec)
|
||||
{
|
||||
snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1524,6 +1570,50 @@ static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
stream_tag, format, substream);
|
||||
}
|
||||
|
||||
static const struct hda_pcm_stream simple_pcm_playback = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.ops = {
|
||||
.open = simple_playback_pcm_open,
|
||||
.close = simple_playback_pcm_close,
|
||||
.prepare = simple_playback_pcm_prepare
|
||||
},
|
||||
};
|
||||
|
||||
static const struct hda_codec_ops simple_hdmi_patch_ops = {
|
||||
.build_controls = simple_playback_build_controls,
|
||||
.build_pcms = simple_playback_build_pcms,
|
||||
.init = simple_playback_init,
|
||||
.free = simple_playback_free,
|
||||
.unsol_event = simple_hdmi_unsol_event,
|
||||
};
|
||||
|
||||
static int patch_simple_hdmi(struct hda_codec *codec,
|
||||
hda_nid_t cvt_nid, hda_nid_t pin_nid)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (!spec)
|
||||
return -ENOMEM;
|
||||
|
||||
codec->spec = spec;
|
||||
|
||||
spec->multiout.num_dacs = 0; /* no analog */
|
||||
spec->multiout.max_channels = 2;
|
||||
spec->multiout.dig_out_nid = cvt_nid;
|
||||
spec->num_cvts = 1;
|
||||
spec->num_pins = 1;
|
||||
spec->cvts[0].cvt_nid = cvt_nid;
|
||||
spec->pins[0].pin_nid = pin_nid;
|
||||
spec->pcm_playback = simple_pcm_playback;
|
||||
|
||||
codec->patch_ops = simple_hdmi_patch_ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec,
|
||||
int channels)
|
||||
{
|
||||
@@ -1696,54 +1786,20 @@ static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.nid = nvhdmi_master_con_nid_7x,
|
||||
.rates = SUPPORTED_RATES,
|
||||
.maxbps = SUPPORTED_MAXBPS,
|
||||
.formats = SUPPORTED_FORMATS,
|
||||
.ops = {
|
||||
.open = simple_playback_pcm_open,
|
||||
.close = simple_playback_pcm_close,
|
||||
.prepare = simple_playback_pcm_prepare
|
||||
},
|
||||
};
|
||||
|
||||
static const struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
|
||||
.build_controls = simple_playback_build_controls,
|
||||
.build_pcms = simple_playback_build_pcms,
|
||||
.init = nvhdmi_7x_init,
|
||||
.free = simple_playback_free,
|
||||
};
|
||||
|
||||
static const struct hda_codec_ops nvhdmi_patch_ops_2ch = {
|
||||
.build_controls = simple_playback_build_controls,
|
||||
.build_pcms = simple_playback_build_pcms,
|
||||
.init = nvhdmi_7x_init,
|
||||
.free = simple_playback_free,
|
||||
};
|
||||
|
||||
static int patch_nvhdmi_2ch(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
int err = patch_simple_hdmi(codec, nvhdmi_master_con_nid_7x,
|
||||
nvhdmi_master_pin_nid_7x);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (spec == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
codec->spec = spec;
|
||||
|
||||
spec->multiout.num_dacs = 0; /* no analog */
|
||||
spec->multiout.max_channels = 2;
|
||||
spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
|
||||
spec->num_cvts = 1;
|
||||
spec->cvts[0].cvt_nid = nvhdmi_master_con_nid_7x;
|
||||
spec->pcm_playback = &nvhdmi_pcm_playback_2ch;
|
||||
|
||||
codec->patch_ops = nvhdmi_patch_ops_2ch;
|
||||
|
||||
codec->patch_ops.init = nvhdmi_7x_init_2ch;
|
||||
/* override the PCM rates, etc, as the codec doesn't give full list */
|
||||
spec = codec->spec;
|
||||
spec->pcm_playback.rates = SUPPORTED_RATES;
|
||||
spec->pcm_playback.maxbps = SUPPORTED_MAXBPS;
|
||||
spec->pcm_playback.formats = SUPPORTED_FORMATS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1751,13 +1807,12 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
int err = patch_nvhdmi_2ch(codec);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
spec = codec->spec;
|
||||
spec->multiout.max_channels = 8;
|
||||
spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x;
|
||||
codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
|
||||
spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x;
|
||||
codec->patch_ops.init = nvhdmi_7x_init_8ch;
|
||||
|
||||
/* Initialize the audio infoframe channel mask and checksum to something
|
||||
* valid */
|
||||
@@ -1801,68 +1856,25 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hda_pcm_stream atihdmi_pcm_digital_playback = {
|
||||
.substreams = 1,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.nid = ATIHDMI_CVT_NID,
|
||||
.ops = {
|
||||
.open = simple_playback_pcm_open,
|
||||
.close = simple_playback_pcm_close,
|
||||
.prepare = atihdmi_playback_pcm_prepare
|
||||
},
|
||||
};
|
||||
|
||||
static const struct hda_verb atihdmi_basic_init[] = {
|
||||
/* enable digital output on pin widget */
|
||||
{ 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
|
||||
{} /* terminator */
|
||||
};
|
||||
|
||||
static int atihdmi_init(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec = codec->spec;
|
||||
|
||||
snd_hda_sequence_write(codec, atihdmi_basic_init);
|
||||
/* SI codec requires to unmute the pin */
|
||||
if (get_wcaps(codec, spec->pins[0].pin_nid) & AC_WCAP_OUT_AMP)
|
||||
snd_hda_codec_write(codec, spec->pins[0].pin_nid, 0,
|
||||
AC_VERB_SET_AMP_GAIN_MUTE,
|
||||
AMP_OUT_UNMUTE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hda_codec_ops atihdmi_patch_ops = {
|
||||
.build_controls = simple_playback_build_controls,
|
||||
.build_pcms = simple_playback_build_pcms,
|
||||
.init = atihdmi_init,
|
||||
.free = simple_playback_free,
|
||||
};
|
||||
|
||||
|
||||
static int patch_atihdmi(struct hda_codec *codec)
|
||||
{
|
||||
struct hdmi_spec *spec;
|
||||
|
||||
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
|
||||
if (spec == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
codec->spec = spec;
|
||||
|
||||
spec->multiout.num_dacs = 0; /* no analog */
|
||||
spec->multiout.max_channels = 2;
|
||||
spec->multiout.dig_out_nid = ATIHDMI_CVT_NID;
|
||||
spec->num_cvts = 1;
|
||||
spec->cvts[0].cvt_nid = ATIHDMI_CVT_NID;
|
||||
spec->pins[0].pin_nid = ATIHDMI_PIN_NID;
|
||||
spec->pcm_playback = &atihdmi_pcm_digital_playback;
|
||||
|
||||
codec->patch_ops = atihdmi_patch_ops;
|
||||
|
||||
int err = patch_simple_hdmi(codec, ATIHDMI_CVT_NID, ATIHDMI_PIN_NID);
|
||||
if (err < 0)
|
||||
return err;
|
||||
spec = codec->spec;
|
||||
spec->pcm_playback.ops.prepare = atihdmi_playback_pcm_prepare;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* VIA HDMI Implementation */
|
||||
#define VIAHDMI_CVT_NID 0x02 /* audio converter1 */
|
||||
#define VIAHDMI_PIN_NID 0x03 /* HDMI output pin1 */
|
||||
|
||||
static int patch_via_hdmi(struct hda_codec *codec)
|
||||
{
|
||||
return patch_simple_hdmi(codec, VIAHDMI_CVT_NID, VIAHDMI_PIN_NID);
|
||||
}
|
||||
|
||||
/*
|
||||
* patch entries
|
||||
@@ -1902,8 +1914,13 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
|
||||
{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
|
||||
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
|
||||
{ .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
|
||||
{ .id = 0x11069f81, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
|
||||
{ .id = 0x11069f84, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x11069f85, .name = "VX11 HDMI/DP", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi },
|
||||
@@ -1911,6 +1928,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
|
||||
{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi },
|
||||
{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
|
||||
{} /* terminator */
|
||||
@@ -1948,8 +1966,13 @@ MODULE_ALIAS("snd-hda-codec-id:10de0041");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0042");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0043");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0044");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0051");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de0067");
|
||||
MODULE_ALIAS("snd-hda-codec-id:10de8001");
|
||||
MODULE_ALIAS("snd-hda-codec-id:11069f80");
|
||||
MODULE_ALIAS("snd-hda-codec-id:11069f81");
|
||||
MODULE_ALIAS("snd-hda-codec-id:11069f84");
|
||||
MODULE_ALIAS("snd-hda-codec-id:11069f85");
|
||||
MODULE_ALIAS("snd-hda-codec-id:17e80047");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80860054");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862801");
|
||||
@@ -1958,6 +1981,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862803");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862804");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862805");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862806");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862807");
|
||||
MODULE_ALIAS("snd-hda-codec-id:80862880");
|
||||
MODULE_ALIAS("snd-hda-codec-id:808629fb");
|
||||
|
||||
|
@@ -170,10 +170,10 @@ struct alc_spec {
|
||||
hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
|
||||
unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
|
||||
int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */
|
||||
hda_nid_t inv_dmic_pin;
|
||||
|
||||
/* hooks */
|
||||
void (*init_hook)(struct hda_codec *codec);
|
||||
void (*unsol_event)(struct hda_codec *codec, unsigned int res);
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
void (*power_hook)(struct hda_codec *codec);
|
||||
#endif
|
||||
@@ -201,6 +201,8 @@ struct alc_spec {
|
||||
unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */
|
||||
unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
|
||||
unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
|
||||
unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
|
||||
unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
|
||||
|
||||
/* auto-mute control */
|
||||
int automute_mode;
|
||||
@@ -298,6 +300,39 @@ static inline hda_nid_t get_capsrc(struct alc_spec *spec, int idx)
|
||||
}
|
||||
|
||||
static void call_update_outputs(struct hda_codec *codec);
|
||||
static void alc_inv_dmic_sync(struct hda_codec *codec, bool force);
|
||||
|
||||
/* for shared I/O, change the pin-control accordingly */
|
||||
static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
unsigned int val;
|
||||
hda_nid_t pin = spec->autocfg.inputs[1].pin;
|
||||
/* NOTE: this assumes that there are only two inputs, the
|
||||
* first is the real internal mic and the second is HP/mic jack.
|
||||
*/
|
||||
|
||||
val = snd_hda_get_default_vref(codec, pin);
|
||||
|
||||
/* This pin does not have vref caps - let's enable vref on pin 0x18
|
||||
instead, as suggested by Realtek */
|
||||
if (val == AC_PINCTL_VREF_HIZ) {
|
||||
const hda_nid_t vref_pin = 0x18;
|
||||
/* Sanity check pin 0x18 */
|
||||
if (get_wcaps_type(get_wcaps(codec, vref_pin)) == AC_WID_PIN &&
|
||||
get_defcfg_connect(snd_hda_codec_get_pincfg(codec, vref_pin)) == AC_JACK_PORT_NONE) {
|
||||
unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
|
||||
if (vref_val != AC_PINCTL_VREF_HIZ)
|
||||
snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0));
|
||||
}
|
||||
}
|
||||
|
||||
val = set_as_mic ? val | PIN_IN : PIN_HP;
|
||||
snd_hda_set_pin_ctl(codec, pin, val);
|
||||
|
||||
spec->automute_speaker = !set_as_mic;
|
||||
call_update_outputs(codec);
|
||||
}
|
||||
|
||||
/* select the given imux item; either unmute exclusively or select the route */
|
||||
static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
|
||||
@@ -325,21 +360,8 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
|
||||
return 0;
|
||||
spec->cur_mux[adc_idx] = idx;
|
||||
|
||||
/* for shared I/O, change the pin-control accordingly */
|
||||
if (spec->shared_mic_hp) {
|
||||
unsigned int val;
|
||||
hda_nid_t pin = spec->autocfg.inputs[1].pin;
|
||||
/* NOTE: this assumes that there are only two inputs, the
|
||||
* first is the real internal mic and the second is HP jack.
|
||||
*/
|
||||
if (spec->cur_mux[adc_idx])
|
||||
val = snd_hda_get_default_vref(codec, pin) | PIN_IN;
|
||||
else
|
||||
val = PIN_HP;
|
||||
snd_hda_set_pin_ctl(codec, pin, val);
|
||||
spec->automute_speaker = !spec->cur_mux[adc_idx];
|
||||
call_update_outputs(codec);
|
||||
}
|
||||
if (spec->shared_mic_hp)
|
||||
update_shared_mic_hp(codec, spec->cur_mux[adc_idx]);
|
||||
|
||||
if (spec->dyn_adc_switch) {
|
||||
alc_dyn_adc_pcm_resetup(codec, idx);
|
||||
@@ -368,6 +390,7 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
|
||||
AC_VERB_SET_CONNECT_SEL,
|
||||
imux->items[idx].index);
|
||||
}
|
||||
alc_inv_dmic_sync(codec, true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -664,7 +687,7 @@ static void alc_update_knob_master(struct hda_codec *codec, hda_nid_t nid)
|
||||
}
|
||||
|
||||
/* unsolicited event for HP jack sensing */
|
||||
static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
static void alc_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
int action;
|
||||
|
||||
@@ -1000,11 +1023,9 @@ static void alc_init_automute(struct hda_codec *codec)
|
||||
spec->automute_lo = spec->automute_lo_possible;
|
||||
spec->automute_speaker = spec->automute_speaker_possible;
|
||||
|
||||
if (spec->automute_speaker_possible || spec->automute_lo_possible) {
|
||||
if (spec->automute_speaker_possible || spec->automute_lo_possible)
|
||||
/* create a control for automute mode */
|
||||
alc_add_automute_mode_enum(codec);
|
||||
spec->unsol_event = alc_sku_unsol_event;
|
||||
}
|
||||
}
|
||||
|
||||
/* return the position of NID in the list, or -1 if not found */
|
||||
@@ -1167,7 +1188,6 @@ static void alc_init_auto_mic(struct hda_codec *codec)
|
||||
|
||||
snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n",
|
||||
ext, fixed, dock);
|
||||
spec->unsol_event = alc_sku_unsol_event;
|
||||
}
|
||||
|
||||
/* check the availabilities of auto-mute and auto-mic switches */
|
||||
@@ -1556,14 +1576,14 @@ typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
|
||||
|
||||
static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol,
|
||||
getput_call_t func, bool check_adc_switch)
|
||||
getput_call_t func, bool is_put)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct alc_spec *spec = codec->spec;
|
||||
int i, err = 0;
|
||||
|
||||
mutex_lock(&codec->control_mutex);
|
||||
if (check_adc_switch && spec->dyn_adc_switch) {
|
||||
if (is_put && spec->dyn_adc_switch) {
|
||||
for (i = 0; i < spec->num_adc_nids; i++) {
|
||||
kcontrol->private_value =
|
||||
HDA_COMPOSE_AMP_VAL(spec->adc_nids[i],
|
||||
@@ -1584,6 +1604,8 @@ static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
|
||||
3, 0, HDA_INPUT);
|
||||
err = func(kcontrol, ucontrol);
|
||||
}
|
||||
if (err >= 0 && is_put)
|
||||
alc_inv_dmic_sync(codec, false);
|
||||
error:
|
||||
mutex_unlock(&codec->control_mutex);
|
||||
return err;
|
||||
@@ -1675,6 +1697,116 @@ DEFINE_CAPMIX_NOSRC(1);
|
||||
DEFINE_CAPMIX_NOSRC(2);
|
||||
DEFINE_CAPMIX_NOSRC(3);
|
||||
|
||||
/*
|
||||
* Inverted digital-mic handling
|
||||
*
|
||||
* First off, it's a bit tricky. The "Inverted Internal Mic Capture Switch"
|
||||
* gives the additional mute only to the right channel of the digital mic
|
||||
* capture stream. This is a workaround for avoiding the almost silence
|
||||
* by summing the stereo stream from some (known to be ForteMedia)
|
||||
* digital mic unit.
|
||||
*
|
||||
* The logic is to call alc_inv_dmic_sync() after each action (possibly)
|
||||
* modifying ADC amp. When the mute flag is set, it mutes the R-channel
|
||||
* without caching so that the cache can still keep the original value.
|
||||
* The cached value is then restored when the flag is set off or any other
|
||||
* than d-mic is used as the current input source.
|
||||
*/
|
||||
static void alc_inv_dmic_sync(struct hda_codec *codec, bool force)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
int i;
|
||||
|
||||
if (!spec->inv_dmic_fixup)
|
||||
return;
|
||||
if (!spec->inv_dmic_muted && !force)
|
||||
return;
|
||||
for (i = 0; i < spec->num_adc_nids; i++) {
|
||||
int src = spec->dyn_adc_switch ? 0 : i;
|
||||
bool dmic_fixup = false;
|
||||
hda_nid_t nid;
|
||||
int parm, dir, v;
|
||||
|
||||
if (spec->inv_dmic_muted &&
|
||||
spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin)
|
||||
dmic_fixup = true;
|
||||
if (!dmic_fixup && !force)
|
||||
continue;
|
||||
if (spec->vol_in_capsrc) {
|
||||
nid = spec->capsrc_nids[i];
|
||||
parm = AC_AMP_SET_RIGHT | AC_AMP_SET_OUTPUT;
|
||||
dir = HDA_OUTPUT;
|
||||
} else {
|
||||
nid = spec->adc_nids[i];
|
||||
parm = AC_AMP_SET_RIGHT | AC_AMP_SET_INPUT;
|
||||
dir = HDA_INPUT;
|
||||
}
|
||||
/* we care only right channel */
|
||||
v = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
|
||||
if (v & 0x80) /* if already muted, we don't need to touch */
|
||||
continue;
|
||||
if (dmic_fixup) /* add mute for d-mic */
|
||||
v |= 0x80;
|
||||
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
|
||||
parm | v);
|
||||
}
|
||||
}
|
||||
|
||||
static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
ucontrol->value.integer.value[0] = !spec->inv_dmic_muted;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
struct alc_spec *spec = codec->spec;
|
||||
unsigned int val = !ucontrol->value.integer.value[0];
|
||||
|
||||
if (val == spec->inv_dmic_muted)
|
||||
return 0;
|
||||
spec->inv_dmic_muted = val;
|
||||
alc_inv_dmic_sync(codec, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new alc_inv_dmic_sw = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = alc_inv_dmic_sw_get,
|
||||
.put = alc_inv_dmic_sw_put,
|
||||
};
|
||||
|
||||
static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
struct snd_kcontrol_new *knew = alc_kcontrol_new(spec);
|
||||
if (!knew)
|
||||
return -ENOMEM;
|
||||
*knew = alc_inv_dmic_sw;
|
||||
knew->name = kstrdup("Inverted Internal Mic Capture Switch", GFP_KERNEL);
|
||||
if (!knew->name)
|
||||
return -ENOMEM;
|
||||
spec->inv_dmic_fixup = 1;
|
||||
spec->inv_dmic_muted = 0;
|
||||
spec->inv_dmic_pin = nid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* typically the digital mic is put at node 0x12 */
|
||||
static void alc_fixup_inv_dmic_0x12(struct hda_codec *codec,
|
||||
const struct alc_fixup *fix, int action)
|
||||
{
|
||||
if (action == ALC_FIXUP_ACT_PROBE)
|
||||
alc_add_inv_dmic_mixer(codec, 0x12);
|
||||
}
|
||||
|
||||
/*
|
||||
* virtual master controls
|
||||
*/
|
||||
@@ -1865,13 +1997,31 @@ static int __alc_build_controls(struct hda_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alc_build_controls(struct hda_codec *codec)
|
||||
static int alc_build_jacks(struct hda_codec *codec)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
if (spec->shared_mic_hp) {
|
||||
int err;
|
||||
int nid = spec->autocfg.inputs[1].pin;
|
||||
err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_hda_jack_detect_enable(codec, nid, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return snd_hda_jack_add_kctls(codec, &spec->autocfg);
|
||||
}
|
||||
|
||||
static int alc_build_controls(struct hda_codec *codec)
|
||||
{
|
||||
int err = __alc_build_controls(codec);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
|
||||
|
||||
err = alc_build_jacks(codec);
|
||||
if (err < 0)
|
||||
return err;
|
||||
alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD);
|
||||
@@ -1908,14 +2058,6 @@ static int alc_init(struct hda_codec *codec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alc_unsol_event(struct hda_codec *codec, unsigned int res)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
if (spec->unsol_event)
|
||||
spec->unsol_event(codec, res);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_HDA_POWER_SAVE
|
||||
static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid)
|
||||
{
|
||||
@@ -2317,6 +2459,7 @@ static int alc_resume(struct hda_codec *codec)
|
||||
codec->patch_ops.init(codec);
|
||||
snd_hda_codec_resume_amp(codec);
|
||||
snd_hda_codec_resume_cache(codec);
|
||||
alc_inv_dmic_sync(codec, true);
|
||||
hda_call_check_power_status(codec, 0x01);
|
||||
return 0;
|
||||
}
|
||||
@@ -4116,14 +4259,12 @@ static void set_capture_mixer(struct hda_codec *codec)
|
||||
*/
|
||||
static void alc_auto_init_std(struct hda_codec *codec)
|
||||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
alc_auto_init_multi_out(codec);
|
||||
alc_auto_init_extra_out(codec);
|
||||
alc_auto_init_analog_input(codec);
|
||||
alc_auto_init_input_src(codec);
|
||||
alc_auto_init_digital(codec);
|
||||
if (spec->unsol_event)
|
||||
alc_inithook(codec);
|
||||
alc_inithook(codec);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4724,7 +4865,6 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec,
|
||||
spec->automute_speaker = 1;
|
||||
spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
|
||||
snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT);
|
||||
spec->unsol_event = alc_sku_unsol_event;
|
||||
snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs);
|
||||
}
|
||||
}
|
||||
@@ -4909,6 +5049,7 @@ enum {
|
||||
ALC889_FIXUP_DAC_ROUTE,
|
||||
ALC889_FIXUP_MBP_VREF,
|
||||
ALC889_FIXUP_IMAC91_VREF,
|
||||
ALC882_FIXUP_INV_DMIC,
|
||||
};
|
||||
|
||||
static void alc889_fixup_coef(struct hda_codec *codec,
|
||||
@@ -5212,6 +5353,10 @@ static const struct alc_fixup alc882_fixups[] = {
|
||||
.chained = true,
|
||||
.chain_id = ALC882_FIXUP_GPIO1,
|
||||
},
|
||||
[ALC882_FIXUP_INV_DMIC] = {
|
||||
.type = ALC_FIXUP_FUNC,
|
||||
.v.func = alc_fixup_inv_dmic_0x12,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk alc882_fixup_tbl[] = {
|
||||
@@ -5286,6 +5431,7 @@ static const struct alc_model_fixup alc882_fixup_models[] = {
|
||||
{.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"},
|
||||
{.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"},
|
||||
{.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"},
|
||||
{.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"},
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -5373,6 +5519,7 @@ enum {
|
||||
ALC262_FIXUP_LENOVO_3000,
|
||||
ALC262_FIXUP_BENQ,
|
||||
ALC262_FIXUP_BENQ_T31,
|
||||
ALC262_FIXUP_INV_DMIC,
|
||||
};
|
||||
|
||||
static const struct alc_fixup alc262_fixups[] = {
|
||||
@@ -5424,6 +5571,10 @@ static const struct alc_fixup alc262_fixups[] = {
|
||||
{}
|
||||
}
|
||||
},
|
||||
[ALC262_FIXUP_INV_DMIC] = {
|
||||
.type = ALC_FIXUP_FUNC,
|
||||
.v.func = alc_fixup_inv_dmic_0x12,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk alc262_fixup_tbl[] = {
|
||||
@@ -5438,6 +5589,10 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct alc_model_fixup alc262_fixup_models[] = {
|
||||
{.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"},
|
||||
{}
|
||||
};
|
||||
|
||||
/*
|
||||
*/
|
||||
@@ -5466,7 +5621,8 @@ static int patch_alc262(struct hda_codec *codec)
|
||||
#endif
|
||||
alc_fix_pll_init(codec, 0x20, 0x0a, 10);
|
||||
|
||||
alc_pick_fixup(codec, NULL, alc262_fixup_tbl, alc262_fixups);
|
||||
alc_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl,
|
||||
alc262_fixups);
|
||||
alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
|
||||
|
||||
alc_auto_parse_customize_define(codec);
|
||||
@@ -5522,6 +5678,22 @@ static const struct hda_verb alc268_beep_init_verbs[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
enum {
|
||||
ALC268_FIXUP_INV_DMIC,
|
||||
};
|
||||
|
||||
static const struct alc_fixup alc268_fixups[] = {
|
||||
[ALC268_FIXUP_INV_DMIC] = {
|
||||
.type = ALC_FIXUP_FUNC,
|
||||
.v.func = alc_fixup_inv_dmic_0x12,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct alc_model_fixup alc268_fixup_models[] = {
|
||||
{.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"},
|
||||
{}
|
||||
};
|
||||
|
||||
/*
|
||||
* BIOS auto configuration
|
||||
*/
|
||||
@@ -5553,6 +5725,9 @@ static int patch_alc268(struct hda_codec *codec)
|
||||
|
||||
spec = codec->spec;
|
||||
|
||||
alc_pick_fixup(codec, alc268_fixup_models, NULL, alc268_fixups);
|
||||
alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
|
||||
|
||||
/* automatic parse from the BIOS config */
|
||||
err = alc268_parse_auto_config(codec);
|
||||
if (err < 0)
|
||||
@@ -5582,6 +5757,8 @@ static int patch_alc268(struct hda_codec *codec)
|
||||
codec->patch_ops = alc_patch_ops;
|
||||
spec->shutup = alc_eapd_shutup;
|
||||
|
||||
alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
@@ -5810,6 +5987,7 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum {
|
||||
ALC269_FIXUP_SONY_VAIO,
|
||||
ALC275_FIXUP_SONY_VAIO_GPIO2,
|
||||
@@ -5828,6 +6006,7 @@ enum {
|
||||
ALC269VB_FIXUP_AMIC,
|
||||
ALC269VB_FIXUP_DMIC,
|
||||
ALC269_FIXUP_MIC2_MUTE_LED,
|
||||
ALC269_FIXUP_INV_DMIC,
|
||||
};
|
||||
|
||||
static const struct alc_fixup alc269_fixups[] = {
|
||||
@@ -5952,12 +6131,19 @@ static const struct alc_fixup alc269_fixups[] = {
|
||||
.type = ALC_FIXUP_FUNC,
|
||||
.v.func = alc269_fixup_mic2_mute,
|
||||
},
|
||||
[ALC269_FIXUP_INV_DMIC] = {
|
||||
.type = ALC_FIXUP_FUNC,
|
||||
.v.func = alc_fixup_inv_dmic_0x12,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC),
|
||||
SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
|
||||
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED),
|
||||
SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC),
|
||||
SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
|
||||
SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC),
|
||||
SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
|
||||
SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC),
|
||||
SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC),
|
||||
@@ -6033,6 +6219,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
static const struct alc_model_fixup alc269_fixup_models[] = {
|
||||
{.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"},
|
||||
{.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"},
|
||||
{.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"},
|
||||
{.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"},
|
||||
{.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"},
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -6329,12 +6518,6 @@ static const struct snd_pci_quirk alc861vd_fixup_tbl[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct hda_verb alc660vd_eapd_verbs[] = {
|
||||
{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
|
||||
{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
*/
|
||||
static int patch_alc861vd(struct hda_codec *codec)
|
||||
@@ -6356,11 +6539,6 @@ static int patch_alc861vd(struct hda_codec *codec)
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (codec->vendor_id == 0x10ec0660) {
|
||||
/* always turn on EAPD */
|
||||
snd_hda_gen_add_verbs(&spec->gen, alc660vd_eapd_verbs);
|
||||
}
|
||||
|
||||
if (!spec->no_analog) {
|
||||
err = snd_hda_attach_beep_device(codec, 0x23);
|
||||
if (err < 0)
|
||||
@@ -6443,6 +6621,7 @@ enum {
|
||||
ALC662_FIXUP_ASUS_MODE8,
|
||||
ALC662_FIXUP_NO_JACK_DETECT,
|
||||
ALC662_FIXUP_ZOTAC_Z68,
|
||||
ALC662_FIXUP_INV_DMIC,
|
||||
};
|
||||
|
||||
static const struct alc_fixup alc662_fixups[] = {
|
||||
@@ -6599,12 +6778,17 @@ static const struct alc_fixup alc662_fixups[] = {
|
||||
{ }
|
||||
}
|
||||
},
|
||||
[ALC662_FIXUP_INV_DMIC] = {
|
||||
.type = ALC_FIXUP_FUNC,
|
||||
.v.func = alc_fixup_inv_dmic_0x12,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2),
|
||||
SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
|
||||
SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),
|
||||
SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),
|
||||
SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
|
||||
SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
|
||||
SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
|
||||
@@ -6685,6 +6869,7 @@ static const struct alc_model_fixup alc662_fixup_models[] = {
|
||||
{.id = ALC662_FIXUP_ASUS_MODE6, .name = "asus-mode6"},
|
||||
{.id = ALC662_FIXUP_ASUS_MODE7, .name = "asus-mode7"},
|
||||
{.id = ALC662_FIXUP_ASUS_MODE8, .name = "asus-mode8"},
|
||||
{.id = ALC662_FIXUP_INV_DMIC, .name = "inv-dmic"},
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -6831,6 +7016,7 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = {
|
||||
{ .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
|
||||
{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
|
||||
.patch = patch_alc861 },
|
||||
{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
|
||||
|
Reference in New Issue
Block a user