summaryrefslogtreecommitdiff
path: root/sound/pci
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci')
-rw-r--r--sound/pci/ac97/ac97_codec.c16
-rw-r--r--sound/pci/ac97/ac97_pcm.c10
-rw-r--r--sound/pci/hda/hda_codec.c17
-rw-r--r--sound/pci/hda/hda_generic.c356
-rw-r--r--sound/pci/hda/hda_generic.h10
-rw-r--r--sound/pci/hda/hda_local.h4
-rw-r--r--sound/pci/hda/patch_conexant.c1
-rw-r--r--sound/pci/hda/patch_hdmi.c198
-rw-r--r--sound/pci/hda/patch_realtek.c35
-rw-r--r--sound/pci/hda/patch_sigmatel.c1
-rw-r--r--sound/pci/rme9652/hdspm.c382
11 files changed, 698 insertions, 332 deletions
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
index 8b0f996..d37c683 100644
--- a/sound/pci/ac97/ac97_codec.c
+++ b/sound/pci/ac97/ac97_codec.c
@@ -299,7 +299,7 @@ EXPORT_SYMBOL(snd_ac97_write);
* Reads a value from the given register. This will invoke the read
* callback directly after the register check.
*
- * Returns the read value.
+ * Return: The read value.
*/
unsigned short snd_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
@@ -352,7 +352,7 @@ EXPORT_SYMBOL(snd_ac97_write_cache);
* Compares the value with the register cache and updates the value
* only when the value is changed.
*
- * Returns 1 if the value is changed, 0 if no change, or a negative
+ * Return: 1 if the value is changed, 0 if no change, or a negative
* code on failure.
*/
int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value)
@@ -384,7 +384,7 @@ EXPORT_SYMBOL(snd_ac97_update);
* Updates the masked-bits on the given register only when the value
* is changed.
*
- * Returns 1 if the bits are changed, 0 if no change, or a negative
+ * Return: 1 if the bits are changed, 0 if no change, or a negative
* code on failure.
*/
int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value)
@@ -1836,7 +1836,7 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m
* snd_ac97_get_short_name - retrieve codec name
* @ac97: the codec instance
*
- * Returns the short identifying name of the codec.
+ * Return: The short identifying name of the codec.
*/
const char *snd_ac97_get_short_name(struct snd_ac97 *ac97)
{
@@ -1910,7 +1910,7 @@ static int ac97_reset_wait(struct snd_ac97 *ac97, int timeout, int with_modem)
* The AC97 bus instance is registered as a low-level device, so you don't
* have to release it manually.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,
void *private_data, struct snd_ac97_bus **rbus)
@@ -2006,7 +2006,7 @@ static void do_update_power(struct work_struct *work)
* The ac97 instance is registered as a low-level device, so you don't
* have to release it manually.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, struct snd_ac97 **rac97)
{
@@ -2373,6 +2373,8 @@ static struct ac97_power_reg power_regs[PWIDX_SIZE] = {
* @powerup: non-zero when power up the part
*
* Update the AC97 powerdown register bits of the given part.
+ *
+ * Return: Zero.
*/
int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
{
@@ -2885,7 +2887,7 @@ static int apply_quirk_str(struct snd_ac97 *ac97, const char *typestr)
* headphone (true line-out) control as "Master".
* The quirk-list must be terminated with a zero-filled entry.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override)
diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c
index f1488fc..eab0fc9 100644
--- a/sound/pci/ac97/ac97_pcm.c
+++ b/sound/pci/ac97/ac97_pcm.c
@@ -253,7 +253,7 @@ static int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate)
* AC97_SPDIF is accepted as a pseudo register to modify the SPDIF
* status bits.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate)
{
@@ -440,6 +440,8 @@ static unsigned int get_rates(struct ac97_pcm *pcm, unsigned int cidx, unsigned
* It assigns available AC97 slots for given PCMs. If none or only
* some slots are available, pcm->xxx.slots and pcm->xxx.rslots[] members
* are reduced and might be zero.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_pcm_assign(struct snd_ac97_bus *bus,
unsigned short pcms_count,
@@ -562,6 +564,8 @@ EXPORT_SYMBOL(snd_ac97_pcm_assign);
* @slots: a subset of allocated slots (snd_ac97_pcm_assign) for this pcm
*
* It locks the specified slots and sets the given rate to AC97 registers.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
enum ac97_pcm_cfg cfg, unsigned short slots)
@@ -644,6 +648,8 @@ EXPORT_SYMBOL(snd_ac97_pcm_open);
* @pcm: the ac97 pcm instance
*
* It frees the locked AC97 slots.
+ *
+ * Return: Zero.
*/
int snd_ac97_pcm_close(struct ac97_pcm *pcm)
{
@@ -718,6 +724,8 @@ static int double_rate_hw_constraint_channels(struct snd_pcm_hw_params *params,
*
* Installs the hardware constraint rules to prevent using double rates and
* more than two channels at the same time.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime)
{
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index a9ebcf9..5868c61 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1300,8 +1300,6 @@ static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec,
static unsigned int hda_set_power_state(struct hda_codec *codec,
unsigned int power_state);
-static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid,
- unsigned int power_state);
/**
* snd_hda_codec_new - create a HDA codec
@@ -1422,7 +1420,6 @@ int snd_hda_codec_new(struct hda_bus *bus,
#endif
codec->epss = snd_hda_codec_get_supported_ps(codec, fg,
AC_PWRST_EPSS);
- codec->power_filter = default_power_filter;
/* power-up all before initialization */
hda_set_power_state(codec, AC_PWRST_D0);
@@ -3770,8 +3767,9 @@ static unsigned int hda_sync_power_state(struct hda_codec *codec,
}
/* don't power down the widget if it controls eapd and EAPD_BTLENABLE is set */
-static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid,
- unsigned int power_state)
+unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state)
{
if (power_state == AC_PWRST_D3 &&
get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN &&
@@ -3783,6 +3781,7 @@ static unsigned int default_power_filter(struct hda_codec *codec, hda_nid_t nid,
}
return power_state;
}
+EXPORT_SYMBOL_HDA(snd_hda_codec_eapd_power_filter);
/*
* set power state of the codec, and return the power state
@@ -3827,8 +3826,8 @@ static void sync_power_up_states(struct hda_codec *codec)
hda_nid_t nid = codec->start_nid;
int i;
- /* don't care if no or standard filter is used */
- if (!codec->power_filter || codec->power_filter == default_power_filter)
+ /* don't care if no filter is used */
+ if (!codec->power_filter)
return;
for (i = 0; i < codec->num_nodes; i++, nid++) {
@@ -5546,14 +5545,12 @@ void *snd_array_new(struct snd_array *array)
if (array->used >= array->alloced) {
int num = array->alloced + array->alloc_align;
int size = (num + 1) * array->elem_size;
- int oldsize = array->alloced * array->elem_size;
void *nlist;
if (snd_BUG_ON(num >= 4096))
return NULL;
- nlist = krealloc(array->list, size, GFP_KERNEL);
+ nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO);
if (!nlist)
return NULL;
- memset(nlist + oldsize, 0, size - oldsize);
array->list = nlist;
array->alloced = num;
}
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 78897d0..aae6b10 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -150,15 +150,25 @@ static void parse_user_hints(struct hda_codec *codec)
val = snd_hda_get_bool_hint(codec, "add_stereo_mix_input");
if (val >= 0)
spec->add_stereo_mix_input = !!val;
+ /* the following two are just for compatibility */
val = snd_hda_get_bool_hint(codec, "add_out_jack_modes");
if (val >= 0)
- spec->add_out_jack_modes = !!val;
+ spec->add_jack_modes = !!val;
val = snd_hda_get_bool_hint(codec, "add_in_jack_modes");
if (val >= 0)
- spec->add_in_jack_modes = !!val;
+ spec->add_jack_modes = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_jack_modes");
+ if (val >= 0)
+ spec->add_jack_modes = !!val;
val = snd_hda_get_bool_hint(codec, "power_down_unused");
if (val >= 0)
spec->power_down_unused = !!val;
+ val = snd_hda_get_bool_hint(codec, "add_hp_mic");
+ if (val >= 0)
+ spec->hp_mic = !!val;
+ val = snd_hda_get_bool_hint(codec, "hp_mic_detect");
+ if (val >= 0)
+ spec->suppress_hp_mic_detect = !val;
if (!snd_hda_get_int_hint(codec, "mixer_nid", &val))
spec->mixer_nid = val;
@@ -1880,6 +1890,17 @@ static int create_speaker_out_ctls(struct hda_codec *codec)
* independent HP controls
*/
+/* update HP auto-mute state too */
+static void update_hp_automute_hook(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (spec->hp_automute_hook)
+ spec->hp_automute_hook(codec, NULL);
+ else
+ snd_hda_gen_hp_automute(codec, NULL);
+}
+
static int indep_hp_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -1940,12 +1961,7 @@ static int indep_hp_put(struct snd_kcontrol *kcontrol,
else
*dacp = spec->alt_dac_nid;
- /* update HP auto-mute state too */
- if (spec->hp_automute_hook)
- spec->hp_automute_hook(codec, NULL);
- else
- snd_hda_gen_hp_automute(codec, NULL);
-
+ update_hp_automute_hook(codec);
ret = 1;
}
unlock:
@@ -2194,63 +2210,94 @@ static int create_loopback_mixing_ctl(struct hda_codec *codec)
static void call_update_outputs(struct hda_codec *codec);
/* for shared I/O, change the pin-control accordingly */
-static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
+static void update_hp_mic(struct hda_codec *codec, int adc_mux, bool force)
{
struct hda_gen_spec *spec = codec->spec;
+ bool as_mic;
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.
- */
+ hda_nid_t pin;
- val = snd_hda_get_default_vref(codec, pin);
+ pin = spec->hp_mic_pin;
+ as_mic = spec->cur_mux[adc_mux] == spec->hp_mic_mux_idx;
- /* This pin does not have vref caps - let's enable vref on pin 0x18
- instead, as suggested by Realtek */
+ if (!force) {
+ val = snd_hda_codec_get_pin_target(codec, pin);
+ if (as_mic) {
+ if (val & PIN_IN)
+ return;
+ } else {
+ if (val & PIN_OUT)
+ return;
+ }
+ }
+
+ val = snd_hda_get_default_vref(codec, pin);
+ /* if the HP pin doesn't support VREF and the codec driver gives an
+ * alternative pin, set up the VREF on that pin instead
+ */
if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) {
const hda_nid_t vref_pin = spec->shared_mic_vref_pin;
unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin);
if (vref_val != AC_PINCTL_VREF_HIZ)
snd_hda_set_pin_ctl_cache(codec, vref_pin,
- PIN_IN | (set_as_mic ? vref_val : 0));
+ PIN_IN | (as_mic ? vref_val : 0));
}
- val = set_as_mic ? val | PIN_IN : PIN_HP;
- set_pin_target(codec, pin, val, true);
-
- spec->automute_speaker = !set_as_mic;
- call_update_outputs(codec);
+ if (!spec->hp_mic_jack_modes) {
+ if (as_mic)
+ val |= PIN_IN;
+ else
+ val = PIN_HP;
+ set_pin_target(codec, pin, val, true);
+ update_hp_automute_hook(codec);
+ }
}
/* create a shared input with the headphone out */
-static int create_shared_input(struct hda_codec *codec)
+static int create_hp_mic(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int defcfg;
hda_nid_t nid;
- /* only one internal input pin? */
- if (cfg->num_inputs != 1)
- return 0;
- defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin);
- if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
+ if (!spec->hp_mic) {
+ if (spec->suppress_hp_mic_detect)
+ return 0;
+ /* automatic detection: only if no input or a single internal
+ * input pin is found, try to detect the shared hp/mic
+ */
+ if (cfg->num_inputs > 1)
+ return 0;
+ else if (cfg->num_inputs == 1) {
+ defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin);
+ if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT)
+ return 0;
+ }
+ }
+
+ spec->hp_mic = 0; /* clear once */
+ if (cfg->num_inputs >= AUTO_CFG_MAX_INS)
return 0;
- if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */
- else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT)
- nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */
- else
- return 0; /* both not available */
+ nid = 0;
+ if (cfg->line_out_type == AUTO_PIN_HP_OUT && cfg->line_outs > 0)
+ nid = cfg->line_out_pins[0];
+ else if (cfg->hp_outs > 0)
+ nid = cfg->hp_pins[0];
+ if (!nid)
+ return 0;
if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN))
return 0; /* no input */
- cfg->inputs[1].pin = nid;
- cfg->inputs[1].type = AUTO_PIN_MIC;
- cfg->num_inputs = 2;
- spec->shared_mic_hp = 1;
+ cfg->inputs[cfg->num_inputs].pin = nid;
+ cfg->inputs[cfg->num_inputs].type = AUTO_PIN_MIC;
+ cfg->num_inputs++;
+ spec->hp_mic = 1;
+ spec->hp_mic_pin = nid;
+ /* we can't handle auto-mic together with HP-mic */
+ spec->suppress_auto_mic = 1;
snd_printdd("hda-codec: Enable shared I/O jack on NID 0x%x\n", nid);
return 0;
}
@@ -2258,13 +2305,17 @@ static int create_shared_input(struct hda_codec *codec)
/*
* output jack mode
*/
+
+static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin);
+
+static const char * const out_jack_texts[] = {
+ "Line Out", "Headphone Out",
+};
+
static int out_jack_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- static const char * const texts[] = {
- "Line Out", "Headphone Out",
- };
- return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts);
+ return snd_hda_enum_helper_info(kcontrol, uinfo, 2, out_jack_texts);
}
static int out_jack_mode_get(struct snd_kcontrol *kcontrol,
@@ -2326,6 +2377,17 @@ static void get_jack_mode_name(struct hda_codec *codec, hda_nid_t pin,
;
}
+static int get_out_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ if (spec->add_jack_modes) {
+ unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
+ if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV))
+ return 2;
+ }
+ return 1;
+}
+
static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
hda_nid_t *pins)
{
@@ -2334,8 +2396,13 @@ static int create_out_jack_modes(struct hda_codec *codec, int num_pins,
for (i = 0; i < num_pins; i++) {
hda_nid_t pin = pins[i];
- unsigned int pincap = snd_hda_query_pin_caps(codec, pin);
- if ((pincap & AC_PINCAP_OUT) && (pincap & AC_PINCAP_HP_DRV)) {
+ if (pin == spec->hp_mic_pin) {
+ int ret = create_hp_mic_jack_mode(codec, pin);
+ if (ret < 0)
+ return ret;
+ continue;
+ }
+ if (get_out_jack_num_items(codec, pin) > 1) {
struct snd_kcontrol_new *knew;
char name[44];
get_jack_mode_name(codec, pin, name, sizeof(name));
@@ -2456,12 +2523,24 @@ static const struct snd_kcontrol_new in_jack_mode_enum = {
.put = in_jack_mode_put,
};
+static int get_in_jack_num_items(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ int nitems = 0;
+ if (spec->add_jack_modes)
+ nitems = hweight32(get_vref_caps(codec, pin));
+ return nitems ? nitems : 1;
+}
+
static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
{
struct hda_gen_spec *spec = codec->spec;
- unsigned int defcfg;
struct snd_kcontrol_new *knew;
char name[44];
+ unsigned int defcfg;
+
+ if (pin == spec->hp_mic_pin)
+ return 0; /* already done in create_out_jack_mode() */
/* no jack mode for fixed pins */
defcfg = snd_hda_codec_get_pincfg(codec, pin);
@@ -2469,7 +2548,7 @@ static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
return 0;
/* no multiple vref caps? */
- if (hweight32(get_vref_caps(codec, pin)) <= 1)
+ if (get_in_jack_num_items(codec, pin) <= 1)
return 0;
get_jack_mode_name(codec, pin, name, sizeof(name));
@@ -2480,6 +2559,132 @@ static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
return 0;
}
+/*
+ * HP/mic shared jack mode
+ */
+static int hp_mic_jack_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ const char *text = NULL;
+ int idx;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = out_jacks + in_jacks;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ idx = uinfo->value.enumerated.item;
+ if (idx < out_jacks) {
+ if (out_jacks > 1)
+ text = out_jack_texts[idx];
+ else
+ text = "Headphone Out";
+ } else {
+ idx -= out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ text = vref_texts[get_vref_idx(vref_caps, idx)];
+ } else
+ text = "Mic In";
+ }
+
+ strcpy(uinfo->value.enumerated.name, text);
+ return 0;
+}
+
+static int get_cur_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t nid)
+{
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ unsigned int val = snd_hda_codec_get_pin_target(codec, nid);
+ int idx = 0;
+
+ if (val & PIN_OUT) {
+ if (out_jacks > 1 && val == PIN_HP)
+ idx = 1;
+ } else if (val & PIN_IN) {
+ idx = out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ val &= AC_PINCTL_VREFEN;
+ idx += cvt_from_vref_idx(vref_caps, val);
+ }
+ }
+ return idx;
+}
+
+static int hp_mic_jack_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ ucontrol->value.enumerated.item[0] =
+ get_cur_hp_mic_jack_mode(codec, nid);
+ return 0;
+}
+
+static int hp_mic_jack_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value;
+ int out_jacks = get_out_jack_num_items(codec, nid);
+ int in_jacks = get_in_jack_num_items(codec, nid);
+ unsigned int val, oldval, idx;
+
+ oldval = get_cur_hp_mic_jack_mode(codec, nid);
+ idx = ucontrol->value.enumerated.item[0];
+ if (oldval == idx)
+ return 0;
+
+ if (idx < out_jacks) {
+ if (out_jacks > 1)
+ val = idx ? PIN_HP : PIN_OUT;
+ else
+ val = PIN_HP;
+ } else {
+ idx -= out_jacks;
+ if (in_jacks > 1) {
+ unsigned int vref_caps = get_vref_caps(codec, nid);
+ val = snd_hda_codec_get_pin_target(codec, nid);
+ val &= ~(AC_PINCTL_VREFEN | PIN_HP);
+ val |= get_vref_idx(vref_caps, idx) | PIN_IN;
+ } else
+ val = snd_hda_get_default_vref(codec, nid);
+ }
+ snd_hda_set_pin_ctl_cache(codec, nid, val);
+ update_hp_automute_hook(codec);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new hp_mic_jack_mode_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = hp_mic_jack_mode_info,
+ .get = hp_mic_jack_mode_get,
+ .put = hp_mic_jack_mode_put,
+};
+
+static int create_hp_mic_jack_mode(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct snd_kcontrol_new *knew;
+
+ if (get_out_jack_num_items(codec, pin) <= 1 &&
+ get_in_jack_num_items(codec, pin) <= 1)
+ return 0; /* no need */
+ knew = snd_hda_gen_add_kctl(spec, "Headphone Mic Jack Mode",
+ &hp_mic_jack_mode_enum);
+ if (!knew)
+ return -ENOMEM;
+ knew->private_value = pin;
+ spec->hp_mic_jack_modes = 1;
+ return 0;
+}
/*
* Parse input paths
@@ -2602,7 +2807,6 @@ static int check_dyn_adc_switch(struct hda_codec *codec)
unsigned int ok_bits;
int i, n, nums;
- again:
nums = 0;
ok_bits = 0;
for (n = 0; n < spec->num_adc_nids; n++) {
@@ -2617,12 +2821,6 @@ static int check_dyn_adc_switch(struct hda_codec *codec)
}
if (!ok_bits) {
- if (spec->shared_mic_hp) {
- spec->shared_mic_hp = 0;
- imux->num_items = 1;
- goto again;
- }
-
/* check whether ADC-switch is possible */
for (i = 0; i < imux->num_items; i++) {
for (n = 0; n < spec->num_adc_nids; n++) {
@@ -2655,7 +2853,8 @@ static int check_dyn_adc_switch(struct hda_codec *codec)
spec->num_adc_nids = nums;
}
- if (imux->num_items == 1 || spec->shared_mic_hp) {
+ if (imux->num_items == 1 ||
+ (imux->num_items == 2 && spec->hp_mic)) {
snd_printdd("hda-codec: reducing to a single ADC\n");
spec->num_adc_nids = 1; /* reduce to a single ADC */
}
@@ -2692,6 +2891,8 @@ static int parse_capture_source(struct hda_codec *codec, hda_nid_t pin,
snd_hda_get_path_idx(codec, path);
if (!imux_added) {
+ if (spec->hp_mic_pin == pin)
+ spec->hp_mic_mux_idx = imux->num_items;
spec->imux_pins[imux->num_items] = pin;
snd_hda_add_imux_item(imux, label, cfg_idx, NULL);
imux_added = true;
@@ -2766,7 +2967,8 @@ static int create_input_ctls(struct hda_codec *codec)
val = PIN_IN;
if (cfg->inputs[i].type == AUTO_PIN_MIC)
val |= snd_hda_get_default_vref(codec, pin);
- set_pin_target(codec, pin, val, false);
+ if (pin != spec->hp_mic_pin)
+ set_pin_target(codec, pin, val, false);
if (mixer) {
if (is_reachable_path(codec, pin, mixer)) {
@@ -2784,7 +2986,7 @@ static int create_input_ctls(struct hda_codec *codec)
if (err < 0)
return err;
- if (spec->add_in_jack_modes) {
+ if (spec->add_jack_modes) {
err = create_in_jack_mode(codec, pin);
if (err < 0)
return err;
@@ -3416,8 +3618,8 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
spec->cur_mux[adc_idx] = idx;
- if (spec->shared_mic_hp)
- update_shared_mic_hp(codec, spec->cur_mux[adc_idx]);
+ if (spec->hp_mic)
+ update_hp_mic(codec, adc_idx, false);
if (spec->dyn_adc_switch)
dyn_adc_pcm_resetup(codec, idx);
@@ -3465,18 +3667,21 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
for (i = 0; i < num_pins; i++) {
hda_nid_t nid = pins[i];
- unsigned int val;
+ unsigned int val, oldval;
if (!nid)
break;
+ oldval = snd_hda_codec_get_pin_target(codec, nid);
+ if (oldval & PIN_IN)
+ continue; /* no mute for inputs */
/* don't reset VREF value in case it's controlling
* the amp (see alc861_fixup_asus_amp_vref_0f())
*/
if (spec->keep_vref_in_automute)
- val = snd_hda_codec_get_pin_target(codec, nid) & ~PIN_HP;
+ val = oldval & ~PIN_HP;
else
val = 0;
if (!mute)
- val |= snd_hda_codec_get_pin_target(codec, nid);
+ val |= oldval;
/* here we call update_pin_ctl() so that the pinctl is changed
* without changing the pinctl target value;
* the original target value will be still referred at the
@@ -3497,8 +3702,7 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
* in general, HP pins/amps control should be enabled in all cases,
* but currently set only for master_mute, just to be safe
*/
- if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */
- do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
+ do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
spec->autocfg.hp_pins, spec->master_mute);
if (!spec->automute_speaker)
@@ -3603,10 +3807,7 @@ static void update_automute_all(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;
- if (spec->hp_automute_hook)
- spec->hp_automute_hook(codec, NULL);
- else
- snd_hda_gen_hp_automute(codec, NULL);
+ update_hp_automute_hook(codec);
if (spec->line_automute_hook)
spec->line_automute_hook(codec, NULL);
else
@@ -3978,7 +4179,7 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
err = create_loopback_mixing_ctl(codec);
if (err < 0)
return err;
- err = create_shared_input(codec);
+ err = create_hp_mic(codec);
if (err < 0)
return err;
err = create_input_ctls(codec);
@@ -4004,11 +4205,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
if (err < 0)
return err;
- if (!spec->shared_mic_hp) {
- err = check_auto_mic_availability(codec);
- if (err < 0)
- return err;
- }
+ err = check_auto_mic_availability(codec);
+ if (err < 0)
+ return err;
err = create_capture_mixers(codec);
if (err < 0)
@@ -4018,7 +4217,7 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
if (err < 0)
return err;
- if (spec->add_out_jack_modes) {
+ if (spec->add_jack_modes) {
if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
err = create_out_jack_modes(codec, cfg->line_outs,
cfg->line_out_pins);
@@ -4115,9 +4314,9 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
free_kctls(spec); /* no longer needed */
- if (spec->shared_mic_hp) {
+ if (spec->hp_mic_pin) {
int err;
- int nid = spec->autocfg.inputs[1].pin;
+ int nid = spec->hp_mic_pin;
err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0);
if (err < 0)
return err;
@@ -4780,11 +4979,10 @@ static void init_input_src(struct hda_codec *codec)
snd_hda_activate_path(codec, path, active, false);
}
}
+ if (spec->hp_mic)
+ update_hp_mic(codec, c, true);
}
- if (spec->shared_mic_hp)
- update_shared_mic_hp(codec, spec->cur_mux[0]);
-
if (spec->cap_sync_hook)
spec->cap_sync_hook(codec, NULL);
}
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index 009b57b..094e6af 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -145,7 +145,10 @@ struct hda_gen_spec {
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS];
+ /* shared hp/mic */
hda_nid_t shared_mic_vref_pin;
+ hda_nid_t hp_mic_pin;
+ int hp_mic_mux_idx;
/* DAC/ADC lists */
int num_all_dacs;
@@ -200,7 +203,8 @@ struct hda_gen_spec {
/* other parse behavior flags */
unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */
- unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
+ unsigned int hp_mic:1; /* Allow HP as a mic-in */
+ unsigned int suppress_hp_mic_detect:1; /* Don't detect HP/mic */
unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
@@ -209,8 +213,7 @@ struct hda_gen_spec {
unsigned int indep_hp:1; /* independent HP supported */
unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */
unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */
- unsigned int add_out_jack_modes:1; /* add output jack mode enum ctls */
- unsigned int add_in_jack_modes:1; /* add input jack mode enum ctls */
+ unsigned int add_jack_modes:1; /* add i/o jack mode enum ctls */
unsigned int power_down_unused:1; /* power down unused widgets */
/* other internal flags */
@@ -218,6 +221,7 @@ struct hda_gen_spec {
unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
unsigned int indep_hp_enabled:1; /* independent HP enabled */
unsigned int have_aamix_ctl:1;
+ unsigned int hp_mic_jack_modes:1;
/* loopback mixing mode */
bool aamix_mode;
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 83b7486..e0bf753 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -670,6 +670,10 @@ snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
return (state != target_state);
}
+unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
+ hda_nid_t nid,
+ unsigned int power_state);
+
/*
* AMP control callbacks
*/
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 2a89d1ee..d0100a8 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -3356,7 +3356,6 @@ static int patch_conexant_auto(struct hda_codec *codec)
switch (codec->vendor_id) {
case 0x14f15045:
codec->single_adc_amp = 1;
- codec->power_filter = NULL; /* Needs speaker amp to D3 to avoid click */
break;
case 0x14f15047:
codec->pin_amp_workaround = 1;
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 78e1827..3e1159d 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -44,16 +44,6 @@ static bool static_hdmi_pcm;
module_param(static_hdmi_pcm, bool, 0644);
MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
-/*
- * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
- * could support N independent pipes, each of them can be connected to one or
- * more ports (DVI, HDMI or DisplayPort).
- *
- * The HDA correspondence of pipes/ports are converter/pin nodes.
- */
-#define MAX_HDMI_CVTS 8
-#define MAX_HDMI_PINS 8
-
struct hdmi_spec_per_cvt {
hda_nid_t cvt_nid;
int assigned;
@@ -80,16 +70,17 @@ struct hdmi_spec_per_pin {
bool non_pcm;
bool chmap_set; /* channel-map override by ALSA API? */
unsigned char chmap[8]; /* ALSA API channel-map */
+ char pcm_name[8]; /* filled in build_pcm callbacks */
};
struct hdmi_spec {
int num_cvts;
- struct hdmi_spec_per_cvt cvts[MAX_HDMI_CVTS];
- hda_nid_t cvt_nids[MAX_HDMI_CVTS];
+ struct snd_array cvts; /* struct hdmi_spec_per_cvt */
+ hda_nid_t cvt_nids[4]; /* only for haswell fix */
int num_pins;
- struct hdmi_spec_per_pin pins[MAX_HDMI_PINS];
- struct hda_pcm pcm_rec[MAX_HDMI_PINS];
+ struct snd_array pins; /* struct hdmi_spec_per_pin */
+ struct snd_array pcm_rec; /* struct hda_pcm */
unsigned int channels_max; /* max over all cvts */
struct hdmi_eld temp_eld;
@@ -304,12 +295,19 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
* HDMI routines
*/
+#define get_pin(spec, idx) \
+ ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx))
+#define get_cvt(spec, idx) \
+ ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx))
+#define get_pcm_rec(spec, idx) \
+ ((struct hda_pcm *)snd_array_elem(&spec->pcm_rec, idx))
+
static int pin_nid_to_pin_index(struct hdmi_spec *spec, hda_nid_t pin_nid)
{
int pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
- if (spec->pins[pin_idx].pin_nid == pin_nid)
+ if (get_pin(spec, pin_idx)->pin_nid == pin_nid)
return pin_idx;
snd_printk(KERN_WARNING "HDMI: pin nid %d not registered\n", pin_nid);
@@ -322,7 +320,7 @@ static int hinfo_to_pin_index(struct hdmi_spec *spec,
int pin_idx;
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
- if (&spec->pcm_rec[pin_idx].stream[0] == hinfo)
+ if (get_pcm_rec(spec, pin_idx)->stream == hinfo)
return pin_idx;
snd_printk(KERN_WARNING "HDMI: hinfo %p not registered\n", hinfo);
@@ -334,7 +332,7 @@ static int cvt_nid_to_cvt_index(struct hdmi_spec *spec, hda_nid_t cvt_nid)
int cvt_idx;
for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++)
- if (spec->cvts[cvt_idx].cvt_nid == cvt_nid)
+ if (get_cvt(spec, cvt_idx)->cvt_nid == cvt_nid)
return cvt_idx;
snd_printk(KERN_WARNING "HDMI: cvt nid %d not registered\n", cvt_nid);
@@ -352,7 +350,7 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
pin_idx = kcontrol->private_value;
- eld = &spec->pins[pin_idx].sink_eld;
+ eld = &get_pin(spec, pin_idx)->sink_eld;
mutex_lock(&eld->lock);
uinfo->count = eld->eld_valid ? eld->eld_size : 0;
@@ -370,7 +368,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
int pin_idx;
pin_idx = kcontrol->private_value;
- eld = &spec->pins[pin_idx].sink_eld;
+ eld = &get_pin(spec, pin_idx)->sink_eld;
mutex_lock(&eld->lock);
if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) {
@@ -410,11 +408,11 @@ static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
kctl->private_value = pin_idx;
kctl->id.device = device;
- err = snd_hda_ctl_add(codec, spec->pins[pin_idx].pin_nid, kctl);
+ err = snd_hda_ctl_add(codec, get_pin(spec, pin_idx)->pin_nid, kctl);
if (err < 0)
return err;
- spec->pins[pin_idx].eld_ctl = kctl;
+ get_pin(spec, pin_idx)->eld_ctl = kctl;
return 0;
}
@@ -875,14 +873,14 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
int channels = substream->runtime->channels;
struct hdmi_eld *eld;
int ca;
union audio_infoframe ai;
- eld = &spec->pins[pin_idx].sink_eld;
+ eld = &per_pin->sink_eld;
if (!eld->monitor_present)
return;
@@ -977,7 +975,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
if (pin_idx < 0)
return;
- hdmi_present_sense(&spec->pins[pin_idx], 1);
+ hdmi_present_sense(get_pin(spec, pin_idx), 1);
snd_hda_jack_report_sync(codec);
}
@@ -1083,12 +1081,12 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
pin_idx = hinfo_to_pin_index(spec, hinfo);
if (snd_BUG_ON(pin_idx < 0))
return -EINVAL;
- per_pin = &spec->pins[pin_idx];
+ per_pin = get_pin(spec, pin_idx);
eld = &per_pin->sink_eld;
/* Dynamically assign converter to stream */
for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
- per_cvt = &spec->cvts[cvt_idx];
+ per_cvt = get_cvt(spec, cvt_idx);
/* Must not already be assigned */
if (per_cvt->assigned)
@@ -1151,7 +1149,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
{
struct hdmi_spec *spec = codec->spec;
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
@@ -1275,14 +1273,13 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
return 0;
- if (snd_BUG_ON(spec->num_pins >= MAX_HDMI_PINS))
- return -E2BIG;
-
if (codec->vendor_id == 0x80862807)
intel_haswell_fixup_connect_list(codec, pin_nid);
pin_idx = spec->num_pins;
- per_pin = &spec->pins[pin_idx];
+ per_pin = snd_array_new(&spec->pins);
+ if (!per_pin)
+ return -ENOMEM;
per_pin->pin_nid = pin_nid;
per_pin->non_pcm = false;
@@ -1299,19 +1296,16 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
{
struct hdmi_spec *spec = codec->spec;
- int cvt_idx;
struct hdmi_spec_per_cvt *per_cvt;
unsigned int chans;
int err;
- if (snd_BUG_ON(spec->num_cvts >= MAX_HDMI_CVTS))
- return -E2BIG;
-
chans = get_wcaps(codec, cvt_nid);
chans = get_wcaps_channels(chans);
- cvt_idx = spec->num_cvts;
- per_cvt = &spec->cvts[cvt_idx];
+ per_cvt = snd_array_new(&spec->cvts);
+ if (!per_cvt)
+ return -ENOMEM;
per_cvt->cvt_nid = cvt_nid;
per_cvt->channels_min = 2;
@@ -1328,7 +1322,9 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
if (err < 0)
return err;
- spec->cvt_nids[spec->num_cvts++] = cvt_nid;
+ if (spec->num_cvts < ARRAY_SIZE(spec->cvt_nids))
+ spec->cvt_nids[spec->num_cvts] = cvt_nid;
+ spec->num_cvts++;
return 0;
}
@@ -1384,13 +1380,6 @@ static int hdmi_parse_codec(struct hda_codec *codec)
/*
*/
-static char *get_hdmi_pcm_name(int idx)
-{
- static char names[MAX_HDMI_PINS][8];
- sprintf(&names[idx][0], "HDMI %d", idx);
- return &names[idx][0];
-}
-
static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
{
struct hda_spdif_out *spdif;
@@ -1417,7 +1406,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
hda_nid_t cvt_nid = hinfo->nid;
struct hdmi_spec *spec = codec->spec;
int pin_idx = hinfo_to_pin_index(spec, hinfo);
- hda_nid_t pin_nid = spec->pins[pin_idx].pin_nid;
+ hda_nid_t pin_nid = get_pin(spec, pin_idx)->pin_nid;
bool non_pcm;
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
@@ -1450,7 +1439,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid);
if (snd_BUG_ON(cvt_idx < 0))
return -EINVAL;
- per_cvt = &spec->cvts[cvt_idx];
+ per_cvt = get_cvt(spec, cvt_idx);
snd_BUG_ON(!per_cvt->assigned);
per_cvt->assigned = 0;
@@ -1459,7 +1448,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
pin_idx = hinfo_to_pin_index(spec, hinfo);
if (snd_BUG_ON(pin_idx < 0))
return -EINVAL;
- per_pin = &spec->pins[pin_idx];
+ per_pin = get_pin(spec, pin_idx);
snd_hda_spdif_ctls_unassign(codec, pin_idx);
per_pin->chmap_set = false;
@@ -1553,7 +1542,7 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
struct hda_codec *codec = info->private_data;
struct hdmi_spec *spec = codec->spec;
int pin_idx = kcontrol->private_value;
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
int i;
for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++)
@@ -1568,7 +1557,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
struct hda_codec *codec = info->private_data;
struct hdmi_spec *spec = codec->spec;
int pin_idx = kcontrol->private_value;
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
unsigned int ctl_idx;
struct snd_pcm_substream *substream;
unsigned char chmap[8];
@@ -1613,9 +1602,14 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hda_pcm *info;
struct hda_pcm_stream *pstr;
-
- info = &spec->pcm_rec[pin_idx];
- info->name = get_hdmi_pcm_name(pin_idx);
+ struct hdmi_spec_per_pin *per_pin;
+
+ per_pin = get_pin(spec, pin_idx);
+ sprintf(per_pin->pcm_name, "HDMI %d", pin_idx);
+ info = snd_array_new(&spec->pcm_rec);
+ if (!info)
+ return -ENOMEM;
+ info->name = per_pin->pcm_name;
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->own_chmap = true;
@@ -1626,7 +1620,7 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
}
codec->num_pcms = spec->num_pins;
- codec->pcm_info = spec->pcm_rec;
+ codec->pcm_info = spec->pcm_rec.list;
return 0;
}
@@ -1635,8 +1629,8 @@ static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
{
char hdmi_str[32] = "HDMI/DP";
struct hdmi_spec *spec = codec->spec;
- struct hdmi_spec_per_pin *per_pin = &spec->pins[pin_idx];
- int pcmdev = spec->pcm_rec[pin_idx].device;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+ int pcmdev = get_pcm_rec(spec, pin_idx)->device;
if (pcmdev > 0)
sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
@@ -1654,7 +1648,7 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
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_spec_per_pin *per_pin = get_pin(spec, pin_idx);
err = generic_hdmi_build_jack(codec, pin_idx);
if (err < 0)
@@ -1669,9 +1663,8 @@ static int generic_hdmi_build_controls(struct hda_codec *codec)
snd_hda_spdif_ctls_unassign(codec, pin_idx);
/* add control for ELD Bytes */
- err = hdmi_create_eld_ctl(codec,
- pin_idx,
- spec->pcm_rec[pin_idx].device);
+ err = hdmi_create_eld_ctl(codec, pin_idx,
+ get_pcm_rec(spec, pin_idx)->device);
if (err < 0)
return err;
@@ -1709,7 +1702,7 @@ static int generic_hdmi_init_per_pins(struct hda_codec *codec)
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_spec_per_pin *per_pin = get_pin(spec, pin_idx);
struct hdmi_eld *eld = &per_pin->sink_eld;
per_pin->codec = codec;
@@ -1726,7 +1719,7 @@ static int generic_hdmi_init(struct hda_codec *codec)
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_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hda_nid_t pin_nid = per_pin->pin_nid;
hdmi_init_pin(codec, pin_nid);
@@ -1735,13 +1728,27 @@ static int generic_hdmi_init(struct hda_codec *codec)
return 0;
}
+static void hdmi_array_init(struct hdmi_spec *spec, int nums)
+{
+ snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums);
+ snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums);
+ snd_array_init(&spec->pcm_rec, sizeof(struct hda_pcm), nums);
+}
+
+static void hdmi_array_free(struct hdmi_spec *spec)
+{
+ snd_array_free(&spec->pins);
+ snd_array_free(&spec->cvts);
+ snd_array_free(&spec->pcm_rec);
+}
+
static void generic_hdmi_free(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_spec_per_pin *per_pin = get_pin(spec, pin_idx);
struct hdmi_eld *eld = &per_pin->sink_eld;
cancel_delayed_work(&per_pin->work);
@@ -1749,6 +1756,7 @@ static void generic_hdmi_free(struct hda_codec *codec)
}
flush_workqueue(codec->bus->workq);
+ hdmi_array_free(spec);
kfree(spec);
}
@@ -1775,6 +1783,7 @@ static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
/* override pins connection list */
snd_printdd("hdmi: haswell: override pin connection 0x%x\n", nid);
+ nconns = max(spec->num_cvts, 4);
snd_hda_override_conn_list(codec, nid, spec->num_cvts, spec->cvt_nids);
}
@@ -1855,6 +1864,7 @@ static int patch_generic_hdmi(struct hda_codec *codec)
return -ENOMEM;
codec->spec = spec;
+ hdmi_array_init(spec, 4);
snd_hda_pick_fixup(codec, hdmi_models, hdmi_fixup_tbl, hdmi_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
@@ -1882,24 +1892,30 @@ static int patch_generic_hdmi(struct hda_codec *codec)
static int simple_playback_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
+ struct hda_pcm *info;
unsigned int chans;
struct hda_pcm_stream *pstr;
+ struct hdmi_spec_per_cvt *per_cvt;
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- chans = get_wcaps(codec, spec->cvts[0].cvt_nid);
+ per_cvt = get_cvt(spec, 0);
+ chans = get_wcaps(codec, per_cvt->cvt_nid);
chans = get_wcaps_channels(chans);
- info->name = get_hdmi_pcm_name(0);
+ info = snd_array_new(&spec->pcm_rec);
+ if (!info)
+ return -ENOMEM;
+ info->name = get_pin(spec, 0)->pcm_name;
+ sprintf(info->name, "HDMI 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;
+ pstr->nid = per_cvt->cvt_nid;
if (pstr->channels_max <= 2 && chans && chans <= 16)
pstr->channels_max = chans;
+ codec->num_pcms = 1;
+ codec->pcm_info = info;
+
return 0;
}
@@ -1919,11 +1935,12 @@ static void simple_hdmi_unsol_event(struct hda_codec *codec,
static int simple_playback_build_controls(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt;
int err;
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->cvts[0].cvt_nid,
- spec->cvts[0].cvt_nid);
+ per_cvt = get_cvt(spec, 0);
+ err = snd_hda_create_spdif_out_ctls(codec, per_cvt->cvt_nid,
+ per_cvt->cvt_nid);
if (err < 0)
return err;
return simple_hdmi_build_jack(codec, 0);
@@ -1932,7 +1949,8 @@ static int simple_playback_build_controls(struct hda_codec *codec)
static int simple_playback_init(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- hda_nid_t pin = spec->pins[0].pin_nid;
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, 0);
+ hda_nid_t pin = per_pin->pin_nid;
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
@@ -1948,6 +1966,7 @@ static void simple_playback_free(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
+ hdmi_array_free(spec);
kfree(spec);
}
@@ -2111,20 +2130,29 @@ static int patch_simple_hdmi(struct hda_codec *codec,
hda_nid_t cvt_nid, hda_nid_t pin_nid)
{
struct hdmi_spec *spec;
+ struct hdmi_spec_per_cvt *per_cvt;
+ struct hdmi_spec_per_pin *per_pin;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
codec->spec = spec;
+ hdmi_array_init(spec, 1);
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;
+ per_pin = snd_array_new(&spec->pins);
+ per_cvt = snd_array_new(&spec->cvts);
+ if (!per_pin || !per_cvt) {
+ simple_playback_free(codec);
+ return -ENOMEM;
+ }
+ per_cvt->cvt_nid = cvt_nid;
+ per_pin->pin_nid = pin_nid;
spec->pcm_playback = simple_pcm_playback;
codec->patch_ops = simple_hdmi_patch_ops;
@@ -2201,9 +2229,11 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
int i;
struct hdmi_spec *spec = codec->spec;
struct hda_spdif_out *spdif;
+ struct hdmi_spec_per_cvt *per_cvt;
mutex_lock(&codec->spdif_mutex);
- spdif = snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
+ per_cvt = get_cvt(spec, 0);
+ spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid);
chs = substream->runtime->channels;
@@ -2325,13 +2355,17 @@ static int nvhdmi_7x_8ch_build_pcms(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int err = simple_playback_build_pcms(codec);
- spec->pcm_rec[0].own_chmap = true;
+ if (!err) {
+ struct hda_pcm *info = get_pcm_rec(spec, 0);
+ info->own_chmap = true;
+ }
return err;
}
static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info;
struct snd_pcm_chmap *chmap;
int err;
@@ -2340,7 +2374,8 @@ static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
return err;
/* add channel maps */
- err = snd_pcm_add_chmap_ctls(spec->pcm_rec[0].pcm,
+ info = get_pcm_rec(spec, 0);
+ err = snd_pcm_add_chmap_ctls(info->pcm,
SNDRV_PCM_STREAM_PLAYBACK,
snd_pcm_alt_chmaps, 8, 0, &chmap);
if (err < 0)
@@ -2395,6 +2430,7 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_cvt *per_cvt = get_cvt(spec, 0);
int chans = substream->runtime->channels;
int i, err;
@@ -2402,11 +2438,11 @@ static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
substream);
if (err < 0)
return err;
- snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0,
+ snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
AC_VERB_SET_CVT_CHAN_COUNT, chans - 1);
/* FIXME: XXX */
for (i = 0; i < chans; i++) {
- snd_hda_codec_write(codec, spec->cvts[0].cvt_nid, 0,
+ snd_hda_codec_write(codec, per_cvt->cvt_nid, 0,
AC_VERB_SET_HDMI_CHAN_SLOT,
(i << 4) | i);
}
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 563c24d..c0bf155 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -1455,6 +1455,7 @@ enum {
ALC260_FIXUP_HP_B1900,
ALC260_FIXUP_KN1,
ALC260_FIXUP_FSC_S7020,
+ ALC260_FIXUP_FSC_S7020_JWSE,
};
static void alc260_gpio1_automute(struct hda_codec *codec)
@@ -1516,14 +1517,17 @@ static void alc260_fixup_fsc_s7020(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
-
- switch (action) {
- case HDA_FIXUP_ACT_PRE_PROBE:
- spec->gen.add_out_jack_modes = 1;
- break;
- case HDA_FIXUP_ACT_PROBE:
+ if (action == HDA_FIXUP_ACT_PROBE)
spec->init_amp = ALC_INIT_NONE;
- break;
+}
+
+static void alc260_fixup_fsc_s7020_jwse(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.add_jack_modes = 1;
+ spec->gen.hp_mic = 1;
}
}
@@ -1586,6 +1590,12 @@ static const struct hda_fixup alc260_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc260_fixup_fsc_s7020,
},
+ [ALC260_FIXUP_FSC_S7020_JWSE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc260_fixup_fsc_s7020_jwse,
+ .chained = true,
+ .chain_id = ALC260_FIXUP_FSC_S7020,
+ },
};
static const struct snd_pci_quirk alc260_fixup_tbl[] = {
@@ -1602,6 +1612,14 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = {
{}
};
+static const struct hda_model_fixup alc260_fixup_models[] = {
+ {.id = ALC260_FIXUP_GPIO1, .name = "gpio1"},
+ {.id = ALC260_FIXUP_COEF, .name = "coef"},
+ {.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu"},
+ {.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse"},
+ {}
+};
+
/*
*/
static int patch_alc260(struct hda_codec *codec)
@@ -1620,7 +1638,8 @@ static int patch_alc260(struct hda_codec *codec)
*/
spec->gen.prefer_hp_amp = 1;
- snd_hda_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups);
+ snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl,
+ alc260_fixups);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
/* automatic parse from the BIOS config */
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index dafe04a..3566731 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -3797,6 +3797,7 @@ static int patch_stac9200(struct hda_codec *codec)
spec->gen.own_eapd_ctl = 1;
codec->patch_ops = stac_patch_ops;
+ codec->power_filter = snd_hda_codec_eapd_power_filter;
snd_hda_add_verbs(codec, stac9200_eapd_init);
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index 223c3d9..9ea05e9 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -969,6 +969,7 @@ static int snd_hdspm_create_pcm(struct snd_card *card,
struct hdspm *hdspm);
static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm);
+static inline int hdspm_get_pll_freq(struct hdspm *hdspm);
static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm);
static int hdspm_autosync_ref(struct hdspm *hdspm);
static int snd_hdspm_set_defaults(struct hdspm *hdspm);
@@ -1075,6 +1076,20 @@ static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm)
return ret;
}
+/* round arbitary sample rates to commonly known rates */
+static int hdspm_round_frequency(int rate)
+{
+ if (rate < 38050)
+ return 32000;
+ if (rate < 46008)
+ return 44100;
+ else
+ return 48000;
+}
+
+static int hdspm_tco_sync_check(struct hdspm *hdspm);
+static int hdspm_sync_in_sync_check(struct hdspm *hdspm);
+
/* check for external sample rate */
static int hdspm_external_sample_rate(struct hdspm *hdspm)
{
@@ -1216,22 +1231,45 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
break;
}
- /* QS and DS rates normally can not be detected
- * automatically by the card. Only exception is MADI
- * in 96k frame mode.
- *
- * So if we read SS values (32 .. 48k), check for
- * user-provided DS/QS bits in the control register
- * and multiply the base frequency accordingly.
- */
- if (rate <= 48000) {
- if (hdspm->control_register & HDSPM_QuadSpeed)
- rate *= 4;
- else if (hdspm->control_register &
- HDSPM_DoubleSpeed)
- rate *= 2;
+ } /* endif HDSPM_madiLock */
+
+ /* check sample rate from TCO or SYNC_IN */
+ {
+ bool is_valid_input = 0;
+ bool has_sync = 0;
+
+ syncref = hdspm_autosync_ref(hdspm);
+ if (HDSPM_AUTOSYNC_FROM_TCO == syncref) {
+ is_valid_input = 1;
+ has_sync = (HDSPM_SYNC_CHECK_SYNC ==
+ hdspm_tco_sync_check(hdspm));
+ } else if (HDSPM_AUTOSYNC_FROM_SYNC_IN == syncref) {
+ is_valid_input = 1;
+ has_sync = (HDSPM_SYNC_CHECK_SYNC ==
+ hdspm_sync_in_sync_check(hdspm));
+ }
+
+ if (is_valid_input && has_sync) {
+ rate = hdspm_round_frequency(
+ hdspm_get_pll_freq(hdspm));
}
}
+
+ /* QS and DS rates normally can not be detected
+ * automatically by the card. Only exception is MADI
+ * in 96k frame mode.
+ *
+ * So if we read SS values (32 .. 48k), check for
+ * user-provided DS/QS bits in the control register
+ * and multiply the base frequency accordingly.
+ */
+ if (rate <= 48000) {
+ if (hdspm->control_register & HDSPM_QuadSpeed)
+ rate *= 4;
+ else if (hdspm->control_register &
+ HDSPM_DoubleSpeed)
+ rate *= 2;
+ }
break;
}
@@ -1979,16 +2017,25 @@ static void hdspm_midi_tasklet(unsigned long arg)
/* get the system sample rate which is set */
+static inline int hdspm_get_pll_freq(struct hdspm *hdspm)
+{
+ unsigned int period, rate;
+
+ period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
+ rate = hdspm_calc_dds_value(hdspm, period);
+
+ return rate;
+}
+
/**
* Calculate the real sample rate from the
* current DDS value.
**/
static int hdspm_get_system_sample_rate(struct hdspm *hdspm)
{
- unsigned int period, rate;
+ unsigned int rate;
- period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ);
- rate = hdspm_calc_dds_value(hdspm, period);
+ rate = hdspm_get_pll_freq(hdspm);
if (rate > 207000) {
/* Unreasonable high sample rate as seen on PCI MADI cards. */
@@ -2128,6 +2175,16 @@ static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx)
return (status >> (idx*4)) & 0xF;
}
+#define ENUMERATED_CTL_INFO(info, texts) \
+{ \
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; \
+ uinfo->count = 1; \
+ uinfo->value.enumerated.items = ARRAY_SIZE(texts); \
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) \
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; \
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); \
+}
+
#define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \
@@ -2143,14 +2200,7 @@ static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx)
static int snd_hdspm_info_autosync_sample_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 10;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts_freq[uinfo->value.enumerated.item]);
+ ENUMERATED_CTL_INFO(uinfo, texts_freq);
return 0;
}
@@ -2316,15 +2366,7 @@ static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "Master", "AutoSync" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -2888,6 +2930,112 @@ static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol,
return 0;
}
+
+
+#define HDSPM_TCO_VIDEO_INPUT_FORMAT(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_tco_video_input_format, \
+ .get = snd_hdspm_get_tco_video_input_format, \
+}
+
+static int snd_hdspm_info_tco_video_input_format(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = {"No video", "NTSC", "PAL"};
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ return 0;
+}
+
+static int snd_hdspm_get_tco_video_input_format(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ u32 status;
+ int ret = 0;
+
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
+ switch (status & (HDSPM_TCO1_Video_Input_Format_NTSC |
+ HDSPM_TCO1_Video_Input_Format_PAL)) {
+ case HDSPM_TCO1_Video_Input_Format_NTSC:
+ /* ntsc */
+ ret = 1;
+ break;
+ case HDSPM_TCO1_Video_Input_Format_PAL:
+ /* pal */
+ ret = 2;
+ break;
+ default:
+ /* no video */
+ ret = 0;
+ break;
+ }
+ ucontrol->value.enumerated.item[0] = ret;
+ return 0;
+}
+
+
+
+#define HDSPM_TCO_LTC_FRAMES(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |\
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_info_tco_ltc_frames, \
+ .get = snd_hdspm_get_tco_ltc_frames, \
+}
+
+static int snd_hdspm_info_tco_ltc_frames(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps",
+ "30 fps"};
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ return 0;
+}
+
+static int hdspm_tco_ltc_frames(struct hdspm *hdspm)
+{
+ u32 status;
+ int ret = 0;
+
+ status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
+ if (status & HDSPM_TCO1_LTC_Input_valid) {
+ switch (status & (HDSPM_TCO1_LTC_Format_LSB |
+ HDSPM_TCO1_LTC_Format_MSB)) {
+ case 0:
+ /* 24 fps */
+ ret = 1;
+ break;
+ case HDSPM_TCO1_LTC_Format_LSB:
+ /* 25 fps */
+ ret = 2;
+ break;
+ case HDSPM_TCO1_LTC_Format_MSB:
+ /* 25 fps */
+ ret = 3;
+ break;
+ default:
+ /* 30 fps */
+ ret = 4;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int snd_hdspm_get_tco_ltc_frames(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.enumerated.item[0] = hdspm_tco_ltc_frames(hdspm);
+ return 0;
+}
+
#define HDSPM_TOGGLE_SETTING(xname, xindex) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
@@ -2974,17 +3122,7 @@ static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "optical", "coaxial" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3046,17 +3184,7 @@ static int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "Single", "Double" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3129,17 +3257,7 @@ static int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "Single", "Double", "Quad" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3215,17 +3333,7 @@ static int snd_hdspm_info_madi_speedmode(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "Single", "Double", "Quad" };
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3445,19 +3553,30 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,
.get = snd_hdspm_get_sync_check \
}
+#define HDSPM_TCO_LOCK_CHECK(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .private_value = xindex, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_hdspm_tco_info_lock_check, \
+ .get = snd_hdspm_get_sync_check \
+}
+
+
static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "No Lock", "Lock", "Sync", "N/A" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 4;
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
+ ENUMERATED_CTL_INFO(uinfo, texts);
+ return 0;
+}
+
+static int snd_hdspm_tco_info_lock_check(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[] = { "No Lock", "Lock" };
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3590,6 +3709,14 @@ static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx)
return 0;
}
+static int hdspm_tco_input_check(struct hdspm *hdspm, u32 mask)
+{
+ u32 status;
+ status = hdspm_read(hdspm, HDSPM_RD_TCO + 4);
+
+ return (status & mask) ? 1 : 0;
+}
+
static int hdspm_tco_sync_check(struct hdspm *hdspm)
{
@@ -3697,6 +3824,22 @@ static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol,
}
+ if (hdspm->tco) {
+ switch (kcontrol->private_value) {
+ case 11:
+ /* Check TCO for lock state of its current input */
+ val = hdspm_tco_input_check(hdspm, HDSPM_TCO1_TCO_lock);
+ break;
+ case 12:
+ /* Check TCO for valid time code on LTC input. */
+ val = hdspm_tco_input_check(hdspm,
+ HDSPM_TCO1_LTC_Input_valid);
+ break;
+ default:
+ break;
+ }
+ }
+
if (-1 == val)
val = 3;
@@ -3813,17 +3956,7 @@ static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "44.1 kHz", "48 kHz" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3869,17 +4002,7 @@ static int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "0", "+ 0.1 %", "- 0.1 %", "+ 4 %", "- 4 %" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 5;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3924,17 +4047,7 @@ static int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -3981,17 +4094,7 @@ static int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol,
{
static char *texts[] = { "24 fps", "25 fps", "29.97fps",
"29.97 dfps", "30 fps", "30 dfps" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 6;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -4037,17 +4140,7 @@ static int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[] = { "LTC", "Video", "WCK" };
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 3;
-
- if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
- uinfo->value.enumerated.item =
- uinfo->value.enumerated.items - 1;
-
- strcpy(uinfo->value.enumerated.name,
- texts[uinfo->value.enumerated.item]);
-
+ ENUMERATED_CTL_INFO(uinfo, texts);
return 0;
}
@@ -4145,6 +4238,7 @@ static struct snd_kcontrol_new snd_hdspm_controls_madi[] = {
HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 3),
HDSPM_TOGGLE_SETTING("Line Out", HDSPM_LineOut),
HDSPM_TOGGLE_SETTING("TX 64 channels mode", HDSPM_TX_64ch),
+ HDSPM_TOGGLE_SETTING("Disable 96K frames", HDSPM_SMUX),
HDSPM_TOGGLE_SETTING("Clear Track Marker", HDSPM_clr_tms),
HDSPM_TOGGLE_SETTING("Safe Mode", HDSPM_AutoInp),
HDSPM_INPUT_SELECT("Input Select", 0),
@@ -4272,7 +4366,11 @@ static struct snd_kcontrol_new snd_hdspm_controls_tco[] = {
HDSPM_TCO_WCK_CONVERSION("TCO WCK Conversion", 0),
HDSPM_TCO_FRAME_RATE("TCO Frame Rate", 0),
HDSPM_TCO_SYNC_SOURCE("TCO Sync Source", 0),
- HDSPM_TCO_WORD_TERM("TCO Word Term", 0)
+ HDSPM_TCO_WORD_TERM("TCO Word Term", 0),
+ HDSPM_TCO_LOCK_CHECK("TCO Input Check", 11),
+ HDSPM_TCO_LOCK_CHECK("TCO LTC Valid", 12),
+ HDSPM_TCO_LTC_FRAMES("TCO Detected Frame Rate", 0),
+ HDSPM_TCO_VIDEO_INPUT_FORMAT("Video Input Format", 0)
};