diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index a46ddb85e83a..f911e3656209 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -296,6 +296,12 @@ Cirrus Logic CS4206/4207 imac27 IMac 27 Inch auto BIOS setup (default) +Cirrus Logic CS4208 +=================== + mba6 MacBook Air 6,1 and 6,2 + gpio0 Enable GPIO 0 amp + auto BIOS setup (default) + VIA VT17xx/VT18xx/VT20xx ======================== auto BIOS setup (default) diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 98969541cbcc..bea523a5d852 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -139,6 +139,18 @@ static int snd_compr_open(struct inode *inode, struct file *f) static int snd_compr_free(struct inode *inode, struct file *f) { struct snd_compr_file *data = f->private_data; + struct snd_compr_runtime *runtime = data->stream.runtime; + + switch (runtime->state) { + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_PAUSED: + data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP); + break; + default: + break; + } + data->stream.ops->free(&data->stream); kfree(data->stream.runtime->buffer); kfree(data->stream.runtime); @@ -837,7 +849,8 @@ static int snd_compress_dev_disconnect(struct snd_device *device) struct snd_compr *compr; compr = device->device_data; - snd_unregister_device(compr->direction, compr->card, compr->device); + snd_unregister_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card, + compr->device); return 0; } diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index b524f89a1f13..18d972501585 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -111,6 +111,9 @@ enum { /* 0x0009 - 0x0014 -> 12 test regs */ /* 0x0015 - visibility reg */ +/* Cirrus Logic CS4208 */ +#define CS4208_VENDOR_NID 0x24 + /* * Cirrus Logic CS4210 * @@ -223,6 +226,16 @@ static const struct hda_verb cs_coef_init_verbs[] = { {} /* terminator */ }; +static const struct hda_verb cs4208_coef_init_verbs[] = { + {0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */ + {0x24, AC_VERB_SET_PROC_STATE, 0x01}, /* VPW: processing on */ + {0x24, AC_VERB_SET_COEF_INDEX, 0x0033}, + {0x24, AC_VERB_SET_PROC_COEF, 0x0001}, /* A1 ICS */ + {0x24, AC_VERB_SET_COEF_INDEX, 0x0034}, + {0x24, AC_VERB_SET_PROC_COEF, 0x1C01}, /* A1 Enable, A Thresh = 300mV */ + {} /* terminator */ +}; + /* Errata: CS4207 rev C0/C1/C2 Silicon * * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf @@ -295,6 +308,8 @@ static int cs_init(struct hda_codec *codec) /* init_verb sequence for C0/C1/C2 errata*/ snd_hda_sequence_write(codec, cs_errata_init_verbs); snd_hda_sequence_write(codec, cs_coef_init_verbs); + } else if (spec->vendor_nid == CS4208_VENDOR_NID) { + snd_hda_sequence_write(codec, cs4208_coef_init_verbs); } snd_hda_gen_init(codec); @@ -434,6 +449,29 @@ static const struct hda_pintbl mba42_pincfgs[] = { {} /* terminator */ }; +static const struct hda_pintbl mba6_pincfgs[] = { + { 0x10, 0x032120f0 }, /* HP */ + { 0x11, 0x500000f0 }, + { 0x12, 0x90100010 }, /* Speaker */ + { 0x13, 0x500000f0 }, + { 0x14, 0x500000f0 }, + { 0x15, 0x770000f0 }, + { 0x16, 0x770000f0 }, + { 0x17, 0x430000f0 }, + { 0x18, 0x43ab9030 }, /* Mic */ + { 0x19, 0x770000f0 }, + { 0x1a, 0x770000f0 }, + { 0x1b, 0x770000f0 }, + { 0x1c, 0x90a00090 }, + { 0x1d, 0x500000f0 }, + { 0x1e, 0x500000f0 }, + { 0x1f, 0x500000f0 }, + { 0x20, 0x500000f0 }, + { 0x21, 0x430000f0 }, + { 0x22, 0x430000f0 }, + {} /* terminator */ +}; + static void cs420x_fixup_gpio_13(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -556,22 +594,23 @@ static int patch_cs420x(struct hda_codec *codec) /* * CS4208 support: - * Its layout is no longer compatible with CS4206/CS4207, and the generic - * parser seems working fairly well, except for trivial fixups. + * Its layout is no longer compatible with CS4206/CS4207 */ enum { + CS4208_MBA6, CS4208_GPIO0, }; static const struct hda_model_fixup cs4208_models[] = { { .id = CS4208_GPIO0, .name = "gpio0" }, + { .id = CS4208_MBA6, .name = "mba6" }, {} }; static const struct snd_pci_quirk cs4208_fixup_tbl[] = { /* codec SSID */ - SND_PCI_QUIRK(0x106b, 0x7100, "MacBookPro 6,1", CS4208_GPIO0), - SND_PCI_QUIRK(0x106b, 0x7200, "MacBookPro 6,2", CS4208_GPIO0), + SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6), + SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6), {} /* terminator */ }; @@ -588,18 +627,35 @@ static void cs4208_fixup_gpio0(struct hda_codec *codec, } static const struct hda_fixup cs4208_fixups[] = { + [CS4208_MBA6] = { + .type = HDA_FIXUP_PINS, + .v.pins = mba6_pincfgs, + .chained = true, + .chain_id = CS4208_GPIO0, + }, [CS4208_GPIO0] = { .type = HDA_FIXUP_FUNC, .v.func = cs4208_fixup_gpio0, }, }; +/* correct the 0dB offset of input pins */ +static void cs4208_fix_amp_caps(struct hda_codec *codec, hda_nid_t adc) +{ + unsigned int caps; + + caps = query_amp_caps(codec, adc, HDA_INPUT); + caps &= ~(AC_AMPCAP_OFFSET); + caps |= 0x02; + snd_hda_override_amp_caps(codec, adc, HDA_INPUT, caps); +} + static int patch_cs4208(struct hda_codec *codec) { struct cs_spec *spec; int err; - spec = cs_alloc_spec(codec, 0); /* no specific w/a */ + spec = cs_alloc_spec(codec, CS4208_VENDOR_NID); if (!spec) return -ENOMEM; @@ -609,6 +665,12 @@ static int patch_cs4208(struct hda_codec *codec) cs4208_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + snd_hda_override_wcaps(codec, 0x18, + get_wcaps(codec, 0x18) | AC_WCAP_STEREO); + cs4208_fix_amp_caps(codec, 0x18); + cs4208_fix_amp_caps(codec, 0x1b); + cs4208_fix_amp_caps(codec, 0x1c); + err = cs_parse_auto_config(codec); if (err < 0) goto error; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 3d8cd04455a6..7ea0245fc6bd 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1149,32 +1149,43 @@ static int hdmi_choose_cvt(struct hda_codec *codec, } static void haswell_config_cvts(struct hda_codec *codec, - int pin_id, int mux_id) + hda_nid_t pin_nid, int mux_idx) { struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin; - int pin_idx, mux_idx; - int curr; - int err; + hda_nid_t nid, end_nid; + int cvt_idx, curr; + struct hdmi_spec_per_cvt *per_cvt; - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - per_pin = get_pin(spec, pin_idx); + /* configure all pins, including "no physical connection" ones */ + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { + unsigned int wid_caps = get_wcaps(codec, nid); + unsigned int wid_type = get_wcaps_type(wid_caps); - if (pin_idx == pin_id) + if (wid_type != AC_WID_PIN) continue; - curr = snd_hda_codec_read(codec, per_pin->pin_nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); + if (nid == pin_nid) + continue; - /* Choose another unused converter */ - if (curr == mux_id) { - err = hdmi_choose_cvt(codec, pin_idx, NULL, &mux_idx); - if (err < 0) - return; - snd_printdd("HDMI: choose converter %d for pin %d\n", mux_idx, pin_idx); - snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, + curr = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); + if (curr != mux_idx) + continue; + + /* choose an unassigned converter. The conveters in the + * connection list are in the same order as in the codec. + */ + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { + per_cvt = get_cvt(spec, cvt_idx); + if (!per_cvt->assigned) { + snd_printdd("choose cvt %d for pin nid %d\n", + cvt_idx, nid); + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, - mux_idx); + cvt_idx); + break; + } } } } @@ -1216,7 +1227,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, /* configure unused pins to choose other converters */ if (is_haswell(codec)) - haswell_config_cvts(codec, pin_idx, mux_idx); + haswell_config_cvts(codec, per_pin->pin_nid, mux_idx); snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index bc07d369fac4..0e303b99a47c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3439,6 +3439,9 @@ static void alc283_fixup_chromebook(struct hda_codec *codec, /* Set to manual mode */ val = alc_read_coef_idx(codec, 0x06); alc_write_coef_idx(codec, 0x06, val & ~0x000c); + /* Enable Line1 input control by verb */ + val = alc_read_coef_idx(codec, 0x1a); + alc_write_coef_idx(codec, 0x1a, val | (1 << 4)); break; } } @@ -3531,6 +3534,7 @@ enum { ALC269VB_FIXUP_ORDISSIMO_EVE2, ALC283_FIXUP_CHROME_BOOK, ALC282_FIXUP_ASUS_TX300, + ALC283_FIXUP_INT_MIC, }; static const struct hda_fixup alc269_fixups[] = { @@ -3790,6 +3794,16 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc282_fixup_asus_tx300, }, + [ALC283_FIXUP_INT_MIC] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x20, AC_VERB_SET_COEF_INDEX, 0x1a}, + {0x20, AC_VERB_SET_PROC_COEF, 0x0011}, + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -3874,7 +3888,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),