diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/core/compress_offload.c | 12 | ||||
-rw-r--r-- | sound/pci/hda/hda_auto_parser.c | 7 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.c | 2 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 1 | ||||
-rw-r--r-- | sound/pci/hda/hda_intel.c | 18 | ||||
-rw-r--r-- | sound/pci/hda/hda_jack.c | 5 | ||||
-rw-r--r-- | sound/pci/hda/patch_cirrus.c | 223 | ||||
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 86 | ||||
-rw-r--r-- | sound/usb/card.c | 4 | ||||
-rw-r--r-- | sound/usb/endpoint.c | 24 | ||||
-rw-r--r-- | sound/usb/endpoint.h | 3 | ||||
-rw-r--r-- | sound/usb/pcm.c | 70 |
12 files changed, 304 insertions, 151 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index ec2118d0e27a..68fe02c7400a 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -80,14 +80,12 @@ static int snd_compr_open(struct inode *inode, struct file *f) int maj = imajor(inode); int ret; - if (f->f_flags & O_WRONLY) + if ((f->f_flags & O_ACCMODE) == O_WRONLY) dirn = SND_COMPRESS_PLAYBACK; - else if (f->f_flags & O_RDONLY) + else if ((f->f_flags & O_ACCMODE) == O_RDONLY) dirn = SND_COMPRESS_CAPTURE; - else { - pr_err("invalid direction\n"); + else return -EINVAL; - } if (maj == snd_major) compr = snd_lookup_minor_data(iminor(inode), @@ -409,6 +407,10 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, unsigned int buffer_size; void *buffer; + if (params->buffer.fragment_size == 0 || + params->buffer.fragments > SIZE_MAX / params->buffer.fragment_size) + return -EINVAL; + buffer_size = params->buffer.fragment_size * params->buffer.fragments; if (stream->ops->copy) { buffer = NULL; diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 4f7d2dfcef7b..a98e25ebfd63 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -141,7 +141,6 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, memset(sequences_hp, 0, sizeof(sequences_hp)); assoc_line_out = 0; - codec->ignore_misc_bit = true; 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); @@ -157,9 +156,6 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, continue; def_conf = snd_hda_codec_get_pincfg(codec, nid); - if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & - AC_DEFCFG_MISC_NO_PRESENCE)) - codec->ignore_misc_bit = false; conn = get_defcfg_connect(def_conf); if (conn == AC_JACK_PORT_NONE) continue; @@ -739,7 +735,8 @@ void snd_hda_pick_fixup(struct hda_codec *codec, for (q = quirk; q->subvendor; q++) { unsigned int vendorid = q->subdevice | (q->subvendor << 16); - if (vendorid == codec->subsystem_id) { + unsigned int mask = 0xffff0000 | q->subdevice_mask; + if ((codec->subsystem_id & mask) == (vendorid & mask)) { id = q->value; #ifdef CONFIG_SND_DEBUG_VERBOSE name = q->name; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index c4c62edca556..960800b862ab 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2368,6 +2368,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) } if (codec->patch_ops.free) codec->patch_ops.free(codec); + memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); snd_hda_jack_tbl_clear(codec); codec->proc_widget_hook = NULL; codec->spec = NULL; @@ -2383,7 +2384,6 @@ int snd_hda_codec_reset(struct hda_codec *codec) codec->num_pcms = 0; codec->pcm_info = NULL; codec->preset = NULL; - memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); codec->slave_dig_outs = NULL; codec->spdif_status_reset = 0; module_put(codec->owner); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index ed3de8d151c8..507fe8a917b6 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -862,7 +862,6 @@ struct hda_codec { unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */ unsigned int pins_shutup:1; /* pins are shut up */ unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ - unsigned int ignore_misc_bit:1; /* ignore MISC_NO_PRESENCE bit */ unsigned int no_jack_detect:1; /* Machine has no jack-detection */ unsigned int pcm_format_first:1; /* PCM format must be set first */ unsigned int epss:1; /* supporting EPSS? */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 6a19f6af9517..195d84726187 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -64,7 +64,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; static char *model[SNDRV_CARDS]; -static int position_fix[SNDRV_CARDS]; +static int position_fix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_only[SNDRV_CARDS]; @@ -88,7 +88,7 @@ module_param_array(model, charp, NULL, 0444); MODULE_PARM_DESC(model, "Use the given board model."); module_param_array(position_fix, int, NULL, 0444); MODULE_PARM_DESC(position_fix, "DMA pointer read method." - "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO)."); + "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO)."); module_param_array(bdl_pos_adj, int, NULL, 0644); MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); module_param_array(probe_mask, int, NULL, 0444); @@ -415,6 +415,7 @@ struct azx_dev { */ unsigned int insufficient :1; unsigned int wc_marked:1; + unsigned int no_period_wakeup:1; }; /* CORB/RIRB */ @@ -1419,7 +1420,7 @@ static int azx_setup_periods(struct azx *chip, ofs = 0; azx_dev->frags = 0; pos_adj = bdl_pos_adj[chip->dev_index]; - if (pos_adj > 0) { + if (!azx_dev->no_period_wakeup && pos_adj > 0) { struct snd_pcm_runtime *runtime = substream->runtime; int pos_align = pos_adj; pos_adj = (pos_adj * runtime->rate + 47999) / 48000; @@ -1435,8 +1436,7 @@ static int azx_setup_periods(struct azx *chip, pos_adj = 0; } else { ofs = setup_bdle(chip, substream, azx_dev, - &bdl, ofs, pos_adj, - !substream->runtime->no_period_wakeup); + &bdl, ofs, pos_adj, true); if (ofs < 0) goto error; } @@ -1449,7 +1449,7 @@ static int azx_setup_periods(struct azx *chip, else ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs, period_bytes, - !substream->runtime->no_period_wakeup); + !azx_dev->no_period_wakeup); if (ofs < 0) goto error; } @@ -1922,10 +1922,12 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) if (bufsize != azx_dev->bufsize || period_bytes != azx_dev->period_bytes || - format_val != azx_dev->format_val) { + format_val != azx_dev->format_val || + runtime->no_period_wakeup != azx_dev->no_period_wakeup) { azx_dev->bufsize = bufsize; azx_dev->period_bytes = period_bytes; azx_dev->format_val = format_val; + azx_dev->no_period_wakeup = runtime->no_period_wakeup; err = azx_setup_periods(chip, substream, azx_dev); if (err < 0) return err; @@ -2794,6 +2796,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1043, 0x1b43, "ASUS K53E", POS_FIX_POSBUF), SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB), SND_PCI_QUIRK(0x10de, 0xcb89, "Macbook Pro 7,1", POS_FIX_LPIB), SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB), @@ -2810,6 +2813,7 @@ static int __devinit check_position_fix(struct azx *chip, int fix) const struct snd_pci_quirk *q; switch (fix) { + case POS_FIX_AUTO: case POS_FIX_LPIB: case POS_FIX_POSBUF: case POS_FIX_VIACOMBO: diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index aaccc0236bda..c9333c9688fb 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -26,9 +26,8 @@ bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) return false; if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_PRES_DETECT)) return false; - if (!codec->ignore_misc_bit && - (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & - AC_DEFCFG_MISC_NO_PRESENCE)) + if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & + AC_DEFCFG_MISC_NO_PRESENCE) return false; if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)) return false; diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 0bddb3ee1101..fcfc9f0a056b 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -34,7 +34,8 @@ */ struct cs_spec { - int board_config; + struct hda_gen_spec gen; + struct auto_pin_cfg autocfg; struct hda_multi_out multiout; struct snd_kcontrol *vmaster_sw; @@ -80,16 +81,20 @@ enum { CS420X_MBP53, CS420X_MBP55, CS420X_IMAC27, - CS420X_IMAC27_122, - CS420X_APPLE, + CS420X_GPIO_13, + CS420X_GPIO_23, + CS420X_MBP101, + CS420X_MBP101_COEF, CS420X_AUTO, - CS420X_MODELS + /* aliases */ + CS420X_IMAC27_122 = CS420X_GPIO_23, + CS420X_APPLE = CS420X_GPIO_13, }; /* CS421x boards */ enum { CS421X_CDB4210, - CS421X_MODELS + CS421X_SENSE_B, }; /* Vendor-specific processing widget */ @@ -1157,6 +1162,14 @@ static const struct hda_verb cs_errata_init_verbs[] = { {} /* terminator */ }; +static const struct hda_verb mbp101_init_verbs[] = { + {0x11, AC_VERB_SET_COEF_INDEX, 0x0002}, + {0x11, AC_VERB_SET_PROC_COEF, 0x100a}, + {0x11, AC_VERB_SET_COEF_INDEX, 0x0004}, + {0x11, AC_VERB_SET_PROC_COEF, 0x000f}, + {} +}; + /* SPDIF setup */ static void init_digital(struct hda_codec *codec) { @@ -1278,38 +1291,32 @@ static int cs_parse_auto_config(struct hda_codec *codec) return 0; } -static const char * const cs420x_models[CS420X_MODELS] = { - [CS420X_MBP53] = "mbp53", - [CS420X_MBP55] = "mbp55", - [CS420X_IMAC27] = "imac27", - [CS420X_IMAC27_122] = "imac27_122", - [CS420X_APPLE] = "apple", - [CS420X_AUTO] = "auto", +static const struct hda_model_fixup cs420x_models[] = { + { .id = CS420X_MBP53, .name = "mbp53" }, + { .id = CS420X_MBP55, .name = "mbp55" }, + { .id = CS420X_IMAC27, .name = "imac27" }, + { .id = CS420X_IMAC27_122, .name = "imac27_122" }, + { .id = CS420X_APPLE, .name = "apple" }, + { .id = CS420X_MBP101, .name = "mbp101" }, + {} }; - -static const struct snd_pci_quirk cs420x_cfg_tbl[] = { +static const struct snd_pci_quirk cs420x_fixup_tbl[] = { SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53), SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55), SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55), SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55), /* this conflicts with too many other models */ /*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/ - {} /* terminator */ -}; -static const struct snd_pci_quirk cs420x_codec_cfg_tbl[] = { + /* codec SSID */ SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122), + SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101), SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS420X_APPLE), {} /* terminator */ }; -struct cs_pincfg { - hda_nid_t nid; - u32 val; -}; - -static const struct cs_pincfg mbp53_pincfgs[] = { +static const struct hda_pintbl mbp53_pincfgs[] = { { 0x09, 0x012b4050 }, { 0x0a, 0x90100141 }, { 0x0b, 0x90100140 }, @@ -1323,7 +1330,7 @@ static const struct cs_pincfg mbp53_pincfgs[] = { {} /* terminator */ }; -static const struct cs_pincfg mbp55_pincfgs[] = { +static const struct hda_pintbl mbp55_pincfgs[] = { { 0x09, 0x012b4030 }, { 0x0a, 0x90100121 }, { 0x0b, 0x90100120 }, @@ -1337,7 +1344,7 @@ static const struct cs_pincfg mbp55_pincfgs[] = { {} /* terminator */ }; -static const struct cs_pincfg imac27_pincfgs[] = { +static const struct hda_pintbl imac27_pincfgs[] = { { 0x09, 0x012b4050 }, { 0x0a, 0x90100140 }, { 0x0b, 0x90100142 }, @@ -1351,22 +1358,78 @@ static const struct cs_pincfg imac27_pincfgs[] = { {} /* terminator */ }; -static const struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = { - [CS420X_MBP53] = mbp53_pincfgs, - [CS420X_MBP55] = mbp55_pincfgs, - [CS420X_IMAC27] = imac27_pincfgs, +static const struct hda_pintbl mbp101_pincfgs[] = { + { 0x0d, 0x40ab90f0 }, + { 0x0e, 0x90a600f0 }, + { 0x12, 0x50a600f0 }, + {} /* terminator */ }; -static void fix_pincfg(struct hda_codec *codec, int model, - const struct cs_pincfg **pin_configs) +static void cs420x_fixup_gpio_13(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + struct cs_spec *spec = codec->spec; + spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */ + spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ + spec->gpio_mask = spec->gpio_dir = + spec->gpio_eapd_hp | spec->gpio_eapd_speaker; + } +} + +static void cs420x_fixup_gpio_23(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - const struct cs_pincfg *cfg = pin_configs[model]; - if (!cfg) - return; - for (; cfg->nid; cfg++) - snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + struct cs_spec *spec = codec->spec; + spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */ + spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ + spec->gpio_mask = spec->gpio_dir = + spec->gpio_eapd_hp | spec->gpio_eapd_speaker; + } } +static const struct hda_fixup cs420x_fixups[] = { + [CS420X_MBP53] = { + .type = HDA_FIXUP_PINS, + .v.pins = mbp53_pincfgs, + .chained = true, + .chain_id = CS420X_APPLE, + }, + [CS420X_MBP55] = { + .type = HDA_FIXUP_PINS, + .v.pins = mbp55_pincfgs, + .chained = true, + .chain_id = CS420X_GPIO_13, + }, + [CS420X_IMAC27] = { + .type = HDA_FIXUP_PINS, + .v.pins = imac27_pincfgs, + .chained = true, + .chain_id = CS420X_GPIO_13, + }, + [CS420X_GPIO_13] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs420x_fixup_gpio_13, + }, + [CS420X_GPIO_23] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs420x_fixup_gpio_23, + }, + [CS420X_MBP101] = { + .type = HDA_FIXUP_PINS, + .v.pins = mbp101_pincfgs, + .chained = true, + .chain_id = CS420X_MBP101_COEF, + }, + [CS420X_MBP101_COEF] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = mbp101_init_verbs, + .chained = true, + .chain_id = CS420X_GPIO_13, + }, +}; + static int patch_cs420x(struct hda_codec *codec) { struct cs_spec *spec; @@ -1379,33 +1442,9 @@ static int patch_cs420x(struct hda_codec *codec) spec->vendor_nid = CS420X_VENDOR_NID; - spec->board_config = - snd_hda_check_board_config(codec, CS420X_MODELS, - cs420x_models, cs420x_cfg_tbl); - if (spec->board_config < 0) - spec->board_config = - snd_hda_check_board_codec_sid_config(codec, - CS420X_MODELS, NULL, cs420x_codec_cfg_tbl); - if (spec->board_config >= 0) - fix_pincfg(codec, spec->board_config, cs_pincfgs); - - switch (spec->board_config) { - case CS420X_IMAC27: - case CS420X_MBP53: - case CS420X_MBP55: - case CS420X_APPLE: - spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */ - spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ - spec->gpio_mask = spec->gpio_dir = - spec->gpio_eapd_hp | spec->gpio_eapd_speaker; - break; - case CS420X_IMAC27_122: - spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */ - spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */ - spec->gpio_mask = spec->gpio_dir = - spec->gpio_eapd_hp | spec->gpio_eapd_speaker; - break; - } + snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl, + cs420x_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = cs_parse_auto_config(codec); if (err < 0) @@ -1413,6 +1452,8 @@ static int patch_cs420x(struct hda_codec *codec) codec->patch_ops = cs_patch_ops; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; error: @@ -1430,11 +1471,12 @@ static int patch_cs420x(struct hda_codec *codec) */ /* CS4210 board names */ -static const char *cs421x_models[CS421X_MODELS] = { - [CS421X_CDB4210] = "cdb4210", +static const struct hda_model_fixup cs421x_models[] = { + { .id = CS421X_CDB4210, .name = "cdb4210" }, + {} }; -static const struct snd_pci_quirk cs421x_cfg_tbl[] = { +static const struct snd_pci_quirk cs421x_fixup_tbl[] = { /* Test Intel board + CDB2410 */ SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210), {} /* terminator */ @@ -1442,7 +1484,7 @@ static const struct snd_pci_quirk cs421x_cfg_tbl[] = { /* CS4210 board pinconfigs */ /* Default CS4210 (CDB4210)*/ -static const struct cs_pincfg cdb4210_pincfgs[] = { +static const struct hda_pintbl cdb4210_pincfgs[] = { { 0x05, 0x0321401f }, { 0x06, 0x90170010 }, { 0x07, 0x03813031 }, @@ -1452,8 +1494,26 @@ static const struct cs_pincfg cdb4210_pincfgs[] = { {} /* terminator */ }; -static const struct cs_pincfg *cs421x_pincfgs[CS421X_MODELS] = { - [CS421X_CDB4210] = cdb4210_pincfgs, +/* Setup GPIO/SENSE for each board (if used) */ +static void cs421x_fixup_sense_b(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct cs_spec *spec = codec->spec; + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->sense_b = 1; +} + +static const struct hda_fixup cs421x_fixups[] = { + [CS421X_CDB4210] = { + .type = HDA_FIXUP_PINS, + .v.pins = cdb4210_pincfgs, + .chained = true, + .chain_id = CS421X_SENSE_B, + }, + [CS421X_SENSE_B] = { + .type = HDA_FIXUP_FUNC, + .v.func = cs421x_fixup_sense_b, + } }; static const struct hda_verb cs421x_coef_init_verbs[] = { @@ -1935,26 +1995,9 @@ static int patch_cs4210(struct hda_codec *codec) spec->vendor_nid = CS4210_VENDOR_NID; - spec->board_config = - snd_hda_check_board_config(codec, CS421X_MODELS, - cs421x_models, cs421x_cfg_tbl); - if (spec->board_config >= 0) - fix_pincfg(codec, spec->board_config, cs421x_pincfgs); - /* - Setup GPIO/SENSE for each board (if used) - */ - switch (spec->board_config) { - case CS421X_CDB4210: - snd_printd("CS4210 board: %s\n", - cs421x_models[spec->board_config]); -/* spec->gpio_mask = 3; - spec->gpio_dir = 3; - spec->gpio_data = 3; -*/ - spec->sense_b = 1; - - break; - } + snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl, + cs421x_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* Update the GPIO/DMIC/SENSE_B pinmux before the configuration @@ -1969,6 +2012,8 @@ static int patch_cs4210(struct hda_codec *codec) codec->patch_ops = cs421x_patch_ops; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; error: diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index c16d0f822b5b..bb6c50e7b5da 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -103,6 +103,7 @@ enum { STAC_HP_ZEPHYR, STAC_92HD83XXX_HP_LED, STAC_92HD83XXX_HP_INV_LED, + STAC_92HD83XXX_HP_MIC_LED, STAC_92HD83XXX_MODELS }; @@ -215,6 +216,9 @@ struct sigmatel_spec { unsigned int vref_mute_led_nid; /* pin NID for mute-LED vref control */ unsigned int vref_led; + unsigned int mic_mute_led_gpio; /* capture mute LED GPIO */ + bool mic_mute_led_on; /* current mic mute state */ + /* stream */ unsigned int stream_delay; @@ -1075,7 +1079,7 @@ static struct snd_kcontrol_new stac_smux_mixer = { static const char * const slave_pfxs[] = { "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Speaker", "IEC958", + "Headphone", "Speaker", "IEC958", "PCM", NULL }; @@ -1679,6 +1683,7 @@ static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { [STAC_HP_ZEPHYR] = "hp-zephyr", [STAC_92HD83XXX_HP_LED] = "hp-led", [STAC_92HD83XXX_HP_INV_LED] = "hp-inv-led", + [STAC_92HD83XXX_HP_MIC_LED] = "hp-mic-led", }; static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { @@ -1703,6 +1708,8 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x165B, "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), + SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x18df, + "HP Folio", STAC_92HD83XXX_HP_MIC_LED), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388, "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389, @@ -2791,18 +2798,27 @@ stac_control_new(struct sigmatel_spec *spec, return knew; } -static int stac92xx_add_control_temp(struct sigmatel_spec *spec, - const struct snd_kcontrol_new *ktemp, - int idx, const char *name, - unsigned long val) +static struct snd_kcontrol_new * +add_control_temp(struct sigmatel_spec *spec, + const struct snd_kcontrol_new *ktemp, + int idx, const char *name, + unsigned long val) { struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name, HDA_SUBDEV_AMP_FLAG); if (!knew) - return -ENOMEM; + return NULL; knew->index = idx; knew->private_value = val; - return 0; + return knew; +} + +static int stac92xx_add_control_temp(struct sigmatel_spec *spec, + const struct snd_kcontrol_new *ktemp, + int idx, const char *name, + unsigned long val) +{ + return add_control_temp(spec, ktemp, idx, name, val) ? 0 : -ENOMEM; } static inline int stac92xx_add_control_idx(struct sigmatel_spec *spec, @@ -3245,18 +3261,56 @@ static int create_multi_out_ctls(struct hda_codec *codec, int num_outs, return 0; } +static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, + unsigned int dir_mask, unsigned int data); + +/* hook for controlling mic-mute LED GPIO */ +static int stac92xx_capture_sw_put_led(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + int err; + bool mute; + + err = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + if (err <= 0) + return err; + mute = !(ucontrol->value.integer.value[0] && + ucontrol->value.integer.value[1]); + if (spec->mic_mute_led_on != mute) { + spec->mic_mute_led_on = mute; + if (mute) + spec->gpio_data |= spec->mic_mute_led_gpio; + else + spec->gpio_data &= ~spec->mic_mute_led_gpio; + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); + } + return err; +} + static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol, unsigned long sw, int idx) { + struct sigmatel_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; int err; + err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, "Capture Volume", vol); if (err < 0) return err; - err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_MUTE, idx, - "Capture Switch", sw); - if (err < 0) - return err; + + knew = add_control_temp(spec, + &stac92xx_control_templates[STAC_CTL_WIDGET_MUTE], + idx, "Capture Switch", sw); + if (!knew) + return -ENOMEM; + /* add a LED hook for some HP laptops */ + if (spec->mic_mute_led_gpio) + knew->put = stac92xx_capture_sw_put_led; + return 0; } @@ -5579,6 +5633,9 @@ again: case STAC_92HD83XXX_HP_INV_LED: default_polarity = 1; break; + case STAC_92HD83XXX_HP_MIC_LED: + spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ + break; } if (find_mute_led_cfg(codec, default_polarity)) @@ -5597,6 +5654,13 @@ again: } } + if (spec->mic_mute_led_gpio) { + spec->gpio_mask |= spec->mic_mute_led_gpio; + spec->gpio_dir |= spec->mic_mute_led_gpio; + spec->mic_mute_led_on = true; + spec->gpio_data |= spec->mic_mute_led_gpio; + } + err = stac92xx_parse_auto_config(codec); if (!err) { if (spec->board_config < 0) { diff --git a/sound/usb/card.c b/sound/usb/card.c index d5b5c3388e28..4a469f0cb6d4 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -553,7 +553,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, struct snd_usb_audio *chip) { struct snd_card *card; - struct list_head *p; + struct list_head *p, *n; if (chip == (void *)-1L) return; @@ -570,7 +570,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, snd_usb_stream_disconnect(p); } /* release the endpoint resources */ - list_for_each(p, &chip->ep_list) { + list_for_each_safe(p, n, &chip->ep_list) { snd_usb_endpoint_free(p); } /* release the midi resources */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 94b08a6087a3..a83a18dbac25 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -142,7 +142,7 @@ int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep) * * For implicit feedback, next_packet_size() is unused. */ -static int next_packet_size(struct snd_usb_endpoint *ep) +int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep) { unsigned long flags; int ret; @@ -183,15 +183,6 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep, ep->retire_data_urb(ep->data_subs, urb); } -static void prepare_outbound_urb_sizes(struct snd_usb_endpoint *ep, - struct snd_urb_ctx *ctx) -{ - int i; - - for (i = 0; i < ctx->packets; ++i) - ctx->packet_size[i] = next_packet_size(ep); -} - /* * Prepare a PLAYBACK urb for submission to the bus. */ @@ -376,7 +367,6 @@ static void snd_complete_urb(struct urb *urb) goto exit_clear; } - prepare_outbound_urb_sizes(ep, ctx); prepare_outbound_urb(ep, ctx); } else { retire_inbound_urb(ep, ctx); @@ -805,7 +795,9 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, /** * snd_usb_endpoint_start: start an snd_usb_endpoint * - * @ep: the endpoint to start + * @ep: the endpoint to start + * @can_sleep: flag indicating whether the operation is executed in + * non-atomic context * * A call to this function will increment the use count of the endpoint. * In case it is not already running, the URBs for this endpoint will be @@ -815,7 +807,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, * * Returns an error if the URB submission failed, 0 in all other cases. */ -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep) { int err; unsigned int i; @@ -827,6 +819,11 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) if (++ep->use_count != 1) return 0; + /* just to be sure */ + deactivate_urbs(ep, 0, can_sleep); + if (can_sleep) + wait_clear_urbs(ep); + ep->active_mask = 0; ep->unlink_mask = 0; ep->phase = 0; @@ -858,7 +855,6 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) goto __error; if (usb_pipeout(ep->pipe)) { - prepare_outbound_urb_sizes(ep, urb->context); prepare_outbound_urb(ep, urb->context); } else { prepare_inbound_urb(ep, urb->context); diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index ee2723fb174f..cbbbdf226d66 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -13,7 +13,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep); -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep); +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep); void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, int force, int can_sleep, int wait); int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); @@ -21,6 +21,7 @@ int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep); void snd_usb_endpoint_free(struct list_head *head); int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep); +int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep); void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, struct snd_usb_endpoint *sender, diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 62ec808ed792..f782ce19bf5a 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -212,7 +212,7 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, } } -static int start_endpoints(struct snd_usb_substream *subs) +static int start_endpoints(struct snd_usb_substream *subs, int can_sleep) { int err; @@ -225,7 +225,7 @@ static int start_endpoints(struct snd_usb_substream *subs) snd_printdd(KERN_DEBUG "Starting data EP @%p\n", ep); ep->data_subs = subs; - err = snd_usb_endpoint_start(ep); + err = snd_usb_endpoint_start(ep, can_sleep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags); return err; @@ -236,10 +236,25 @@ static int start_endpoints(struct snd_usb_substream *subs) !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { struct snd_usb_endpoint *ep = subs->sync_endpoint; + if (subs->data_endpoint->iface != subs->sync_endpoint->iface || + subs->data_endpoint->alt_idx != subs->sync_endpoint->alt_idx) { + err = usb_set_interface(subs->dev, + subs->sync_endpoint->iface, + subs->sync_endpoint->alt_idx); + if (err < 0) { + snd_printk(KERN_ERR + "%d:%d:%d: cannot set interface (%d)\n", + subs->dev->devnum, + subs->sync_endpoint->iface, + subs->sync_endpoint->alt_idx, err); + return -EIO; + } + } + snd_printdd(KERN_DEBUG "Starting sync EP @%p\n", ep); ep->sync_slave = subs->data_endpoint; - err = snd_usb_endpoint_start(ep); + err = snd_usb_endpoint_start(ep, can_sleep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags); return err; @@ -544,13 +559,10 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) subs->last_frame_number = 0; runtime->delay = 0; - /* clear the pending deactivation on the target EPs */ - deactivate_endpoints(subs); - /* for playback, submit the URBs now; otherwise, the first hwptr_done * updates for all URBs would happen at the same time when starting */ if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) - return start_endpoints(subs); + return start_endpoints(subs, 1); return 0; } @@ -1032,6 +1044,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, struct urb *urb) { struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; + struct snd_usb_endpoint *ep = subs->data_endpoint; struct snd_urb_ctx *ctx = urb->context; unsigned int counts, frames, bytes; int i, stride, period_elapsed = 0; @@ -1043,7 +1056,11 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, urb->number_of_packets = 0; spin_lock_irqsave(&subs->lock, flags); for (i = 0; i < ctx->packets; i++) { - counts = ctx->packet_size[i]; + if (ctx->packet_size[i]) + counts = ctx->packet_size[i]; + else + counts = snd_usb_endpoint_next_packet_size(ep); + /* set up descriptor */ urb->iso_frame_desc[i].offset = frames * stride; urb->iso_frame_desc[i].length = counts * stride; @@ -1094,7 +1111,16 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, subs->hwptr_done += bytes; if (subs->hwptr_done >= runtime->buffer_size * stride) subs->hwptr_done -= runtime->buffer_size * stride; + + /* update delay with exact number of samples queued */ + runtime->delay = subs->last_delay; runtime->delay += frames; + subs->last_delay = runtime->delay; + + /* realign last_frame_number */ + subs->last_frame_number = usb_get_current_frame_number(subs->dev); + subs->last_frame_number &= 0xFF; /* keep 8 LSBs */ + spin_unlock_irqrestore(&subs->lock, flags); urb->transfer_buffer_length = bytes; if (period_elapsed) @@ -1112,12 +1138,32 @@ static void retire_playback_urb(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; int stride = runtime->frame_bits >> 3; int processed = urb->transfer_buffer_length / stride; + int est_delay; + + /* ignore the delay accounting when procssed=0 is given, i.e. + * silent payloads are procssed before handling the actual data + */ + if (!processed) + return; spin_lock_irqsave(&subs->lock, flags); - if (processed > runtime->delay) - runtime->delay = 0; + est_delay = snd_usb_pcm_delay(subs, runtime->rate); + /* update delay with exact number of samples played */ + if (processed > subs->last_delay) + subs->last_delay = 0; else - runtime->delay -= processed; + subs->last_delay -= processed; + runtime->delay = subs->last_delay; + + /* + * Report when delay estimate is off by more than 2ms. + * The error should be lower than 2ms since the estimate relies + * on two reads of a counter updated every ms. + */ + if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2) + snd_printk(KERN_DEBUG "delay: estimated %d, actual %d\n", + est_delay, subs->last_delay); + spin_unlock_irqrestore(&subs->lock, flags); } @@ -1175,7 +1221,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream switch (cmd) { case SNDRV_PCM_TRIGGER_START: - err = start_endpoints(subs); + err = start_endpoints(subs, 0); if (err < 0) return err; |