CS8409 TAS5764L support
Signed-off-by: Alexander Egorenkov <egorenar-dev@posteo.net>
This commit is contained in:
parent
0b6c2ab84a
commit
8315f6ad70
9 changed files with 15937 additions and 3 deletions
5
Makefile
5
Makefile
|
@ -2,10 +2,9 @@ snd-hda-codec-cs8409-objs := patch_cs8409.o patch_cs8409-tables.o
|
||||||
obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o
|
obj-$(CONFIG_SND_HDA_CODEC_CS8409) += snd-hda-codec-cs8409.o
|
||||||
|
|
||||||
# debug build flags
|
# debug build flags
|
||||||
#KBUILD_EXTRA_CFLAGS = "-DCONFIG_SND_DEBUG=1 -DMYSOUNDDEBUGFULL -DCONFIG_SND_HDA_RECONFIG=1 -Wno-unused-variable -Wno-unused-function"
|
#KBUILD_EXTRA_CFLAGS = "-DCONFIG_SND_DEBUG=1 -DMYSOUNDDEBUGFULL -DAPPLE_PINSENSE_FIXUP -DAPPLE_CODECS -DCONFIG_SND_HDA_RECONFIG=1 -Wno-unused-variable -Wno-unused-function"
|
||||||
# normal build flags
|
# normal build flags
|
||||||
KBUILD_EXTRA_CFLAGS = "-DCONFIG_SND_HDA_RECONFIG=1 -Wno-unused-variable -Wno-unused-function"
|
KBUILD_EXTRA_CFLAGS = "-DAPPLE_PINSENSE_FIXUP -DAPPLE_CODECS -DCONFIG_SND_HDA_RECONFIG=1 -Wno-unused-variable -Wno-unused-function"
|
||||||
|
|
||||||
|
|
||||||
ifdef KVER
|
ifdef KVER
|
||||||
KDIR := /lib/modules/$(KVER)
|
KDIR := /lib/modules/$(KVER)
|
||||||
|
|
3083
patch_cirrus_apple.h
Normal file
3083
patch_cirrus_apple.h
Normal file
File diff suppressed because it is too large
Load diff
2452
patch_cirrus_boot84.h
Normal file
2452
patch_cirrus_boot84.h
Normal file
File diff suppressed because it is too large
Load diff
426
patch_cirrus_hda_generic_copy.h
Normal file
426
patch_cirrus_hda_generic_copy.h
Normal file
|
@ -0,0 +1,426 @@
|
||||||
|
|
||||||
|
/* playback mute control with the software mute bit check */
|
||||||
|
static void sync_auto_mute_bits(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||||
|
struct hda_gen_spec *spec = codec->spec;
|
||||||
|
|
||||||
|
if (spec->auto_mute_via_amp) {
|
||||||
|
hda_nid_t nid = get_amp_nid(kcontrol);
|
||||||
|
bool enabled = !((spec->mute_bits >> nid) & 1);
|
||||||
|
ucontrol->value.integer.value[0] &= enabled;
|
||||||
|
ucontrol->value.integer.value[1] &= enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
sync_auto_mute_bits(kcontrol, ucontrol);
|
||||||
|
return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bound mute controls
|
||||||
|
*/
|
||||||
|
#define AMP_VAL_IDX_SHIFT 19
|
||||||
|
#define AMP_VAL_IDX_MASK (0x0f<<19)
|
||||||
|
|
||||||
|
static int hda_gen_bind_mute_get(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||||
|
unsigned long pval;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&codec->control_mutex);
|
||||||
|
pval = kcontrol->private_value;
|
||||||
|
kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */
|
||||||
|
err = snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
|
||||||
|
kcontrol->private_value = pval;
|
||||||
|
mutex_unlock(&codec->control_mutex);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol)
|
||||||
|
{
|
||||||
|
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||||
|
unsigned long pval;
|
||||||
|
int i, indices, err = 0, change = 0;
|
||||||
|
|
||||||
|
sync_auto_mute_bits(kcontrol, ucontrol);
|
||||||
|
|
||||||
|
mutex_lock(&codec->control_mutex);
|
||||||
|
pval = kcontrol->private_value;
|
||||||
|
indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT;
|
||||||
|
for (i = 0; i < indices; i++) {
|
||||||
|
kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) |
|
||||||
|
(i << AMP_VAL_IDX_SHIFT);
|
||||||
|
err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
|
||||||
|
if (err < 0)
|
||||||
|
break;
|
||||||
|
change |= err;
|
||||||
|
}
|
||||||
|
kcontrol->private_value = pval;
|
||||||
|
mutex_unlock(&codec->control_mutex);
|
||||||
|
return err < 0 ? err : change;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum {
|
||||||
|
HDA_CTL_WIDGET_VOL,
|
||||||
|
HDA_CTL_WIDGET_MUTE,
|
||||||
|
HDA_CTL_BIND_MUTE,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const struct snd_kcontrol_new control_templates[] = {
|
||||||
|
HDA_CODEC_VOLUME(NULL, 0, 0, 0),
|
||||||
|
/* only the put callback is replaced for handling the special mute */
|
||||||
|
{
|
||||||
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
|
.subdevice = HDA_SUBDEV_AMP_FLAG,
|
||||||
|
.info = snd_hda_mixer_amp_switch_info,
|
||||||
|
.get = snd_hda_mixer_amp_switch_get,
|
||||||
|
.put = hda_gen_mixer_mute_put, /* replaced */
|
||||||
|
.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||||
|
.info = snd_hda_mixer_amp_switch_info,
|
||||||
|
.get = hda_gen_bind_mute_get,
|
||||||
|
.put = hda_gen_bind_mute_put, /* replaced */
|
||||||
|
.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* add the powersave loopback-list entry */
|
||||||
|
static int add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx)
|
||||||
|
{
|
||||||
|
struct hda_amp_list *list;
|
||||||
|
|
||||||
|
list = snd_array_new(&spec->loopback_list);
|
||||||
|
if (!list)
|
||||||
|
return -ENOMEM;
|
||||||
|
list->nid = mix;
|
||||||
|
list->dir = HDA_INPUT;
|
||||||
|
list->idx = idx;
|
||||||
|
spec->loopback.amplist = spec->loopback_list.list;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int is_input_pin(struct hda_codec *codec, hda_nid_t nid)
|
||||||
|
{
|
||||||
|
unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
|
||||||
|
return (pincap & AC_PINCAP_IN) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin)
|
||||||
|
//{
|
||||||
|
// struct hda_gen_spec *spec = codec->spec;
|
||||||
|
// struct snd_kcontrol_new *knew;
|
||||||
|
// char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
|
||||||
|
// 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);
|
||||||
|
// if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT)
|
||||||
|
// return 0;
|
||||||
|
//
|
||||||
|
// /* no multiple vref caps? */
|
||||||
|
// if (get_in_jack_num_items(codec, pin) <= 1)
|
||||||
|
// return 0;
|
||||||
|
//
|
||||||
|
// get_jack_mode_name(codec, pin, name, sizeof(name));
|
||||||
|
// knew = snd_hda_gen_add_kctl(spec, name, &in_jack_mode_enum);
|
||||||
|
// if (!knew)
|
||||||
|
// return -ENOMEM;
|
||||||
|
// knew->private_value = pin;
|
||||||
|
// return 0;
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
static void print_nid_path(struct hda_codec *codec,
|
||||||
|
const char *pfx, struct nid_path *path)
|
||||||
|
{
|
||||||
|
char buf[40];
|
||||||
|
char *pos = buf;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
*pos = 0;
|
||||||
|
for (i = 0; i < path->depth; i++)
|
||||||
|
pos += scnprintf(pos, sizeof(buf) - (pos - buf), "%s%02x",
|
||||||
|
pos != buf ? ":" : "",
|
||||||
|
path->path[i]);
|
||||||
|
|
||||||
|
codec_dbg(codec, "%s path: depth=%d '%s'\n", pfx, path->depth, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* check whether the given two widgets can be connected */
|
||||||
|
static bool is_reachable_path(struct hda_codec *codec,
|
||||||
|
hda_nid_t from_nid, hda_nid_t to_nid)
|
||||||
|
{
|
||||||
|
if (!from_nid || !to_nid)
|
||||||
|
return false;
|
||||||
|
return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* fill the label for each input at first */
|
||||||
|
static int fill_input_pin_labels(struct hda_codec *codec)
|
||||||
|
{
|
||||||
|
struct hda_gen_spec *spec = codec->spec;
|
||||||
|
const struct auto_pin_cfg *cfg = &spec->autocfg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cfg->num_inputs; i++) {
|
||||||
|
hda_nid_t pin = cfg->inputs[i].pin;
|
||||||
|
const char *label;
|
||||||
|
int j, idx;
|
||||||
|
|
||||||
|
if (!is_input_pin(codec, pin))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
label = hda_get_autocfg_input_label(codec, cfg, i);
|
||||||
|
idx = 0;
|
||||||
|
for (j = i - 1; j >= 0; j--) {
|
||||||
|
if (spec->input_labels[j] &&
|
||||||
|
!strcmp(spec->input_labels[j], label)) {
|
||||||
|
idx = spec->input_label_idxs[j] + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spec->input_labels[i] = label;
|
||||||
|
spec->input_label_idxs[i] = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse the codec tree and retrieve ADCs */
|
||||||
|
static int fill_adc_nids(struct hda_codec *codec)
|
||||||
|
{
|
||||||
|
struct hda_gen_spec *spec = codec->spec;
|
||||||
|
hda_nid_t nid;
|
||||||
|
hda_nid_t *adc_nids = spec->adc_nids;
|
||||||
|
int max_nums = ARRAY_SIZE(spec->adc_nids);
|
||||||
|
int nums = 0;
|
||||||
|
|
||||||
|
for_each_hda_codec_node(nid, codec) {
|
||||||
|
unsigned int caps = get_wcaps(codec, nid);
|
||||||
|
int type = get_wcaps_type(caps);
|
||||||
|
|
||||||
|
if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL))
|
||||||
|
continue;
|
||||||
|
adc_nids[nums] = nid;
|
||||||
|
if (++nums >= max_nums)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
spec->num_adc_nids = nums;
|
||||||
|
|
||||||
|
codec_dbg(codec, "fill_adc_nids num nids %d\n",nums);
|
||||||
|
|
||||||
|
/* copy the detected ADCs to all_adcs[] */
|
||||||
|
spec->num_all_adcs = nums;
|
||||||
|
memcpy(spec->all_adcs, spec->adc_nids, nums * sizeof(hda_nid_t));
|
||||||
|
|
||||||
|
return nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define update_pin_ctl(codec, pin, val) \
|
||||||
|
snd_hda_codec_write_cache(codec, pin, 0, \
|
||||||
|
AC_VERB_SET_PIN_WIDGET_CONTROL, val)
|
||||||
|
|
||||||
|
/* set the pinctl target value and write it if requested */
|
||||||
|
static void set_pin_target(struct hda_codec *codec, hda_nid_t pin,
|
||||||
|
unsigned int val, bool do_write)
|
||||||
|
{
|
||||||
|
if (!pin)
|
||||||
|
return;
|
||||||
|
val = snd_hda_correct_pin_ctl(codec, pin, val);
|
||||||
|
snd_hda_codec_set_pin_target(codec, pin, val);
|
||||||
|
if (do_write)
|
||||||
|
update_pin_ctl(codec, pin, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* nid, dir and idx */
|
||||||
|
#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19))
|
||||||
|
|
||||||
|
/* check whether the given ctl is already assigned in any path elements */
|
||||||
|
static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type)
|
||||||
|
{
|
||||||
|
struct hda_gen_spec *spec = codec->spec;
|
||||||
|
const struct nid_path *path;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
val &= AMP_VAL_COMPARE_MASK;
|
||||||
|
snd_array_for_each(&spec->paths, i, path) {
|
||||||
|
if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check whether a control with the given (nid, dir, idx) was assigned */
|
||||||
|
static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
|
||||||
|
int dir, int idx, int type)
|
||||||
|
{
|
||||||
|
unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir);
|
||||||
|
return is_ctl_used(codec, val, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* return true if either a volume or a mute amp is found for the given
|
||||||
|
* aamix path; the amp has to be either in the mixer node or its direct leaf
|
||||||
|
*/
|
||||||
|
static bool look_for_mix_leaf_ctls(struct hda_codec *codec, hda_nid_t mix_nid,
|
||||||
|
hda_nid_t pin, unsigned int *mix_val,
|
||||||
|
unsigned int *mute_val)
|
||||||
|
{
|
||||||
|
int idx, num_conns;
|
||||||
|
const hda_nid_t *list;
|
||||||
|
hda_nid_t nid;
|
||||||
|
|
||||||
|
idx = snd_hda_get_conn_index(codec, mix_nid, pin, true);
|
||||||
|
if (idx < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*mix_val = *mute_val = 0;
|
||||||
|
if (nid_has_volume(codec, mix_nid, HDA_INPUT))
|
||||||
|
*mix_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
|
||||||
|
if (nid_has_mute(codec, mix_nid, HDA_INPUT))
|
||||||
|
*mute_val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT);
|
||||||
|
if (*mix_val && *mute_val)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* check leaf node */
|
||||||
|
num_conns = snd_hda_get_conn_list(codec, mix_nid, &list);
|
||||||
|
if (num_conns < idx)
|
||||||
|
return false;
|
||||||
|
nid = list[idx];
|
||||||
|
if (!*mix_val && nid_has_volume(codec, nid, HDA_OUTPUT) &&
|
||||||
|
!is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_VOL_CTL))
|
||||||
|
*mix_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
|
||||||
|
if (!*mute_val && nid_has_mute(codec, nid, HDA_OUTPUT) &&
|
||||||
|
!is_ctl_associated(codec, nid, HDA_OUTPUT, 0, NID_PATH_MUTE_CTL))
|
||||||
|
*mute_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
|
||||||
|
|
||||||
|
return *mix_val || *mute_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* add dynamic controls from template */
|
||||||
|
static struct snd_kcontrol_new *
|
||||||
|
add_control(struct hda_gen_spec *spec, int type, const char *name,
|
||||||
|
int cidx, unsigned long val)
|
||||||
|
{
|
||||||
|
struct snd_kcontrol_new *knew;
|
||||||
|
|
||||||
|
knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]);
|
||||||
|
if (!knew)
|
||||||
|
return NULL;
|
||||||
|
knew->index = cidx;
|
||||||
|
if (get_amp_nid_(val))
|
||||||
|
knew->subdevice = HDA_SUBDEV_AMP_FLAG;
|
||||||
|
knew->private_value = val;
|
||||||
|
return knew;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int add_control_with_pfx(struct hda_gen_spec *spec, int type,
|
||||||
|
const char *pfx, const char *dir,
|
||||||
|
const char *sfx, int cidx, unsigned long val)
|
||||||
|
{
|
||||||
|
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
|
||||||
|
snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
|
||||||
|
if (!add_control(spec, type, name, cidx, val))
|
||||||
|
return -ENOMEM;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define add_pb_vol_ctrl(spec, type, pfx, val) \
|
||||||
|
add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
|
||||||
|
#define add_pb_sw_ctrl(spec, type, pfx, val) \
|
||||||
|
add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
|
||||||
|
#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \
|
||||||
|
add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
|
||||||
|
#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \
|
||||||
|
add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
|
||||||
|
|
||||||
|
|
||||||
|
/* create input playback/capture controls for the given pin */
|
||||||
|
static int new_analog_input(struct hda_codec *codec, int input_idx,
|
||||||
|
hda_nid_t pin, const char *ctlname, int ctlidx,
|
||||||
|
hda_nid_t mix_nid)
|
||||||
|
{
|
||||||
|
struct hda_gen_spec *spec = codec->spec;
|
||||||
|
struct nid_path *path;
|
||||||
|
unsigned int mix_val, mute_val;
|
||||||
|
int err, idx;
|
||||||
|
|
||||||
|
if (!look_for_mix_leaf_ctls(codec, mix_nid, pin, &mix_val, &mute_val))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
path = snd_hda_add_new_path(codec, pin, mix_nid, 0);
|
||||||
|
if (!path)
|
||||||
|
return -EINVAL;
|
||||||
|
print_nid_path(codec, "loopback", path);
|
||||||
|
spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path);
|
||||||
|
|
||||||
|
idx = path->idx[path->depth - 1];
|
||||||
|
if (mix_val) {
|
||||||
|
err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, mix_val);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
path->ctls[NID_PATH_VOL_CTL] = mix_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mute_val) {
|
||||||
|
err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, mute_val);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
path->ctls[NID_PATH_MUTE_CTL] = mute_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
path->active = true;
|
||||||
|
path->stream_enabled = true; /* no DAC/ADC involved */
|
||||||
|
err = add_loopback_list(spec, mix_nid, idx);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (spec->mixer_nid != spec->mixer_merge_nid &&
|
||||||
|
!spec->loopback_merge_path) {
|
||||||
|
path = snd_hda_add_new_path(codec, spec->mixer_nid,
|
||||||
|
spec->mixer_merge_nid, 0);
|
||||||
|
if (path) {
|
||||||
|
print_nid_path(codec, "loopback-merge", path);
|
||||||
|
path->active = true;
|
||||||
|
path->pin_fixed = true; /* static route */
|
||||||
|
path->stream_enabled = true; /* no DAC/ADC involved */
|
||||||
|
spec->loopback_merge_path =
|
||||||
|
snd_hda_get_path_idx(codec, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
1906
patch_cirrus_new84.h
Normal file
1906
patch_cirrus_new84.h
Normal file
File diff suppressed because it is too large
Load diff
5954
patch_cirrus_real84.h
Normal file
5954
patch_cirrus_real84.h
Normal file
File diff suppressed because it is too large
Load diff
1955
patch_cirrus_real84_i2c.h
Normal file
1955
patch_cirrus_real84_i2c.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1444,6 +1444,8 @@ void dolphin_fixups(struct hda_codec *codec, const struct hda_fixup *fix, int ac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int patch_cs8409_apple(struct hda_codec *codec);
|
||||||
|
|
||||||
static int patch_cs8409(struct hda_codec *codec)
|
static int patch_cs8409(struct hda_codec *codec)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
@ -1451,8 +1453,19 @@ static int patch_cs8409(struct hda_codec *codec)
|
||||||
if (!cs8409_alloc_spec(codec))
|
if (!cs8409_alloc_spec(codec))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
printk("snd_hda_intel: Primary patch_cs8409\n");
|
||||||
|
|
||||||
snd_hda_pick_fixup(codec, cs8409_models, cs8409_fixup_tbl, cs8409_fixups);
|
snd_hda_pick_fixup(codec, cs8409_models, cs8409_fixup_tbl, cs8409_fixups);
|
||||||
|
|
||||||
|
// this seems the easiest way to separate and jump into the code for handling Apple machines using the 8409
|
||||||
|
// note now freeing the just allocated spec - this undos the delayed work as not using mutex yet
|
||||||
|
if (codec->fixup_id == HDA_FIXUP_ID_NOT_SET) {
|
||||||
|
printk("snd_hda_intel: Primary patch_cs8409 NOT FOUND trying APPLE\n");
|
||||||
|
cs8409_free(codec);
|
||||||
|
err = patch_cs8409_apple(codec);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
codec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id,
|
codec_dbg(codec, "Picked ID=%d, VID=%08x, DEV=%08x\n", codec->fixup_id,
|
||||||
codec->bus->pci->subsystem_vendor,
|
codec->bus->pci->subsystem_vendor,
|
||||||
codec->bus->pci->subsystem_device);
|
codec->bus->pci->subsystem_device);
|
||||||
|
@ -1469,6 +1482,12 @@ static int patch_cs8409(struct hda_codec *codec)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// for the moment split the new code into an include file
|
||||||
|
|
||||||
|
#include "patch_cirrus_apple.h"
|
||||||
|
|
||||||
|
|
||||||
static const struct hda_device_id snd_hda_id_cs8409[] = {
|
static const struct hda_device_id snd_hda_id_cs8409[] = {
|
||||||
HDA_CODEC_ENTRY(0x10138409, "CS8409", patch_cs8409),
|
HDA_CODEC_ENTRY(0x10138409, "CS8409", patch_cs8409),
|
||||||
{} /* terminator */
|
{} /* terminator */
|
||||||
|
|
140
patch_cs8409.h
140
patch_cs8409.h
|
@ -297,6 +297,23 @@ struct cs8409_cir_param {
|
||||||
unsigned int coeff;
|
unsigned int coeff;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef APPLE_CODECS
|
||||||
|
struct unsol_item {
|
||||||
|
struct list_head list;
|
||||||
|
unsigned int idx;
|
||||||
|
unsigned int res;
|
||||||
|
};
|
||||||
|
struct hda_cvt_setup_apple {
|
||||||
|
hda_nid_t nid;
|
||||||
|
u8 stream_tag;
|
||||||
|
u8 channel_id;
|
||||||
|
u16 format_id;
|
||||||
|
unsigned char active; /* cvt is currently used */
|
||||||
|
unsigned char dirty; /* setups should be cleared */
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
struct sub_codec {
|
struct sub_codec {
|
||||||
struct hda_codec *codec;
|
struct hda_codec *codec;
|
||||||
unsigned int addr;
|
unsigned int addr;
|
||||||
|
@ -307,6 +324,9 @@ struct sub_codec {
|
||||||
|
|
||||||
unsigned int hp_jack_in:1;
|
unsigned int hp_jack_in:1;
|
||||||
unsigned int mic_jack_in:1;
|
unsigned int mic_jack_in:1;
|
||||||
|
#ifdef APPLE_CODECS
|
||||||
|
unsigned int linein_jack_in:1;
|
||||||
|
#endif
|
||||||
unsigned int suspended:1;
|
unsigned int suspended:1;
|
||||||
unsigned int paged:1;
|
unsigned int paged:1;
|
||||||
unsigned int last_page;
|
unsigned int last_page;
|
||||||
|
@ -340,6 +360,126 @@ struct cs8409_spec {
|
||||||
unsigned int init_done:1;
|
unsigned int init_done:1;
|
||||||
unsigned int build_ctrl_done:1;
|
unsigned int build_ctrl_done:1;
|
||||||
|
|
||||||
|
#ifdef APPLE_CODECS
|
||||||
|
|
||||||
|
// additional data for Apple 8409 system
|
||||||
|
|
||||||
|
unsigned int spdif_detect:1;
|
||||||
|
unsigned int spdif_present:1;
|
||||||
|
unsigned int sense_b:1;
|
||||||
|
hda_nid_t vendor_nid;
|
||||||
|
|
||||||
|
/* digital beep */
|
||||||
|
hda_nid_t beep_nid;
|
||||||
|
|
||||||
|
/* for MBP SPDIF control */
|
||||||
|
int (*spdif_sw_put)(struct snd_kcontrol *kcontrol,
|
||||||
|
struct snd_ctl_elem_value *ucontrol);
|
||||||
|
|
||||||
|
// so it appears we have "concurrency" in the linux HDA code
|
||||||
|
// in that if unsolicited responses occur which perform extensive verbs
|
||||||
|
// the hda verbs are intermixed with eg extensive start playback verbs
|
||||||
|
// on OSX we appear to have blocks of verbs during which unsolicited responses
|
||||||
|
// are logged but the unsolicited verbs occur after the verb block
|
||||||
|
// this flag is used to flag such verb blocks and the list will store the
|
||||||
|
// responses
|
||||||
|
// we use a pre-allocated list - if we have more than 10 outstanding unsols
|
||||||
|
// we will drop
|
||||||
|
// not clear if mutexes would be the way to go
|
||||||
|
int block_unsol;
|
||||||
|
struct list_head unsol_list;
|
||||||
|
struct unsol_item unsol_items_prealloc[10];
|
||||||
|
int unsol_items_prealloc_used[10];
|
||||||
|
|
||||||
|
// add in specific nids for the intmike and linein as they seem to swap
|
||||||
|
// between macbook pros (14,3) and imacs (18,3)
|
||||||
|
int intmike_nid;
|
||||||
|
int linein_nid;
|
||||||
|
int intmike_adc_nid;
|
||||||
|
int linein_amp_nid;
|
||||||
|
|
||||||
|
// the following flag bits also need swapping
|
||||||
|
int reg9_intmike_dmic_mo;
|
||||||
|
int reg9_linein_dmic_mo;
|
||||||
|
int reg82_intmike_dmic_scl;
|
||||||
|
int reg82_linein_dmic_scl;
|
||||||
|
|
||||||
|
|
||||||
|
// add explicit stream format store entries as per hda_codec using a local definition
|
||||||
|
// of hda_cvt_setup (which is local to hda_codec.c)
|
||||||
|
// also use explicit nid versions
|
||||||
|
// (except that means either need explicit functions for each nid or have to lookup
|
||||||
|
// nid each time want to use in a generic function with nid argument)
|
||||||
|
struct hda_cvt_setup_apple nid_0x02;
|
||||||
|
struct hda_cvt_setup_apple nid_0x03;
|
||||||
|
struct hda_cvt_setup_apple nid_0x0a;
|
||||||
|
struct hda_cvt_setup_apple nid_0x22;
|
||||||
|
struct hda_cvt_setup_apple nid_0x23;
|
||||||
|
struct hda_cvt_setup_apple nid_0x1a;
|
||||||
|
|
||||||
|
|
||||||
|
// new item to deal with jack presence as Apple (and now Dell) seems to have barfed
|
||||||
|
// the HDA spec by using a separate headphone chip
|
||||||
|
int jack_present;
|
||||||
|
|
||||||
|
// save the type of headphone connected
|
||||||
|
int headset_type;
|
||||||
|
|
||||||
|
// if headphone has mike or not
|
||||||
|
int have_mike;
|
||||||
|
|
||||||
|
// if headphone has buttons or not
|
||||||
|
int have_buttons;
|
||||||
|
|
||||||
|
// current stream channel count
|
||||||
|
int stream_channels;
|
||||||
|
|
||||||
|
// set when playing for plug/unplug events while playing
|
||||||
|
int playing;
|
||||||
|
|
||||||
|
// set when capturing for plug/unplug events while capturing
|
||||||
|
int capturing;
|
||||||
|
|
||||||
|
// changing coding - OSX sets up the format on plugin
|
||||||
|
// then does some minimal setup when start play
|
||||||
|
// initial coding delayed any format setup till actually play
|
||||||
|
// this works for no mike but not for mike - we need to initialize
|
||||||
|
// the mike on plugin
|
||||||
|
// this flag will be set when we have done the format setup
|
||||||
|
// so know if need to do it on play or not
|
||||||
|
// now need 2 flags - one for play and one for capture
|
||||||
|
int headset_play_format_setup_needed;
|
||||||
|
int headset_capture_format_setup_needed;
|
||||||
|
|
||||||
|
int headset_presetup_done;
|
||||||
|
|
||||||
|
|
||||||
|
int use_data;
|
||||||
|
|
||||||
|
|
||||||
|
// this is new item for dealing with headset plugins
|
||||||
|
// so can distinguish which phase we are in if have multiple interrupts
|
||||||
|
// not really used now have analyzed interrupts properly
|
||||||
|
int headset_phase;
|
||||||
|
|
||||||
|
// another dirty hack item to manage the different headset enable codes
|
||||||
|
int headset_enable;
|
||||||
|
|
||||||
|
int play_init;
|
||||||
|
int capture_init;
|
||||||
|
|
||||||
|
|
||||||
|
// new item to limit times we redo unmute/play
|
||||||
|
struct timespec64 last_play_time;
|
||||||
|
// record the first play time - we have a problem there
|
||||||
|
// some initial plays that I dont understand - so skip any setup
|
||||||
|
// till sometime after the first play
|
||||||
|
struct timespec64 first_play_time;
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* verb exec op override */
|
/* verb exec op override */
|
||||||
int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
|
int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, unsigned int flags,
|
||||||
unsigned int *res);
|
unsigned int *res);
|
||||||
|
|
Loading…
Reference in a new issue