From a686632fd9a857776798f3479e2b58b07d938076 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Mar 2016 12:18:33 +0100 Subject: ALSA: hda - Split out Intel-specific codes from patch_generic_hdmi() We have too many Intel-specific codes in patch_hdmi_generic() despite its function name. And this makes it difficult to adjust per chipset, e.g. for allowing the audio notifier on an old chipset, one would need to add an explicit if() check. This patch attempts some code refactoring and cleanups in this regard; the Intel-specific codes are moved out of patch_generic_hdmi() into the new functions, patch_i915_hsw_hdmi() and patch_i915_byt_hdmi(), depending on the chipset. The other old Intel chipsets keep using patch_generic_hdmi() without Intel hacks. The existing patch_generic_hdmi() is also split to a few components so that they can be called from the Intel codec parsers. There are still many is_haswell*() and is_valleyview*() macro usages in the code. They will be cleaned up later. For the time being, only the entry are concerned. Along with this change, the i915_bound flag and the on-demand i915 component binding have been removed as a cleanup, since there is no user at this moment. This will be added back later once when Cougar Point and else start using the i915 eld notifier. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 5af372d..48c63fe 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -154,7 +154,6 @@ struct hdmi_spec { /* i915/powerwell (Haswell+/Valleyview+) specific */ bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */ struct i915_audio_component_audio_ops i915_audio_ops; - bool i915_bound; /* was i915 bound in this driver? */ struct hdac_chmap chmap; }; @@ -2074,6 +2073,18 @@ static void hdmi_array_free(struct hdmi_spec *spec) snd_array_free(&spec->cvts); } +static void generic_spec_free(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec) { + hdmi_array_free(spec); + kfree(spec); + codec->spec = NULL; + } + codec->dp_mst = false; +} + static void generic_hdmi_free(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; @@ -2098,10 +2109,7 @@ static void generic_hdmi_free(struct hda_codec *codec) spec->pcm_rec[pcm_idx].jack = NULL; } - if (spec->i915_bound) - snd_hdac_i915_exit(&codec->bus->core); - hdmi_array_free(spec); - kfree(spec); + generic_spec_free(codec); } #ifdef CONFIG_PM @@ -2139,6 +2147,54 @@ static const struct hdmi_ops generic_standard_hdmi_ops = { .setup_stream = hdmi_setup_stream, }; +/* allocate codec->spec and assign/initialize generic parser ops */ +static int alloc_generic_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + + spec->ops = generic_standard_hdmi_ops; + mutex_init(&spec->pcm_lock); + snd_hdac_register_chmap_ops(&codec->core, &spec->chmap); + + spec->chmap.ops.get_chmap = hdmi_get_chmap; + spec->chmap.ops.set_chmap = hdmi_set_chmap; + spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; + + codec->spec = spec; + hdmi_array_init(spec, 4); + + codec->patch_ops = generic_hdmi_patch_ops; + + return 0; +} + +/* generic HDMI parser */ +static int patch_generic_hdmi(struct hda_codec *codec) +{ + int err; + + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + + err = hdmi_parse_codec(codec); + if (err < 0) { + generic_spec_free(codec); + return err; + } + + generic_hdmi_init_per_pins(codec); + return 0; +} + +/* + * Intel codec parsers and helpers + */ + static void intel_haswell_fixup_connect_list(struct hda_codec *codec, hda_nid_t nid) { @@ -2234,92 +2290,96 @@ static void intel_pin_eld_notify(void *audio_ptr, int port) check_presence_and_report(codec, pin_nid); } -static int patch_generic_hdmi(struct hda_codec *codec) +/* register i915 component pin_eld_notify callback */ +static void register_i915_notifier(struct hda_codec *codec) { - struct hdmi_spec *spec; - - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) - return -ENOMEM; - - spec->ops = generic_standard_hdmi_ops; - mutex_init(&spec->pcm_lock); - snd_hdac_register_chmap_ops(&codec->core, &spec->chmap); + struct hdmi_spec *spec = codec->spec; - spec->chmap.ops.get_chmap = hdmi_get_chmap; - spec->chmap.ops.set_chmap = hdmi_set_chmap; - spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; + spec->use_acomp_notifier = true; + spec->i915_audio_ops.audio_ptr = codec; + /* intel_audio_codec_enable() or intel_audio_codec_disable() + * will call pin_eld_notify with using audio_ptr pointer + * We need make sure audio_ptr is really setup + */ + wmb(); + spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify; + snd_hdac_i915_register_notifier(&spec->i915_audio_ops); +} - codec->spec = spec; - hdmi_array_init(spec, 4); +/* Intel Haswell and onwards; audio component with eld notifier */ +static int patch_i915_hsw_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; -#ifdef CONFIG_SND_HDA_I915 - /* Try to bind with i915 for Intel HSW+ codecs (if not done yet) */ - if ((codec->core.vendor_id >> 16) == 0x8086 && - is_haswell_plus(codec)) { -#if 0 - /* on-demand binding leads to an unbalanced refcount when - * both i915 and hda drivers are probed concurrently; - * disabled temporarily for now - */ - if (!codec->bus->core.audio_component) - if (!snd_hdac_i915_init(&codec->bus->core)) - spec->i915_bound = true; -#endif - /* use i915 audio component notifier for hotplug */ - if (codec->bus->core.audio_component) - spec->use_acomp_notifier = true; + /* HSW+ requires i915 binding */ + if (!codec->bus->core.audio_component) { + codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); + return -ENODEV; } -#endif - if (is_haswell_plus(codec)) { - intel_haswell_enable_all_pins(codec, true); - intel_haswell_fixup_enable_dp12(codec); - } + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + spec = codec->spec; - /* For Valleyview/Cherryview, only the display codec is in the display - * power well and can use link_power ops to request/release the power. - * For Haswell/Broadwell, the controller is also in the power well and + intel_haswell_enable_all_pins(codec, true); + intel_haswell_fixup_enable_dp12(codec); + + /* For Haswell/Broadwell, the controller is also in the power well and * can cover the codec power request, and so need not set this flag. - * For previous platforms, there is no such power well feature. */ - if (is_valleyview_plus(codec) || is_skylake(codec) || - is_broxton(codec)) + if (!is_haswell(codec) && !is_broadwell(codec)) codec->core.link_power_control = 1; - if (hdmi_parse_codec(codec) < 0) { - if (spec->i915_bound) - snd_hdac_i915_exit(&codec->bus->core); - codec->spec = NULL; - kfree(spec); - return -EINVAL; + codec->patch_ops.set_power_state = haswell_set_power_state; + codec->dp_mst = true; + codec->depop_delay = 0; + codec->auto_runtime_pm = 1; + + err = hdmi_parse_codec(codec); + if (err < 0) { + generic_spec_free(codec); + return err; } - codec->patch_ops = generic_hdmi_patch_ops; - if (is_haswell_plus(codec)) { - codec->patch_ops.set_power_state = haswell_set_power_state; - codec->dp_mst = true; + + generic_hdmi_init_per_pins(codec); + register_i915_notifier(codec); + return 0; +} + +/* Intel Baytrail and Braswell; without get_eld notifier */ +static int patch_i915_byt_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + /* requires i915 binding */ + if (!codec->bus->core.audio_component) { + codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n"); + return -ENODEV; } - /* Enable runtime pm for HDMI audio codec of HSW/BDW/SKL/BYT/BSW */ - if (is_haswell_plus(codec) || is_valleyview_plus(codec)) - codec->auto_runtime_pm = 1; + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + spec = codec->spec; - generic_hdmi_init_per_pins(codec); + /* For Valleyview/Cherryview, only the display codec is in the display + * power well and can use link_power ops to request/release the power. + */ + codec->core.link_power_control = 1; + codec->depop_delay = 0; + codec->auto_runtime_pm = 1; - if (codec_has_acomp(codec)) { - codec->depop_delay = 0; - spec->i915_audio_ops.audio_ptr = codec; - /* intel_audio_codec_enable() or intel_audio_codec_disable() - * will call pin_eld_notify with using audio_ptr pointer - * We need make sure audio_ptr is really setup - */ - wmb(); - spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify; - snd_hdac_i915_register_notifier(&spec->i915_audio_ops); + err = hdmi_parse_codec(codec); + if (err < 0) { + generic_spec_free(codec); + return err; } - WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec)); + generic_hdmi_init_per_pins(codec); return 0; } @@ -3498,14 +3558,14 @@ HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi), +HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi), HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi), +HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi), HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi), /* special ID for generic HDMI */ HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi), -- cgit v0.10.2 From 44bb6d0c3f690e60670517859925417bd7c42a22 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Mar 2016 12:36:44 +0100 Subject: ALSA: hda - Apply AMP fix in hdmi_setup_audio_infoframe() generically The need for reprogramming the AMP mute bit at each audio info frame setup isn't always specific to Intel chips. It's safer to set it generically for all codecs with the amp bit, as this verb execution itself isn't too much load. This eliminates one usage of is_haswell_plus() macro, after all. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 48c63fe..fcd207d 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -683,7 +683,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, if (!channels) return; - if (is_haswell_plus(codec)) + /* some HW (e.g. HSW+) needs reprogramming the amp at each time */ + if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP) snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); -- cgit v0.10.2 From 2c1c9b86c6b22dc0cbac3f4ca2c8272c472dc463 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Mar 2016 12:42:06 +0100 Subject: ALSA: hda - Override HDMI setup_stream ops for Intel HSW+ Instead of checking at each time with is_haswell_plus() macro, override the setup_stream ops itself for HSW+ chips. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index fcd207d..9855188 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -864,9 +864,6 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, struct hdmi_spec *spec = codec->spec; int err; - if (is_haswell_plus(codec)) - haswell_verify_D0(codec, cvt_nid, pin_nid); - err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format)); if (err) { @@ -2307,6 +2304,14 @@ static void register_i915_notifier(struct hda_codec *codec) snd_hdac_i915_register_notifier(&spec->i915_audio_ops); } +/* setup_stream ops override for HSW+ */ +static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, u32 stream_tag, int format) +{ + haswell_verify_D0(codec, cvt_nid, pin_nid); + return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); +} + /* Intel Haswell and onwards; audio component with eld notifier */ static int patch_i915_hsw_hdmi(struct hda_codec *codec) { @@ -2338,6 +2343,8 @@ static int patch_i915_hsw_hdmi(struct hda_codec *codec) codec->depop_delay = 0; codec->auto_runtime_pm = 1; + spec->ops.setup_stream = i915_hsw_setup_stream; + err = hdmi_parse_codec(codec); if (err < 0) { generic_spec_free(codec); -- cgit v0.10.2 From 4846a67eb5a1d7cac76e1b22f66e88a8cbbdff3f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Mar 2016 12:56:46 +0100 Subject: ALSA: hda - Introduce pin_cvt_fixup() ops to hdmi parser For reducing the splat of is_haswell_plus() or such macros, this patch introduces pin_cvt_fixup() ops to hdmi_spec. For HSW+ and VLV+ codecs, set this ops so that the driver can call the Intel-specific workarounds appropriately. A gratis bonus that we can remove the mux_id argument from hdmi_choose_cvt(), too, since the fixup function always refers the mux_idx from the given per_pin object. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 9855188..3481b43 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -114,6 +114,9 @@ struct hdmi_ops { int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, hda_nid_t pin_nid, u32 stream_tag, int format); + void (*pin_cvt_fixup)(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + hda_nid_t cvt_nid); }; struct hdmi_pcm { @@ -881,7 +884,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, * of the pin. */ static int hdmi_choose_cvt(struct hda_codec *codec, - int pin_idx, int *cvt_id, int *mux_id) + int pin_idx, int *cvt_id) { struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin; @@ -922,8 +925,6 @@ static int hdmi_choose_cvt(struct hda_codec *codec, if (cvt_id) *cvt_id = cvt_idx; - if (mux_id) - *mux_id = mux_idx; return 0; } @@ -1016,9 +1017,6 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, int mux_idx; struct hdmi_spec *spec = codec->spec; - if (!is_haswell_plus(codec) && !is_valleyview_plus(codec)) - return; - /* On Intel platform, the mapping of converter nid to * mux index of the pins are always the same. * The pin nid may be 0, this means all pins will not @@ -1029,6 +1027,17 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, intel_not_share_assigned_cvt(codec, pin_nid, mux_idx); } +/* skeleton caller of pin_cvt_fixup ops */ +static void pin_cvt_fixup(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + hda_nid_t cvt_nid) +{ + struct hdmi_spec *spec = codec->spec; + + if (spec->ops.pin_cvt_fixup) + spec->ops.pin_cvt_fixup(codec, per_pin, cvt_nid); +} + /* called in hdmi_pcm_open when no pin is assigned to the PCM * in dyn_pcm_assign mode. */ @@ -1046,7 +1055,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo, if (pcm_idx < 0) return -EINVAL; - err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL); + err = hdmi_choose_cvt(codec, -1, &cvt_idx); if (err) return err; @@ -1054,7 +1063,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo, per_cvt->assigned = 1; hinfo->nid = per_cvt->cvt_nid; - intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid); + pin_cvt_fixup(codec, NULL, per_cvt->cvt_nid); set_bit(pcm_idx, &spec->pcm_in_use); /* todo: setup spdif ctls assign */ @@ -1086,7 +1095,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, { struct hdmi_spec *spec = codec->spec; struct snd_pcm_runtime *runtime = substream->runtime; - int pin_idx, cvt_idx, pcm_idx, mux_idx = 0; + int pin_idx, cvt_idx, pcm_idx; struct hdmi_spec_per_pin *per_pin; struct hdmi_eld *eld; struct hdmi_spec_per_cvt *per_cvt = NULL; @@ -1115,7 +1124,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, } } - err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx); + err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx); if (err < 0) { mutex_unlock(&spec->pcm_lock); return err; @@ -1132,11 +1141,10 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, AC_VERB_SET_CONNECT_SEL, - mux_idx); + per_pin->mux_idx); /* configure unused pins to choose other converters */ - if (is_haswell_plus(codec) || is_valleyview_plus(codec)) - intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx); + pin_cvt_fixup(codec, per_pin, 0); snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid); @@ -1369,12 +1377,7 @@ static void update_eld(struct hda_codec *codec, * and this can make HW reset converter selection on a pin. */ if (eld->eld_valid && !old_eld_valid && per_pin->setup) { - if (is_haswell_plus(codec) || is_valleyview_plus(codec)) { - intel_verify_pin_cvt_connect(codec, per_pin); - intel_not_share_assigned_cvt(codec, per_pin->pin_nid, - per_pin->mux_idx); - } - + pin_cvt_fixup(codec, per_pin, 0); hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); } @@ -1709,7 +1712,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, * skip pin setup and return 0 to make audio playback * be ongoing */ - intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid); + pin_cvt_fixup(codec, NULL, cvt_nid); snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format); mutex_unlock(&spec->pcm_lock); @@ -1722,18 +1725,16 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, } per_pin = get_pin(spec, pin_idx); pin_nid = per_pin->pin_nid; - if (is_haswell_plus(codec) || is_valleyview_plus(codec)) { - /* Verify pin:cvt selections to avoid silent audio after S3. - * After S3, the audio driver restores pin:cvt selections - * but this can happen before gfx is ready and such selection - * is overlooked by HW. Thus multiple pins can share a same - * default convertor and mute control will affect each other, - * which can cause a resumed audio playback become silent - * after S3. - */ - intel_verify_pin_cvt_connect(codec, per_pin); - intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx); - } + + /* Verify pin:cvt selections to avoid silent audio after S3. + * After S3, the audio driver restores pin:cvt selections + * but this can happen before gfx is ready and such selection + * is overlooked by HW. Thus multiple pins can share a same + * default convertor and mute control will affect each other, + * which can cause a resumed audio playback become silent + * after S3. + */ + pin_cvt_fixup(codec, per_pin, 0); /* Call sync_audio_rate to set the N/CTS/M manually if necessary */ /* Todo: add DP1.2 MST audio support later */ @@ -2312,6 +2313,20 @@ static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); } +/* pin_cvt_fixup ops override for HSW+ and VLV+ */ +static void i915_pin_cvt_fixup(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + hda_nid_t cvt_nid) +{ + if (per_pin) { + intel_verify_pin_cvt_connect(codec, per_pin); + intel_not_share_assigned_cvt(codec, per_pin->pin_nid, + per_pin->mux_idx); + } else { + intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid); + } +} + /* Intel Haswell and onwards; audio component with eld notifier */ static int patch_i915_hsw_hdmi(struct hda_codec *codec) { @@ -2344,6 +2359,7 @@ static int patch_i915_hsw_hdmi(struct hda_codec *codec) codec->auto_runtime_pm = 1; spec->ops.setup_stream = i915_hsw_setup_stream; + spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; err = hdmi_parse_codec(codec); if (err < 0) { @@ -2381,6 +2397,8 @@ static int patch_i915_byt_hdmi(struct hda_codec *codec) codec->depop_delay = 0; codec->auto_runtime_pm = 1; + spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; + err = hdmi_parse_codec(codec); if (err < 0) { generic_spec_free(codec); -- cgit v0.10.2 From e85015a3797f2665cc6f0339e6407adc00ac4245 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Mar 2016 13:56:19 +0100 Subject: ALSA: hda - Use eld notifier for Intel SandyBridge and IvyBridge HDMI/DP Intel SandyBridge and IvyBridge (CougarPoint and PantherPoint platforms) have also the same digital port vs audio widget mapping (from port B = NID 0x05 to port D = NID 0x07) as Haswell & co. So, we can reuse the existing functions for HSW+ for these platforms without changing there, but just by re-adding the on-demand i915 binding in HDMI codec driver. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 3481b43..09eb26c 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -157,6 +157,7 @@ struct hdmi_spec { /* i915/powerwell (Haswell+/Valleyview+) specific */ bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */ struct i915_audio_component_audio_ops i915_audio_ops; + bool i915_bound; /* was i915 bound in this driver? */ struct hdac_chmap chmap; }; @@ -2077,6 +2078,8 @@ static void generic_spec_free(struct hda_codec *codec) struct hdmi_spec *spec = codec->spec; if (spec) { + if (spec->i915_bound) + snd_hdac_i915_exit(&codec->bus->core); hdmi_array_free(spec); kfree(spec); codec->spec = NULL; @@ -2409,6 +2412,40 @@ static int patch_i915_byt_hdmi(struct hda_codec *codec) return 0; } +/* Intel SandyBridge and IvyBridge; with i915 eld notifier */ +static int patch_i915_cpt_hdmi(struct hda_codec *codec) +{ + struct hdmi_spec *spec; + int err; + + /* no i915 component should have been bound before this */ + if (WARN_ON(codec->bus->core.audio_component)) + return -EBUSY; + + err = alloc_generic_hdmi(codec); + if (err < 0) + return err; + spec = codec->spec; + + /* Try to bind with i915 now */ + err = snd_hdac_i915_init(&codec->bus->core); + if (err < 0) + goto error; + spec->i915_bound = true; + + err = hdmi_parse_codec(codec); + if (err < 0) + goto error; + + generic_hdmi_init_per_pins(codec); + register_i915_notifier(codec); + return 0; + + error: + generic_spec_free(codec); + return err; +} + /* * Shared non-generic implementations */ @@ -3582,8 +3619,8 @@ HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_i915_cpt_hdmi), +HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi), HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi), HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi), HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi), -- cgit v0.10.2 From d745f5e7b8b2961f68b0b9093a0f914a8a83c2ae Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Mar 2016 14:41:58 +0100 Subject: ALSA: hda - Add the pin / port mapping on Intel ILK and VLV Intel IronLake and ValleyView platforms have different HDMI widget pin and digital port mapping from other newer ones. The recent ones (HSW+) have NID 0x05 to 0x07 for port B to port D, while these chips have NID 0x04 to 0x06. For adapting this mapping, pass the codec object instead of the bus object to snd_hdac_sync_audio_rate() and snd_hdac_acomp_get_eld() so that they can check the codec ID and calculate the mapping properly. The changes in the HDMI codec driver side will follow in the later patch. Signed-off-by: Takashi Iwai diff --git a/include/sound/hda_i915.h b/include/sound/hda_i915.h index fa341fc..eed87a7 100644 --- a/include/sound/hda_i915.h +++ b/include/sound/hda_i915.h @@ -10,8 +10,8 @@ int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable); int snd_hdac_display_power(struct hdac_bus *bus, bool enable); int snd_hdac_get_display_clk(struct hdac_bus *bus); -int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate); -int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid, +int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, int rate); +int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, bool *audio_enabled, char *buffer, int max_bytes); int snd_hdac_i915_init(struct hdac_bus *bus); int snd_hdac_i915_exit(struct hdac_bus *bus); @@ -29,12 +29,12 @@ static inline int snd_hdac_get_display_clk(struct hdac_bus *bus) { return 0; } -static inline int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, - int rate) +static inline int snd_hdac_sync_audio_rate(struct hdac_device *codec, + hda_nid_t nid, int rate) { return 0; } -static inline int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid, +static inline int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, bool *audio_enabled, char *buffer, int max_bytes) { diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index fb96aea..750a4ea 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -118,22 +118,40 @@ int snd_hdac_get_display_clk(struct hdac_bus *bus) } EXPORT_SYMBOL_GPL(snd_hdac_get_display_clk); -/* There is a fixed mapping between audio pin node and display port - * on current Intel platforms: +/* There is a fixed mapping between audio pin node and display port. + * on SNB, IVY, HSW, BSW, SKL, BXT, KBL: * Pin Widget 5 - PORT B (port = 1 in i915 driver) * Pin Widget 6 - PORT C (port = 2 in i915 driver) * Pin Widget 7 - PORT D (port = 3 in i915 driver) + * + * on VLV, ILK: + * Pin Widget 4 - PORT B (port = 1 in i915 driver) + * Pin Widget 5 - PORT C (port = 2 in i915 driver) + * Pin Widget 6 - PORT D (port = 3 in i915 driver) */ -static int pin2port(hda_nid_t pin_nid) +static int pin2port(struct hdac_device *codec, hda_nid_t pin_nid) { - if (WARN_ON(pin_nid < 5 || pin_nid > 7)) + int base_nid; + + switch (codec->vendor_id) { + case 0x80860054: /* ILK */ + case 0x80862804: /* ILK */ + case 0x80862882: /* VLV */ + base_nid = 3; + break; + default: + base_nid = 4; + break; + } + + if (WARN_ON(pin_nid <= base_nid || pin_nid > base_nid + 3)) return -1; - return pin_nid - 4; + return pin_nid - base_nid; } /** * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate - * @bus: HDA core bus + * @codec: HDA codec * @nid: the pin widget NID * @rate: the sample rate to set * @@ -143,14 +161,15 @@ static int pin2port(hda_nid_t pin_nid) * This function sets N/CTS value based on the given sample rate. * Returns zero for success, or a negative error code. */ -int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate) +int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, int rate) { + struct hdac_bus *bus = codec->bus; struct i915_audio_component *acomp = bus->audio_component; int port; if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate) return -ENODEV; - port = pin2port(nid); + port = pin2port(codec, nid); if (port < 0) return -EINVAL; return acomp->ops->sync_audio_rate(acomp->dev, port, rate); @@ -159,7 +178,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate); /** * snd_hdac_acomp_get_eld - Get the audio state and ELD via component - * @bus: HDA core bus + * @codec: HDA codec * @nid: the pin widget NID * @audio_enabled: the pointer to store the current audio state * @buffer: the buffer pointer to store ELD bytes @@ -177,16 +196,17 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate); * thus it may be over @max_bytes. If it's over @max_bytes, it implies * that only a part of ELD bytes have been fetched. */ -int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid, +int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid, bool *audio_enabled, char *buffer, int max_bytes) { + struct hdac_bus *bus = codec->bus; struct i915_audio_component *acomp = bus->audio_component; int port; if (!acomp || !acomp->ops || !acomp->ops->get_eld) return -ENODEV; - port = pin2port(nid); + port = pin2port(codec, nid); if (port < 0) return -EINVAL; return acomp->ops->get_eld(acomp->dev, port, audio_enabled, @@ -286,6 +306,9 @@ int snd_hdac_i915_init(struct hdac_bus *bus) struct i915_audio_component *acomp; int ret; + if (WARN_ON(hdac_acomp)) + return -EBUSY; + acomp = kzalloc(sizeof(*acomp), GFP_KERNEL); if (!acomp) return -ENOMEM; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 09eb26c..7e09f5e 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1486,7 +1486,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec, mutex_lock(&per_pin->lock); eld->monitor_present = false; - size = snd_hdac_acomp_get_eld(&codec->bus->core, per_pin->pin_nid, + size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid, &eld->monitor_present, eld->eld_buffer, ELD_MAX_SIZE); if (size > 0) { @@ -1740,7 +1740,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, /* Call sync_audio_rate to set the N/CTS/M manually if necessary */ /* Todo: add DP1.2 MST audio support later */ if (codec_has_acomp(codec)) - snd_hdac_sync_audio_rate(&codec->bus->core, pin_nid, runtime->rate); + snd_hdac_sync_audio_rate(&codec->core, pin_nid, runtime->rate); non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); mutex_lock(&per_pin->lock); -- cgit v0.10.2 From 7ff652ffc06afc7f81839fb1780c57470ac09db6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Mar 2016 14:50:24 +0100 Subject: ALSA: hda - Enable i915 ELD notifier for Intel IronLake and Baytrail Since we have the fixed pin-port mapping for Intel IronLake (IbexPeak) and Baytrail (ValleyView) platforms in the code side, now it's time to add the support in the codec driver side. This patch simply enables the i915 ELD notifier for these in addition with the fix of the mapping from the port to NID in the callback. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 7e09f5e..4833c7b 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2274,12 +2274,23 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg, static void intel_pin_eld_notify(void *audio_ptr, int port) { struct hda_codec *codec = audio_ptr; - int pin_nid = port + 0x04; + int pin_nid; /* we assume only from port-B to port-D */ if (port < 1 || port > 3) return; + switch (codec->core.vendor_id) { + case 0x80860054: /* ILK */ + case 0x80862804: /* ILK */ + case 0x80862882: /* VLV */ + pin_nid = port + 0x03; + break; + default: + pin_nid = port + 0x04; + break; + } + /* skip notification during system suspend (but not in runtime PM); * the state will be updated at resume */ @@ -2375,7 +2386,7 @@ static int patch_i915_hsw_hdmi(struct hda_codec *codec) return 0; } -/* Intel Baytrail and Braswell; without get_eld notifier */ +/* Intel Baytrail and Braswell; with eld notifier */ static int patch_i915_byt_hdmi(struct hda_codec *codec) { struct hdmi_spec *spec; @@ -2409,10 +2420,11 @@ static int patch_i915_byt_hdmi(struct hda_codec *codec) } generic_hdmi_init_per_pins(codec); + register_i915_notifier(codec); return 0; } -/* Intel SandyBridge and IvyBridge; with i915 eld notifier */ +/* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */ static int patch_i915_cpt_hdmi(struct hda_codec *codec) { struct hdmi_spec *spec; @@ -3614,11 +3626,11 @@ HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi), HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi), HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_i915_cpt_hdmi), HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi), -HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_i915_cpt_hdmi), HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_i915_cpt_hdmi), HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi), HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi), -- cgit v0.10.2 From 093dd449782737b50f8e1ee1608720dfd46d8ed2 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 27 Mar 2016 16:09:06 +0900 Subject: ALSA: bebob: remove needless argument from local function The 'vendor_id' argument is not used in the local function. Let's remove it. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 3e4e075..932901d 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -67,7 +67,7 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS); #define MODEL_MAUDIO_PROJECTMIX 0x00010091 static int -name_device(struct snd_bebob *bebob, unsigned int vendor_id) +name_device(struct snd_bebob *bebob) { struct fw_device *fw_dev = fw_parent_device(bebob->unit); char vendor[24] = {0}; @@ -232,7 +232,7 @@ bebob_probe(struct fw_unit *unit, spin_lock_init(&bebob->lock); init_waitqueue_head(&bebob->hwdep_wait); - err = name_device(bebob, entry->vendor_id); + err = name_device(bebob); if (err < 0) goto error; -- cgit v0.10.2 From 329fec2f7f7ead9dcab0a08684c700a5c55f3884 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 27 Mar 2016 16:09:07 +0900 Subject: ALSA: oxfw: remove needless member from private structure In former commit, 'struct device_info' is obsoleted, whereas private structure still keeps a pointer to it. This commit remove the member. d6ce6bbd7d83('ALSA: oxfw: rename a structure so that it means backward compatibility to old drivers') Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index 9beecc2..2c84714e 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -36,7 +36,6 @@ struct snd_oxfw { struct snd_card *card; struct fw_unit *unit; - const struct device_info *device_info; struct mutex mutex; spinlock_t lock; -- cgit v0.10.2 From 0655ac2f4089321766bb9af19586900ad6cef7bc Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 27 Mar 2016 16:09:08 +0900 Subject: ALSA: fireworks: move model quirk detection code to information parser Currently, model-specific quirks are detected out of information parser, however it's natural to detect it in the parser. This commit applies the idea. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index 8f27b67..8380fb5 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -168,6 +168,17 @@ get_hardware_info(struct snd_efw *efw) sizeof(struct snd_efw_phys_grp) * hwinfo->phys_in_grp_count); memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps, sizeof(struct snd_efw_phys_grp) * hwinfo->phys_out_grp_count); + + /* AudioFire8 (since 2009) and AudioFirePre8 */ + if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_9) + efw->is_af9 = true; + /* These models uses the same firmware. */ + if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_2 || + hwinfo->type == MODEL_ECHO_AUDIOFIRE_4 || + hwinfo->type == MODEL_ECHO_AUDIOFIRE_9 || + hwinfo->type == MODEL_GIBSON_RIP || + hwinfo->type == MODEL_GIBSON_GOLDTOP) + efw->is_fireworks3 = true; end: kfree(hwinfo); return err; @@ -248,16 +259,6 @@ efw_probe(struct fw_unit *unit, err = get_hardware_info(efw); if (err < 0) goto error; - /* AudioFire8 (since 2009) and AudioFirePre8 */ - if (entry->model_id == MODEL_ECHO_AUDIOFIRE_9) - efw->is_af9 = true; - /* These models uses the same firmware. */ - if (entry->model_id == MODEL_ECHO_AUDIOFIRE_2 || - entry->model_id == MODEL_ECHO_AUDIOFIRE_4 || - entry->model_id == MODEL_ECHO_AUDIOFIRE_9 || - entry->model_id == MODEL_GIBSON_RIP || - entry->model_id == MODEL_GIBSON_GOLDTOP) - efw->is_fireworks3 = true; snd_efw_proc_init(efw); -- cgit v0.10.2 From 25c0e953eb56168c05fed88fbad00f76e105e2c8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 27 Mar 2016 16:09:09 +0900 Subject: ALSA: firewire-tascam: add Kconfig entry for TASCAM FW-1804 I forgot it. Fixes: 3e78e1518e12('ALSA: firewire-tascam: add support for FW-1804') Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 2a779c2..ab894ed 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -134,6 +134,7 @@ config SND_FIREWIRE_TASCAM Say Y here to include support for TASCAM. * FW-1884 * FW-1082 + * FW-1804 To compile this driver as a module, choose M here: the module will be called snd-firewire-tascam. -- cgit v0.10.2 From faafd03d23c913633d2ef7e6ffebdce01b164409 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Mar 2016 12:29:24 +0200 Subject: ALSA: hda - Clear the leftover component assignment at snd_hdac_i915_exit() The commit [d745f5e7b8b2: ALSA: hda - Add the pin / port mapping on Intel ILK and VLV] introduced a WARN_ON() to check the pointer for avoiding the double initializations. But hdac_acomp pointer wasn't cleared at snd_hdac_i915_exit(), thus after reloading the HD-audio driver, it may result in the false positive warning. This patch makes sure to clear the leftover pointer at exit. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=94736 Reported-by: Daniela Doras-prodan Signed-off-by: Takashi Iwai diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 750a4ea..ae0f305 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -372,6 +372,7 @@ int snd_hdac_i915_exit(struct hdac_bus *bus) kfree(acomp); bus->audio_component = NULL; + hdac_acomp = NULL; return 0; } -- cgit v0.10.2 From 97cc2ed27e5a168cf423f67c3bc7c6cc41d12f82 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 29 Mar 2016 18:48:07 +0200 Subject: ALSA: hda - Fix yet another i915 pointer leftover in error path The hdac_acomp object in hdac_i915.c is left as assigned even after binding with i915 actually fails, and this leads to the WARN_ON() at the next load of the module. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=94736 Signed-off-by: Takashi Iwai diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index ae0f305..d0da250 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -339,6 +339,7 @@ out_master_del: out_err: kfree(acomp); bus->audio_component = NULL; + hdac_acomp = NULL; dev_info(dev, "failed to add i915 component master (%d)\n", ret); return ret; -- cgit v0.10.2 From 44c376b9596ca00d1bdee37e716d1bd4dd36c955 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 31 Mar 2016 08:47:02 +0900 Subject: ALSA: firewire-lib: suppress kernel warnings when releasing uninitialized stream data When any of AMDTP stream data are not initialized and private data is going to be released, WARN_ON() in amdtp_stream_destroy() is hit and dump messages. This may take users irritated. This commit fixes the bug to skip releasing when it's not initialized. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index ed29026..4484242 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -102,6 +102,10 @@ EXPORT_SYMBOL(amdtp_stream_init); */ void amdtp_stream_destroy(struct amdtp_stream *s) { + /* Not initialized. */ + if (s->protocol == NULL) + return; + WARN_ON(amdtp_stream_running(s)); kfree(s->protocol); mutex_destroy(&s->mutex); -- cgit v0.10.2 From 0eced45ca60666aff4c12f31fef4005ae37e859e Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 31 Mar 2016 08:47:03 +0900 Subject: ALSA: dice: simplify unit probe processing In former commit, ALSA dice driver doesn't generate kernel warnings when unplugging units before initializing stream data. This commit moves the initialization to delayed registration of sound card, to simplify unit probe processing. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 8b64aef..53ca441 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -201,6 +201,10 @@ static void do_registration(struct work_struct *work) dice_card_strings(dice); + err = snd_dice_stream_init_duplex(dice); + if (err < 0) + goto error; + snd_dice_create_proc(dice); err = snd_dice_create_pcm(dice); @@ -229,6 +233,7 @@ static void do_registration(struct work_struct *work) return; error: + snd_dice_stream_destroy_duplex(dice); snd_dice_transaction_destroy(dice); snd_card_free(dice->card); dev_info(&dice->unit->device, @@ -273,12 +278,6 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) init_completion(&dice->clock_accepted); init_waitqueue_head(&dice->hwdep_wait); - err = snd_dice_stream_init_duplex(dice); - if (err < 0) { - dice_free(dice); - return err; - } - /* Allocate and register this sound card later. */ INIT_DEFERRABLE_WORK(&dice->dwork, do_registration); schedule_registration(dice); -- cgit v0.10.2 From 923f92ebb43e7a09915a5708d4805c1e099db46c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 31 Mar 2016 08:47:04 +0900 Subject: ALSA: firewire-lib: add new function to schedule a work for sound card registration In former commit, ALSA dice driver postpone sound card registration after IEEE 1394 bus is calm. This idea has advantages for the other drivers. This commit adds a helper function for it to firewire-lib module. The function is really for the specific purpose. Callers should initialize delayed work structure with callback function. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 53ca441..96fe68f4 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -20,8 +20,6 @@ MODULE_LICENSE("GPL v2"); #define WEISS_CATEGORY_ID 0x00 #define LOUD_CATEGORY_ID 0x10 -#define PROBE_DELAY_MS (2 * MSEC_PER_SEC) - /* * Some models support several isochronous channels, while these streams are not * always available. In this case, add the model name to this list. @@ -235,27 +233,12 @@ static void do_registration(struct work_struct *work) error: snd_dice_stream_destroy_duplex(dice); snd_dice_transaction_destroy(dice); + snd_dice_stream_destroy_duplex(dice); snd_card_free(dice->card); dev_info(&dice->unit->device, "Sound card registration failed: %d\n", err); } -static void schedule_registration(struct snd_dice *dice) -{ - struct fw_card *fw_card = fw_parent_device(dice->unit)->card; - u64 now, delay; - - now = get_jiffies_64(); - delay = fw_card->reset_jiffies + msecs_to_jiffies(PROBE_DELAY_MS); - - if (time_after64(delay, now)) - delay -= now; - else - delay = 0; - - mod_delayed_work(system_wq, &dice->dwork, delay); -} - static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) { struct snd_dice *dice; @@ -280,7 +263,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) /* Allocate and register this sound card later. */ INIT_DEFERRABLE_WORK(&dice->dwork, do_registration); - schedule_registration(dice); + snd_fw_schedule_registration(unit, &dice->dwork); return 0; } @@ -311,7 +294,7 @@ static void dice_bus_reset(struct fw_unit *unit) /* Postpone a workqueue for deferred registration. */ if (!dice->registered) - schedule_registration(dice); + snd_fw_schedule_registration(unit, &dice->dwork); /* The handler address register becomes initialized. */ snd_dice_transaction_reinit(dice); diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c index f80aafa..ca4dfcf 100644 --- a/sound/firewire/lib.c +++ b/sound/firewire/lib.c @@ -67,6 +67,38 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode, } EXPORT_SYMBOL(snd_fw_transaction); +#define PROBE_DELAY_MS (2 * MSEC_PER_SEC) + +/** + * snd_fw_schedule_registration - schedule work for sound card registration + * @unit: an instance for unit on IEEE 1394 bus + * @dwork: delayed work with callback function + * + * This function is not designed for general purposes. When new unit is + * connected to IEEE 1394 bus, the bus is under bus-reset state because of + * topological change. In this state, units tend to fail both of asynchronous + * and isochronous communication. To avoid this problem, this function is used + * to postpone sound card registration after the state. The callers must + * set up instance of delayed work in advance. + */ +void snd_fw_schedule_registration(struct fw_unit *unit, + struct delayed_work *dwork) +{ + u64 now, delay; + + now = get_jiffies_64(); + delay = fw_parent_device(unit)->card->reset_jiffies + + msecs_to_jiffies(PROBE_DELAY_MS); + + if (time_after64(delay, now)) + delay -= now; + else + delay = 0; + + mod_delayed_work(system_wq, dwork, delay); +} +EXPORT_SYMBOL(snd_fw_schedule_registration); + static void async_midi_port_callback(struct fw_card *card, int rcode, void *data, size_t length, void *callback_data) diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h index f3f6f84..f676931 100644 --- a/sound/firewire/lib.h +++ b/sound/firewire/lib.h @@ -22,6 +22,9 @@ static inline bool rcode_is_permanent_error(int rcode) return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR; } +void snd_fw_schedule_registration(struct fw_unit *unit, + struct delayed_work *dwork); + struct snd_fw_async_midi_port; typedef int (*snd_fw_async_midi_port_fill)( struct snd_rawmidi_substream *substream, -- cgit v0.10.2 From 04a2c73c97ebb224dfb411ab35bb18d7b8245e39 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 31 Mar 2016 08:47:05 +0900 Subject: ALSA: bebob: delayed registration of sound card Some bebob based units tends to fail asynchronous communication when IEEE 1394 bus is under bus-reset state. When registering sound card instance at unit probe callback, userspace applications can be involved to the state. This commit postpones the registration till the bus is calm. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 932901d..f7e2cbd 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -126,6 +126,17 @@ end: return err; } +static void bebob_free(struct snd_bebob *bebob) +{ + snd_bebob_stream_destroy_duplex(bebob); + fw_unit_put(bebob->unit); + + kfree(bebob->maudio_special_quirk); + + mutex_destroy(&bebob->mutex); + kfree(bebob); +} + /* * This module releases the FireWire unit data after all ALSA character devices * are released by applications. This is for releasing stream data or finishing @@ -137,18 +148,11 @@ bebob_card_free(struct snd_card *card) { struct snd_bebob *bebob = card->private_data; - snd_bebob_stream_destroy_duplex(bebob); - fw_unit_put(bebob->unit); - - kfree(bebob->maudio_special_quirk); - - if (bebob->card_index >= 0) { - mutex_lock(&devices_mutex); - clear_bit(bebob->card_index, devices_used); - mutex_unlock(&devices_mutex); - } + mutex_lock(&devices_mutex); + clear_bit(bebob->card_index, devices_used); + mutex_unlock(&devices_mutex); - mutex_destroy(&bebob->mutex); + bebob_free(card->private_data); } static const struct snd_bebob_spec * @@ -176,16 +180,17 @@ check_audiophile_booted(struct fw_unit *unit) return strncmp(name, "FW Audiophile Bootloader", 15) != 0; } -static int -bebob_probe(struct fw_unit *unit, - const struct ieee1394_device_id *entry) +static void +do_registration(struct work_struct *work) { - struct snd_card *card; - struct snd_bebob *bebob; - const struct snd_bebob_spec *spec; + struct snd_bebob *bebob = + container_of(work, struct snd_bebob, dwork.work); unsigned int card_index; int err; + if (bebob->registered) + return; + mutex_lock(&devices_mutex); for (card_index = 0; card_index < SNDRV_CARDS; card_index++) { @@ -193,64 +198,39 @@ bebob_probe(struct fw_unit *unit, break; } if (card_index >= SNDRV_CARDS) { - err = -ENOENT; - goto end; + mutex_unlock(&devices_mutex); + return; } - if ((entry->vendor_id == VEN_FOCUSRITE) && - (entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)) - spec = get_saffire_spec(unit); - else if ((entry->vendor_id == VEN_MAUDIO1) && - (entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH) && - !check_audiophile_booted(unit)) - spec = NULL; - else - spec = (const struct snd_bebob_spec *)entry->driver_data; - - if (spec == NULL) { - if ((entry->vendor_id == VEN_MAUDIO1) || - (entry->vendor_id == VEN_MAUDIO2)) - err = snd_bebob_maudio_load_firmware(unit); - else - err = -ENOSYS; - goto end; + err = snd_card_new(&bebob->unit->device, index[card_index], + id[card_index], THIS_MODULE, 0, &bebob->card); + if (err < 0) { + mutex_unlock(&devices_mutex); + return; } - err = snd_card_new(&unit->device, index[card_index], id[card_index], - THIS_MODULE, sizeof(struct snd_bebob), &card); - if (err < 0) - goto end; - bebob = card->private_data; - bebob->card_index = card_index; - set_bit(card_index, devices_used); - card->private_free = bebob_card_free; - - bebob->card = card; - bebob->unit = fw_unit_get(unit); - bebob->spec = spec; - mutex_init(&bebob->mutex); - spin_lock_init(&bebob->lock); - init_waitqueue_head(&bebob->hwdep_wait); - err = name_device(bebob); if (err < 0) goto error; - if ((entry->vendor_id == VEN_MAUDIO1) && - (entry->model_id == MODEL_MAUDIO_FW1814)) - err = snd_bebob_maudio_special_discover(bebob, true); - else if ((entry->vendor_id == VEN_MAUDIO1) && - (entry->model_id == MODEL_MAUDIO_PROJECTMIX)) - err = snd_bebob_maudio_special_discover(bebob, false); - else + if (bebob->spec == &maudio_special_spec) { + if (bebob->entry->model_id == MODEL_MAUDIO_FW1814) + err = snd_bebob_maudio_special_discover(bebob, true); + else + err = snd_bebob_maudio_special_discover(bebob, false); + } else { err = snd_bebob_stream_discover(bebob); + } + if (err < 0) + goto error; + + err = snd_bebob_stream_init_duplex(bebob); if (err < 0) goto error; snd_bebob_proc_init(bebob); - if ((bebob->midi_input_ports > 0) || - (bebob->midi_output_ports > 0)) { + if (bebob->midi_input_ports > 0 || bebob->midi_output_ports > 0) { err = snd_bebob_create_midi_devices(bebob); if (err < 0) goto error; @@ -264,16 +244,75 @@ bebob_probe(struct fw_unit *unit, if (err < 0) goto error; - err = snd_bebob_stream_init_duplex(bebob); + err = snd_card_register(bebob->card); if (err < 0) goto error; - if (!bebob->maudio_special_quirk) { - err = snd_card_register(card); - if (err < 0) { - snd_bebob_stream_destroy_duplex(bebob); - goto error; - } + set_bit(card_index, devices_used); + mutex_unlock(&devices_mutex); + + /* + * After registered, bebob instance can be released corresponding to + * releasing the sound card instance. + */ + bebob->card->private_free = bebob_card_free; + bebob->card->private_data = bebob; + bebob->registered = true; + + return; +error: + mutex_unlock(&devices_mutex); + snd_bebob_stream_destroy_duplex(bebob); + snd_card_free(bebob->card); + dev_info(&bebob->unit->device, + "Sound card registration failed: %d\n", err); +} + +static int +bebob_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry) +{ + struct snd_bebob *bebob; + const struct snd_bebob_spec *spec; + + if (entry->vendor_id == VEN_FOCUSRITE && + entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH) + spec = get_saffire_spec(unit); + else if (entry->vendor_id == VEN_MAUDIO1 && + entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH && + !check_audiophile_booted(unit)) + spec = NULL; + else + spec = (const struct snd_bebob_spec *)entry->driver_data; + + if (spec == NULL) { + if (entry->vendor_id == VEN_MAUDIO1 || + entry->vendor_id == VEN_MAUDIO2) + return snd_bebob_maudio_load_firmware(unit); + else + return -ENODEV; + } + + /* Allocate this independent of sound card instance. */ + bebob = kzalloc(sizeof(struct snd_bebob), GFP_KERNEL); + if (bebob == NULL) + return -ENOMEM; + + bebob->unit = fw_unit_get(unit); + bebob->entry = entry; + bebob->spec = spec; + dev_set_drvdata(&unit->device, bebob); + + mutex_init(&bebob->mutex); + spin_lock_init(&bebob->lock); + init_waitqueue_head(&bebob->hwdep_wait); + + /* Allocate and register this sound card later. */ + INIT_DEFERRABLE_WORK(&bebob->dwork, do_registration); + + if (entry->vendor_id != VEN_MAUDIO1 || + (entry->model_id != MODEL_MAUDIO_FW1814 && + entry->model_id != MODEL_MAUDIO_PROJECTMIX)) { + snd_fw_schedule_registration(unit, &bebob->dwork); } else { /* * This is a workaround. This bus reset seems to have an effect @@ -285,19 +324,11 @@ bebob_probe(struct fw_unit *unit, * signals from dbus and starts I/Os. To avoid I/Os till the * future bus reset, registration is done in next update(). */ - bebob->deferred_registration = true; fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card, false, true); } - dev_set_drvdata(&unit->device, bebob); -end: - mutex_unlock(&devices_mutex); - return err; -error: - mutex_unlock(&devices_mutex); - snd_card_free(card); - return err; + return 0; } /* @@ -324,15 +355,11 @@ bebob_update(struct fw_unit *unit) if (bebob == NULL) return; - fcp_bus_reset(bebob->unit); - - if (bebob->deferred_registration) { - if (snd_card_register(bebob->card) < 0) { - snd_bebob_stream_destroy_duplex(bebob); - snd_card_free(bebob->card); - } - bebob->deferred_registration = false; - } + /* Postpone a workqueue for deferred registration. */ + if (!bebob->registered) + snd_fw_schedule_registration(unit, &bebob->dwork); + else + fcp_bus_reset(bebob->unit); } static void bebob_remove(struct fw_unit *unit) @@ -342,8 +369,20 @@ static void bebob_remove(struct fw_unit *unit) if (bebob == NULL) return; - /* No need to wait for releasing card object in this context. */ - snd_card_free_when_closed(bebob->card); + /* + * Confirm to stop the work for registration before the sound card is + * going to be released. The work is not scheduled again because bus + * reset handler is not called anymore. + */ + cancel_delayed_work_sync(&bebob->dwork); + + if (bebob->registered) { + /* No need to wait for releasing card object in this context. */ + snd_card_free_when_closed(bebob->card); + } else { + /* Don't forget this case. */ + bebob_free(bebob); + } } static const struct snd_bebob_rate_spec normal_rate_spec = { diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index b50bb33d..2a442a7 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -83,6 +83,10 @@ struct snd_bebob { struct mutex mutex; spinlock_t lock; + bool registered; + struct delayed_work dwork; + + const struct ieee1394_device_id *entry; const struct snd_bebob_spec *spec; unsigned int midi_input_ports; @@ -111,7 +115,6 @@ struct snd_bebob { /* for M-Audio special devices */ void *maudio_special_quirk; - bool deferred_registration; /* For BeBoB version quirk. */ unsigned int version; -- cgit v0.10.2 From 7d3c1d5901aac873d13c0a03b29ee2bda183383f Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 31 Mar 2016 08:47:06 +0900 Subject: ALSA: fireworks: delayed registration of sound card When some fireworks units are connected sequentially, userspace applications are involved at bus-reset state on IEEE 1394 bus. In the state, any communications can be canceled. Therefore, sound card registration should be delayed till the bus gets calm. This commit achieves it. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index 8380fb5..71a0613 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -184,6 +184,18 @@ end: return err; } +static void efw_free(struct snd_efw *efw) +{ + snd_efw_stream_destroy_duplex(efw); + snd_efw_transaction_remove_instance(efw); + fw_unit_put(efw->unit); + + kfree(efw->resp_buf); + + mutex_destroy(&efw->mutex); + kfree(efw); +} + /* * This module releases the FireWire unit data after all ALSA character devices * are released by applications. This is for releasing stream data or finishing @@ -195,28 +207,24 @@ efw_card_free(struct snd_card *card) { struct snd_efw *efw = card->private_data; - snd_efw_stream_destroy_duplex(efw); - snd_efw_transaction_remove_instance(efw); - fw_unit_put(efw->unit); - - kfree(efw->resp_buf); - if (efw->card_index >= 0) { mutex_lock(&devices_mutex); clear_bit(efw->card_index, devices_used); mutex_unlock(&devices_mutex); } - mutex_destroy(&efw->mutex); + efw_free(card->private_data); } -static int -efw_probe(struct fw_unit *unit, - const struct ieee1394_device_id *entry) +static void +do_registration(struct work_struct *work) { - struct snd_card *card; - struct snd_efw *efw; - int card_index, err; + struct snd_efw *efw = container_of(work, struct snd_efw, dwork.work); + unsigned int card_index; + int err; + + if (efw->registered) + return; mutex_lock(&devices_mutex); @@ -226,24 +234,16 @@ efw_probe(struct fw_unit *unit, break; } if (card_index >= SNDRV_CARDS) { - err = -ENOENT; - goto end; + mutex_unlock(&devices_mutex); + return; } - err = snd_card_new(&unit->device, index[card_index], id[card_index], - THIS_MODULE, sizeof(struct snd_efw), &card); - if (err < 0) - goto end; - efw = card->private_data; - efw->card_index = card_index; - set_bit(card_index, devices_used); - card->private_free = efw_card_free; - - efw->card = card; - efw->unit = fw_unit_get(unit); - mutex_init(&efw->mutex); - spin_lock_init(&efw->lock); - init_waitqueue_head(&efw->hwdep_wait); + err = snd_card_new(&efw->unit->device, index[card_index], + id[card_index], THIS_MODULE, 0, &efw->card); + if (err < 0) { + mutex_unlock(&devices_mutex); + return; + } /* prepare response buffer */ snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size, @@ -260,6 +260,10 @@ efw_probe(struct fw_unit *unit, if (err < 0) goto error; + err = snd_efw_stream_init_duplex(efw); + if (err < 0) + goto error; + snd_efw_proc_init(efw); if (efw->midi_out_ports || efw->midi_in_ports) { @@ -276,44 +280,93 @@ efw_probe(struct fw_unit *unit, if (err < 0) goto error; - err = snd_efw_stream_init_duplex(efw); + err = snd_card_register(efw->card); if (err < 0) goto error; - err = snd_card_register(card); - if (err < 0) { - snd_efw_stream_destroy_duplex(efw); - goto error; - } - - dev_set_drvdata(&unit->device, efw); -end: + set_bit(card_index, devices_used); mutex_unlock(&devices_mutex); - return err; + + /* + * After registered, efw instance can be released corresponding to + * releasing the sound card instance. + */ + efw->card->private_free = efw_card_free; + efw->card->private_data = efw; + efw->registered = true; + + return; error: - snd_efw_transaction_remove_instance(efw); mutex_unlock(&devices_mutex); - snd_card_free(card); - return err; + snd_efw_transaction_remove_instance(efw); + snd_efw_stream_destroy_duplex(efw); + snd_card_free(efw->card); + dev_info(&efw->unit->device, + "Sound card registration failed: %d\n", err); +} + +static int +efw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry) +{ + struct snd_efw *efw; + + efw = kzalloc(sizeof(struct snd_efw), GFP_KERNEL); + if (efw == NULL) + return -ENOMEM; + + efw->unit = fw_unit_get(unit); + dev_set_drvdata(&unit->device, efw); + + mutex_init(&efw->mutex); + spin_lock_init(&efw->lock); + init_waitqueue_head(&efw->hwdep_wait); + + /* Allocate and register this sound card later. */ + INIT_DEFERRABLE_WORK(&efw->dwork, do_registration); + snd_fw_schedule_registration(unit, &efw->dwork); + + return 0; } static void efw_update(struct fw_unit *unit) { struct snd_efw *efw = dev_get_drvdata(&unit->device); + /* Postpone a workqueue for deferred registration. */ + if (!efw->registered) + snd_fw_schedule_registration(unit, &efw->dwork); + snd_efw_transaction_bus_reset(efw->unit); - mutex_lock(&efw->mutex); - snd_efw_stream_update_duplex(efw); - mutex_unlock(&efw->mutex); + /* + * After registration, userspace can start packet streaming, then this + * code block works fine. + */ + if (efw->registered) { + mutex_lock(&efw->mutex); + snd_efw_stream_update_duplex(efw); + mutex_unlock(&efw->mutex); + } } static void efw_remove(struct fw_unit *unit) { struct snd_efw *efw = dev_get_drvdata(&unit->device); - /* No need to wait for releasing card object in this context. */ - snd_card_free_when_closed(efw->card); + /* + * Confirm to stop the work for registration before the sound card is + * going to be released. The work is not scheduled again because bus + * reset handler is not called anymore. + */ + cancel_delayed_work_sync(&efw->dwork); + + if (efw->registered) { + /* No need to wait for releasing card object in this context. */ + snd_card_free_when_closed(efw->card); + } else { + /* Don't forget this case. */ + efw_free(efw); + } } static const struct ieee1394_device_id efw_id_table[] = { diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index 96c4e0c..471c772 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -65,6 +65,9 @@ struct snd_efw { struct mutex mutex; spinlock_t lock; + bool registered; + struct delayed_work dwork; + /* for transaction */ u32 seqnum; bool resp_addr_changable; -- cgit v0.10.2 From 6c29230e2a5ff84df2b1358681414bad3e4bd220 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 31 Mar 2016 08:47:07 +0900 Subject: ALSA: oxfw: delayed registration of sound card Some oxfw based units tends to fail asynchronous communication when IEEE 1394 bus is under bus-reset state. When registering sound card instance at unit probe callback, userspace applications can be involved to the state. This commit postpones the registration till the bus is calm. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index abedc22..e629b88 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -118,15 +118,8 @@ end: return err; } -/* - * This module releases the FireWire unit data after all ALSA character devices - * are released by applications. This is for releasing stream data or finishing - * transactions safely. Thus at returning from .remove(), this module still keep - * references for the unit. - */ -static void oxfw_card_free(struct snd_card *card) +static void oxfw_free(struct snd_oxfw *oxfw) { - struct snd_oxfw *oxfw = card->private_data; unsigned int i; snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); @@ -144,6 +137,17 @@ static void oxfw_card_free(struct snd_card *card) mutex_destroy(&oxfw->mutex); } +/* + * This module releases the FireWire unit data after all ALSA character devices + * are released by applications. This is for releasing stream data or finishing + * transactions safely. Thus at returning from .remove(), this module still keep + * references for the unit. + */ +static void oxfw_card_free(struct snd_card *card) +{ + oxfw_free(card->private_data); +} + static int detect_quirks(struct snd_oxfw *oxfw) { struct fw_device *fw_dev = fw_parent_device(oxfw->unit); @@ -205,41 +209,39 @@ static int detect_quirks(struct snd_oxfw *oxfw) return 0; } -static int oxfw_probe(struct fw_unit *unit, - const struct ieee1394_device_id *entry) +static void do_registration(struct work_struct *work) { - struct snd_card *card; - struct snd_oxfw *oxfw; + struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work); int err; - if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit)) - return -ENODEV; + if (oxfw->registered) + return; - err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, - sizeof(*oxfw), &card); + err = snd_card_new(&oxfw->unit->device, -1, NULL, THIS_MODULE, 0, + &oxfw->card); if (err < 0) - return err; + return; - card->private_free = oxfw_card_free; - oxfw = card->private_data; - oxfw->card = card; - mutex_init(&oxfw->mutex); - oxfw->unit = fw_unit_get(unit); - oxfw->entry = entry; - spin_lock_init(&oxfw->lock); - init_waitqueue_head(&oxfw->hwdep_wait); + err = name_card(oxfw); + if (err < 0) + goto error; - err = snd_oxfw_stream_discover(oxfw); + err = detect_quirks(oxfw); if (err < 0) goto error; - err = name_card(oxfw); + err = snd_oxfw_stream_discover(oxfw); if (err < 0) goto error; - err = detect_quirks(oxfw); + err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream); if (err < 0) goto error; + if (oxfw->has_output) { + err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream); + if (err < 0) + goto error; + } err = snd_oxfw_create_pcm(oxfw); if (err < 0) @@ -255,54 +257,97 @@ static int oxfw_probe(struct fw_unit *unit, if (err < 0) goto error; - err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream); + err = snd_card_register(oxfw->card); if (err < 0) goto error; - if (oxfw->has_output) { - err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream); - if (err < 0) - goto error; - } - err = snd_card_register(card); - if (err < 0) { - snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); - if (oxfw->has_output) - snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); - goto error; - } + /* + * After registered, oxfw instance can be released corresponding to + * releasing the sound card instance. + */ + oxfw->card->private_free = oxfw_card_free; + oxfw->card->private_data = oxfw; + oxfw->registered = true; + + return; +error: + snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); + if (oxfw->has_output) + snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); + snd_card_free(oxfw->card); + dev_info(&oxfw->unit->device, + "Sound card registration failed: %d\n", err); +} + +static int oxfw_probe(struct fw_unit *unit, + const struct ieee1394_device_id *entry) +{ + struct snd_oxfw *oxfw; + + if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit)) + return -ENODEV; + + /* Allocate this independent of sound card instance. */ + oxfw = kzalloc(sizeof(struct snd_oxfw), GFP_KERNEL); + if (oxfw == NULL) + return -ENOMEM; + + oxfw->entry = entry; + oxfw->unit = fw_unit_get(unit); dev_set_drvdata(&unit->device, oxfw); + mutex_init(&oxfw->mutex); + spin_lock_init(&oxfw->lock); + init_waitqueue_head(&oxfw->hwdep_wait); + + /* Allocate and register this sound card later. */ + INIT_DEFERRABLE_WORK(&oxfw->dwork, do_registration); + snd_fw_schedule_registration(unit, &oxfw->dwork); + return 0; -error: - snd_card_free(card); - return err; } static void oxfw_bus_reset(struct fw_unit *unit) { struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); + if (!oxfw->registered) + snd_fw_schedule_registration(unit, &oxfw->dwork); + fcp_bus_reset(oxfw->unit); - mutex_lock(&oxfw->mutex); + if (oxfw->registered) { + mutex_lock(&oxfw->mutex); - snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream); - if (oxfw->has_output) - snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream); + snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream); + if (oxfw->has_output) + snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream); - mutex_unlock(&oxfw->mutex); + mutex_unlock(&oxfw->mutex); - if (oxfw->entry->vendor_id == OUI_STANTON) - snd_oxfw_scs1x_update(oxfw); + if (oxfw->entry->vendor_id == OUI_STANTON) + snd_oxfw_scs1x_update(oxfw); + } } static void oxfw_remove(struct fw_unit *unit) { struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); - /* No need to wait for releasing card object in this context. */ - snd_card_free_when_closed(oxfw->card); + /* + * Confirm to stop the work for registration before the sound card is + * going to be released. The work is not scheduled again because bus + * reset handler is not called anymore. + */ + cancel_delayed_work_sync(&oxfw->dwork); + + if (oxfw->registered) { + /* No need to wait for releasing card object in this context. */ + snd_card_free_when_closed(oxfw->card); + } else { + /* Don't forget this case. */ + oxfw_free(oxfw); + } } static const struct compat_info griffin_firewave = { diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index 2c84714e..2047dcb 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -39,6 +39,9 @@ struct snd_oxfw { struct mutex mutex; spinlock_t lock; + bool registered; + struct delayed_work dwork; + bool wrong_dbs; bool has_output; u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES]; -- cgit v0.10.2 From 86c8dd7f4da3fb3f92fc5ab5144c971639d39745 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 31 Mar 2016 08:47:08 +0900 Subject: ALSA: firewire-digi00x: delayed registration of sound card When some digi00x units are connected sequentially, userspace applications are involved at bus-reset state on IEEE 1394 bus. In the state, any communications can be canceled. Therefore, sound card registration should be delayed till the bus gets calm. This commit achieves it. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c index 554324d..735d356 100644 --- a/sound/firewire/digi00x/digi00x-transaction.c +++ b/sound/firewire/digi00x/digi00x-transaction.c @@ -126,12 +126,17 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x) return err; error: fw_core_remove_address_handler(&dg00x->async_handler); - dg00x->async_handler.address_callback = NULL; + dg00x->async_handler.callback_data = NULL; return err; } void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x) { + if (dg00x->async_handler.callback_data == NULL) + return; + snd_fw_async_midi_port_destroy(&dg00x->out_control); fw_core_remove_address_handler(&dg00x->async_handler); + + dg00x->async_handler.callback_data = NULL; } diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c index 1f33b7a..cc4776c 100644 --- a/sound/firewire/digi00x/digi00x.c +++ b/sound/firewire/digi00x/digi00x.c @@ -40,10 +40,8 @@ static int name_card(struct snd_dg00x *dg00x) return 0; } -static void dg00x_card_free(struct snd_card *card) +static void dg00x_free(struct snd_dg00x *dg00x) { - struct snd_dg00x *dg00x = card->private_data; - snd_dg00x_stream_destroy_duplex(dg00x); snd_dg00x_transaction_unregister(dg00x); @@ -52,28 +50,24 @@ static void dg00x_card_free(struct snd_card *card) mutex_destroy(&dg00x->mutex); } -static int snd_dg00x_probe(struct fw_unit *unit, - const struct ieee1394_device_id *entry) +static void dg00x_card_free(struct snd_card *card) { - struct snd_card *card; - struct snd_dg00x *dg00x; - int err; + dg00x_free(card->private_data); +} - /* create card */ - err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, - sizeof(struct snd_dg00x), &card); - if (err < 0) - return err; - card->private_free = dg00x_card_free; +static void do_registration(struct work_struct *work) +{ + struct snd_dg00x *dg00x = + container_of(work, struct snd_dg00x, dwork.work); + int err; - /* initialize myself */ - dg00x = card->private_data; - dg00x->card = card; - dg00x->unit = fw_unit_get(unit); + if (dg00x->registered) + return; - mutex_init(&dg00x->mutex); - spin_lock_init(&dg00x->lock); - init_waitqueue_head(&dg00x->hwdep_wait); + err = snd_card_new(&dg00x->unit->device, -1, NULL, THIS_MODULE, 0, + &dg00x->card); + if (err < 0) + return; err = name_card(dg00x); if (err < 0) @@ -101,35 +95,86 @@ static int snd_dg00x_probe(struct fw_unit *unit, if (err < 0) goto error; - err = snd_card_register(card); + err = snd_card_register(dg00x->card); if (err < 0) goto error; - dev_set_drvdata(&unit->device, dg00x); + dg00x->card->private_free = dg00x_card_free; + dg00x->card->private_data = dg00x; + dg00x->registered = true; - return err; + return; error: - snd_card_free(card); - return err; + snd_dg00x_transaction_unregister(dg00x); + snd_dg00x_stream_destroy_duplex(dg00x); + snd_card_free(dg00x->card); + dev_info(&dg00x->unit->device, + "Sound card registration failed: %d\n", err); +} + +static int snd_dg00x_probe(struct fw_unit *unit, + const struct ieee1394_device_id *entry) +{ + struct snd_dg00x *dg00x; + + /* Allocate this independent of sound card instance. */ + dg00x = kzalloc(sizeof(struct snd_dg00x), GFP_KERNEL); + if (dg00x == NULL) + return -ENOMEM; + + dg00x->unit = fw_unit_get(unit); + dev_set_drvdata(&unit->device, dg00x); + + mutex_init(&dg00x->mutex); + spin_lock_init(&dg00x->lock); + init_waitqueue_head(&dg00x->hwdep_wait); + + /* Allocate and register this sound card later. */ + INIT_DEFERRABLE_WORK(&dg00x->dwork, do_registration); + snd_fw_schedule_registration(unit, &dg00x->dwork); + + return 0; } static void snd_dg00x_update(struct fw_unit *unit) { struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device); + /* Postpone a workqueue for deferred registration. */ + if (!dg00x->registered) + snd_fw_schedule_registration(unit, &dg00x->dwork); + snd_dg00x_transaction_reregister(dg00x); - mutex_lock(&dg00x->mutex); - snd_dg00x_stream_update_duplex(dg00x); - mutex_unlock(&dg00x->mutex); + /* + * After registration, userspace can start packet streaming, then this + * code block works fine. + */ + if (dg00x->registered) { + mutex_lock(&dg00x->mutex); + snd_dg00x_stream_update_duplex(dg00x); + mutex_unlock(&dg00x->mutex); + } } static void snd_dg00x_remove(struct fw_unit *unit) { struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device); - /* No need to wait for releasing card object in this context. */ - snd_card_free_when_closed(dg00x->card); + /* + * Confirm to stop the work for registration before the sound card is + * going to be released. The work is not scheduled again because bus + * reset handler is not called anymore. + */ + cancel_delayed_work_sync(&dg00x->dwork); + + if (dg00x->registered) { + /* No need to wait for releasing card object in this context. */ + snd_card_free_when_closed(dg00x->card); + } else { + /* Don't forget this case. */ + dg00x_free(dg00x); + } } static const struct ieee1394_device_id snd_dg00x_id_table[] = { diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 907e739..2cd465c 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -37,6 +37,9 @@ struct snd_dg00x { struct mutex mutex; spinlock_t lock; + bool registered; + struct delayed_work dwork; + struct amdtp_stream tx_stream; struct fw_iso_resources tx_resources; -- cgit v0.10.2 From 44fde3b89ba1e154b3cec7d711703fff53852983 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Mon, 4 Apr 2016 19:23:54 +0530 Subject: ALSA: hda - Update chmap tlv to report sink's capability The existing TLV callback implementation copies all of the cea_channel_speaker_allocation map table to the TLV container irrespective of what is reported by sink. This is of little use to the userspace application. With this patch, it parses the spk_alloc block as queried from the ELD, and copies only the corresponding mapping channel allocation entries from the cea channel speaker allocation table. Thus the user can parse the TLV container to identify sink's capability and set the channel map accordingly. It shouldn't impact the behavior in AMD chipset, as this makes use of already parsed spk alloc block to calculate the channel map. Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h index e20d219..babd445 100644 --- a/include/sound/hda_chmap.h +++ b/include/sound/hda_chmap.h @@ -36,6 +36,8 @@ struct hdac_chmap_ops { int (*chmap_validate)(struct hdac_chmap *hchmap, int ca, int channels, unsigned char *chmap); + int (*get_spk_alloc)(struct hdac_device *hdac, int pcm_idx); + void (*get_chmap)(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap); void (*set_chmap)(struct hdac_device *hdac, int pcm_idx, diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c index d7ec862..c6c75e7 100644 --- a/sound/hda/hdmi_chmap.c +++ b/sound/hda/hdmi_chmap.c @@ -625,13 +625,30 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, WARN_ON(count != channels); } +static int spk_mask_from_spk_alloc(int spk_alloc) +{ + int i; + int spk_mask = eld_speaker_allocation_bits[0]; + + for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { + if (spk_alloc & (1 << i)) + spk_mask |= eld_speaker_allocation_bits[i]; + } + + return spk_mask; +} + static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hdac_chmap *chmap = info->private_data; + int pcm_idx = kcontrol->private_value; unsigned int __user *dst; int chs, count = 0; + unsigned long max_chs; + int type; + int spk_alloc, spk_mask; if (size < 8) return -ENOMEM; @@ -639,40 +656,59 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -EFAULT; size -= 8; dst = tlv + 2; - for (chs = 2; chs <= chmap->channels_max; chs++) { + + spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx); + spk_mask = spk_mask_from_spk_alloc(spk_alloc); + + max_chs = hweight_long(spk_mask); + + for (chs = 2; chs <= max_chs; chs++) { int i; struct hdac_cea_channel_speaker_allocation *cap; cap = channel_allocations; for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { int chs_bytes = chs * 4; - int type = chmap->ops.chmap_cea_alloc_validate_get_type( - chmap, cap, chs); unsigned int tlv_chmap[8]; - if (type < 0) + if (cap->channels != chs) + continue; + + if (!(cap->spk_mask == (spk_mask & cap->spk_mask))) continue; + + type = chmap->ops.chmap_cea_alloc_validate_get_type( + chmap, cap, chs); + if (type < 0) + return -ENODEV; if (size < 8) return -ENOMEM; + if (put_user(type, dst) || put_user(chs_bytes, dst + 1)) return -EFAULT; + dst += 2; size -= 8; count += 8; + if (size < chs_bytes) return -ENOMEM; + size -= chs_bytes; count += chs_bytes; chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap, tlv_chmap, chs); + if (copy_to_user(dst, tlv_chmap, chs_bytes)) return -EFAULT; dst += chs; } } + if (put_user(count, tlv + 1)) return -EFAULT; + return 0; } diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 4833c7b..9452384 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1837,6 +1837,18 @@ static const struct hda_pcm_ops generic_ops = { .cleanup = generic_hdmi_playback_pcm_cleanup, }; +static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) +{ + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + + if (!per_pin) + return 0; + + return per_pin->sink_eld.info.spk_alloc; +} + static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap) { @@ -2165,6 +2177,7 @@ static int alloc_generic_hdmi(struct hda_codec *codec) spec->chmap.ops.get_chmap = hdmi_get_chmap; spec->chmap.ops.set_chmap = hdmi_set_chmap; spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; + spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc, codec->spec = spec; hdmi_array_init(spec, 4); -- cgit v0.10.2 From 4926c8046549cc3c9689e8050e303c016a0b0cba Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 4 Apr 2016 11:33:54 +0200 Subject: ALSA: intel8x0: Drop superfluous VM checks intel8x0 driver has the inside_vm check for skipping a buggy hardware workaround in the PCM pointer callback in the commit [228cf79376f1: ALSA: intel8x0: Improve performance in virtual environment]. This was originally applied to all devices on known VMs, but the code was switched to use the PCI ID matching for applying to only known devices (KVM and Parallels), in order to avoid applying wrongly to VT-d and other such cases, in the commit [7fb4f392bd27: ALSA: intel8x0: improve virtual environment detection]. Meanwhile, the original VM check was kept even after switching to the PCI ID matching. It was partly because we weren't 100% sure whether we had covered all well, and partly because this would help identifying the issue once when a user of another VM hit the same problem or a regression. Currently the VM check is used only for showing the kernel message that the VM-optimization isn't applied, and the VM check itself doesn't change the actual driver behavior at all. Despite the relatively safe driver behavior, the code caught attention of developers badly and brought many confusion / misunderstanding. Since we've got neither regression nor enhancement report for other VMs for five years long, it's likely safe to drop this superfluous VM check now. The module option is still kept, so if a user still needs to adjust, it can be applied as was. Acked-by: Konstantin Ozerkov Signed-off-by: Takashi Iwai diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 8151318..9720a30 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -42,12 +42,6 @@ #include #include -#ifdef CONFIG_KVM_GUEST -#include -#else -#define kvm_para_available() (0) -#endif - MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455"); MODULE_LICENSE("GPL"); @@ -2972,25 +2966,17 @@ static int snd_intel8x0_inside_vm(struct pci_dev *pci) goto fini; } - /* detect KVM and Parallels virtual environments */ - result = kvm_para_available(); -#ifdef X86_FEATURE_HYPERVISOR - result = result || boot_cpu_has(X86_FEATURE_HYPERVISOR); -#endif - if (!result) - goto fini; - /* check for known (emulated) devices */ + result = 0; if (pci->subsystem_vendor == PCI_SUBVENDOR_ID_REDHAT_QUMRANET && pci->subsystem_device == PCI_SUBDEVICE_ID_QEMU) { /* KVM emulated sound, PCI SSID: 1af4:1100 */ msg = "enable KVM"; + result = 1; } else if (pci->subsystem_vendor == 0x1ab8) { /* Parallels VM emulated sound, PCI SSID: 1ab8:xxxx */ msg = "enable Parallels VM"; - } else { - msg = "disable (unknown or VT-d) VM"; - result = 0; + result = 1; } fini: -- cgit v0.10.2 From e8369d65693bd4e21e043c0e66eff1056ed1e7a3 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Fri, 8 Apr 2016 12:45:25 +0900 Subject: ALSA: Fix a typo in timestamping.txt This patch fix a spelling typo found in Documentation/sound/alsa/timestamping.txt Signed-off-by: Masanari Iida Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/timestamping.txt b/Documentation/sound/alsa/timestamping.txt index 0b191a2..1b6473f 100644 --- a/Documentation/sound/alsa/timestamping.txt +++ b/Documentation/sound/alsa/timestamping.txt @@ -129,7 +129,7 @@ will be required to issue multiple queries and perform an interpolation of the results In some hardware-specific configuration, the system timestamp is -latched by a low-level audio subsytem, and the information provided +latched by a low-level audio subsystem, and the information provided back to the driver. Due to potential delays in the communication with the hardware, there is a risk of misalignment with the avail and delay information. To make sure applications are not confused, a -- cgit v0.10.2 From 5305239312a5fcc50849e157a3178778c6914aa0 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 9 Apr 2016 10:36:15 +0200 Subject: ALSA: constify ct_timer_ops structures The ct_timer_ops structures are never modified, so declare them as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Takashi Iwai diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c index a5d4604..8f94534 100644 --- a/sound/pci/ctxfi/cttimer.c +++ b/sound/pci/ctxfi/cttimer.c @@ -49,7 +49,7 @@ struct ct_timer { spinlock_t lock; /* global timer lock (for xfitimer) */ spinlock_t list_lock; /* lock for instance list */ struct ct_atc *atc; - struct ct_timer_ops *ops; + const struct ct_timer_ops *ops; struct list_head instance_head; struct list_head running_head; unsigned int wc; /* current wallclock */ @@ -128,7 +128,7 @@ static void ct_systimer_prepare(struct ct_timer_instance *ti) #define ct_systimer_free ct_systimer_prepare -static struct ct_timer_ops ct_systimer_ops = { +static const struct ct_timer_ops ct_systimer_ops = { .init = ct_systimer_init, .free_instance = ct_systimer_free, .prepare = ct_systimer_prepare, @@ -322,7 +322,7 @@ static void ct_xfitimer_free_global(struct ct_timer *atimer) ct_xfitimer_irq_stop(atimer); } -static struct ct_timer_ops ct_xfitimer_ops = { +static const struct ct_timer_ops ct_xfitimer_ops = { .prepare = ct_xfitimer_prepare, .start = ct_xfitimer_start, .stop = ct_xfitimer_stop, -- cgit v0.10.2 From cddaafb9a4a7b6d19030d2632107ba2aa068474d Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 9 Apr 2016 11:21:46 +0200 Subject: ALSA: usb-audio: add UAC2 clock sources as mixer controls UAC2 specifies clock sources that optionally have validity controls. This patch exposes them as mixer controls, so they can be read (and at least in theory even be written) by userspace applications in order to make clock selection policy decisions. This implementation does nothing if the device is not UAC2 compliant, or if the clock source does not define said validity control bits. Tested with a miniDSP USBStreamer (0x2752/0x0016). Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 4f85757..cab6f52 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -1378,6 +1379,71 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, snd_usb_mixer_add_control(&cval->head, kctl); } +static int parse_clock_source_unit(struct mixer_build *state, int unitid, + void *_ftr) +{ + struct uac_clock_source_descriptor *hdr = _ftr; + struct usb_mixer_elem_info *cval; + struct snd_kcontrol *kctl; + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + int ret; + + if (state->mixer->protocol != UAC_VERSION_2) + return -EINVAL; + + if (hdr->bLength != sizeof(*hdr)) { + usb_audio_dbg(state->chip, + "Bogus clock source descriptor length of %d, ignoring.\n", + hdr->bLength); + return 0; + } + + /* + * The only property of this unit we are interested in is the + * clock source validity. If that isn't readable, just bail out. + */ + if (!uac2_control_is_readable(hdr->bmControls, + ilog2(UAC2_CS_CONTROL_CLOCK_VALID))) + return 0; + + cval = kzalloc(sizeof(*cval), GFP_KERNEL); + if (!cval) + return -ENOMEM; + + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, hdr->bClockID); + + cval->min = 0; + cval->max = 1; + cval->channels = 1; + cval->val_type = USB_MIXER_BOOLEAN; + cval->control = UAC2_CS_CONTROL_CLOCK_VALID; + + if (uac2_control_is_writeable(hdr->bmControls, + ilog2(UAC2_CS_CONTROL_CLOCK_VALID))) + kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); + else { + cval->master_readonly = 1; + kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval); + } + + if (!kctl) { + kfree(cval); + return -ENOMEM; + } + + kctl->private_free = snd_usb_mixer_elem_free; + ret = snd_usb_copy_string_desc(state, hdr->iClockSource, + name, sizeof(name)); + if (ret > 0) + snprintf(kctl->id.name, sizeof(kctl->id.name), + "%s Validity", name); + else + snprintf(kctl->id.name, sizeof(kctl->id.name), + "Clock Source %d Validity", hdr->bClockID); + + return snd_usb_mixer_add_control(&cval->head, kctl); +} + /* * parse a feature unit * @@ -2126,10 +2192,11 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) switch (p1[2]) { case UAC_INPUT_TERMINAL: - case UAC2_CLOCK_SOURCE: return 0; /* NOP */ case UAC_MIXER_UNIT: return parse_audio_mixer_unit(state, unitid, p1); + case UAC2_CLOCK_SOURCE: + return parse_clock_source_unit(state, unitid, p1); case UAC_SELECTOR_UNIT: case UAC2_CLOCK_SELECTOR: return parse_audio_selector_unit(state, unitid, p1); -- cgit v0.10.2 From 191227d99a281333b50aaf82ab4152fbfa249c19 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 8 Apr 2016 19:52:02 +0200 Subject: ALSA: usb-audio: allow clock source validity interrupts miniDSP USBStreamer UAC2 devices send clock validity changes with the control field set to zero. The current interrupt handler ignores all packets if the control field does not match the mixer element's, but it really should only do that in case that field is needed to distinguish multiple elements with the same ID. This patch implements a logic that lets notifications packets pass if the element ID is unique for a given device. Signed-off-by: Daniel Mack Signed-off-by: Takashi Iwai diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index cab6f52..2f8c388 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2374,6 +2374,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, __u8 unitid = (index >> 8) & 0xff; __u8 control = (value >> 8) & 0xff; __u8 channel = value & 0xff; + unsigned int count = 0; if (channel >= MAX_CHANNELS) { usb_audio_dbg(mixer->chip, @@ -2382,6 +2383,12 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, return; } + for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) + count++; + + if (count == 0) + return; + for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) { struct usb_mixer_elem_info *info; @@ -2389,7 +2396,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, continue; info = (struct usb_mixer_elem_info *)list; - if (info->control != control) + if (count > 1 && info->control != control) continue; switch (attribute) { -- cgit v0.10.2 From 4f29efc0ea61de1482a27c580575d860385cd54f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 12 Apr 2016 15:16:24 +0200 Subject: ALSA: hda - Add missing capture_hook calls for dyn-ADC PCM streams The calls for capture_hook were missing in dyn_adc_capture_pcm_prepare and cleanup callbacks. Luckily there are no users of the capture hooks with dyn-adc PCM, so far, thus this doesn't change the behavior of existing devices, but it's a fix for a future usage. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 7ca5b89..dc2c136 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -5432,6 +5432,7 @@ static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, spec->cur_adc_stream_tag = stream_tag; spec->cur_adc_format = format; snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); + call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE); return 0; } @@ -5442,6 +5443,7 @@ static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_gen_spec *spec = codec->spec; snd_hda_codec_cleanup_stream(codec, spec->cur_adc); spec->cur_adc = 0; + call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP); return 0; } -- cgit v0.10.2 From fa44b7ec9bc4115513e59f31da1167166bd6346a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 12 Apr 2016 15:23:05 +0200 Subject: ALSA: hda - Update documentation Update the URLs for alsa-info.sh and hda-emu. Also drop the alsa-driver snapshot URL since it's been discontinued recently. Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt index e7193aa..d4510eb 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/alsa/HD-Audio.txt @@ -655,17 +655,6 @@ development branches in general while the development for the current and next kernels are found in for-linus and for-next branches, respectively. -If you are using the latest Linus tree, it'd be better to pull the -above GIT tree onto it. If you are using the older kernels, an easy -way to try the latest ALSA code is to build from the snapshot -tarball. There are daily tarballs and the latest snapshot tarball. -All can be built just like normal alsa-driver release packages, that -is, installed via the usual spells: configure, make and make -install(-modules). See INSTALL in the package. The snapshot tarballs -are found at: - -- ftp://ftp.suse.com/pub/people/tiwai/snapshot/ - Sending a Bug Report ~~~~~~~~~~~~~~~~~~~~ @@ -699,7 +688,12 @@ problems. alsa-info ~~~~~~~~~ The script `alsa-info.sh` is a very useful tool to gather the audio -device information. You can fetch the latest version from: +device information. It's included in alsa-utils package. The latest +version can be found on git repository: + +- git://git.alsa-project.org/alsa-utils.git + +The script can be fetched directly from the following URL, too: - http://www.alsa-project.org/alsa-info.sh @@ -836,15 +830,11 @@ can get a proc-file dump at the current state, get a list of control (mixer) elements, set/get the control element value, simulate the PCM operation, the jack plugging simulation, etc. -The package is found in: - -- ftp://ftp.suse.com/pub/people/tiwai/misc/ - -A git repository is available: +The program is found in the git repository below: - git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-emu.git -See README file in the tarball for more details about hda-emu +See README file in the repository for more details about hda-emu program. -- cgit v0.10.2 From 3aa02cb664c5fb1042958c8d1aa8c35055a2ebc4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 Apr 2016 18:02:37 +0200 Subject: ALSA: pcm : Call kill_fasync() in stream lock Currently kill_fasync() is called outside the stream lock in snd_pcm_period_elapsed(). This is potentially racy, since the stream may get released even during the irq handler is running. Although snd_pcm_release_substream() calls snd_pcm_drop(), this doesn't guarantee that the irq handler finishes, thus the kill_fasync() call outside the stream spin lock may be invoked after the substream is detached, as recently reported by KASAN. As a quick workaround, move kill_fasync() call inside the stream lock. The fasync is rarely used interface, so this shouldn't have a big impact from the performance POV. Ideally, we should implement some sync mechanism for the proper finish of stream and irq handler. But this oneliner should suffice for most cases, so far. Reported-by: Baozeng Ding Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 3a9b66c..0aca397 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1886,8 +1886,8 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) snd_timer_interrupt(substream->timer, 1); #endif _end: - snd_pcm_stream_unlock_irqrestore(substream, flags); kill_fasync(&runtime->fasync, SIGIO, POLL_IN); + snd_pcm_stream_unlock_irqrestore(substream, flags); } EXPORT_SYMBOL(snd_pcm_period_elapsed); -- cgit v0.10.2 From a19c921fca0a865b657d59b2c9a05aa0a2905126 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 15 Apr 2016 15:28:52 +0200 Subject: ALSA: lx646es: Fix possible uninitialized variable reference lx_pipe_state() checks the return value from lx_message_send_atomic() and breaks the loop only when it's a negative value. However, lx_message_send_atomic() may return a positive error code (as the return code from the hardware), and then lx_pipe_state() tries to compare the uninitialized current_state variable. Fix this behavior by checking the positive non-zero error code as well. Reported-by: Dan Carpenter Signed-off-by: Takashi Iwai diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c index f3d6202..a80684b 100644 --- a/sound/pci/lx6464es/lx_core.c +++ b/sound/pci/lx6464es/lx_core.c @@ -644,7 +644,7 @@ static int lx_pipe_wait_for_state(struct lx6464es *chip, u32 pipe, if (err < 0) return err; - if (current_state == state) + if (!err && current_state == state) return 0; mdelay(1); -- cgit v0.10.2 From d23f0517357ef48d2845846e899d125b3c1a492e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 19 Apr 2016 15:28:39 +0200 Subject: ALSA: ens1371: Fix "Line In->Rear Out Switch" control The "Line In->Rear Out Switch" control on ens1371 driver returns a bogus value, always true, as its check is totally broken. Fix it to check the proper GPIO bit mask. Reported-by: David Binderman Signed-off-by: Takashi Iwai diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 0dc44eb..626cd21 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1548,7 +1548,7 @@ static int snd_es1373_line_get(struct snd_kcontrol *kcontrol, int val = 0; spin_lock_irq(&ensoniq->reg_lock); - if ((ensoniq->ctrl & ES_1371_GPIO_OUTM) >= 4) + if (ensoniq->ctrl & ES_1371_GPIO_OUT(4)) val = 1; ucontrol->value.integer.value[0] = val; spin_unlock_irq(&ensoniq->reg_lock); -- cgit v0.10.2 From b610386c8afba397238329c50c45a3abc79ba45f Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 31 Mar 2016 08:47:09 +0900 Subject: ALSA: firewire-tascam: deleyed registration of sound card When some tascam units are connected sequentially, userspace applications are involved at bus-reset state on IEEE 1394 bus. In the state, any communications can be canceled. Therefore, sound card registration should be delayed till the bus gets calm. This commit achieves it. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c index e281c33..9dc93a7 100644 --- a/sound/firewire/tascam/tascam.c +++ b/sound/firewire/tascam/tascam.c @@ -85,10 +85,8 @@ static int identify_model(struct snd_tscm *tscm) return 0; } -static void tscm_card_free(struct snd_card *card) +static void tscm_free(struct snd_tscm *tscm) { - struct snd_tscm *tscm = card->private_data; - snd_tscm_transaction_unregister(tscm); snd_tscm_stream_destroy_duplex(tscm); @@ -97,44 +95,36 @@ static void tscm_card_free(struct snd_card *card) mutex_destroy(&tscm->mutex); } -static int snd_tscm_probe(struct fw_unit *unit, - const struct ieee1394_device_id *entry) +static void tscm_card_free(struct snd_card *card) { - struct snd_card *card; - struct snd_tscm *tscm; + tscm_free(card->private_data); +} + +static void do_registration(struct work_struct *work) +{ + struct snd_tscm *tscm = container_of(work, struct snd_tscm, dwork.work); int err; - /* create card */ - err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, - sizeof(struct snd_tscm), &card); + err = snd_card_new(&tscm->unit->device, -1, NULL, THIS_MODULE, 0, + &tscm->card); if (err < 0) - return err; - card->private_free = tscm_card_free; - - /* initialize myself */ - tscm = card->private_data; - tscm->card = card; - tscm->unit = fw_unit_get(unit); - - mutex_init(&tscm->mutex); - spin_lock_init(&tscm->lock); - init_waitqueue_head(&tscm->hwdep_wait); + return; err = identify_model(tscm); if (err < 0) goto error; - snd_tscm_proc_init(tscm); - - err = snd_tscm_stream_init_duplex(tscm); + err = snd_tscm_transaction_register(tscm); if (err < 0) goto error; - err = snd_tscm_create_pcm_devices(tscm); + err = snd_tscm_stream_init_duplex(tscm); if (err < 0) goto error; - err = snd_tscm_transaction_register(tscm); + snd_tscm_proc_init(tscm); + + err = snd_tscm_create_pcm_devices(tscm); if (err < 0) goto error; @@ -146,35 +136,91 @@ static int snd_tscm_probe(struct fw_unit *unit, if (err < 0) goto error; - err = snd_card_register(card); + err = snd_card_register(tscm->card); if (err < 0) goto error; - dev_set_drvdata(&unit->device, tscm); + /* + * After registered, tscm instance can be released corresponding to + * releasing the sound card instance. + */ + tscm->card->private_free = tscm_card_free; + tscm->card->private_data = tscm; + tscm->registered = true; - return err; + return; error: - snd_card_free(card); - return err; + snd_tscm_transaction_unregister(tscm); + snd_tscm_stream_destroy_duplex(tscm); + snd_card_free(tscm->card); + dev_info(&tscm->unit->device, + "Sound card registration failed: %d\n", err); +} + +static int snd_tscm_probe(struct fw_unit *unit, + const struct ieee1394_device_id *entry) +{ + struct snd_tscm *tscm; + + /* Allocate this independent of sound card instance. */ + tscm = kzalloc(sizeof(struct snd_tscm), GFP_KERNEL); + if (tscm == NULL) + return -ENOMEM; + + /* initialize myself */ + tscm->unit = fw_unit_get(unit); + dev_set_drvdata(&unit->device, tscm); + + mutex_init(&tscm->mutex); + spin_lock_init(&tscm->lock); + init_waitqueue_head(&tscm->hwdep_wait); + + /* Allocate and register this sound card later. */ + INIT_DEFERRABLE_WORK(&tscm->dwork, do_registration); + snd_fw_schedule_registration(unit, &tscm->dwork); + + return 0; } static void snd_tscm_update(struct fw_unit *unit) { struct snd_tscm *tscm = dev_get_drvdata(&unit->device); + /* Postpone a workqueue for deferred registration. */ + if (!tscm->registered) + snd_fw_schedule_registration(unit, &tscm->dwork); + snd_tscm_transaction_reregister(tscm); - mutex_lock(&tscm->mutex); - snd_tscm_stream_update_duplex(tscm); - mutex_unlock(&tscm->mutex); + /* + * After registration, userspace can start packet streaming, then this + * code block works fine. + */ + if (tscm->registered) { + mutex_lock(&tscm->mutex); + snd_tscm_stream_update_duplex(tscm); + mutex_unlock(&tscm->mutex); + } } static void snd_tscm_remove(struct fw_unit *unit) { struct snd_tscm *tscm = dev_get_drvdata(&unit->device); - /* No need to wait for releasing card object in this context. */ - snd_card_free_when_closed(tscm->card); + /* + * Confirm to stop the work for registration before the sound card is + * going to be released. The work is not scheduled again because bus + * reset handler is not called anymore. + */ + cancel_delayed_work_sync(&tscm->dwork); + + if (tscm->registered) { + /* No need to wait for releasing card object in this context. */ + snd_card_free_when_closed(tscm->card); + } else { + /* Don't forget this case. */ + tscm_free(tscm); + } } static const struct ieee1394_device_id snd_tscm_id_table[] = { diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 30ab77e..1f61011 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -51,6 +51,8 @@ struct snd_tscm { struct mutex mutex; spinlock_t lock; + bool registered; + struct delayed_work dwork; const struct snd_tscm_spec *spec; struct fw_iso_resources tx_resources; -- cgit v0.10.2 From 34ce71a96dcba24c71b07f1b087bd179b885784d Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Sat, 23 Apr 2016 02:58:05 +0200 Subject: ALSA: timer: remove legacy rtctimer There are no users of rtctimer left. Remove its code as this is the in-kernel user of the legacy PC RTC driver that will hopefully be removed at some point. Signed-off-by: Alexandre Belloni Signed-off-by: Takashi Iwai diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 67bf49d..609cadb 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -672,7 +672,7 @@ enum { /* global timers (device member) */ #define SNDRV_TIMER_GLOBAL_SYSTEM 0 -#define SNDRV_TIMER_GLOBAL_RTC 1 +#define SNDRV_TIMER_GLOBAL_RTC 1 /* unused */ #define SNDRV_TIMER_GLOBAL_HPET 2 #define SNDRV_TIMER_GLOBAL_HRTIMER 3 diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 6d12ca9..9749f9e 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -141,35 +141,6 @@ config SND_SEQ_HRTIMER_DEFAULT Say Y here to use the HR-timer backend as the default sequencer timer. -config SND_RTCTIMER - tristate "RTC Timer support" - depends on RTC - select SND_TIMER - help - Say Y here to enable RTC timer support for ALSA. ALSA uses - the RTC timer as a precise timing source and maps the RTC - timer to ALSA's timer interface. The ALSA sequencer code also - can use this timing source. - - To compile this driver as a module, choose M here: the module - will be called snd-rtctimer. - - Note that this option is exclusive with the new RTC drivers - (CONFIG_RTC_CLASS) since this requires the old API. - -config SND_SEQ_RTCTIMER_DEFAULT - bool "Use RTC as default sequencer timer" - depends on SND_RTCTIMER && SND_SEQUENCER - depends on !SND_SEQ_HRTIMER_DEFAULT - default y - help - Say Y here to use the RTC timer as the default sequencer - timer. This is strongly recommended because it ensures - precise MIDI timing even when the system timer runs at less - than 1000 Hz. - - If in doubt, say Y. - config SND_DYNAMIC_MINORS bool "Dynamic device file minor numbers" help diff --git a/sound/core/Makefile b/sound/core/Makefile index 48ab4b8..e85d9dd 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -37,7 +37,6 @@ obj-$(CONFIG_SND) += snd.o obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o obj-$(CONFIG_SND_TIMER) += snd-timer.o obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o -obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o obj-$(CONFIG_SND_PCM) += snd-pcm.o obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c deleted file mode 100644 index f3420d1..0000000 --- a/sound/core/rtctimer.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * RTC based high-frequency timer - * - * Copyright (C) 2000 Takashi Iwai - * based on rtctimer.c by Steve Ratcliffe - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include -#include - -#if IS_ENABLED(CONFIG_RTC) - -#include - -#define RTC_FREQ 1024 /* default frequency */ -#define NANO_SEC 1000000000L /* 10^9 in sec */ - -/* - * prototypes - */ -static int rtctimer_open(struct snd_timer *t); -static int rtctimer_close(struct snd_timer *t); -static int rtctimer_start(struct snd_timer *t); -static int rtctimer_stop(struct snd_timer *t); - - -/* - * The hardware dependent description for this timer. - */ -static struct snd_timer_hardware rtc_hw = { - .flags = SNDRV_TIMER_HW_AUTO | - SNDRV_TIMER_HW_FIRST | - SNDRV_TIMER_HW_TASKLET, - .ticks = 100000000L, /* FIXME: XXX */ - .open = rtctimer_open, - .close = rtctimer_close, - .start = rtctimer_start, - .stop = rtctimer_stop, -}; - -static int rtctimer_freq = RTC_FREQ; /* frequency */ -static struct snd_timer *rtctimer; -static struct tasklet_struct rtc_tasklet; -static rtc_task_t rtc_task; - - -static int -rtctimer_open(struct snd_timer *t) -{ - int err; - - err = rtc_register(&rtc_task); - if (err < 0) - return err; - t->private_data = &rtc_task; - return 0; -} - -static int -rtctimer_close(struct snd_timer *t) -{ - rtc_task_t *rtc = t->private_data; - if (rtc) { - rtc_unregister(rtc); - tasklet_kill(&rtc_tasklet); - t->private_data = NULL; - } - return 0; -} - -static int -rtctimer_start(struct snd_timer *timer) -{ - rtc_task_t *rtc = timer->private_data; - if (snd_BUG_ON(!rtc)) - return -EINVAL; - rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq); - rtc_control(rtc, RTC_PIE_ON, 0); - return 0; -} - -static int -rtctimer_stop(struct snd_timer *timer) -{ - rtc_task_t *rtc = timer->private_data; - if (snd_BUG_ON(!rtc)) - return -EINVAL; - rtc_control(rtc, RTC_PIE_OFF, 0); - return 0; -} - -static void rtctimer_tasklet(unsigned long data) -{ - snd_timer_interrupt((struct snd_timer *)data, 1); -} - -/* - * interrupt - */ -static void rtctimer_interrupt(void *private_data) -{ - tasklet_schedule(private_data); -} - - -/* - * ENTRY functions - */ -static int __init rtctimer_init(void) -{ - int err; - struct snd_timer *timer; - - if (rtctimer_freq < 2 || rtctimer_freq > 8192 || - !is_power_of_2(rtctimer_freq)) { - pr_err("ALSA: rtctimer: invalid frequency %d\n", rtctimer_freq); - return -EINVAL; - } - - /* Create a new timer and set up the fields */ - err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer); - if (err < 0) - return err; - - timer->module = THIS_MODULE; - strcpy(timer->name, "RTC timer"); - timer->hw = rtc_hw; - timer->hw.resolution = NANO_SEC / rtctimer_freq; - - tasklet_init(&rtc_tasklet, rtctimer_tasklet, (unsigned long)timer); - - /* set up RTC callback */ - rtc_task.func = rtctimer_interrupt; - rtc_task.private_data = &rtc_tasklet; - - err = snd_timer_global_register(timer); - if (err < 0) { - snd_timer_global_free(timer); - return err; - } - rtctimer = timer; /* remember this */ - - return 0; -} - -static void __exit rtctimer_exit(void) -{ - if (rtctimer) { - snd_timer_global_free(rtctimer); - rtctimer = NULL; - } -} - - -/* - * exported stuff - */ -module_init(rtctimer_init) -module_exit(rtctimer_exit) - -module_param(rtctimer_freq, int, 0444); -MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz"); - -MODULE_LICENSE("GPL"); - -MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC)); - -#endif /* IS_ENABLED(CONFIG_RTC) */ diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c index 7e0aabb..639544b 100644 --- a/sound/core/seq/seq.c +++ b/sound/core/seq/seq.c @@ -47,8 +47,6 @@ int seq_default_timer_card = -1; int seq_default_timer_device = #ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT SNDRV_TIMER_GLOBAL_HRTIMER -#elif defined(CONFIG_SND_SEQ_RTCTIMER_DEFAULT) - SNDRV_TIMER_GLOBAL_RTC #else SNDRV_TIMER_GLOBAL_SYSTEM #endif diff --git a/sound/core/timer.c b/sound/core/timer.c index 6469bed..0cfc028 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -37,8 +37,6 @@ #if IS_ENABLED(CONFIG_SND_HRTIMER) #define DEFAULT_TIMER_LIMIT 4 -#elif IS_ENABLED(CONFIG_SND_RTCTIMER) -#define DEFAULT_TIMER_LIMIT 2 #else #define DEFAULT_TIMER_LIMIT 1 #endif -- cgit v0.10.2 From 8d84c1973b69621bcb89d5d8a69699e8347a3d90 Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Mon, 25 Apr 2016 07:37:02 +0100 Subject: ALSA: doc: fix spelling mistakes Signed-off-by: Eric Engestrom Acked-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/compress_offload.txt b/Documentation/sound/alsa/compress_offload.txt index 630c492..81476b4 100644 --- a/Documentation/sound/alsa/compress_offload.txt +++ b/Documentation/sound/alsa/compress_offload.txt @@ -149,7 +149,7 @@ Gapless Playback ================ When playing thru an album, the decoders have the ability to skip the encoder delay and padding and directly move from one track content to another. The end -user can perceive this as gapless playback as we dont have silence while +user can perceive this as gapless playback as we don't have silence while switching from one track to another Also, there might be low-intensity noises due to encoding. Perfect gapless is diff --git a/Documentation/sound/alsa/soc/dapm.txt b/Documentation/sound/alsa/soc/dapm.txt index 6faab48..c45bd79 100644 --- a/Documentation/sound/alsa/soc/dapm.txt +++ b/Documentation/sound/alsa/soc/dapm.txt @@ -132,7 +132,7 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0), SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls, ARRAY_SIZE(wm8731_output_mixer_controls)), -If you dont want the mixer elements prefixed with the name of the mixer widget, +If you don't want the mixer elements prefixed with the name of the mixer widget, you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same as for SND_SOC_DAPM_MIXER. diff --git a/Documentation/sound/alsa/soc/overview.txt b/Documentation/sound/alsa/soc/overview.txt index ff88f52..f3f28b7 100644 --- a/Documentation/sound/alsa/soc/overview.txt +++ b/Documentation/sound/alsa/soc/overview.txt @@ -63,7 +63,7 @@ multiple re-usable component drivers :- and any audio DSP drivers for that platform. * Machine class driver: The machine driver class acts as the glue that - decribes and binds the other component drivers together to form an ALSA + describes and binds the other component drivers together to form an ALSA "sound card device". It handles any machine specific controls and machine level audio events (e.g. turning on an amp at start of playback). -- cgit v0.10.2 From d2c5cf88d5282de258f4eb6ab40040b80a075cd8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 24 Apr 2016 22:52:18 +0200 Subject: ALSA: hrtimer: Handle start/stop more properly This patch tries to address the still remaining issues in ALSA hrtimer driver: - Spurious use-after-free was detected in hrtimer callback - Incorrect rescheduling due to delayed start - WARN_ON() is triggered in hrtimer_forward() invoked in hrtimer callback The first issue happens only when the new timer is scheduled even while hrtimer is being closed. It's related with the second and third items; since ALSA timer core invokes hw.start callback during hrtimer interrupt, this may result in the explicit call of hrtimer_start(). Also, the similar problem is seen for the stop; ALSA timer core invokes hw.stop callback even in the hrtimer handler, too. Since we must not call the synced hrtimer_cancel() in such a context, it's just a hrtimer_try_to_cancel() call that doesn't properly work. Another culprit of the second and third items is the call of hrtimer_forward_now() before snd_timer_interrupt(). The timer->stick value may change during snd_timer_interrupt() call, but this possibility is ignored completely. For covering these subtle and messy issues, the following changes have been done in this patch: - A new flag, in_callback, is introduced in the private data to indicate that the hrtimer handler is being processed. - Both start and stop callbacks skip when called from (during) in_callback flag. - The hrtimer handler returns properly HRTIMER_RESTART and NORESTART depending on the running state now. - The hrtimer handler reprograms the expiry properly after snd_timer_interrupt() call, instead of before. - The close callback clears running flag and sets in_callback flag to block any further start/stop calls. Signed-off-by: Takashi Iwai diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index 656d9a9..e2f2702 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -38,37 +38,53 @@ static unsigned int resolution; struct snd_hrtimer { struct snd_timer *timer; struct hrtimer hrt; - atomic_t running; + bool in_callback; }; static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) { struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt); struct snd_timer *t = stime->timer; - unsigned long oruns; - - if (!atomic_read(&stime->running)) - return HRTIMER_NORESTART; - - oruns = hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution)); - snd_timer_interrupt(stime->timer, t->sticks * oruns); + ktime_t delta; + unsigned long ticks; + enum hrtimer_restart ret = HRTIMER_NORESTART; + + spin_lock(&t->lock); + if (!t->running) + goto out; /* fast path */ + stime->in_callback = true; + ticks = t->sticks; + spin_unlock(&t->lock); + + /* calculate the drift */ + delta = ktime_sub(hrt->base->get_time(), hrtimer_get_expires(hrt)); + if (delta.tv64 > 0) + ticks += ktime_divns(delta, ticks * resolution); + + snd_timer_interrupt(stime->timer, ticks); + + spin_lock(&t->lock); + if (t->running) { + hrtimer_add_expires_ns(hrt, t->sticks * resolution); + ret = HRTIMER_RESTART; + } - if (!atomic_read(&stime->running)) - return HRTIMER_NORESTART; - return HRTIMER_RESTART; + stime->in_callback = false; + out: + spin_unlock(&t->lock); + return ret; } static int snd_hrtimer_open(struct snd_timer *t) { struct snd_hrtimer *stime; - stime = kmalloc(sizeof(*stime), GFP_KERNEL); + stime = kzalloc(sizeof(*stime), GFP_KERNEL); if (!stime) return -ENOMEM; hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); stime->timer = t; stime->hrt.function = snd_hrtimer_callback; - atomic_set(&stime->running, 0); t->private_data = stime; return 0; } @@ -78,6 +94,11 @@ static int snd_hrtimer_close(struct snd_timer *t) struct snd_hrtimer *stime = t->private_data; if (stime) { + spin_lock_irq(&t->lock); + t->running = 0; /* just to be sure */ + stime->in_callback = 1; /* skip start/stop */ + spin_unlock_irq(&t->lock); + hrtimer_cancel(&stime->hrt); kfree(stime); t->private_data = NULL; @@ -89,18 +110,19 @@ static int snd_hrtimer_start(struct snd_timer *t) { struct snd_hrtimer *stime = t->private_data; - atomic_set(&stime->running, 0); - hrtimer_try_to_cancel(&stime->hrt); + if (stime->in_callback) + return 0; hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution), HRTIMER_MODE_REL); - atomic_set(&stime->running, 1); return 0; } static int snd_hrtimer_stop(struct snd_timer *t) { struct snd_hrtimer *stime = t->private_data; - atomic_set(&stime->running, 0); + + if (stime->in_callback) + return 0; hrtimer_try_to_cancel(&stime->hrt); return 0; } -- cgit v0.10.2 From 58a8738cfcdec389b3764a636303f97b57f85193 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Mar 2016 21:03:09 +0100 Subject: ALSA: au88x0: Fix overlapped PCM pointer au88x0 hardware seems returning the current pointer at the buffer boundary instead of going back to zero. This results in spewing warnings from PCM core. This patch corrects the return value from the pointer callback within the proper value range, just returning zero if the position is equal or above the buffer size. Signed-off-by: Takashi Iwai diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index a6d6d8d..df5741a 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -432,7 +432,10 @@ static snd_pcm_uframes_t snd_vortex_pcm_pointer(struct snd_pcm_substream *substr #endif //printk(KERN_INFO "vortex: pointer = 0x%x\n", current_ptr); spin_unlock(&chip->lock); - return (bytes_to_frames(substream->runtime, current_ptr)); + current_ptr = bytes_to_frames(substream->runtime, current_ptr); + if (current_ptr >= substream->runtime->buffer_size) + current_ptr = 0; + return current_ptr; } /* operators */ -- cgit v0.10.2 From 57dd5414a087991d427067b32dc3324af61b1c8b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 29 Apr 2016 11:49:04 +0200 Subject: ALSA: usb-audio: Limit retrying sample rate reads There are many USB audio devices with buggy firmware that don't react with the sample rate reading properly. This often results in the flood of error messages and slowing down the operation. The sample rate read back is basically only for confirming the sample rate setup, and it's not critically important. As a compromise, in this patch, we stop the sample rate read back once when the device gives errors more than tolerance (twice, as of now). This should improve most of error cases while we still can catch the firmware bugginess. Signed-off-by: Takashi Iwai diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 7ccbcaf..26dd5f2 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -309,6 +309,9 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, * support reading */ if (snd_usb_get_sample_rate_quirk(chip)) return 0; + /* the firmware is likely buggy, don't repeat to fail too many times */ + if (chip->sample_rate_read_error > 2) + return 0; if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, @@ -316,6 +319,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, data, sizeof(data))) < 0) { dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n", iface, fmt->altsetting, ep); + chip->sample_rate_read_error++; return 0; /* some devices don't support reading */ } diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index a161c7c..89b6853 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -50,6 +50,7 @@ struct snd_usb_audio { int num_interfaces; int num_suspended_intf; + int sample_rate_read_error; struct list_head pcm_list; /* list of pcm streams */ struct list_head ep_list; /* list of audio-related endpoints */ -- cgit v0.10.2 From f8ff65bce4fe9e94794beb21a3ba5e0cced43b1a Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 30 Apr 2016 22:06:46 +0900 Subject: ALSA: dice: add support for M-Audio Profire 610 and perhaps 2626 M-Audio Profire 610 has an unexpected value in version field of its config ROM, thus ALSA dice driver is not assigned to the model due to a mismatch of modalias. This commit adds an entry to support the model. I expect the entry is also for Profire 2626. I note that Profire 610 uses TCD2220 (so-called Dice Jr.), and supports a part of Extended Application Protocol (EAP). $ cd linux-firewire-utils/src $ ./crpp < /sys/bus/firewire/devices/fw1/config_rom ROM header and bus information block ------------------------------------------------------------ 400 04047689 bus_info_length 4, crc_length 4, crc 30345 404 31333934 bus_name "1394" 408 e0ff8112 irmc 1, cmc 1, isc 1, bmc 0, pmc 0, cyc_clk_acc 255, max_rec 8 (512), max_rom 1, gen 1, spd 2 (S400) 40c 000d6c04 company_id 000d6c | 410 04400002 device_id 0404400002 | EUI-64 000d6c0404400002 root directory ------------------------------------------------------------ 414 000695fe directory_length 6, crc 38398 418 03000d6c vendor 41c 8100000a --> descriptor leaf at 444 420 17000011 model 424 8100000d --> descriptor leaf at 458 428 0c0087c0 node capabilities per IEEE 1394 42c d1000001 --> unit directory at 430 unit directory at 430 ------------------------------------------------------------ 430 0004fb14 directory_length 4, crc 64276 434 12000d6c specifier id 438 130100d1 version 43c 17000011 model 440 8100000c --> descriptor leaf at 470 descriptor leaf at 444 ------------------------------------------------------------ 444 0004b8e4 leaf_length 4, crc 47332 448 00000000 textual descriptor 44c 00000000 minimal ASCII 450 4d2d4175 "M-Au" 454 64696f00 "dio" descriptor leaf at 458 ------------------------------------------------------------ 458 00053128 leaf_length 5, crc 12584 45c 00000000 textual descriptor 460 00000000 minimal ASCII 464 50726f46 "ProF" 468 69726520 "ire " 46c 36313000 "610" descriptor leaf at 470 ------------------------------------------------------------ 470 00053128 leaf_length 5, crc 12584 474 00000000 textual descriptor 478 00000000 minimal ASCII 47c 50726f46 "ProF" 480 69726520 "ire " 484 36313000 "610" $ cat /proc/asound/card1/dice sections: global: offset 10, size 90 tx: offset 100, size 142 rx: offset 242, size 282 ext_sync: offset 524, size 4 unused2: offset 0, size 0 global: owner: ffc0:000100000000 notification: 00000040 nick name: FW610 clock select: internal 48000 enable: 1 status: locked 48000 ext status: 00000040 sample rate: 48000 version: 1.0.4.0 clock caps: 32000 44100 48000 88200 96000 176400 192000 aes1 aes4 aes adat tdif wc arx1 arx2 internal clock source names: SPDIF\AES34\AES56\TOS\AES_ANY\ADAT\ADAT_AUX\Word Clock\Unused\Unused\Unused\Unused\Internal\\ ... Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 96fe68f4..25e9f77 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -317,6 +317,13 @@ static const struct ieee1394_device_id dice_id_table[] = { .match_flags = IEEE1394_MATCH_VERSION, .version = DICE_INTERFACE, }, + /* M-Audio Profire 610/2626 has a different value in version field. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_SPECIFIER_ID, + .vendor_id = 0x000d6c, + .specifier_id = 0x000d6c, + }, { } }; MODULE_DEVICE_TABLE(ieee1394, dice_id_table); -- cgit v0.10.2 From 242658ff99ab9d87e704475ef78c3102ead344cf Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 2 May 2016 14:06:28 +0530 Subject: ALSA: compress: fix some typo Noticed two typos in Documentation, so fix them up Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/compress_offload.txt b/Documentation/sound/alsa/compress_offload.txt index 81476b4..8ba556a 100644 --- a/Documentation/sound/alsa/compress_offload.txt +++ b/Documentation/sound/alsa/compress_offload.txt @@ -184,7 +184,7 @@ Sequence flow for gapless would be: - Fill data of the first track - Trigger start - User-space finished sending all, -- Indicaite next track data by sending set_next_track +- Indicate next track data by sending set_next_track - Set metadata of the next track - then call partial_drain to flush most of buffer in DSP - Fill data of the next track -- cgit v0.10.2 From cec8f96e49d9be372fdb0c3836dcf31ec71e457e Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 3 May 2016 16:44:07 -0400 Subject: ALSA: timer: Fix leak in SNDRV_TIMER_IOCTL_PARAMS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stack object “tread” has a total size of 32 bytes. Its field “event” and “val” both contain 4 bytes padding. These 8 bytes padding bytes are sent to user without being initialized. Signed-off-by: Kangjie Lu Signed-off-by: Takashi Iwai diff --git a/sound/core/timer.c b/sound/core/timer.c index 0cfc028..306a93d 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1737,6 +1737,7 @@ static int snd_timer_user_params(struct file *file, if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { if (tu->tread) { struct snd_timer_tread tread; + memset(&tread, 0, sizeof(tread)); tread.event = SNDRV_TIMER_EVENT_EARLY; tread.tstamp.tv_sec = 0; tread.tstamp.tv_nsec = 0; -- cgit v0.10.2 From 9a47e9cff994f37f7f0dbd9ae23740d0f64f9fe6 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 3 May 2016 16:44:20 -0400 Subject: ALSA: timer: Fix leak in events via snd_timer_user_ccallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stack object “r1” has a total size of 32 bytes. Its field “event” and “val” both contain 4 bytes padding. These 8 bytes padding bytes are sent to user without being initialized. Signed-off-by: Kangjie Lu Signed-off-by: Takashi Iwai diff --git a/sound/core/timer.c b/sound/core/timer.c index 306a93d..cc3c08d 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1223,6 +1223,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, tu->tstamp = *tstamp; if ((tu->filter & (1 << event)) == 0 || !tu->tread) return; + memset(&r1, 0, sizeof(r1)); r1.event = event; r1.tstamp = *tstamp; r1.val = resolution; -- cgit v0.10.2 From e4ec8cc8039a7063e24204299b462bd1383184a5 Mon Sep 17 00:00:00 2001 From: Kangjie Lu Date: Tue, 3 May 2016 16:44:32 -0400 Subject: ALSA: timer: Fix leak in events via snd_timer_user_tinterrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stack object “r1” has a total size of 32 bytes. Its field “event” and “val” both contain 4 bytes padding. These 8 bytes padding bytes are sent to user without being initialized. Signed-off-by: Kangjie Lu Signed-off-by: Takashi Iwai diff --git a/sound/core/timer.c b/sound/core/timer.c index cc3c08d..e722022 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1266,6 +1266,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, } if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && tu->last_resolution != resolution) { + memset(&r1, 0, sizeof(r1)); r1.event = SNDRV_TIMER_EVENT_RESOLUTION; r1.tstamp = tstamp; r1.val = resolution; -- cgit v0.10.2 From 84d7a4470dbac0dd9389050100b54a1625d04264 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 4 May 2016 09:27:37 +0300 Subject: ALSA: isa/wavefront: prevent some out of bound writes "header->number" can be up to USHRT_MAX and it comes from the ioctl so it needs to be capped. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c index 69f76ff..718d5e3 100644 --- a/sound/isa/wavefront/wavefront_synth.c +++ b/sound/isa/wavefront/wavefront_synth.c @@ -785,6 +785,9 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header) DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n", header->number); + if (header->number >= ARRAY_SIZE(dev->patch_status)) + return -EINVAL; + dev->patch_status[header->number] |= WF_SLOT_FILLED; bptr = buf; @@ -809,6 +812,9 @@ wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header) DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n", header->number); + if (header->number >= ARRAY_SIZE(dev->prog_status)) + return -EINVAL; + dev->prog_status[header->number] = WF_SLOT_USED; /* XXX need to zero existing SLOT_USED bit for program_status[i] @@ -898,6 +904,9 @@ wavefront_send_sample (snd_wavefront_t *dev, header->number = x; } + if (header->number >= WF_MAX_SAMPLE) + return -EINVAL; + if (header->size) { /* XXX it's a debatable point whether or not RDONLY semantics -- cgit v0.10.2 From dcd4f0db6141d6bf2cb897309d5d6f53d1b1696f Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Wed, 4 May 2016 15:50:18 +0800 Subject: ALSA: hda/realtek - New codecs support for ALC234/ALC274/ALC294 Support new codecs for ALC234/ALC274/ALC294. This three codecs was the same IC. But bonding is not the same. Signed-off-by: Kailang Yang Cc: Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ac4490a..a2fdf5f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -342,6 +342,11 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0293: alc_update_coef_idx(codec, 0xa, 1<<13, 0); break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_update_coef_idx(codec, 0x10, 1<<15, 0); + break; case 0x10ec0662: if ((coef & 0x00f0) == 0x0030) alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */ @@ -2647,6 +2652,7 @@ enum { ALC269_TYPE_ALC255, ALC269_TYPE_ALC256, ALC269_TYPE_ALC225, + ALC269_TYPE_ALC294, }; /* @@ -2677,6 +2683,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) case ALC269_TYPE_ALC255: case ALC269_TYPE_ALC256: case ALC269_TYPE_ALC225: + case ALC269_TYPE_ALC294: ssids = alc269_ssids; break; default: @@ -6028,6 +6035,11 @@ static int patch_alc269(struct hda_codec *codec) case 0x10ec0225: spec->codec_variant = ALC269_TYPE_ALC225; break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + spec->codec_variant = ALC269_TYPE_ALC294; + break; } if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) { @@ -6929,6 +6941,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269), HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269), HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269), HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269), HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269), HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269), @@ -6939,6 +6952,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269), HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269), HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0274, "ALC274", patch_alc269), HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269), HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269), HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269), @@ -6951,6 +6965,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269), HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269), HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269), HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269), HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861), HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd), -- cgit v0.10.2 From 748a1ccc433f1c4edb214fff4b340af9c1da3f88 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 4 May 2016 14:18:39 +0200 Subject: ALSA: usb-audio: correct speed checking Allow handling SS+ USB devices correctly. Signed-off-by: Oliver Neukum Signed-off-by: Takashi Iwai diff --git a/sound/usb/card.c b/sound/usb/card.c index 63244bb..c4665dc 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -351,6 +351,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, case USB_SPEED_HIGH: case USB_SPEED_WIRELESS: case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: break; default: dev_err(&dev->dev, "unknown device speed %d\n", snd_usb_get_speed(dev)); @@ -451,6 +452,9 @@ static int snd_usb_audio_create(struct usb_interface *intf, case USB_SPEED_SUPER: strlcat(card->longname, ", super speed", sizeof(card->longname)); break; + case USB_SPEED_SUPER_PLUS: + strlcat(card->longname, ", super speed plus", sizeof(card->longname)); + break; default: break; } diff --git a/sound/usb/helper.c b/sound/usb/helper.c index 51ed1ac..7712e2b 100644 --- a/sound/usb/helper.c +++ b/sound/usb/helper.c @@ -120,6 +120,7 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip, case USB_SPEED_HIGH: case USB_SPEED_WIRELESS: case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: if (get_endpoint(alts, 0)->bInterval >= 1 && get_endpoint(alts, 0)->bInterval <= 4) return get_endpoint(alts, 0)->bInterval - 1; -- cgit v0.10.2 From 89e448b33a101dfc7218f3181bc3095af850db7a Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 4 May 2016 14:18:40 +0200 Subject: ALSA: usb-midi: correct speed checking Allow for SS+ USB devices Signed-off-by: Oliver Neukum Signed-off-by: Takashi Iwai diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 47de8af..7ba9292 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -911,6 +911,7 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep, switch (snd_usb_get_speed(ep->umidi->dev)) { case USB_SPEED_HIGH: case USB_SPEED_SUPER: + case USB_SPEED_SUPER_PLUS: count = 1; break; default: -- cgit v0.10.2 From 94e9080ce22d9fb7c0a4361a5890b2c6affc9b1b Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 5 May 2016 11:24:42 +0530 Subject: ALSA: hda: fix the missing ptr initialization ebus is a member of extended device and was never initialized, so do this at device creation. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 2433f7c..64de0a3 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -144,6 +144,7 @@ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) if (!edev) return -ENOMEM; hdev = &edev->hdac; + edev->ebus = ebus; snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); -- cgit v0.10.2 From 38b19ed7f81ec930f3ad2066ae088f574970c814 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Thu, 5 May 2016 11:24:43 +0530 Subject: ALSA: hda: fix to wait for RIRB & CORB DMA to set If the DMAs are not being quiesced properly, it may lead to stability issues, so the recommendation is to wait till DMAs are stopped. After setting the stop bit of RIRB/CORB DMA, we should wait for stop bit to be set. Signed-off-by: Jeeja KP Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 8c48623..9fee464 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -80,6 +80,22 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) } EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io); +/* wait for cmd dmas till they are stopped */ +static void hdac_wait_for_cmd_dmas(struct hdac_bus *bus) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(100); + while ((snd_hdac_chip_readb(bus, RIRBCTL) & AZX_RBCTL_DMA_EN) + && time_before(jiffies, timeout)) + udelay(10); + + timeout = jiffies + msecs_to_jiffies(100); + while ((snd_hdac_chip_readb(bus, CORBCTL) & AZX_CORBCTL_RUN) + && time_before(jiffies, timeout)) + udelay(10); +} + /** * snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers * @bus: HD-audio core bus @@ -90,6 +106,7 @@ void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus) /* disable ringbuffer DMAs */ snd_hdac_chip_writeb(bus, RIRBCTL, 0); snd_hdac_chip_writeb(bus, CORBCTL, 0); + hdac_wait_for_cmd_dmas(bus); /* disable unsolicited responses */ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0); spin_unlock_irq(&bus->reg_lock); -- cgit v0.10.2 From 73fc7f080105b16d9f189c82d0092f22107cc67b Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 May 2016 21:12:44 +0900 Subject: ALSA: firewire-lib: compute the value of second field in cycle count for IT context In callback function of isochronous context, u32 variable is passed for cycle count. The value of this variable comes from DMA descriptors of 1394 Open Host Controller Interface (1394 OHCI). In the specification, DMA descriptors transport lower 3 bits for second field and full cycle field in 16 bits field, therefore 16 bits of the u32 variable are available. The value for second is modulo 8, and the value for cycle is modulo 8,000. Currently, ALSA firewire-lib module don't use the value of the second field, because the value is useless to calculate presentation timestamp in IEC 61883-6. However, the value may be useful for debugging. In later commit, it will be printed with the other parameters for debugging. This commit makes this module to handle the whole cycle count including second. The value is calculated by cycle unit. The existed code is already written with ignoring the value of second, thus this commit causes no issues. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 4484242..46f1167 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -548,26 +548,44 @@ end: return 0; } -static void out_stream_callback(struct fw_iso_context *context, u32 cycle, +/* + * In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On + * the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent + * it. Thus, via Linux firewire subsystem, we can get the 3 bits for second. + */ +static inline u32 compute_cycle_count(u32 tstamp) +{ + return (((tstamp >> 13) & 0x07) * 8000) + (tstamp & 0x1fff); +} + +static inline u32 increment_cycle_count(u32 cycle, unsigned int addend) +{ + cycle += addend; + if (cycle >= 8 * CYCLES_PER_SECOND) + cycle -= 8 * CYCLES_PER_SECOND; + return cycle; +} + +static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, void *header, void *private_data) { struct amdtp_stream *s = private_data; unsigned int i, syt, packets = header_length / 4; unsigned int data_blocks; + u32 cycle; if (s->packet_index < 0) return; - /* - * Compute the cycle of the last queued packet. - * (We need only the four lowest bits for the SYT, so we can ignore - * that bits 0-11 must wrap around at 3072.) - */ - cycle += QUEUE_LENGTH - packets; + cycle = compute_cycle_count(tstamp); + + /* Align to actual cycle count for the last packet. */ + cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets); for (i = 0; i < packets; ++i) { - syt = calculate_syt(s, ++cycle); + cycle = increment_cycle_count(cycle, 1); + syt = calculate_syt(s, cycle); data_blocks = calculate_data_blocks(s, syt); if (handle_out_packet(s, data_blocks, syt) < 0) { @@ -580,7 +598,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle, fw_iso_context_queue_flush(s->context); } -static void in_stream_callback(struct fw_iso_context *context, u32 cycle, +static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, void *header, void *private_data) { @@ -650,7 +668,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle, } /* processing is done by master callback */ -static void slave_stream_callback(struct fw_iso_context *context, u32 cycle, +static void slave_stream_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, void *header, void *private_data) { @@ -659,7 +677,7 @@ static void slave_stream_callback(struct fw_iso_context *context, u32 cycle, /* this is executed one time */ static void amdtp_stream_first_callback(struct fw_iso_context *context, - u32 cycle, size_t header_length, + u32 tstamp, size_t header_length, void *header, void *private_data) { struct amdtp_stream *s = private_data; @@ -678,7 +696,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, else context->callback.sc = out_stream_callback; - context->callback.sc(context, cycle, header_length, header, s); + context->callback.sc(context, tstamp, header_length, header, s); } /** -- cgit v0.10.2 From f90e2dedf7f47ff4f2f757188a0360fbf526a81e Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 May 2016 21:12:45 +0900 Subject: ALSA: firewire-lib: compute the value of second field in cycle count for IR context In callback function of isochronous context, modules can queue packets to indicated isochronous cycles. Although the cycle to queue a packet is deterministic by calculation, this module doesn't implement the calculation because it's useless for processing. In future, the cycle count is going to be printed with the other parameters for debugging. This commit is the preparation. The cycle count is computed by cycle unit, and correctly arranged to corresponding packets. The calculated count is used in later commit. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 46f1167..4d86da0 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -566,6 +566,13 @@ static inline u32 increment_cycle_count(u32 cycle, unsigned int addend) return cycle; } +static inline u32 decrement_cycle_count(u32 cycle, unsigned int subtrahend) +{ + if (cycle < subtrahend) + cycle += 8 * CYCLES_PER_SECOND; + return cycle - subtrahend; +} + static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, void *header, void *private_data) @@ -607,6 +614,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, unsigned int payload_quadlets, max_payload_quadlets; unsigned int data_blocks; __be32 *buffer, *headers = header; + u32 cycle; if (s->packet_index < 0) return; @@ -614,10 +622,16 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, /* The number of packets in buffer */ packets = header_length / IN_PACKET_HEADER_SIZE; + cycle = compute_cycle_count(tstamp); + + /* Align to actual cycle count for the last packet. */ + cycle = decrement_cycle_count(cycle, packets); + /* For buffer-over-run prevention. */ max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4; for (p = 0; p < packets; p++) { + cycle = increment_cycle_count(cycle, 1); buffer = s->buffer.packets[s->packet_index].buffer; /* The number of quadlets in this packet */ -- cgit v0.10.2 From 0c95c1d6197f3edd3f6ef76f927d67e8ec0794ed Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 May 2016 21:12:46 +0900 Subject: ALSA: firewire-lib: add tracepoints to dump a part of isochronous packet data When audio and music units have some quirks in their sequence of packet, it's really hard for non-owners to identify the quirks. Although developers need dumps for sequence of packets, it's difficult for users who have no knowledges and no equipments for this purpose. This commit adds tracepoints for this situation. When users encounter the issue, they can dump a part of packet data via Linux tracing framework as long as using drivers in ALSA firewire stack. Additionally, tracepoints for outgoing packets will be our help to check and debug packet processing of ALSA firewire stack. This commit newly adds 'snd_firewire_lib' subsystem with 'in_packet' and 'out_packet' events. In the events, some attributes of packets and the index of packet managed by this module are recorded per packet. This is an usage: $ trace-cmd record -e snd_firewire_lib:out_packet \ -e snd_firewire_lib:in_packet /sys/kernel/tracing/events/snd_firewire_lib/out_packet/filter /sys/kernel/tracing/events/snd_firewire_lib/in_packet/filter Hit Ctrl^C to stop recording ^C $ trace-cmd report trace.dat ... 23647.033934: in_packet: 01 4073 ffc0 ffc1 00 000f0040 9001b2d1 122 44 23647.033936: in_packet: 01 4074 ffc0 ffc1 00 000f0048 9001c83b 122 45 23647.033937: in_packet: 01 4075 ffc0 ffc1 00 000f0050 9001ffff 002 46 23647.033938: in_packet: 01 4076 ffc0 ffc1 00 000f0050 9001e1a6 122 47 23647.035426: out_packet: 01 4123 ffc1 ffc0 01 010f00d0 9001fb40 122 17 23647.035428: out_packet: 01 4124 ffc1 ffc0 01 010f00d8 9001ffff 002 18 23647.035429: out_packet: 01 4125 ffc1 ffc0 01 010f00d8 900114aa 122 19 23647.035430: out_packet: 01 4126 ffc1 ffc0 01 010f00e0 90012a15 122 20 (Here, some common fields are omitted so that a line to be within 80 characters.) ... One line represent one packet. The legend for the last nine fields is: - The second of cycle scheduled for the packet - The count of cycle scheduled for the packet - The ID of node as source (hex) - Some devices transfer packets with invalid source node ID in their CIP header. - The ID of node as destination (hex) - The value is not in CIP header of packets. - The value of isochronous channel - The first quadlet of CIP header (hex) - The second quadlet of CIP header (hex) - The number of included quadlets - The index of packet in a buffer maintained by this module This is an example to parse these lines from text file by Python3 script: \#!/usr/bin/env python3 import sys def parse_ts(second, cycle, syt): offset = syt & 0xfff syt >>= 12 if cycle & 0x0f > syt: cycle += 0x10 cycle &= 0x1ff0 cycle |= syt second += cycle // 8000 cycle %= 8000 # In CYCLE_TIMER of 1394 OHCI, second is represented in 8 bit. second %= 128 return (second, cycle, offset) def calc_ts(second, cycle, offset): ts = offset ts += cycle * 3072 # In DMA descriptor of 1394 OHCI, second is represented in 3 bit. ts += (second % 8) * 8000 * 3072 return ts def subtract_ts(minuend, subtrahend): # In DMA descriptor of 1394 OHCI, second is represented in 3 bit. if minuend < subtrahend: minuend += 8 * 8000 * 3072 return minuend - subtrahend if len(sys.argv) != 2: print('At least, one argument is required for packet dump.') sys.exit() filename = sys.argv[1] data = [] prev = 0 with open(filename, 'r') as f: for line in f: pos = line.find('packet:') if pos < 0: continue pos += len('packet:') line = line[pos:].strip() fields = line.split(' ') datum = [] datum.append(fields[8]) syt = int(fields[6][4:], 16) # Empty packet in IEC 61883-1, or NODATA in IEC 61883-6 if syt == 0xffff: data_blocks = 0 else: payload_size = int(fields[7], 10) data_block_size = int(fields[5][2:4], 16) data_blocks = (payload_size - 2) / data_block_size datum.append(data_blocks) second = int(fields[0], 10) cycle = int(fields[1], 10) start = (second << 25) | (cycle << 12) datum.append('0x{0:08x}'.format(start)) start = calc_ts(second, cycle, 0) datum.append("0x" + fields[5]) datum.append("0x" + fields[6]) if syt == 0xffff: second = 0 cycle = 0 tick = 0 else: second, cycle, tick = parse_ts(second, cycle, syt) ts = calc_ts(second, cycle, tick) datum.append(start) datum.append(ts) if ts == 0: datum.append(0) datum.append(0) else: # Usual case, or a case over 8 seconds. if ts > start or start > 7 * 8000 * 3072: datum.append(subtract_ts(ts, start)) if ts > prev or start > 7 * 8000 * 3072: gap = subtract_ts(ts, prev) datum.append(gap) else: datum.append('backward') else: datum.append('invalid') prev = ts data.append(datum) sys.exit() The data variable includes array with these elements: - The index of the packet - The number of data blocks in the packet - The value of cycle count (hex) - The value of CIP header 1 (hex) - The value of CIP header 2 (hex) - The value of cycle count (tick) - The value of calculated presentation timestamp (tick) - The offset between the cycle count and presentation timestamp - The elapsed ticks from the previous presentation timestamp Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 003c090..0ee1fb1 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,3 +1,6 @@ +# To find a header included by define_trace.h. +CFLAGS_amdtp-stream.o := -I$(src) + snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ fcp.o cmp.o amdtp-stream.o amdtp-am824.o snd-isight-objs := isight.o diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h new file mode 100644 index 0000000..425d1d7 --- /dev/null +++ b/sound/firewire/amdtp-stream-trace.h @@ -0,0 +1,98 @@ +/* + * amdtp-stream-trace.h - tracepoint definitions to dump a part of packet data + * + * Copyright (c) 2016 Takashi Sakamoto + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM snd_firewire_lib + +#if !defined(_AMDTP_STREAM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _AMDTP_STREAM_TRACE_H + +#include + +TRACE_EVENT(in_packet, + TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 cip_header[2], unsigned int payload_quadlets), + TP_ARGS(s, cycles, cip_header, payload_quadlets), + TP_STRUCT__entry( + __field(unsigned int, second) + __field(unsigned int, cycle) + __field(int, channel) + __field(int, src) + __field(int, dest) + __field(u32, cip_header0) + __field(u32, cip_header1) + __field(unsigned int, payload_quadlets) + __field(unsigned int, index) + ), + TP_fast_assign( + __entry->second = cycles / CYCLES_PER_SECOND; + __entry->cycle = cycles % CYCLES_PER_SECOND; + __entry->channel = s->context->channel; + __entry->src = fw_parent_device(s->unit)->node_id; + __entry->dest = fw_parent_device(s->unit)->card->node_id; + __entry->cip_header0 = cip_header[0]; + __entry->cip_header1 = cip_header[1]; + __entry->payload_quadlets = payload_quadlets; + __entry->index = s->packet_index; + ), + TP_printk( + "%02u %04u %04x %04x %02d %08x %08x %03u %02u", + __entry->second, + __entry->cycle, + __entry->src, + __entry->dest, + __entry->channel, + __entry->cip_header0, + __entry->cip_header1, + __entry->payload_quadlets, + __entry->index) +); + +TRACE_EVENT(out_packet, + TP_PROTO(const struct amdtp_stream *s, u32 cycles, __be32 *cip_header, unsigned int payload_length), + TP_ARGS(s, cycles, cip_header, payload_length), + TP_STRUCT__entry( + __field(unsigned int, second) + __field(unsigned int, cycle) + __field(int, channel) + __field(int, src) + __field(int, dest) + __field(u32, cip_header0) + __field(u32, cip_header1) + __field(unsigned int, payload_quadlets) + __field(unsigned int, index) + ), + TP_fast_assign( + __entry->second = cycles / CYCLES_PER_SECOND; + __entry->cycle = cycles % CYCLES_PER_SECOND; + __entry->channel = s->context->channel; + __entry->src = fw_parent_device(s->unit)->card->node_id; + __entry->dest = fw_parent_device(s->unit)->node_id; + __entry->cip_header0 = be32_to_cpu(cip_header[0]); + __entry->cip_header1 = be32_to_cpu(cip_header[1]); + __entry->payload_quadlets = payload_length / 4; + __entry->index = s->packet_index; + ), + TP_printk( + "%02u %04u %04x %04x %02d %08x %08x %03u %02u", + __entry->second, + __entry->cycle, + __entry->src, + __entry->dest, + __entry->channel, + __entry->cip_header0, + __entry->cip_header1, + __entry->payload_quadlets, + __entry->index) +); + +#endif + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE amdtp-stream-trace +#include diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 4d86da0..a22e559 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -19,6 +19,10 @@ #define CYCLES_PER_SECOND 8000 #define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) +/* Always support Linux tracing subsystem. */ +#define CREATE_TRACE_POINTS +#include "amdtp-stream-trace.h" + #define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */ /* isochronous header parameters */ @@ -409,7 +413,7 @@ static inline int queue_in_packet(struct amdtp_stream *s) } static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, - unsigned int syt) + unsigned int cycle, unsigned int syt) { __be32 *buffer; unsigned int payload_length; @@ -428,8 +432,10 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, (syt & CIP_SYT_MASK)); s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; - payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; + + trace_out_packet(s, cycle, buffer, payload_length); + if (queue_out_packet(s, payload_length, false) < 0) return -EIO; @@ -443,7 +449,8 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, static int handle_in_packet(struct amdtp_stream *s, unsigned int payload_quadlets, __be32 *buffer, - unsigned int *data_blocks, unsigned int syt) + unsigned int *data_blocks, unsigned int cycle, + unsigned int syt) { u32 cip_header[2]; unsigned int fmt, fdf; @@ -455,6 +462,8 @@ static int handle_in_packet(struct amdtp_stream *s, cip_header[0] = be32_to_cpu(buffer[0]); cip_header[1] = be32_to_cpu(buffer[1]); + trace_in_packet(s, cycle, cip_header, payload_quadlets); + /* * This module supports 'Two-quadlet CIP header with SYT field'. * For convenience, also check FMT field is AM824 or not. @@ -595,7 +604,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, syt = calculate_syt(s, cycle); data_blocks = calculate_data_blocks(s, syt); - if (handle_out_packet(s, data_blocks, syt) < 0) { + if (handle_out_packet(s, data_blocks, cycle, syt) < 0) { s->packet_index = -1; amdtp_stream_pcm_abort(s); return; @@ -647,7 +656,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; if (handle_in_packet(s, payload_quadlets, buffer, - &data_blocks, syt) < 0) { + &data_blocks, cycle, syt) < 0) { s->packet_index = -1; break; } @@ -655,7 +664,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, /* Process sync slave stream */ if (s->sync_slave && s->sync_slave->callbacked) { if (handle_out_packet(s->sync_slave, - data_blocks, syt) < 0) { + data_blocks, cycle, syt) < 0) { s->packet_index = -1; break; } -- cgit v0.10.2 From e099aeea639ce491d3cd1c3802fe34d98045ffd8 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 May 2016 14:59:07 +0100 Subject: ALSA: pcm: Fix poll error return codes We can't return a negative error code from the poll callback the return type is unsigned and is checked against the poll specific flags we need to return POLLERR if we encounter an error. Signed-off-by: Charles Keepax Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 9106d8e..c61fd50 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3161,7 +3161,7 @@ static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait) substream = pcm_file->substream; if (PCM_RUNTIME_CHECK(substream)) - return -ENXIO; + return POLLOUT | POLLWRNORM | POLLERR; runtime = substream->runtime; poll_wait(file, &runtime->sleep, wait); @@ -3200,7 +3200,7 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait) substream = pcm_file->substream; if (PCM_RUNTIME_CHECK(substream)) - return -ENXIO; + return POLLIN | POLLRDNORM | POLLERR; runtime = substream->runtime; poll_wait(file, &runtime->sleep, wait); -- cgit v0.10.2 From 0b92b0cdbe419575b2233c08192b2ad28e7dbcfa Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 May 2016 14:59:08 +0100 Subject: ALSA: compress: Use snd_compr_get_poll on error path We have a function that returns the appropriate flags for the stream direction, so we should use it. Signed-off-by: Charles Keepax Acked-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index a9933c0..5268546 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -421,10 +421,7 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait) retval = snd_compr_get_poll(stream); break; default: - if (stream->direction == SND_COMPRESS_PLAYBACK) - retval = POLLOUT | POLLWRNORM | POLLERR; - else - retval = POLLIN | POLLRDNORM | POLLERR; + retval = snd_compr_get_poll(stream) | POLLERR; break; } out: -- cgit v0.10.2 From 5bd05390ff084d7a1ea7efa8f8dc111c24b2454c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 May 2016 14:59:09 +0100 Subject: ALSA: compress: Remove pointless NULL check stream can't be NULL here as we have just taken the address of it, so no need for the check. Signed-off-by: Charles Keepax Acked-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 5268546..5215df2 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -392,9 +392,8 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait) if (snd_BUG_ON(!data)) return -EFAULT; + stream = &data->stream; - if (snd_BUG_ON(!stream)) - return -EFAULT; mutex_lock(&stream->device->lock); if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { @@ -799,9 +798,9 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) if (snd_BUG_ON(!data)) return -EFAULT; + stream = &data->stream; - if (snd_BUG_ON(!stream)) - return -EFAULT; + mutex_lock(&stream->device->lock); switch (_IOC_NR(cmd)) { case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): -- cgit v0.10.2 From 1d03f2bd56f3a45123a7572fb536c063068cfb83 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 May 2016 14:59:10 +0100 Subject: ALSA: compress: Fix poll error return codes We can't return a negative error code from the poll callback the return type is unsigned and is checked against the poll specific flags we need to return POLLERR if we encounter an error. Signed-off-by: Charles Keepax Acked-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 5215df2..f56f4e3 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -391,13 +391,13 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait) int retval = 0; if (snd_BUG_ON(!data)) - return -EFAULT; + return POLLERR; stream = &data->stream; mutex_lock(&stream->device->lock); if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { - retval = -EBADFD; + retval = snd_compr_get_poll(stream) | POLLERR; goto out; } poll_wait(f, &stream->runtime->sleep, wait); -- cgit v0.10.2 From 875f6fffa2e09b48fa07ecbf2e28dd2425b5ce01 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 4 May 2016 14:59:11 +0100 Subject: ALSA: compress: Replace complex if statement with switch A switch statement looks a bit cleaner than an if statement spread over 3 lines, as such update this to a switch. Signed-off-by: Charles Keepax Acked-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index f56f4e3..9b3334b 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -288,9 +288,12 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf, stream = &data->stream; mutex_lock(&stream->device->lock); /* write is allowed when stream is running or has been steup */ - if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && - stream->runtime->state != SNDRV_PCM_STATE_PREPARED && - stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_RUNNING: + break; + default: mutex_unlock(&stream->device->lock); return -EBADFD; } -- cgit v0.10.2 From 39f0ccde3624e7cf882faccf7f72a47b7a763bfb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 9 May 2016 17:47:37 +0200 Subject: ALSA: hda - Clarify CONFIG_SND_HDA_RECONFIG usages Since the recent rewrite of HD-audio infrastructure, CONFIG_SND_HDA_RECONFIG has a slightly different meaning. In the earlier versions, it implicitly assumed only the usage via hwdep sysfs entries. Meanwhile, in the recent version, this option is meant to enable the reconfig code in HD-audio driver, which may be used by the patch loader without hwdep interface. This patch tries to clarify the usage pattern a bit better, hopefully avoid the further confusion. Reported-by: Jochen Henneberg Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index bb02c2d..7f3b5ed 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -50,9 +50,13 @@ config SND_HDA_RECONFIG bool "Allow dynamic codec reconfiguration" help Say Y here to enable the HD-audio codec re-configuration feature. - This adds the sysfs interfaces to allow user to clear the whole - codec configuration, change the codec setup, add extra verbs, - and re-configure the codec dynamically. + It allows user to clear the whole codec configuration, change the + codec setup, add extra verbs, and re-configure the codec dynamically. + + Note that this item alone doesn't provide the sysfs interface, but + enables the feature just for the patch loader below. + If you need the traditional sysfs entries for the manual interaction, + turn on CONFIG_SND_HDA_HWDEP as well. config SND_HDA_INPUT_BEEP bool "Support digital beep via input layer" -- cgit v0.10.2 From c71283cb682c28085125bea35e4c6149b538f5db Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 May 2016 23:15:50 +0900 Subject: ALSA: bebob: drop reuse of incoming packet parameter for outgoing packet parameter Windows driver for BeBoB-based models mostly wait for transmitted packets, then transfer packets to the models. This looks for the relationship between incoming packets and outgoing packets to synchronize the sequence of presentation timestamp. However, the sequence between packets of both direction has no relationship. Even if receiving NO-DATA packets, the drivers transfer packets with meaningful value in SYT field. Additionally, the order of starting packets is always the same, independently of the source of clock. The corresponding driver is expected as a generator of presentation timestamp and these models can select it as a source of sampling clock. This commit drops reusing SYT sequence from ALSA bebob driver. The driver always transfer packets with presentation timestamp generated by ALSA firewire stack, without re-using the sequence of value in SYT field in incoming packets to outgoing packets. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 2a442a7..e7f1bb9 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -94,7 +94,6 @@ struct snd_bebob { bool connected; - struct amdtp_stream *master; struct amdtp_stream tx_stream; struct amdtp_stream rx_stream; struct cmp_connection out_conn; diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 77cbb02..0141813 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -484,30 +484,6 @@ destroy_both_connections(struct snd_bebob *bebob) } static int -get_sync_mode(struct snd_bebob *bebob, enum cip_flags *sync_mode) -{ - enum snd_bebob_clock_type src; - int err; - - err = snd_bebob_stream_get_clock_src(bebob, &src); - if (err < 0) - return err; - - switch (src) { - case SND_BEBOB_CLOCK_TYPE_INTERNAL: - case SND_BEBOB_CLOCK_TYPE_EXTERNAL: - *sync_mode = CIP_SYNC_TO_DEVICE; - break; - default: - case SND_BEBOB_CLOCK_TYPE_SYT: - *sync_mode = 0; - break; - } - - return 0; -} - -static int start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream, unsigned int rate) { @@ -584,8 +560,6 @@ end: int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) { const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate; - struct amdtp_stream *master, *slave; - enum cip_flags sync_mode; unsigned int curr_rate; int err = 0; @@ -593,22 +567,11 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) if (bebob->substreams_counter == 0) goto end; - err = get_sync_mode(bebob, &sync_mode); - if (err < 0) - goto end; - if (sync_mode == CIP_SYNC_TO_DEVICE) { - master = &bebob->tx_stream; - slave = &bebob->rx_stream; - } else { - master = &bebob->rx_stream; - slave = &bebob->tx_stream; - } - /* * Considering JACK/FFADO streaming: * TODO: This can be removed hwdep functionality becomes popular. */ - err = check_connection_used_by_others(bebob, master); + err = check_connection_used_by_others(bebob, &bebob->rx_stream); if (err < 0) goto end; @@ -618,11 +581,12 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) * At bus reset, connections should not be broken here. So streams need * to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag. */ - if (amdtp_streaming_error(master)) - amdtp_stream_stop(master); - if (amdtp_streaming_error(slave)) - amdtp_stream_stop(slave); - if (!amdtp_stream_running(master) && !amdtp_stream_running(slave)) + if (amdtp_streaming_error(&bebob->rx_stream)) + amdtp_stream_stop(&bebob->rx_stream); + if (amdtp_streaming_error(&bebob->tx_stream)) + amdtp_stream_stop(&bebob->tx_stream); + if (!amdtp_stream_running(&bebob->rx_stream) && + !amdtp_stream_running(&bebob->tx_stream)) break_both_connections(bebob); /* stop streams if rate is different */ @@ -635,16 +599,13 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) if (rate == 0) rate = curr_rate; if (rate != curr_rate) { - amdtp_stream_stop(master); - amdtp_stream_stop(slave); + amdtp_stream_stop(&bebob->rx_stream); + amdtp_stream_stop(&bebob->tx_stream); break_both_connections(bebob); } /* master should be always running */ - if (!amdtp_stream_running(master)) { - amdtp_stream_set_sync(sync_mode, master, slave); - bebob->master = master; - + if (!amdtp_stream_running(&bebob->rx_stream)) { /* * NOTE: * If establishing connections at first, Yamaha GO46 @@ -666,7 +627,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) if (err < 0) goto end; - err = start_stream(bebob, master, rate); + err = start_stream(bebob, &bebob->rx_stream, rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to run AMDTP master stream:%d\n", err); @@ -685,15 +646,16 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) dev_err(&bebob->unit->device, "fail to ensure sampling rate: %d\n", err); - amdtp_stream_stop(master); + amdtp_stream_stop(&bebob->rx_stream); break_both_connections(bebob); goto end; } } /* wait first callback */ - if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT)) { - amdtp_stream_stop(master); + if (!amdtp_stream_wait_callback(&bebob->rx_stream, + CALLBACK_TIMEOUT)) { + amdtp_stream_stop(&bebob->rx_stream); break_both_connections(bebob); err = -ETIMEDOUT; goto end; @@ -701,20 +663,21 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) } /* start slave if needed */ - if (!amdtp_stream_running(slave)) { - err = start_stream(bebob, slave, rate); + if (!amdtp_stream_running(&bebob->tx_stream)) { + err = start_stream(bebob, &bebob->tx_stream, rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to run AMDTP slave stream:%d\n", err); - amdtp_stream_stop(master); + amdtp_stream_stop(&bebob->rx_stream); break_both_connections(bebob); goto end; } /* wait first callback */ - if (!amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) { - amdtp_stream_stop(slave); - amdtp_stream_stop(master); + if (!amdtp_stream_wait_callback(&bebob->tx_stream, + CALLBACK_TIMEOUT)) { + amdtp_stream_stop(&bebob->tx_stream); + amdtp_stream_stop(&bebob->rx_stream); break_both_connections(bebob); err = -ETIMEDOUT; } @@ -725,22 +688,12 @@ end: void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) { - struct amdtp_stream *master, *slave; - - if (bebob->master == &bebob->rx_stream) { - slave = &bebob->tx_stream; - master = &bebob->rx_stream; - } else { - slave = &bebob->rx_stream; - master = &bebob->tx_stream; - } - if (bebob->substreams_counter == 0) { - amdtp_stream_pcm_abort(master); - amdtp_stream_stop(master); + amdtp_stream_pcm_abort(&bebob->rx_stream); + amdtp_stream_stop(&bebob->rx_stream); - amdtp_stream_pcm_abort(slave); - amdtp_stream_stop(slave); + amdtp_stream_pcm_abort(&bebob->tx_stream); + amdtp_stream_stop(&bebob->tx_stream); break_both_connections(bebob); } -- cgit v0.10.2 From eb4a378fc99d876e98e01d67701c49343fae3e39 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 May 2016 23:15:51 +0900 Subject: ALSA: fireworks: drop reuse of incoming packet parameter for ougoing packet parameter On Fireworks board module of Echo Audio, TSB43Cx43A (IceLynx Micro, iCEM) is used to process payload of isochronous packets. There's an public document of this chip[1]. This document is for firmware programmers to transfer/receive AMDTP with IEC60958 data format, however in clause 2.5, 2.6 and 2.7, we can see system design to utilize the sequence of value in SYT field of CIP header. In clause 2.3, we can see the specification of Audio Master Clock (MCLK) from iCEM. Well, this clock is actually not used for sampling clock. This can be confirmed when corresponding driver transfer random value as the sequence of SYT field. Even if in this case, the unit generates proper sound. Additionally, in unique command set for this board module, the format of CIP is changed; for IEC 61883-6 mode which we use, and for Windows Operating System. In the latter mode, the whole 32 bit field in second CIP header from Windows driver is used to represent counter of packets (NO-DATA code is still used for packets without data blocks). If the master clock was physically used by DSP on the board module, the Windows driver must have transferred correct sequence of SYT field. Furthermore, as long as seeing capacities of AudioFire2, AudioFire4, AudioFire8, AudioFirePre8 and AudioFire12, these models don't support SYT-Match clock source. Summary, we have no need to relate incoming/outgoing packets. This commit drops reusing SYT sequence of incoming packets for outgoing packets. [1] Using TSB43Cx43A: S/PDIF over 1394 (2003, Texus Instruments Incorporated) http://www.ti.com/analog/docs/litabsmultiplefilelist.tsp?literatureNumber=slla148&docCategoryId=1&familyId=361 Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index 471c772..03ed352 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -84,7 +84,6 @@ struct snd_efw { unsigned int pcm_capture_channels[SND_EFW_MULTIPLIER_MODES]; unsigned int pcm_playback_channels[SND_EFW_MULTIPLIER_MODES]; - struct amdtp_stream *master; struct amdtp_stream tx_stream; struct amdtp_stream rx_stream; struct cmp_connection out_conn; diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 425db8d..ee47924 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -121,23 +121,6 @@ destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream) } static int -get_sync_mode(struct snd_efw *efw, enum cip_flags *sync_mode) -{ - enum snd_efw_clock_source clock_source; - int err; - - err = snd_efw_command_get_clock_source(efw, &clock_source); - if (err < 0) - return err; - - if (clock_source == SND_EFW_CLOCK_SOURCE_SYTMATCH) - return -ENOSYS; - - *sync_mode = CIP_SYNC_TO_DEVICE; - return 0; -} - -static int check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s) { struct cmp_connection *conn; @@ -208,9 +191,6 @@ end: int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate) { - struct amdtp_stream *master, *slave; - unsigned int slave_substreams; - enum cip_flags sync_mode; unsigned int curr_rate; int err = 0; @@ -218,32 +198,19 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate) if (efw->playback_substreams == 0 && efw->capture_substreams == 0) goto end; - err = get_sync_mode(efw, &sync_mode); - if (err < 0) - goto end; - if (sync_mode == CIP_SYNC_TO_DEVICE) { - master = &efw->tx_stream; - slave = &efw->rx_stream; - slave_substreams = efw->playback_substreams; - } else { - master = &efw->rx_stream; - slave = &efw->tx_stream; - slave_substreams = efw->capture_substreams; - } - /* * Considering JACK/FFADO streaming: * TODO: This can be removed hwdep functionality becomes popular. */ - err = check_connection_used_by_others(efw, master); + err = check_connection_used_by_others(efw, &efw->rx_stream); if (err < 0) goto end; /* packet queueing error */ - if (amdtp_streaming_error(slave)) - stop_stream(efw, slave); - if (amdtp_streaming_error(master)) - stop_stream(efw, master); + if (amdtp_streaming_error(&efw->tx_stream)) + stop_stream(efw, &efw->tx_stream); + if (amdtp_streaming_error(&efw->rx_stream)) + stop_stream(efw, &efw->rx_stream); /* stop streams if rate is different */ err = snd_efw_command_get_sampling_rate(efw, &curr_rate); @@ -252,20 +219,17 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate) if (rate == 0) rate = curr_rate; if (rate != curr_rate) { - stop_stream(efw, slave); - stop_stream(efw, master); + stop_stream(efw, &efw->tx_stream); + stop_stream(efw, &efw->rx_stream); } /* master should be always running */ - if (!amdtp_stream_running(master)) { - amdtp_stream_set_sync(sync_mode, master, slave); - efw->master = master; - + if (!amdtp_stream_running(&efw->rx_stream)) { err = snd_efw_command_set_sampling_rate(efw, rate); if (err < 0) goto end; - err = start_stream(efw, master, rate); + err = start_stream(efw, &efw->rx_stream, rate); if (err < 0) { dev_err(&efw->unit->device, "fail to start AMDTP master stream:%d\n", err); @@ -274,12 +238,13 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate) } /* start slave if needed */ - if (slave_substreams > 0 && !amdtp_stream_running(slave)) { - err = start_stream(efw, slave, rate); + if (efw->capture_substreams > 0 && + !amdtp_stream_running(&efw->tx_stream)) { + err = start_stream(efw, &efw->tx_stream, rate); if (err < 0) { dev_err(&efw->unit->device, "fail to start AMDTP slave stream:%d\n", err); - stop_stream(efw, master); + stop_stream(efw, &efw->rx_stream); } } end: @@ -288,26 +253,11 @@ end: void snd_efw_stream_stop_duplex(struct snd_efw *efw) { - struct amdtp_stream *master, *slave; - unsigned int master_substreams, slave_substreams; - - if (efw->master == &efw->rx_stream) { - slave = &efw->tx_stream; - master = &efw->rx_stream; - slave_substreams = efw->capture_substreams; - master_substreams = efw->playback_substreams; - } else { - slave = &efw->rx_stream; - master = &efw->tx_stream; - slave_substreams = efw->playback_substreams; - master_substreams = efw->capture_substreams; - } - - if (slave_substreams == 0) { - stop_stream(efw, slave); + if (efw->capture_substreams == 0) { + stop_stream(efw, &efw->tx_stream); - if (master_substreams == 0) - stop_stream(efw, master); + if (efw->playback_substreams == 0) + stop_stream(efw, &efw->rx_stream); } } -- cgit v0.10.2 From 28e64f5176387bf5b9458d213650b90fa719be88 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 May 2016 23:15:52 +0900 Subject: ALSA: firewire-tascam: drop reuse of incoming packet parameter for outgoing packet parameter In packet streaming protocol applied to TASCAM FireWire series, the value of SYT field in CIP header is always zero, therefore it has no meaning. There's no need to synchronize packets in both direction for the series. In current implementation of ALSA firewire stack, driver for the series uses incoming packet parameter for outgoing packet parameter to calculate the number of data blocks. This can be simplified because the task of corresponding driver is to transfer data blocks enough to sampling transfer frequency. This commit purges support of full duplex synchronization to prevent over-engineering implementation. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c index 0e6dd5c6..4ad3bd7 100644 --- a/sound/firewire/tascam/tascam-stream.c +++ b/sound/firewire/tascam/tascam-stream.c @@ -381,19 +381,17 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) if (err < 0) return err; if (curr_rate != rate || - amdtp_streaming_error(&tscm->tx_stream) || - amdtp_streaming_error(&tscm->rx_stream)) { + amdtp_streaming_error(&tscm->rx_stream) || + amdtp_streaming_error(&tscm->tx_stream)) { finish_session(tscm); - amdtp_stream_stop(&tscm->tx_stream); amdtp_stream_stop(&tscm->rx_stream); + amdtp_stream_stop(&tscm->tx_stream); release_resources(tscm); } - if (!amdtp_stream_running(&tscm->tx_stream)) { - amdtp_stream_set_sync(CIP_SYNC_TO_DEVICE, - &tscm->tx_stream, &tscm->rx_stream); + if (!amdtp_stream_running(&tscm->rx_stream)) { err = keep_resources(tscm, rate); if (err < 0) goto error; @@ -406,27 +404,27 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) if (err < 0) goto error; - err = amdtp_stream_start(&tscm->tx_stream, - tscm->tx_resources.channel, + err = amdtp_stream_start(&tscm->rx_stream, + tscm->rx_resources.channel, fw_parent_device(tscm->unit)->max_speed); if (err < 0) goto error; - if (!amdtp_stream_wait_callback(&tscm->tx_stream, + if (!amdtp_stream_wait_callback(&tscm->rx_stream, CALLBACK_TIMEOUT)) { err = -ETIMEDOUT; goto error; } } - if (!amdtp_stream_running(&tscm->rx_stream)) { - err = amdtp_stream_start(&tscm->rx_stream, - tscm->rx_resources.channel, + if (!amdtp_stream_running(&tscm->tx_stream)) { + err = amdtp_stream_start(&tscm->tx_stream, + tscm->tx_resources.channel, fw_parent_device(tscm->unit)->max_speed); if (err < 0) goto error; - if (!amdtp_stream_wait_callback(&tscm->rx_stream, + if (!amdtp_stream_wait_callback(&tscm->tx_stream, CALLBACK_TIMEOUT)) { err = -ETIMEDOUT; goto error; @@ -435,8 +433,8 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) return 0; error: - amdtp_stream_stop(&tscm->tx_stream); amdtp_stream_stop(&tscm->rx_stream); + amdtp_stream_stop(&tscm->tx_stream); finish_session(tscm); release_resources(tscm); -- cgit v0.10.2 From dec63cc8b65b04c0ebb5d82b6b399665d6d44dca Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 May 2016 23:15:53 +0900 Subject: ALSA: firewire-lib: handle IT/IR contexts in each software interrupt context In clause 6.3 of IEC 61883-6:2000, there's an explanation about processing of presentation timestamp. In the clause, we can see "If a function block receives a CIP, processes it and subsequently re-transmits it, then the SYT of the outgoing CIP shall be the sum of the incoming SYT and the processing delay." ALSA firewire stack has an implementation to partly satisfy this specification. Developers assumed the stack to perform as an Audio function block[1]. Following to the assumption, current implementation of ALSA firewire stack use one software interrupt context to handle both of in/out packets. In most case, this is processed in 1394 OHCI IR context independently of the opposite context. Thus, this implementation uses longer CPU time in the software interrupt context. This is not better for whole system. Against the assumption, I confirmed that each ASIC for IEC 61883-1/6 doesn't necessarily expect it to the stack. Thus, current implementation of ALSA firewire stack includes over-engineering. This commit purges the implementation. As a result, packets of one direction are handled in one software interrupt context and spends minimum CPU time. [1] [alsa-devel] [PATCH 0/8] [RFC] new driver for Echo Audio's Fireworks based devices http://mailman.alsa-project.org/pipermail/alsa-devel/2013-June/062660.html Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index a22e559..3468419 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -91,7 +91,6 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, init_waitqueue_head(&s->callback_wait); s->callbacked = false; - s->sync_slave = NULL; s->fmt = fmt; s->process_data_blocks = process_data_blocks; @@ -650,54 +649,25 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, dev_err(&s->unit->device, "Detect jumbo payload: %02x %02x\n", payload_quadlets, max_payload_quadlets); - s->packet_index = -1; break; } syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; if (handle_in_packet(s, payload_quadlets, buffer, - &data_blocks, cycle, syt) < 0) { - s->packet_index = -1; + &data_blocks, cycle, syt) < 0) break; - } - - /* Process sync slave stream */ - if (s->sync_slave && s->sync_slave->callbacked) { - if (handle_out_packet(s->sync_slave, - data_blocks, cycle, syt) < 0) { - s->packet_index = -1; - break; - } - } } - /* Queueing error or detecting discontinuity */ - if (s->packet_index < 0) { + /* Queueing error or detecting invalid payload. */ + if (p < packets) { + s->packet_index = -1; amdtp_stream_pcm_abort(s); - - /* Abort sync slave. */ - if (s->sync_slave) { - s->sync_slave->packet_index = -1; - amdtp_stream_pcm_abort(s->sync_slave); - } return; } - /* when sync to device, flush the packets for slave stream */ - if (s->sync_slave && s->sync_slave->callbacked) - fw_iso_context_queue_flush(s->sync_slave->context); - fw_iso_context_queue_flush(s->context); } -/* processing is done by master callback */ -static void slave_stream_callback(struct fw_iso_context *context, u32 tstamp, - size_t header_length, void *header, - void *private_data) -{ - return; -} - /* this is executed one time */ static void amdtp_stream_first_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, @@ -714,8 +684,6 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, if (s->direction == AMDTP_IN_STREAM) context->callback.sc = in_stream_callback; - else if (s->flags & CIP_SYNC_TO_DEVICE) - context->callback.sc = slave_stream_callback; else context->callback.sc = out_stream_callback; diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 8775704..da028b0 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -17,8 +17,6 @@ * @CIP_BLOCKING: In blocking mode, each packet contains either zero or * SYT_INTERVAL samples, with these two types alternating so that * the overall sample rate comes out right. - * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is - * generated by in packets. Defaultly this driver generates timestamp. * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0. * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet * corresponds to the end of event in the packet. Out of IEC 61883. @@ -37,14 +35,13 @@ enum cip_flags { CIP_NONBLOCKING = 0x00, CIP_BLOCKING = 0x01, - CIP_SYNC_TO_DEVICE = 0x02, - CIP_EMPTY_WITH_TAG0 = 0x04, - CIP_DBC_IS_END_EVENT = 0x08, - CIP_WRONG_DBS = 0x10, - CIP_SKIP_DBC_ZERO_CHECK = 0x20, - CIP_SKIP_INIT_DBC_CHECK = 0x40, - CIP_EMPTY_HAS_WRONG_DBC = 0x80, - CIP_JUMBO_PAYLOAD = 0x100, + CIP_EMPTY_WITH_TAG0 = 0x02, + CIP_DBC_IS_END_EVENT = 0x04, + CIP_WRONG_DBS = 0x08, + CIP_SKIP_DBC_ZERO_CHECK = 0x10, + CIP_SKIP_INIT_DBC_CHECK = 0x20, + CIP_EMPTY_HAS_WRONG_DBC = 0x40, + CIP_JUMBO_PAYLOAD = 0x80, }; /** @@ -137,7 +134,6 @@ struct amdtp_stream { /* To wait for first packet. */ bool callbacked; wait_queue_head_t callback_wait; - struct amdtp_stream *sync_slave; /* For backends to process data blocks. */ void *protocol; @@ -223,23 +219,6 @@ static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) return sfc & 1; } -static inline void amdtp_stream_set_sync(enum cip_flags sync_mode, - struct amdtp_stream *master, - struct amdtp_stream *slave) -{ - if (sync_mode == CIP_SYNC_TO_DEVICE) { - master->flags |= CIP_SYNC_TO_DEVICE; - slave->flags |= CIP_SYNC_TO_DEVICE; - master->sync_slave = slave; - } else { - master->flags &= ~CIP_SYNC_TO_DEVICE; - slave->flags &= ~CIP_SYNC_TO_DEVICE; - master->sync_slave = NULL; - } - - slave->sync_slave = NULL; -} - /** * amdtp_stream_wait_callback - sleep till callbacked or timeout * @s: the AMDTP stream -- cgit v0.10.2 From d9a16fc926a950c9e481cb7e89c554593b8e29e2 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 May 2016 23:15:54 +0900 Subject: ALSA: firewire-lib: code cleanup for incoming packet handling In previous commit, this module has no need to reuse parameters of incoming packets for outgoing packets anymore. This commit arranges some needless codes for incoming packet processing. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 3468419..f1ebb7b 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -447,17 +447,18 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, } static int handle_in_packet(struct amdtp_stream *s, - unsigned int payload_quadlets, __be32 *buffer, - unsigned int *data_blocks, unsigned int cycle, - unsigned int syt) + unsigned int payload_quadlets, unsigned int cycle) { + __be32 *buffer; u32 cip_header[2]; - unsigned int fmt, fdf; + unsigned int fmt, fdf, syt; unsigned int data_block_quadlets, data_block_counter, dbc_interval; + unsigned int data_blocks; struct snd_pcm_substream *pcm; unsigned int pcm_frames; bool lost; + buffer = s->buffer.packets[s->packet_index].buffer; cip_header[0] = be32_to_cpu(buffer[0]); cip_header[1] = be32_to_cpu(buffer[1]); @@ -472,7 +473,7 @@ static int handle_in_packet(struct amdtp_stream *s, dev_info_ratelimited(&s->unit->device, "Invalid CIP header for AMDTP: %08X:%08X\n", cip_header[0], cip_header[1]); - *data_blocks = 0; + data_blocks = 0; pcm_frames = 0; goto end; } @@ -483,7 +484,7 @@ static int handle_in_packet(struct amdtp_stream *s, dev_info_ratelimited(&s->unit->device, "Detect unexpected protocol: %08x %08x\n", cip_header[0], cip_header[1]); - *data_blocks = 0; + data_blocks = 0; pcm_frames = 0; goto end; } @@ -492,7 +493,7 @@ static int handle_in_packet(struct amdtp_stream *s, fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT; if (payload_quadlets < 3 || (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) { - *data_blocks = 0; + data_blocks = 0; } else { data_block_quadlets = (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT; @@ -506,12 +507,12 @@ static int handle_in_packet(struct amdtp_stream *s, if (s->flags & CIP_WRONG_DBS) data_block_quadlets = s->data_block_quadlets; - *data_blocks = (payload_quadlets - 2) / data_block_quadlets; + data_blocks = (payload_quadlets - 2) / data_block_quadlets; } /* Check data block counter continuity */ data_block_counter = cip_header[0] & CIP_DBC_MASK; - if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) && + if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) && s->data_block_counter != UINT_MAX) data_block_counter = s->data_block_counter; @@ -522,10 +523,10 @@ static int handle_in_packet(struct amdtp_stream *s, } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) { lost = data_block_counter != s->data_block_counter; } else { - if ((*data_blocks > 0) && (s->tx_dbc_interval > 0)) + if (data_blocks > 0 && s->tx_dbc_interval > 0) dbc_interval = s->tx_dbc_interval; else - dbc_interval = *data_blocks; + dbc_interval = data_blocks; lost = data_block_counter != ((s->data_block_counter + dbc_interval) & 0xff); @@ -538,13 +539,14 @@ static int handle_in_packet(struct amdtp_stream *s, return -EIO; } - pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt); + syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; + pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt); if (s->flags & CIP_DBC_IS_END_EVENT) s->data_block_counter = data_block_counter; else s->data_block_counter = - (data_block_counter + *data_blocks) & 0xff; + (data_block_counter + data_blocks) & 0xff; end: if (queue_in_packet(s) < 0) return -EIO; @@ -618,10 +620,9 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, void *private_data) { struct amdtp_stream *s = private_data; - unsigned int p, syt, packets; + unsigned int i, packets; unsigned int payload_quadlets, max_payload_quadlets; - unsigned int data_blocks; - __be32 *buffer, *headers = header; + __be32 *headers = header; u32 cycle; if (s->packet_index < 0) @@ -638,13 +639,12 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, /* For buffer-over-run prevention. */ max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4; - for (p = 0; p < packets; p++) { + for (i = 0; i < packets; i++) { cycle = increment_cycle_count(cycle, 1); - buffer = s->buffer.packets[s->packet_index].buffer; /* The number of quadlets in this packet */ payload_quadlets = - (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; + (be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT) / 4; if (payload_quadlets > max_payload_quadlets) { dev_err(&s->unit->device, "Detect jumbo payload: %02x %02x\n", @@ -652,14 +652,12 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, break; } - syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; - if (handle_in_packet(s, payload_quadlets, buffer, - &data_blocks, cycle, syt) < 0) + if (handle_in_packet(s, payload_quadlets, cycle) < 0) break; } /* Queueing error or detecting invalid payload. */ - if (p < packets) { + if (i < packets) { s->packet_index = -1; amdtp_stream_pcm_abort(s); return; -- cgit v0.10.2 From 390a1512e6ccda2ec32ea1395814f36cf4d30e48 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 May 2016 23:15:55 +0900 Subject: ALSA: firewire-lib: code cleanup for outgoing packet handling In previous commit, this module has no need to reuse parameters of incoming packets for outgoing packets anymore. This commit arranges some needless codes for outgoing packet processing. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index f1ebb7b..6db2a73 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -411,15 +411,18 @@ static inline int queue_in_packet(struct amdtp_stream *s) amdtp_stream_get_max_payload(s), false); } -static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, - unsigned int cycle, unsigned int syt) +static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle) { __be32 *buffer; + unsigned int syt; + unsigned int data_blocks; unsigned int payload_length; unsigned int pcm_frames; struct snd_pcm_substream *pcm; buffer = s->buffer.packets[s->packet_index].buffer; + syt = calculate_syt(s, cycle); + data_blocks = calculate_data_blocks(s, syt); pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt); buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | @@ -588,8 +591,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, void *private_data) { struct amdtp_stream *s = private_data; - unsigned int i, syt, packets = header_length / 4; - unsigned int data_blocks; + unsigned int i, packets = header_length / 4; u32 cycle; if (s->packet_index < 0) @@ -602,10 +604,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, for (i = 0; i < packets; ++i) { cycle = increment_cycle_count(cycle, 1); - syt = calculate_syt(s, cycle); - data_blocks = calculate_data_blocks(s, syt); - - if (handle_out_packet(s, data_blocks, cycle, syt) < 0) { + if (handle_out_packet(s, cycle) < 0) { s->packet_index = -1; amdtp_stream_pcm_abort(s); return; -- cgit v0.10.2 From 62f00e40b0718ebd8bd54fc7a9e89e873524d495 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Mon, 9 May 2016 23:15:56 +0900 Subject: ALSA: firewire-lib: enable the same feature as CIP_SKIP_INIT_DBC_CHECK flag In former commit, drivers in ALSA firewire stack always starts IT context before IR context. If IR context starts after packets are transmitted by peer unit, packet discontinuity may be detected because the context starts in the middle of packet streaming. This situation is rare because IT context usually starts immediately. However, it's better to solve this issue. This is suppressed with CIP_SKIP_INIT_DBC_CHECK flag. This commit enables the same feature as CIP_SKIP_INIT_DBC_CHECK. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 6db2a73..830a95c 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -723,8 +723,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) goto err_unlock; } - if (s->direction == AMDTP_IN_STREAM && - s->flags & CIP_SKIP_INIT_DBC_CHECK) + if (s->direction == AMDTP_IN_STREAM) s->data_block_counter = UINT_MAX; else s->data_block_counter = 0; diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index da028b0..349c405 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -24,8 +24,6 @@ * The value of data_block_quadlets is used instead of reported value. * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is * skipped for detecting discontinuity. - * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first - * packet is not continuous from an initial value. * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty * packet is wrong but the others are correct. * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an @@ -39,9 +37,8 @@ enum cip_flags { CIP_DBC_IS_END_EVENT = 0x04, CIP_WRONG_DBS = 0x08, CIP_SKIP_DBC_ZERO_CHECK = 0x10, - CIP_SKIP_INIT_DBC_CHECK = 0x20, - CIP_EMPTY_HAS_WRONG_DBC = 0x40, - CIP_JUMBO_PAYLOAD = 0x80, + CIP_EMPTY_HAS_WRONG_DBC = 0x20, + CIP_JUMBO_PAYLOAD = 0x40, }; /** diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 0141813..4d3034a 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -526,8 +526,6 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) goto end; } - bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK; - /* * BeBoB v3 transfers packets with these qurks: * - In the beginning of streaming, the value of dbc is incremented diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index 0ac92ab..b3cffd0 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -421,7 +421,7 @@ int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit, /* Use different mode between incoming/outgoing. */ if (dir == AMDTP_IN_STREAM) { - flags = CIP_NONBLOCKING | CIP_SKIP_INIT_DBC_CHECK; + flags = CIP_NONBLOCKING; process_data_blocks = process_tx_data_blocks; } else { flags = CIP_BLOCKING; diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 7cb5743..d9361f3 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -242,8 +242,7 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, * blocks than IEC 61883-6 defines. */ if (stream == &oxfw->tx_stream) { - oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK | - CIP_JUMBO_PAYLOAD; + oxfw->tx_stream.flags |= CIP_JUMBO_PAYLOAD; if (oxfw->wrong_dbs) oxfw->tx_stream.flags |= CIP_WRONG_DBS; } -- cgit v0.10.2 From 8d879be882ba5a8bd4c2bc39bd2c336392564e13 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 10 May 2016 16:07:40 +0200 Subject: ALSA: pcm: Bail out when chmap is already present When snd_pcm_add_chmap_ctls() is called to the PCM stream to which a chmap has been already assigned, it returns as an error due to the conflicting snd_ctl_add() result. However, this also clears the already assigned chmap_kctl field via pcm_chmap_ctl_private_free(), and becomes inconsistent in the later operation. This patch adds the check of the conflicting chmap kctl before actually trying to allocate / assign. The check failure is treated as a kernel warning, as the double call of snd_pcm_add_chmap_ctls() is basically a driver bug and having the stack trace would help developers to figure out the bad code path. Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 0aca397..bb12615 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2595,6 +2595,8 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream, }; int err; + if (WARN_ON(pcm->streams[stream].chmap_kctl)) + return -EBUSY; info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; -- cgit v0.10.2 From 1dba9db0eaa64d362d9d9afb5eeaececdaef948d Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Thu, 12 May 2016 02:17:39 +0900 Subject: ALSA: firewire-lib: permit to flush queued packets only in process context for better PCM period granularity These three commits were merged to improve PCM pointer granularity. commit 76fb87894828 ("ALSA: firewire-lib: taskletize the snd_pcm_period_elapsed() call") commit e9148dddc3c7 ("ALSA: firewire-lib: flush completed packets when reading PCM position") commit 92b862c7d685 ("ALSA: firewire-lib: optimize packet flushing") The point of them is to handle queued packets not only in software IRQ context of IR/IT contexts, but also in process context. As a result of handling packets, period tasklet is scheduled when acrossing PCM period boundary. This is to prevent recursive call of 'struct snd_pcm_ops.pointer()' in the same context. When the pointer callback is executed in the process context, it's better to avoid the second callback in the software IRQ context. The software IRQ context runs immediately after scheduled in the process context because few packets are queued yet. For the aim, 'pointer_flush' is used, however it causes a race condition between the process context and software IRQ context of IR/IT contexts. Practically, this race is not so critical because it influences process context to skip flushing queued packet and to get worse granularity of PCM pointer. The race condition is quite rare but it should be improved for stable service. The similar effect can be achieved by using 'in_interrupt()' macro. This commit obsoletes 'pointer_flush' with it. Acked-by: Clemens Ladisch Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 830a95c..024ab7f 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -251,7 +251,6 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s) tasklet_kill(&s->period_tasklet); s->pcm_buffer_pointer = 0; s->pcm_period_pointer = 0; - s->pointer_flush = true; } EXPORT_SYMBOL(amdtp_stream_pcm_prepare); @@ -356,7 +355,6 @@ static void update_pcm_pointers(struct amdtp_stream *s, s->pcm_period_pointer += frames; if (s->pcm_period_pointer >= pcm->runtime->period_size) { s->pcm_period_pointer -= pcm->runtime->period_size; - s->pointer_flush = false; tasklet_hi_schedule(&s->period_tasklet); } } @@ -803,11 +801,24 @@ EXPORT_SYMBOL(amdtp_stream_start); */ unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s) { - /* this optimization is allowed to be racy */ - if (s->pointer_flush && amdtp_stream_running(s)) + /* + * This function is called in software IRQ context of period_tasklet or + * process context. + * + * When the software IRQ context was scheduled by software IRQ context + * of IR/IT contexts, queued packets were already handled. Therefore, + * no need to flush the queue in buffer anymore. + * + * When the process context reach here, some packets will be already + * queued in the buffer. These packets should be handled immediately + * to keep better granularity of PCM pointer. + * + * Later, the process context will sometimes schedules software IRQ + * context of the period_tasklet. Then, no need to flush the queue by + * the same reason as described for IR/IT contexts. + */ + if (!in_interrupt() && amdtp_stream_running(s)) fw_iso_context_flush_completions(s->context); - else - s->pointer_flush = true; return ACCESS_ONCE(s->pcm_buffer_pointer); } diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 349c405..c1bc7fa 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -126,7 +126,6 @@ struct amdtp_stream { struct tasklet_struct period_tasklet; unsigned int pcm_buffer_pointer; unsigned int pcm_period_pointer; - bool pointer_flush; /* To wait for first packet. */ bool callbacked; -- cgit v0.10.2 From a9c4284bf5a95c4788e7fbf3c46b14dcbfda3a6d Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 11 May 2016 07:33:27 +0900 Subject: ALSA: firewire-lib: add context information to tracepoints In current implementation, packet processing is done in both of software IRQ contexts of IR/IT contexts and process contexts. This is usual interrupt handling of IR/IT context for 1394 OHCI. (in hardware IRQ context) irq_handler() (drivers/firewire/ohci.c) ->tasklet_schedule() (in software IRQ context) handle_it_packet() or handle_ir_packet_per_buffer() (drivers/firewire/ohci.c) ->flush_iso_completions() ->struct fw_iso_context.callback.sc() = out_stream_callback() or in_stream_callback() However, we have another chance for packet processing. It's done in PCM frame handling via ALSA PCM interfaces. (in process context) ioctl(i.e. SNDRV_PCM_IOCTL_HWSYNC) ->snd_pcm_hwsync() (sound/core/pcm_native.c) ->snd_pcm_update_hw_ptr() (sound/core/pcm_lib.c) ->snd_pcm_update_hw_ptr0() ->struct snd_pcm_ops.pointer() = amdtp_stream_pcm_pointer() ->fw_iso_context_flush_completions() (drivers/firewire/core-iso.c) ->struct fw_card_driver.flush_iso_completions() = ohci_flush_iso_completions() (drivers/firewire/ohci.c) ->flush_iso_completions() ->struct fw_iso_context.callback.sc() = out_stream_callback() or in_stream_callback() This design is for a better granularity of PCM pointer. When ioctl(2) is executed with some commands for ALSA PCM interface, queued packets are handled at first. Then, the latest number of handled PCM frames is reported. The number can represent PCM frames transferred in most near isochronous cycle. Current tracepoints include no information to distinguish running contexts. When tracing the interval of software IRQ context, this is not good. This commit adds more information for current context. Additionally, the index of packet processed in one context is added in a case that packet processing is executed in continuous context of the same kind, As a result, the output includes 11 fields with additional two fields to commit 0c95c1d6197f ("ALSA: firewire-lib: add tracepoints to dump a part of isochronous packet data"): 17131.9186: out_packet: 07 7494 ffc0 ffc1 00 000700c0 9001a496 058 45 1 13 17131.9186: out_packet: 07 7495 ffc0 ffc1 00 000700c8 9001ba00 058 46 1 14 17131.9186: out_packet: 07 7496 ffc0 ffc1 00 000700d0 9001ffff 002 47 1 15 17131.9189: out_packet: 07 7497 ffc0 ffc1 00 000700d0 9001d36a 058 00 0 00 17131.9189: out_packet: 07 7498 ffc0 ffc1 00 000700d8 9001e8d4 058 01 0 01 17131.9189: out_packet: 07 7499 ffc0 ffc1 00 000700e0 9001023e 058 02 0 00 17131.9206: in_packet: 07 7447 ffc1 ffc0 01 3f070072 9001783d 058 32 1 00 17131.9206: in_packet: 07 7448 ffc1 ffc0 01 3f070072 90ffffff 002 33 1 01 17131.9206: in_packet: 07 7449 ffc1 ffc0 01 3f07007a 900191a8 058 34 1 02 (Here, some common fields are omitted so that a line is within 80 characters.) The legend is: - The second of cycle scheduled for the packet - The count of cycle scheduled for the packet - The ID of node as source (hex) - The ID of node as destination (hex) - The value of isochronous channel - The first quadlet of CIP header (hex) - The second quadlet of CIP header (hex) - The number of included quadlets - The index of packet in a buffer maintained by this module - 0 in process context, 1 in IRQ context - The index of packet processed in the context Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h index 425d1d7..1622579 100644 --- a/sound/firewire/amdtp-stream-trace.h +++ b/sound/firewire/amdtp-stream-trace.h @@ -14,8 +14,8 @@ #include TRACE_EVENT(in_packet, - TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 cip_header[2], unsigned int payload_quadlets), - TP_ARGS(s, cycles, cip_header, payload_quadlets), + TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 cip_header[2], unsigned int payload_quadlets, unsigned int index), + TP_ARGS(s, cycles, cip_header, payload_quadlets, index), TP_STRUCT__entry( __field(unsigned int, second) __field(unsigned int, cycle) @@ -25,6 +25,8 @@ TRACE_EVENT(in_packet, __field(u32, cip_header0) __field(u32, cip_header1) __field(unsigned int, payload_quadlets) + __field(unsigned int, packet_index) + __field(bool, irq) __field(unsigned int, index) ), TP_fast_assign( @@ -36,10 +38,12 @@ TRACE_EVENT(in_packet, __entry->cip_header0 = cip_header[0]; __entry->cip_header1 = cip_header[1]; __entry->payload_quadlets = payload_quadlets; - __entry->index = s->packet_index; + __entry->packet_index = s->packet_index; + __entry->irq = in_interrupt(); + __entry->index = index; ), TP_printk( - "%02u %04u %04x %04x %02d %08x %08x %03u %02u", + "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u", __entry->second, __entry->cycle, __entry->src, @@ -48,12 +52,14 @@ TRACE_EVENT(in_packet, __entry->cip_header0, __entry->cip_header1, __entry->payload_quadlets, + __entry->packet_index, + __entry->irq, __entry->index) ); TRACE_EVENT(out_packet, - TP_PROTO(const struct amdtp_stream *s, u32 cycles, __be32 *cip_header, unsigned int payload_length), - TP_ARGS(s, cycles, cip_header, payload_length), + TP_PROTO(const struct amdtp_stream *s, u32 cycles, __be32 *cip_header, unsigned int payload_length, unsigned int index), + TP_ARGS(s, cycles, cip_header, payload_length, index), TP_STRUCT__entry( __field(unsigned int, second) __field(unsigned int, cycle) @@ -63,6 +69,8 @@ TRACE_EVENT(out_packet, __field(u32, cip_header0) __field(u32, cip_header1) __field(unsigned int, payload_quadlets) + __field(unsigned int, packet_index) + __field(bool, irq) __field(unsigned int, index) ), TP_fast_assign( @@ -74,10 +82,12 @@ TRACE_EVENT(out_packet, __entry->cip_header0 = be32_to_cpu(cip_header[0]); __entry->cip_header1 = be32_to_cpu(cip_header[1]); __entry->payload_quadlets = payload_length / 4; - __entry->index = s->packet_index; + __entry->packet_index = s->packet_index; + __entry->irq = in_interrupt(); + __entry->index = index; ), TP_printk( - "%02u %04u %04x %04x %02d %08x %08x %03u %02u", + "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u", __entry->second, __entry->cycle, __entry->src, @@ -86,6 +96,8 @@ TRACE_EVENT(out_packet, __entry->cip_header0, __entry->cip_header1, __entry->payload_quadlets, + __entry->packet_index, + __entry->irq, __entry->index) ); diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 024ab7f..bf10ca3 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -409,7 +409,8 @@ static inline int queue_in_packet(struct amdtp_stream *s) amdtp_stream_get_max_payload(s), false); } -static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle) +static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle, + unsigned int index) { __be32 *buffer; unsigned int syt; @@ -434,7 +435,7 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle) s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; - trace_out_packet(s, cycle, buffer, payload_length); + trace_out_packet(s, cycle, buffer, payload_length, index); if (queue_out_packet(s, payload_length, false) < 0) return -EIO; @@ -448,7 +449,8 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle) } static int handle_in_packet(struct amdtp_stream *s, - unsigned int payload_quadlets, unsigned int cycle) + unsigned int payload_quadlets, unsigned int cycle, + unsigned int index) { __be32 *buffer; u32 cip_header[2]; @@ -463,7 +465,7 @@ static int handle_in_packet(struct amdtp_stream *s, cip_header[0] = be32_to_cpu(buffer[0]); cip_header[1] = be32_to_cpu(buffer[1]); - trace_in_packet(s, cycle, cip_header, payload_quadlets); + trace_in_packet(s, cycle, cip_header, payload_quadlets, index); /* * This module supports 'Two-quadlet CIP header with SYT field'. @@ -602,7 +604,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, for (i = 0; i < packets; ++i) { cycle = increment_cycle_count(cycle, 1); - if (handle_out_packet(s, cycle) < 0) { + if (handle_out_packet(s, cycle, i) < 0) { s->packet_index = -1; amdtp_stream_pcm_abort(s); return; @@ -649,7 +651,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, break; } - if (handle_in_packet(s, payload_quadlets, cycle) < 0) + if (handle_in_packet(s, payload_quadlets, cycle, i) < 0) break; } -- cgit v0.10.2 From ff38e0c70adce96de0be0bf470cbb9e283ba6965 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 11 May 2016 07:35:09 +0900 Subject: ALSA: firewire-lib: drop skip argument from helper functions to queue a packet On most of audio and music units on IEEE 1394 bus which ALSA firewire stack supports (or plans to support), CIP with two quadlets header is used. Thus, there's no cases to queue packets with blank payload. If such packets are going to be queued, it means that they're for skips of the cycle. This commit simplifies helper functions to queue a packet. Signed-off-by: Takashi Sakamoto Acked-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index bf10ca3..00060c4 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -368,9 +368,8 @@ static void pcm_period_tasklet(unsigned long data) snd_pcm_period_elapsed(pcm); } -static int queue_packet(struct amdtp_stream *s, - unsigned int header_length, - unsigned int payload_length, bool skip) +static int queue_packet(struct amdtp_stream *s, unsigned int header_length, + unsigned int payload_length) { struct fw_iso_packet p = {0}; int err = 0; @@ -381,8 +380,10 @@ static int queue_packet(struct amdtp_stream *s, p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); p.tag = TAG_CIP; p.header_length = header_length; - p.payload_length = (!skip) ? payload_length : 0; - p.skip = skip; + if (payload_length > 0) + p.payload_length = payload_length; + else + p.skip = true; err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer, s->buffer.packets[s->packet_index].offset); if (err < 0) { @@ -397,16 +398,15 @@ end: } static inline int queue_out_packet(struct amdtp_stream *s, - unsigned int payload_length, bool skip) + unsigned int payload_length) { - return queue_packet(s, OUT_PACKET_HEADER_SIZE, - payload_length, skip); + return queue_packet(s, OUT_PACKET_HEADER_SIZE, payload_length); } static inline int queue_in_packet(struct amdtp_stream *s) { return queue_packet(s, IN_PACKET_HEADER_SIZE, - amdtp_stream_get_max_payload(s), false); + amdtp_stream_get_max_payload(s)); } static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle, @@ -437,7 +437,7 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle, trace_out_packet(s, cycle, buffer, payload_length, index); - if (queue_out_packet(s, payload_length, false) < 0) + if (queue_out_packet(s, payload_length) < 0) return -EIO; pcm = ACCESS_ONCE(s->pcm); @@ -764,7 +764,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) if (s->direction == AMDTP_IN_STREAM) err = queue_in_packet(s); else - err = queue_out_packet(s, 0, true); + err = queue_out_packet(s, 0); if (err < 0) goto err_context; } while (s->packet_index > 0); -- cgit v0.10.2 From 639db596165746ca87bbcb56559b094fd9042890 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 May 2016 18:04:16 +0200 Subject: ALSA: au88x0: Fix zero clear of stream->resources There are a few calls of memset() to stream->resources, but they all are called in a wrong size, sizeof(unsigned char) * VORTEX_RESOURCE_LAST, while this field is a u32 array. This may leave the memories not zero-cleared. Fix it by replacing them with a simpler sizeof(stream->resources) instead. Reported-by: David Binderman Signed-off-by: Takashi Iwai diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 4667c32..4a054d7 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -2151,8 +2151,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, stream->resources, en, VORTEX_RESOURCE_SRC)) < 0) { memset(stream->resources, 0, - sizeof(unsigned char) * - VORTEX_RESOURCE_LAST); + sizeof(stream->resources)); return -EBUSY; } if (stream->type != VORTEX_PCM_A3D) { @@ -2162,7 +2161,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, VORTEX_RESOURCE_MIXIN)) < 0) { memset(stream->resources, 0, - sizeof(unsigned char) * VORTEX_RESOURCE_LAST); + sizeof(stream->resources)); return -EBUSY; } } @@ -2175,8 +2174,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, stream->resources, en, VORTEX_RESOURCE_A3D)) < 0) { memset(stream->resources, 0, - sizeof(unsigned char) * - VORTEX_RESOURCE_LAST); + sizeof(stream->resources)); dev_err(vortex->card->dev, "out of A3D sources. Sorry\n"); return -EBUSY; @@ -2290,8 +2288,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, VORTEX_RESOURCE_MIXOUT)) < 0) { memset(stream->resources, 0, - sizeof(unsigned char) * - VORTEX_RESOURCE_LAST); + sizeof(stream->resources)); return -EBUSY; } if ((src[i] = @@ -2299,8 +2296,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, stream->resources, en, VORTEX_RESOURCE_SRC)) < 0) { memset(stream->resources, 0, - sizeof(unsigned char) * - VORTEX_RESOURCE_LAST); + sizeof(stream->resources)); return -EBUSY; } } -- cgit v0.10.2