From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id A5CFB1581E7 for ; Sat, 27 Apr 2024 17:05:40 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id C784BE2A02; Sat, 27 Apr 2024 17:05:39 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [IPv6:2001:470:ea4a:1:5054:ff:fec7:86e4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 5B88BE2A02 for ; Sat, 27 Apr 2024 17:05:39 +0000 (UTC) Received: from oystercatcher.gentoo.org (oystercatcher.gentoo.org [148.251.78.52]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id C02A5340C42 for ; Sat, 27 Apr 2024 17:05:37 +0000 (UTC) Received: from localhost.localdomain (localhost [IPv6:::1]) by oystercatcher.gentoo.org (Postfix) with ESMTP id 6066F15EC for ; Sat, 27 Apr 2024 17:05:36 +0000 (UTC) From: "Mike Pagano" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Mike Pagano" Message-ID: <1714237526.5bbaececb184905605c4be94fc33dba4d1ccbd05.mpagano@gentoo> Subject: [gentoo-commits] proj/linux-patches:6.6 commit in: / X-VCS-Repository: proj/linux-patches X-VCS-Files: 0000_README 1028_linux-6.6.29.patch X-VCS-Directories: / X-VCS-Committer: mpagano X-VCS-Committer-Name: Mike Pagano X-VCS-Revision: 5bbaececb184905605c4be94fc33dba4d1ccbd05 X-VCS-Branch: 6.6 Date: Sat, 27 Apr 2024 17:05:36 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply X-Archives-Salt: 1bfa4896-b205-4f98-a813-b8f60f80f89d X-Archives-Hash: 5fd274924351f6369423aaed85af02e0 commit: 5bbaececb184905605c4be94fc33dba4d1ccbd05 Author: Mike Pagano gentoo org> AuthorDate: Sat Apr 27 17:05:26 2024 +0000 Commit: Mike Pagano gentoo org> CommitDate: Sat Apr 27 17:05:26 2024 +0000 URL: https://gitweb.gentoo.org/proj/linux-patches.git/commit/?id=5bbaecec Linux patch 6.6.29 Signed-off-by: Mike Pagano gentoo.org> 0000_README | 4 + 1028_linux-6.6.29.patch | 17666 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 17670 insertions(+) diff --git a/0000_README b/0000_README index a8e3c8db..b08c709c 100644 --- a/0000_README +++ b/0000_README @@ -155,6 +155,10 @@ Patch: 1027_linux-6.6.28.patch From: https://www.kernel.org Desc: Linux 6.6.28 +Patch: 1028_linux-6.6.29.patch +From: https://www.kernel.org +Desc: Linux 6.6.29 + Patch: 1510_fs-enable-link-security-restrictions-by-default.patch From: http://sources.debian.net/src/linux/3.16.7-ckt4-3/debian/patches/debian/fs-enable-link-security-restrictions-by-default.patch/ Desc: Enable link security restrictions by default. diff --git a/1028_linux-6.6.29.patch b/1028_linux-6.6.29.patch new file mode 100644 index 00000000..75e76ec6 --- /dev/null +++ b/1028_linux-6.6.29.patch @@ -0,0 +1,17666 @@ +diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt +index 4cd15aee16c20..66dfc348043d6 100644 +--- a/Documentation/admin-guide/kernel-parameters.txt ++++ b/Documentation/admin-guide/kernel-parameters.txt +@@ -6853,6 +6853,9 @@ + pause after every control message); + o = USB_QUIRK_HUB_SLOW_RESET (Hub needs extra + delay after resetting its port); ++ p = USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT ++ (Reduce timeout of the SET_ADDRESS ++ request from 5000 ms to 500 ms); + Example: quirks=0781:5580:bk,0a5c:5834:gij + + usbhid.mousepoll= +diff --git a/MAINTAINERS b/MAINTAINERS +index 40312bb550f06..72a2880afab7a 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -8142,7 +8142,7 @@ M: Geoffrey D. Bennett + L: alsa-devel@alsa-project.org (moderated for non-subscribers) + S: Maintained + T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git +-F: sound/usb/mixer_scarlett_gen2.c ++F: sound/usb/mixer_scarlett2.c + + FORCEDETH GIGABIT ETHERNET DRIVER + M: Rain River +diff --git a/Makefile b/Makefile +index a3839877aafd6..bb103505791e4 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,7 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0 + VERSION = 6 + PATCHLEVEL = 6 +-SUBLEVEL = 28 ++SUBLEVEL = 29 + EXTRAVERSION = + NAME = Hurr durr I'ma ninja sloth + +diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c +index c1c0121f478d6..b947bacf23a37 100644 +--- a/arch/arm/mach-omap2/pdata-quirks.c ++++ b/arch/arm/mach-omap2/pdata-quirks.c +@@ -275,9 +275,19 @@ static struct platform_device pandora_backlight = { + .id = -1, + }; + ++static struct gpiod_lookup_table pandora_soc_audio_gpios = { ++ .dev_id = "soc-audio", ++ .table = { ++ GPIO_LOOKUP("gpio-112-127", 6, "dac", GPIO_ACTIVE_HIGH), ++ GPIO_LOOKUP("gpio-0-15", 14, "amp", GPIO_ACTIVE_HIGH), ++ { } ++ }, ++}; ++ + static void __init omap3_pandora_legacy_init(void) + { + platform_device_register(&pandora_backlight); ++ gpiod_add_lookup_table(&pandora_soc_audio_gpios); + } + #endif /* CONFIG_ARCH_OMAP3 */ + +diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h +index b149cf9f91bc9..b73baaf8ae47b 100644 +--- a/arch/arm64/include/asm/tlbflush.h ++++ b/arch/arm64/include/asm/tlbflush.h +@@ -152,12 +152,18 @@ static inline unsigned long get_trans_granule(void) + #define MAX_TLBI_RANGE_PAGES __TLBI_RANGE_PAGES(31, 3) + + /* +- * Generate 'num' values from -1 to 30 with -1 rejected by the +- * __flush_tlb_range() loop below. ++ * Generate 'num' values from -1 to 31 with -1 rejected by the ++ * __flush_tlb_range() loop below. Its return value is only ++ * significant for a maximum of MAX_TLBI_RANGE_PAGES pages. If ++ * 'pages' is more than that, you must iterate over the overall ++ * range. + */ +-#define TLBI_RANGE_MASK GENMASK_ULL(4, 0) +-#define __TLBI_RANGE_NUM(pages, scale) \ +- ((((pages) >> (5 * (scale) + 1)) & TLBI_RANGE_MASK) - 1) ++#define __TLBI_RANGE_NUM(pages, scale) \ ++ ({ \ ++ int __pages = min((pages), \ ++ __TLBI_RANGE_PAGES(31, (scale))); \ ++ (__pages >> (5 * (scale) + 1)) - 1; \ ++ }) + + /* + * TLB Invalidation +@@ -351,29 +357,25 @@ static inline void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch) + * entries one by one at the granularity of 'stride'. If the TLB + * range ops are supported, then: + * +- * 1. If 'pages' is odd, flush the first page through non-range +- * operations; +- * +- * 2. For remaining pages: the minimum range granularity is decided +- * by 'scale', so multiple range TLBI operations may be required. +- * Start from scale = 0, flush the corresponding number of pages +- * ((num+1)*2^(5*scale+1) starting from 'addr'), then increase it +- * until no pages left. ++ * 1. The minimum range granularity is decided by 'scale', so multiple range ++ * TLBI operations may be required. Start from scale = 3, flush the largest ++ * possible number of pages ((num+1)*2^(5*scale+1)) that fit into the ++ * requested range, then decrement scale and continue until one or zero pages ++ * are left. + * +- * Note that certain ranges can be represented by either num = 31 and +- * scale or num = 0 and scale + 1. The loop below favours the latter +- * since num is limited to 30 by the __TLBI_RANGE_NUM() macro. ++ * 2. If there is 1 page remaining, flush it through non-range operations. Range ++ * operations can only span an even number of pages. + */ + #define __flush_tlb_range_op(op, start, pages, stride, \ + asid, tlb_level, tlbi_user) \ + do { \ + int num = 0; \ +- int scale = 0; \ ++ int scale = 3; \ + unsigned long addr; \ + \ + while (pages > 0) { \ + if (!system_supports_tlb_range() || \ +- pages % 2 == 1) { \ ++ pages == 1) { \ + addr = __TLBI_VADDR(start, asid); \ + __tlbi_level(op, addr, tlb_level); \ + if (tlbi_user) \ +@@ -393,7 +395,7 @@ do { \ + start += __TLBI_RANGE_PAGES(num, scale) << PAGE_SHIFT; \ + pages -= __TLBI_RANGE_PAGES(num, scale); \ + } \ +- scale++; \ ++ scale--; \ + } \ + } while (0) + +diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S +index 7b236994f0e15..6517bf2644a08 100644 +--- a/arch/arm64/kernel/head.S ++++ b/arch/arm64/kernel/head.S +@@ -569,6 +569,11 @@ SYM_INNER_LABEL(init_el2, SYM_L_LOCAL) + adr_l x1, __hyp_text_end + adr_l x2, dcache_clean_poc + blr x2 ++ ++ mov_q x0, INIT_SCTLR_EL2_MMU_OFF ++ pre_disable_mmu_workaround ++ msr sctlr_el2, x0 ++ isb + 0: + mov_q x0, HCR_HOST_NVHE_FLAGS + msr hcr_el2, x0 +diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c +index 924843f1f661b..0a62f458c5cb0 100644 +--- a/arch/arm64/mm/pageattr.c ++++ b/arch/arm64/mm/pageattr.c +@@ -219,9 +219,6 @@ bool kernel_page_present(struct page *page) + pte_t *ptep; + unsigned long addr = (unsigned long)page_address(page); + +- if (!can_set_direct_map()) +- return true; +- + pgdp = pgd_offset_k(addr); + if (pgd_none(READ_ONCE(*pgdp))) + return false; +diff --git a/arch/powerpc/include/asm/ftrace.h b/arch/powerpc/include/asm/ftrace.h +index 9e5a39b6a3114..107fc5a484569 100644 +--- a/arch/powerpc/include/asm/ftrace.h ++++ b/arch/powerpc/include/asm/ftrace.h +@@ -20,14 +20,6 @@ + #ifndef __ASSEMBLY__ + extern void _mcount(void); + +-static inline unsigned long ftrace_call_adjust(unsigned long addr) +-{ +- if (IS_ENABLED(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY)) +- addr += MCOUNT_INSN_SIZE; +- +- return addr; +-} +- + unsigned long prepare_ftrace_return(unsigned long parent, unsigned long ip, + unsigned long sp); + +@@ -142,8 +134,10 @@ static inline u8 this_cpu_get_ftrace_enabled(void) { return 1; } + #ifdef CONFIG_FUNCTION_TRACER + extern unsigned int ftrace_tramp_text[], ftrace_tramp_init[]; + void ftrace_free_init_tramp(void); ++unsigned long ftrace_call_adjust(unsigned long addr); + #else + static inline void ftrace_free_init_tramp(void) { } ++static inline unsigned long ftrace_call_adjust(unsigned long addr) { return addr; } + #endif + #endif /* !__ASSEMBLY__ */ + +diff --git a/arch/powerpc/include/asm/sections.h b/arch/powerpc/include/asm/sections.h +index ea26665f82cfc..f43f3a6b0051c 100644 +--- a/arch/powerpc/include/asm/sections.h ++++ b/arch/powerpc/include/asm/sections.h +@@ -14,6 +14,7 @@ typedef struct func_desc func_desc_t; + + extern char __head_end[]; + extern char __srwx_boundary[]; ++extern char __exittext_begin[], __exittext_end[]; + + /* Patch sites */ + extern s32 patch__call_flush_branch_caches1; +diff --git a/arch/powerpc/kernel/trace/ftrace.c b/arch/powerpc/kernel/trace/ftrace.c +index 82010629cf887..d8d6b4fd9a14c 100644 +--- a/arch/powerpc/kernel/trace/ftrace.c ++++ b/arch/powerpc/kernel/trace/ftrace.c +@@ -27,10 +27,22 @@ + #include + #include + #include ++#include + + #define NUM_FTRACE_TRAMPS 2 + static unsigned long ftrace_tramps[NUM_FTRACE_TRAMPS]; + ++unsigned long ftrace_call_adjust(unsigned long addr) ++{ ++ if (addr >= (unsigned long)__exittext_begin && addr < (unsigned long)__exittext_end) ++ return 0; ++ ++ if (IS_ENABLED(CONFIG_ARCH_USING_PATCHABLE_FUNCTION_ENTRY)) ++ addr += MCOUNT_INSN_SIZE; ++ ++ return addr; ++} ++ + static ppc_inst_t ftrace_create_branch_inst(unsigned long ip, unsigned long addr, int link) + { + ppc_inst_t op; +diff --git a/arch/powerpc/kernel/trace/ftrace_64_pg.c b/arch/powerpc/kernel/trace/ftrace_64_pg.c +index 7b85c3b460a3c..12fab1803bcf4 100644 +--- a/arch/powerpc/kernel/trace/ftrace_64_pg.c ++++ b/arch/powerpc/kernel/trace/ftrace_64_pg.c +@@ -37,6 +37,11 @@ + #define NUM_FTRACE_TRAMPS 8 + static unsigned long ftrace_tramps[NUM_FTRACE_TRAMPS]; + ++unsigned long ftrace_call_adjust(unsigned long addr) ++{ ++ return addr; ++} ++ + static ppc_inst_t + ftrace_call_replace(unsigned long ip, unsigned long addr, int link) + { +diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S +index 1c5970df32336..f420df7888a75 100644 +--- a/arch/powerpc/kernel/vmlinux.lds.S ++++ b/arch/powerpc/kernel/vmlinux.lds.S +@@ -281,7 +281,9 @@ SECTIONS + * to deal with references from __bug_table + */ + .exit.text : AT(ADDR(.exit.text) - LOAD_OFFSET) { ++ __exittext_begin = .; + EXIT_TEXT ++ __exittext_end = .; + } + + . = ALIGN(PAGE_SIZE); +diff --git a/arch/x86/include/asm/barrier.h b/arch/x86/include/asm/barrier.h +index 0216f63a366b5..d0795b5fab46a 100644 +--- a/arch/x86/include/asm/barrier.h ++++ b/arch/x86/include/asm/barrier.h +@@ -79,6 +79,9 @@ do { \ + #define __smp_mb__before_atomic() do { } while (0) + #define __smp_mb__after_atomic() do { } while (0) + ++/* Writing to CR3 provides a full memory barrier in switch_mm(). */ ++#define smp_mb__after_switch_mm() do { } while (0) ++ + #include + + #endif /* _ASM_X86_BARRIER_H */ +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index fb9f5fa96cc96..ccba66da7a5d7 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -828,6 +828,7 @@ struct kvm_vcpu_arch { + int cpuid_nent; + struct kvm_cpuid_entry2 *cpuid_entries; + struct kvm_hypervisor_cpuid kvm_cpuid; ++ bool is_amd_compatible; + + /* + * FIXME: Drop this macro and use KVM_NR_GOVERNED_FEATURES directly +diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c +index 5ff69b1d39b20..c2dc9b7426acb 100644 +--- a/arch/x86/kernel/cpu/bugs.c ++++ b/arch/x86/kernel/cpu/bugs.c +@@ -1651,7 +1651,8 @@ static void __init bhi_select_mitigation(void) + return; + + /* Retpoline mitigates against BHI unless the CPU has RRSBA behavior */ +- if (cpu_feature_enabled(X86_FEATURE_RETPOLINE)) { ++ if (boot_cpu_has(X86_FEATURE_RETPOLINE) && ++ !boot_cpu_has(X86_FEATURE_RETPOLINE_LFENCE)) { + spec_ctrl_disable_kernel_rrsba(); + if (rrsba_disabled) + return; +@@ -2803,11 +2804,13 @@ static const char *spectre_bhi_state(void) + { + if (!boot_cpu_has_bug(X86_BUG_BHI)) + return "; BHI: Not affected"; +- else if (boot_cpu_has(X86_FEATURE_CLEAR_BHB_HW)) ++ else if (boot_cpu_has(X86_FEATURE_CLEAR_BHB_HW)) + return "; BHI: BHI_DIS_S"; +- else if (boot_cpu_has(X86_FEATURE_CLEAR_BHB_LOOP)) ++ else if (boot_cpu_has(X86_FEATURE_CLEAR_BHB_LOOP)) + return "; BHI: SW loop, KVM: SW loop"; +- else if (boot_cpu_has(X86_FEATURE_RETPOLINE) && rrsba_disabled) ++ else if (boot_cpu_has(X86_FEATURE_RETPOLINE) && ++ !boot_cpu_has(X86_FEATURE_RETPOLINE_LFENCE) && ++ rrsba_disabled) + return "; BHI: Retpoline"; + else if (boot_cpu_has(X86_FEATURE_CLEAR_BHB_LOOP_ON_VMEXIT)) + return "; BHI: Vulnerable, KVM: SW loop"; +diff --git a/arch/x86/kernel/cpu/cpuid-deps.c b/arch/x86/kernel/cpu/cpuid-deps.c +index e462c1d3800a6..6fb6d8a57ceca 100644 +--- a/arch/x86/kernel/cpu/cpuid-deps.c ++++ b/arch/x86/kernel/cpu/cpuid-deps.c +@@ -44,7 +44,10 @@ static const struct cpuid_dep cpuid_deps[] = { + { X86_FEATURE_F16C, X86_FEATURE_XMM2, }, + { X86_FEATURE_AES, X86_FEATURE_XMM2 }, + { X86_FEATURE_SHA_NI, X86_FEATURE_XMM2 }, ++ { X86_FEATURE_GFNI, X86_FEATURE_XMM2 }, + { X86_FEATURE_FMA, X86_FEATURE_AVX }, ++ { X86_FEATURE_VAES, X86_FEATURE_AVX }, ++ { X86_FEATURE_VPCLMULQDQ, X86_FEATURE_AVX }, + { X86_FEATURE_AVX2, X86_FEATURE_AVX, }, + { X86_FEATURE_AVX512F, X86_FEATURE_AVX, }, + { X86_FEATURE_AVX512IFMA, X86_FEATURE_AVX512F }, +@@ -56,9 +59,6 @@ static const struct cpuid_dep cpuid_deps[] = { + { X86_FEATURE_AVX512VL, X86_FEATURE_AVX512F }, + { X86_FEATURE_AVX512VBMI, X86_FEATURE_AVX512F }, + { X86_FEATURE_AVX512_VBMI2, X86_FEATURE_AVX512VL }, +- { X86_FEATURE_GFNI, X86_FEATURE_AVX512VL }, +- { X86_FEATURE_VAES, X86_FEATURE_AVX512VL }, +- { X86_FEATURE_VPCLMULQDQ, X86_FEATURE_AVX512VL }, + { X86_FEATURE_AVX512_VNNI, X86_FEATURE_AVX512VL }, + { X86_FEATURE_AVX512_BITALG, X86_FEATURE_AVX512VL }, + { X86_FEATURE_AVX512_4VNNIW, X86_FEATURE_AVX512F }, +diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c +index d65f91d0a8bca..77458137cab17 100644 +--- a/arch/x86/kvm/cpuid.c ++++ b/arch/x86/kvm/cpuid.c +@@ -362,6 +362,7 @@ static void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) + + kvm_update_pv_runtime(vcpu); + ++ vcpu->arch.is_amd_compatible = guest_cpuid_is_amd_or_hygon(vcpu); + vcpu->arch.maxphyaddr = cpuid_query_maxphyaddr(vcpu); + vcpu->arch.reserved_gpa_bits = kvm_vcpu_reserved_gpa_bits_raw(vcpu); + +diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h +index 284fa4704553d..57ee789ada141 100644 +--- a/arch/x86/kvm/cpuid.h ++++ b/arch/x86/kvm/cpuid.h +@@ -125,6 +125,16 @@ static inline bool guest_cpuid_is_intel(struct kvm_vcpu *vcpu) + return best && is_guest_vendor_intel(best->ebx, best->ecx, best->edx); + } + ++static inline bool guest_cpuid_is_amd_compatible(struct kvm_vcpu *vcpu) ++{ ++ return vcpu->arch.is_amd_compatible; ++} ++ ++static inline bool guest_cpuid_is_intel_compatible(struct kvm_vcpu *vcpu) ++{ ++ return !guest_cpuid_is_amd_compatible(vcpu); ++} ++ + static inline int guest_cpuid_family(struct kvm_vcpu *vcpu) + { + struct kvm_cpuid_entry2 *best; +diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c +index 23fab75993a51..66a2c4c0ae106 100644 +--- a/arch/x86/kvm/lapic.c ++++ b/arch/x86/kvm/lapic.c +@@ -2772,7 +2772,8 @@ int kvm_apic_local_deliver(struct kvm_lapic *apic, int lvt_type) + trig_mode = reg & APIC_LVT_LEVEL_TRIGGER; + + r = __apic_accept_irq(apic, mode, vector, 1, trig_mode, NULL); +- if (r && lvt_type == APIC_LVTPC) ++ if (r && lvt_type == APIC_LVTPC && ++ guest_cpuid_is_intel_compatible(apic->vcpu)) + kvm_lapic_set_reg(apic, APIC_LVTPC, reg | APIC_LVT_MASKED); + return r; + } +diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c +index 11c484d72eab2..294775b7383b4 100644 +--- a/arch/x86/kvm/mmu/mmu.c ++++ b/arch/x86/kvm/mmu/mmu.c +@@ -4788,7 +4788,7 @@ static void reset_guest_rsvds_bits_mask(struct kvm_vcpu *vcpu, + context->cpu_role.base.level, is_efer_nx(context), + guest_can_use(vcpu, X86_FEATURE_GBPAGES), + is_cr4_pse(context), +- guest_cpuid_is_amd_or_hygon(vcpu)); ++ guest_cpuid_is_amd_compatible(vcpu)); + } + + static void __reset_rsvds_bits_mask_ept(struct rsvd_bits_validate *rsvd_check, +diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c +index 6cd4dd631a2fa..8eef3ed5fe04e 100644 +--- a/arch/x86/kvm/mmu/tdp_mmu.c ++++ b/arch/x86/kvm/mmu/tdp_mmu.c +@@ -1506,6 +1506,16 @@ void kvm_tdp_mmu_try_split_huge_pages(struct kvm *kvm, + } + } + ++static bool tdp_mmu_need_write_protect(struct kvm_mmu_page *sp) ++{ ++ /* ++ * All TDP MMU shadow pages share the same role as their root, aside ++ * from level, so it is valid to key off any shadow page to determine if ++ * write protection is needed for an entire tree. ++ */ ++ return kvm_mmu_page_ad_need_write_protect(sp) || !kvm_ad_enabled(); ++} ++ + /* + * Clear the dirty status of all the SPTEs mapping GFNs in the memslot. If + * AD bits are enabled, this will involve clearing the dirty bit on each SPTE. +@@ -1516,7 +1526,8 @@ void kvm_tdp_mmu_try_split_huge_pages(struct kvm *kvm, + static bool clear_dirty_gfn_range(struct kvm *kvm, struct kvm_mmu_page *root, + gfn_t start, gfn_t end) + { +- u64 dbit = kvm_ad_enabled() ? shadow_dirty_mask : PT_WRITABLE_MASK; ++ const u64 dbit = tdp_mmu_need_write_protect(root) ? PT_WRITABLE_MASK : ++ shadow_dirty_mask; + struct tdp_iter iter; + bool spte_set = false; + +@@ -1530,7 +1541,7 @@ static bool clear_dirty_gfn_range(struct kvm *kvm, struct kvm_mmu_page *root, + if (!is_shadow_present_pte(iter.old_spte)) + continue; + +- KVM_MMU_WARN_ON(kvm_ad_enabled() && ++ KVM_MMU_WARN_ON(dbit == shadow_dirty_mask && + spte_ad_need_write_protect(iter.old_spte)); + + if (!(iter.old_spte & dbit)) +@@ -1578,8 +1589,8 @@ bool kvm_tdp_mmu_clear_dirty_slot(struct kvm *kvm, + static void clear_dirty_pt_masked(struct kvm *kvm, struct kvm_mmu_page *root, + gfn_t gfn, unsigned long mask, bool wrprot) + { +- u64 dbit = (wrprot || !kvm_ad_enabled()) ? PT_WRITABLE_MASK : +- shadow_dirty_mask; ++ const u64 dbit = (wrprot || tdp_mmu_need_write_protect(root)) ? PT_WRITABLE_MASK : ++ shadow_dirty_mask; + struct tdp_iter iter; + + lockdep_assert_held_write(&kvm->mmu_lock); +@@ -1591,7 +1602,7 @@ static void clear_dirty_pt_masked(struct kvm *kvm, struct kvm_mmu_page *root, + if (!mask) + break; + +- KVM_MMU_WARN_ON(kvm_ad_enabled() && ++ KVM_MMU_WARN_ON(dbit == shadow_dirty_mask && + spte_ad_need_write_protect(iter.old_spte)); + + if (iter.level > PG_LEVEL_4K || +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index b2ed051611b08..dae499e2da84e 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -7858,8 +7858,28 @@ static u64 vmx_get_perf_capabilities(void) + + if (vmx_pebs_supported()) { + perf_cap |= host_perf_cap & PERF_CAP_PEBS_MASK; +- if ((perf_cap & PERF_CAP_PEBS_FORMAT) < 4) +- perf_cap &= ~PERF_CAP_PEBS_BASELINE; ++ ++ /* ++ * Disallow adaptive PEBS as it is functionally broken, can be ++ * used by the guest to read *host* LBRs, and can be used to ++ * bypass userspace event filters. To correctly and safely ++ * support adaptive PEBS, KVM needs to: ++ * ++ * 1. Account for the ADAPTIVE flag when (re)programming fixed ++ * counters. ++ * ++ * 2. Gain support from perf (or take direct control of counter ++ * programming) to support events without adaptive PEBS ++ * enabled for the hardware counter. ++ * ++ * 3. Ensure LBR MSRs cannot hold host data on VM-Entry with ++ * adaptive PEBS enabled and MSR_PEBS_DATA_CFG.LBRS=1. ++ * ++ * 4. Document which PMU events are effectively exposed to the ++ * guest via adaptive PEBS, and make adaptive PEBS mutually ++ * exclusive with KVM_SET_PMU_EVENT_FILTER if necessary. ++ */ ++ perf_cap &= ~PERF_CAP_PEBS_BASELINE; + } + + return perf_cap; +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index 4ed8a7dc05369..cab7680f0d681 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -3317,7 +3317,7 @@ static bool is_mci_status_msr(u32 msr) + static bool can_set_mci_status(struct kvm_vcpu *vcpu) + { + /* McStatusWrEn enabled? */ +- if (guest_cpuid_is_amd_or_hygon(vcpu)) ++ if (guest_cpuid_is_amd_compatible(vcpu)) + return !!(vcpu->arch.msr_hwcr & BIT_ULL(18)); + + return false; +diff --git a/drivers/accessibility/speakup/main.c b/drivers/accessibility/speakup/main.c +index 1fbc9b921c4fc..736c2eb8c0f37 100644 +--- a/drivers/accessibility/speakup/main.c ++++ b/drivers/accessibility/speakup/main.c +@@ -574,7 +574,7 @@ static u_long get_word(struct vc_data *vc) + } + attr_ch = get_char(vc, (u_short *)tmp_pos, &spk_attr); + buf[cnt++] = attr_ch; +- while (tmpx < vc->vc_cols - 1) { ++ while (tmpx < vc->vc_cols - 1 && cnt < sizeof(buf) - 1) { + tmp_pos += 2; + tmpx++; + ch = get_char(vc, (u_short *)tmp_pos, &temp); +diff --git a/drivers/android/binder.c b/drivers/android/binder.c +index 8460458ebe3d4..64f4573656c6d 100644 +--- a/drivers/android/binder.c ++++ b/drivers/android/binder.c +@@ -1708,8 +1708,10 @@ static size_t binder_get_object(struct binder_proc *proc, + size_t object_size = 0; + + read_size = min_t(size_t, sizeof(*object), buffer->data_size - offset); +- if (offset > buffer->data_size || read_size < sizeof(*hdr)) ++ if (offset > buffer->data_size || read_size < sizeof(*hdr) || ++ !IS_ALIGNED(offset, sizeof(u32))) + return 0; ++ + if (u) { + if (copy_from_user(object, u + offset, read_size)) + return 0; +diff --git a/drivers/char/random.c b/drivers/char/random.c +index 3cb37760dfec2..7b5d4822fa3ae 100644 +--- a/drivers/char/random.c ++++ b/drivers/char/random.c +@@ -702,7 +702,7 @@ static void extract_entropy(void *buf, size_t len) + + static void __cold _credit_init_bits(size_t bits) + { +- static struct execute_work set_ready; ++ static DECLARE_WORK(set_ready, crng_set_ready); + unsigned int new, orig, add; + unsigned long flags; + +@@ -718,8 +718,8 @@ static void __cold _credit_init_bits(size_t bits) + + if (orig < POOL_READY_BITS && new >= POOL_READY_BITS) { + crng_reseed(NULL); /* Sets crng_init to CRNG_READY under base_crng.lock. */ +- if (static_key_initialized) +- execute_in_process_context(crng_set_ready, &set_ready); ++ if (static_key_initialized && system_unbound_wq) ++ queue_work(system_unbound_wq, &set_ready); + atomic_notifier_call_chain(&random_ready_notifier, 0, NULL); + wake_up_interruptible(&crng_init_wait); + kill_fasync(&fasync, SIGIO, POLL_IN); +@@ -890,8 +890,8 @@ void __init random_init(void) + + /* + * If we were initialized by the cpu or bootloader before jump labels +- * are initialized, then we should enable the static branch here, where +- * it's guaranteed that jump labels have been initialized. ++ * or workqueues are initialized, then we should enable the static ++ * branch here, where it's guaranteed that these have been initialized. + */ + if (!static_branch_likely(&crng_is_ready) && crng_init >= CRNG_READY) + crng_set_ready(NULL); +diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c +index 50228cb0c5590..4a67c0d4823cf 100644 +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -37,6 +37,10 @@ static HLIST_HEAD(clk_root_list); + static HLIST_HEAD(clk_orphan_list); + static LIST_HEAD(clk_notifier_list); + ++/* List of registered clks that use runtime PM */ ++static HLIST_HEAD(clk_rpm_list); ++static DEFINE_MUTEX(clk_rpm_list_lock); ++ + static const struct hlist_head *all_lists[] = { + &clk_root_list, + &clk_orphan_list, +@@ -59,6 +63,7 @@ struct clk_core { + struct clk_hw *hw; + struct module *owner; + struct device *dev; ++ struct hlist_node rpm_node; + struct device_node *of_node; + struct clk_core *parent; + struct clk_parent_map *parents; +@@ -122,6 +127,89 @@ static void clk_pm_runtime_put(struct clk_core *core) + pm_runtime_put_sync(core->dev); + } + ++/** ++ * clk_pm_runtime_get_all() - Runtime "get" all clk provider devices ++ * ++ * Call clk_pm_runtime_get() on all runtime PM enabled clks in the clk tree so ++ * that disabling unused clks avoids a deadlock where a device is runtime PM ++ * resuming/suspending and the runtime PM callback is trying to grab the ++ * prepare_lock for something like clk_prepare_enable() while ++ * clk_disable_unused_subtree() holds the prepare_lock and is trying to runtime ++ * PM resume/suspend the device as well. ++ * ++ * Context: Acquires the 'clk_rpm_list_lock' and returns with the lock held on ++ * success. Otherwise the lock is released on failure. ++ * ++ * Return: 0 on success, negative errno otherwise. ++ */ ++static int clk_pm_runtime_get_all(void) ++{ ++ int ret; ++ struct clk_core *core, *failed; ++ ++ /* ++ * Grab the list lock to prevent any new clks from being registered ++ * or unregistered until clk_pm_runtime_put_all(). ++ */ ++ mutex_lock(&clk_rpm_list_lock); ++ ++ /* ++ * Runtime PM "get" all the devices that are needed for the clks ++ * currently registered. Do this without holding the prepare_lock, to ++ * avoid the deadlock. ++ */ ++ hlist_for_each_entry(core, &clk_rpm_list, rpm_node) { ++ ret = clk_pm_runtime_get(core); ++ if (ret) { ++ failed = core; ++ pr_err("clk: Failed to runtime PM get '%s' for clk '%s'\n", ++ dev_name(failed->dev), failed->name); ++ goto err; ++ } ++ } ++ ++ return 0; ++ ++err: ++ hlist_for_each_entry(core, &clk_rpm_list, rpm_node) { ++ if (core == failed) ++ break; ++ ++ clk_pm_runtime_put(core); ++ } ++ mutex_unlock(&clk_rpm_list_lock); ++ ++ return ret; ++} ++ ++/** ++ * clk_pm_runtime_put_all() - Runtime "put" all clk provider devices ++ * ++ * Put the runtime PM references taken in clk_pm_runtime_get_all() and release ++ * the 'clk_rpm_list_lock'. ++ */ ++static void clk_pm_runtime_put_all(void) ++{ ++ struct clk_core *core; ++ ++ hlist_for_each_entry(core, &clk_rpm_list, rpm_node) ++ clk_pm_runtime_put(core); ++ mutex_unlock(&clk_rpm_list_lock); ++} ++ ++static void clk_pm_runtime_init(struct clk_core *core) ++{ ++ struct device *dev = core->dev; ++ ++ if (dev && pm_runtime_enabled(dev)) { ++ core->rpm_enabled = true; ++ ++ mutex_lock(&clk_rpm_list_lock); ++ hlist_add_head(&core->rpm_node, &clk_rpm_list); ++ mutex_unlock(&clk_rpm_list_lock); ++ } ++} ++ + /*** locking ***/ + static void clk_prepare_lock(void) + { +@@ -1362,9 +1450,6 @@ static void __init clk_unprepare_unused_subtree(struct clk_core *core) + if (core->flags & CLK_IGNORE_UNUSED) + return; + +- if (clk_pm_runtime_get(core)) +- return; +- + if (clk_core_is_prepared(core)) { + trace_clk_unprepare(core); + if (core->ops->unprepare_unused) +@@ -1373,8 +1458,6 @@ static void __init clk_unprepare_unused_subtree(struct clk_core *core) + core->ops->unprepare(core->hw); + trace_clk_unprepare_complete(core); + } +- +- clk_pm_runtime_put(core); + } + + static void __init clk_disable_unused_subtree(struct clk_core *core) +@@ -1390,9 +1473,6 @@ static void __init clk_disable_unused_subtree(struct clk_core *core) + if (core->flags & CLK_OPS_PARENT_ENABLE) + clk_core_prepare_enable(core->parent); + +- if (clk_pm_runtime_get(core)) +- goto unprepare_out; +- + flags = clk_enable_lock(); + + if (core->enable_count) +@@ -1417,8 +1497,6 @@ static void __init clk_disable_unused_subtree(struct clk_core *core) + + unlock_out: + clk_enable_unlock(flags); +- clk_pm_runtime_put(core); +-unprepare_out: + if (core->flags & CLK_OPS_PARENT_ENABLE) + clk_core_disable_unprepare(core->parent); + } +@@ -1434,6 +1512,7 @@ __setup("clk_ignore_unused", clk_ignore_unused_setup); + static int __init clk_disable_unused(void) + { + struct clk_core *core; ++ int ret; + + if (clk_ignore_unused) { + pr_warn("clk: Not disabling unused clocks\n"); +@@ -1442,6 +1521,13 @@ static int __init clk_disable_unused(void) + + pr_info("clk: Disabling unused clocks\n"); + ++ ret = clk_pm_runtime_get_all(); ++ if (ret) ++ return ret; ++ /* ++ * Grab the prepare lock to keep the clk topology stable while iterating ++ * over clks. ++ */ + clk_prepare_lock(); + + hlist_for_each_entry(core, &clk_root_list, child_node) +@@ -1458,6 +1544,8 @@ static int __init clk_disable_unused(void) + + clk_prepare_unlock(); + ++ clk_pm_runtime_put_all(); ++ + return 0; + } + late_initcall_sync(clk_disable_unused); +@@ -3191,28 +3279,41 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c, + int level) + { + int phase; ++ struct clk *clk_user; ++ int multi_node = 0; + +- seq_printf(s, "%*s%-*s %7d %8d %8d %11lu %10lu ", ++ seq_printf(s, "%*s%-*s %-7d %-8d %-8d %-11lu %-10lu ", + level * 3 + 1, "", +- 30 - level * 3, c->name, ++ 35 - level * 3, c->name, + c->enable_count, c->prepare_count, c->protect_count, + clk_core_get_rate_recalc(c), + clk_core_get_accuracy_recalc(c)); + + phase = clk_core_get_phase(c); + if (phase >= 0) +- seq_printf(s, "%5d", phase); ++ seq_printf(s, "%-5d", phase); + else + seq_puts(s, "-----"); + +- seq_printf(s, " %6d", clk_core_get_scaled_duty_cycle(c, 100000)); ++ seq_printf(s, " %-6d", clk_core_get_scaled_duty_cycle(c, 100000)); + + if (c->ops->is_enabled) +- seq_printf(s, " %9c\n", clk_core_is_enabled(c) ? 'Y' : 'N'); ++ seq_printf(s, " %5c ", clk_core_is_enabled(c) ? 'Y' : 'N'); + else if (!c->ops->enable) +- seq_printf(s, " %9c\n", 'Y'); ++ seq_printf(s, " %5c ", 'Y'); + else +- seq_printf(s, " %9c\n", '?'); ++ seq_printf(s, " %5c ", '?'); ++ ++ hlist_for_each_entry(clk_user, &c->clks, clks_node) { ++ seq_printf(s, "%*s%-*s %-25s\n", ++ level * 3 + 2 + 105 * multi_node, "", ++ 30, ++ clk_user->dev_id ? clk_user->dev_id : "deviceless", ++ clk_user->con_id ? clk_user->con_id : "no_connection_id"); ++ ++ multi_node = 1; ++ } ++ + } + + static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c, +@@ -3220,9 +3321,7 @@ static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c, + { + struct clk_core *child; + +- clk_pm_runtime_get(c); + clk_summary_show_one(s, c, level); +- clk_pm_runtime_put(c); + + hlist_for_each_entry(child, &c->children, child_node) + clk_summary_show_subtree(s, child, level + 1); +@@ -3232,10 +3331,15 @@ static int clk_summary_show(struct seq_file *s, void *data) + { + struct clk_core *c; + struct hlist_head **lists = s->private; ++ int ret; + +- seq_puts(s, " enable prepare protect duty hardware\n"); +- seq_puts(s, " clock count count count rate accuracy phase cycle enable\n"); +- seq_puts(s, "-------------------------------------------------------------------------------------------------------\n"); ++ seq_puts(s, " enable prepare protect duty hardware connection\n"); ++ seq_puts(s, " clock count count count rate accuracy phase cycle enable consumer id\n"); ++ seq_puts(s, "---------------------------------------------------------------------------------------------------------------------------------------------\n"); ++ ++ ret = clk_pm_runtime_get_all(); ++ if (ret) ++ return ret; + + clk_prepare_lock(); + +@@ -3244,6 +3348,7 @@ static int clk_summary_show(struct seq_file *s, void *data) + clk_summary_show_subtree(s, c, 0); + + clk_prepare_unlock(); ++ clk_pm_runtime_put_all(); + + return 0; + } +@@ -3291,8 +3396,14 @@ static int clk_dump_show(struct seq_file *s, void *data) + struct clk_core *c; + bool first_node = true; + struct hlist_head **lists = s->private; ++ int ret; ++ ++ ret = clk_pm_runtime_get_all(); ++ if (ret) ++ return ret; + + seq_putc(s, '{'); ++ + clk_prepare_lock(); + + for (; *lists; lists++) { +@@ -3305,6 +3416,7 @@ static int clk_dump_show(struct seq_file *s, void *data) + } + + clk_prepare_unlock(); ++ clk_pm_runtime_put_all(); + + seq_puts(s, "}\n"); + return 0; +@@ -3919,8 +4031,6 @@ static int __clk_core_init(struct clk_core *core) + } + + clk_core_reparent_orphans_nolock(); +- +- kref_init(&core->ref); + out: + clk_pm_runtime_put(core); + unlock: +@@ -4149,6 +4259,22 @@ static void clk_core_free_parent_map(struct clk_core *core) + kfree(core->parents); + } + ++/* Free memory allocated for a struct clk_core */ ++static void __clk_release(struct kref *ref) ++{ ++ struct clk_core *core = container_of(ref, struct clk_core, ref); ++ ++ if (core->rpm_enabled) { ++ mutex_lock(&clk_rpm_list_lock); ++ hlist_del(&core->rpm_node); ++ mutex_unlock(&clk_rpm_list_lock); ++ } ++ ++ clk_core_free_parent_map(core); ++ kfree_const(core->name); ++ kfree(core); ++} ++ + static struct clk * + __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw) + { +@@ -4169,6 +4295,8 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw) + goto fail_out; + } + ++ kref_init(&core->ref); ++ + core->name = kstrdup_const(init->name, GFP_KERNEL); + if (!core->name) { + ret = -ENOMEM; +@@ -4181,9 +4309,8 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw) + } + core->ops = init->ops; + +- if (dev && pm_runtime_enabled(dev)) +- core->rpm_enabled = true; + core->dev = dev; ++ clk_pm_runtime_init(core); + core->of_node = np; + if (dev && dev->driver) + core->owner = dev->driver->owner; +@@ -4223,12 +4350,10 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw) + hw->clk = NULL; + + fail_create_clk: +- clk_core_free_parent_map(core); + fail_parents: + fail_ops: +- kfree_const(core->name); + fail_name: +- kfree(core); ++ kref_put(&core->ref, __clk_release); + fail_out: + return ERR_PTR(ret); + } +@@ -4308,18 +4433,6 @@ int of_clk_hw_register(struct device_node *node, struct clk_hw *hw) + } + EXPORT_SYMBOL_GPL(of_clk_hw_register); + +-/* Free memory allocated for a clock. */ +-static void __clk_release(struct kref *ref) +-{ +- struct clk_core *core = container_of(ref, struct clk_core, ref); +- +- lockdep_assert_held(&prepare_lock); +- +- clk_core_free_parent_map(core); +- kfree_const(core->name); +- kfree(core); +-} +- + /* + * Empty clk_ops for unregistered clocks. These are used temporarily + * after clk_unregister() was called on a clock and until last clock +diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c +index 2e55368dc4d82..bd37ab4d1a9bb 100644 +--- a/drivers/clk/mediatek/clk-mtk.c ++++ b/drivers/clk/mediatek/clk-mtk.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + + #include "clk-mtk.h" +@@ -494,6 +495,16 @@ static int __mtk_clk_simple_probe(struct platform_device *pdev, + return IS_ERR(base) ? PTR_ERR(base) : -ENOMEM; + } + ++ ++ devm_pm_runtime_enable(&pdev->dev); ++ /* ++ * Do a pm_runtime_resume_and_get() to workaround a possible ++ * deadlock between clk_register() and the genpd framework. ++ */ ++ r = pm_runtime_resume_and_get(&pdev->dev); ++ if (r) ++ return r; ++ + /* Calculate how many clk_hw_onecell_data entries to allocate */ + num_clks = mcd->num_clks + mcd->num_composite_clks; + num_clks += mcd->num_fixed_clks + mcd->num_factor_clks; +@@ -574,6 +585,8 @@ static int __mtk_clk_simple_probe(struct platform_device *pdev, + goto unregister_clks; + } + ++ pm_runtime_put(&pdev->dev); ++ + return r; + + unregister_clks: +@@ -604,6 +617,8 @@ static int __mtk_clk_simple_probe(struct platform_device *pdev, + free_base: + if (mcd->shared_io && base) + iounmap(base); ++ ++ pm_runtime_put(&pdev->dev); + return r; + } + +diff --git a/drivers/comedi/drivers/vmk80xx.c b/drivers/comedi/drivers/vmk80xx.c +index 4536ed43f65b2..84dce5184a77a 100644 +--- a/drivers/comedi/drivers/vmk80xx.c ++++ b/drivers/comedi/drivers/vmk80xx.c +@@ -641,33 +641,22 @@ static int vmk80xx_find_usb_endpoints(struct comedi_device *dev) + struct vmk80xx_private *devpriv = dev->private; + struct usb_interface *intf = comedi_to_usb_interface(dev); + struct usb_host_interface *iface_desc = intf->cur_altsetting; +- struct usb_endpoint_descriptor *ep_desc; +- int i; +- +- if (iface_desc->desc.bNumEndpoints != 2) +- return -ENODEV; +- +- for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { +- ep_desc = &iface_desc->endpoint[i].desc; +- +- if (usb_endpoint_is_int_in(ep_desc) || +- usb_endpoint_is_bulk_in(ep_desc)) { +- if (!devpriv->ep_rx) +- devpriv->ep_rx = ep_desc; +- continue; +- } ++ struct usb_endpoint_descriptor *ep_rx_desc, *ep_tx_desc; ++ int ret; + +- if (usb_endpoint_is_int_out(ep_desc) || +- usb_endpoint_is_bulk_out(ep_desc)) { +- if (!devpriv->ep_tx) +- devpriv->ep_tx = ep_desc; +- continue; +- } +- } ++ if (devpriv->model == VMK8061_MODEL) ++ ret = usb_find_common_endpoints(iface_desc, &ep_rx_desc, ++ &ep_tx_desc, NULL, NULL); ++ else ++ ret = usb_find_common_endpoints(iface_desc, NULL, NULL, ++ &ep_rx_desc, &ep_tx_desc); + +- if (!devpriv->ep_rx || !devpriv->ep_tx) ++ if (ret) + return -ENODEV; + ++ devpriv->ep_rx = ep_rx_desc; ++ devpriv->ep_tx = ep_tx_desc; ++ + if (!usb_endpoint_maxp(devpriv->ep_rx) || !usb_endpoint_maxp(devpriv->ep_tx)) + return -EINVAL; + +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +index 928107d0bfea4..1124e2d4f8530 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +@@ -562,7 +562,6 @@ static int amdgpu_ttm_io_mem_reserve(struct ttm_device *bdev, + struct ttm_resource *mem) + { + struct amdgpu_device *adev = amdgpu_ttm_adev(bdev); +- size_t bus_size = (size_t)mem->size; + + switch (mem->mem_type) { + case TTM_PL_SYSTEM: +@@ -573,9 +572,6 @@ static int amdgpu_ttm_io_mem_reserve(struct ttm_device *bdev, + break; + case TTM_PL_VRAM: + mem->bus.offset = mem->start << PAGE_SHIFT; +- /* check if it's visible */ +- if ((mem->bus.offset + bus_size) > adev->gmc.visible_vram_size) +- return -EINVAL; + + if (adev->mman.aper_base_kaddr && + mem->placement & TTM_PL_FLAG_CONTIGUOUS) +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +index 9fe1278fd5861..f5e78b0c08f7e 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +@@ -1500,6 +1500,37 @@ static void amdgpu_vm_bo_insert_map(struct amdgpu_device *adev, + trace_amdgpu_vm_bo_map(bo_va, mapping); + } + ++/* Validate operation parameters to prevent potential abuse */ ++static int amdgpu_vm_verify_parameters(struct amdgpu_device *adev, ++ struct amdgpu_bo *bo, ++ uint64_t saddr, ++ uint64_t offset, ++ uint64_t size) ++{ ++ uint64_t tmp, lpfn; ++ ++ if (saddr & AMDGPU_GPU_PAGE_MASK ++ || offset & AMDGPU_GPU_PAGE_MASK ++ || size & AMDGPU_GPU_PAGE_MASK) ++ return -EINVAL; ++ ++ if (check_add_overflow(saddr, size, &tmp) ++ || check_add_overflow(offset, size, &tmp) ++ || size == 0 /* which also leads to end < begin */) ++ return -EINVAL; ++ ++ /* make sure object fit at this offset */ ++ if (bo && offset + size > amdgpu_bo_size(bo)) ++ return -EINVAL; ++ ++ /* Ensure last pfn not exceed max_pfn */ ++ lpfn = (saddr + size - 1) >> AMDGPU_GPU_PAGE_SHIFT; ++ if (lpfn >= adev->vm_manager.max_pfn) ++ return -EINVAL; ++ ++ return 0; ++} ++ + /** + * amdgpu_vm_bo_map - map bo inside a vm + * +@@ -1526,21 +1557,14 @@ int amdgpu_vm_bo_map(struct amdgpu_device *adev, + struct amdgpu_bo *bo = bo_va->base.bo; + struct amdgpu_vm *vm = bo_va->base.vm; + uint64_t eaddr; ++ int r; + +- /* validate the parameters */ +- if (saddr & ~PAGE_MASK || offset & ~PAGE_MASK || size & ~PAGE_MASK) +- return -EINVAL; +- if (saddr + size <= saddr || offset + size <= offset) +- return -EINVAL; +- +- /* make sure object fit at this offset */ +- eaddr = saddr + size - 1; +- if ((bo && offset + size > amdgpu_bo_size(bo)) || +- (eaddr >= adev->vm_manager.max_pfn << AMDGPU_GPU_PAGE_SHIFT)) +- return -EINVAL; ++ r = amdgpu_vm_verify_parameters(adev, bo, saddr, offset, size); ++ if (r) ++ return r; + + saddr /= AMDGPU_GPU_PAGE_SIZE; +- eaddr /= AMDGPU_GPU_PAGE_SIZE; ++ eaddr = saddr + (size - 1) / AMDGPU_GPU_PAGE_SIZE; + + tmp = amdgpu_vm_it_iter_first(&vm->va, saddr, eaddr); + if (tmp) { +@@ -1593,17 +1617,9 @@ int amdgpu_vm_bo_replace_map(struct amdgpu_device *adev, + uint64_t eaddr; + int r; + +- /* validate the parameters */ +- if (saddr & ~PAGE_MASK || offset & ~PAGE_MASK || size & ~PAGE_MASK) +- return -EINVAL; +- if (saddr + size <= saddr || offset + size <= offset) +- return -EINVAL; +- +- /* make sure object fit at this offset */ +- eaddr = saddr + size - 1; +- if ((bo && offset + size > amdgpu_bo_size(bo)) || +- (eaddr >= adev->vm_manager.max_pfn << AMDGPU_GPU_PAGE_SHIFT)) +- return -EINVAL; ++ r = amdgpu_vm_verify_parameters(adev, bo, saddr, offset, size); ++ if (r) ++ return r; + + /* Allocate all the needed memory */ + mapping = kmalloc(sizeof(*mapping), GFP_KERNEL); +@@ -1617,7 +1633,7 @@ int amdgpu_vm_bo_replace_map(struct amdgpu_device *adev, + } + + saddr /= AMDGPU_GPU_PAGE_SIZE; +- eaddr /= AMDGPU_GPU_PAGE_SIZE; ++ eaddr = saddr + (size - 1) / AMDGPU_GPU_PAGE_SIZE; + + mapping->start = saddr; + mapping->last = eaddr; +@@ -1704,10 +1720,14 @@ int amdgpu_vm_bo_clear_mappings(struct amdgpu_device *adev, + struct amdgpu_bo_va_mapping *before, *after, *tmp, *next; + LIST_HEAD(removed); + uint64_t eaddr; ++ int r; ++ ++ r = amdgpu_vm_verify_parameters(adev, NULL, saddr, 0, size); ++ if (r) ++ return r; + +- eaddr = saddr + size - 1; + saddr /= AMDGPU_GPU_PAGE_SIZE; +- eaddr /= AMDGPU_GPU_PAGE_SIZE; ++ eaddr = saddr + (size - 1) / AMDGPU_GPU_PAGE_SIZE; + + /* Allocate all the needed memory */ + before = kzalloc(sizeof(*before), GFP_KERNEL); +diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c +index fbf053001af97..7a1a574106fac 100644 +--- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c ++++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c +@@ -818,9 +818,9 @@ struct kfd_process *kfd_create_process(struct task_struct *thread) + mutex_lock(&kfd_processes_mutex); + + if (kfd_is_locked()) { +- mutex_unlock(&kfd_processes_mutex); + pr_debug("KFD is locked! Cannot create process"); +- return ERR_PTR(-EINVAL); ++ process = ERR_PTR(-EINVAL); ++ goto out; + } + + /* A prior open of /dev/kfd could have already created the process. */ +diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.c +index e817fa4efeee5..058dee76054ea 100644 +--- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.c ++++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.c +@@ -236,9 +236,6 @@ static void optc32_setup_manual_trigger(struct timing_generator *optc) + OTG_V_TOTAL_MAX_SEL, 1, + OTG_FORCE_LOCK_ON_EVENT, 0, + OTG_SET_V_TOTAL_MIN_MASK, (1 << 1)); /* TRIGA */ +- +- // Setup manual flow control for EOF via TRIG_A +- optc->funcs->setup_manual_trigger(optc); + } + } + +diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c +index 3fe5e6439c401..aa93129c3397e 100644 +--- a/drivers/gpu/drm/drm_panel_orientation_quirks.c ++++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c +@@ -348,6 +348,12 @@ static const struct dmi_system_id orientation_data[] = { + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "IdeaPad Duet 3 10IGL5"), + }, + .driver_data = (void *)&lcd1200x1920_rightside_up, ++ }, { /* Lenovo Legion Go 8APU1 */ ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Legion Go 8APU1"), ++ }, ++ .driver_data = (void *)&lcd1600x2560_leftside_up, + }, { /* Lenovo Yoga Book X90F / X90L */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), +diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c +index 7cf51dd8c0567..aaddd8c0cfa0e 100644 +--- a/drivers/gpu/drm/i915/display/intel_atomic.c ++++ b/drivers/gpu/drm/i915/display/intel_atomic.c +@@ -259,6 +259,7 @@ intel_crtc_duplicate_state(struct drm_crtc *crtc) + drm_property_blob_get(crtc_state->post_csc_lut); + + crtc_state->update_pipe = false; ++ crtc_state->update_m_n = false; + crtc_state->disable_lp_wm = false; + crtc_state->disable_cxsr = false; + crtc_state->update_wm_pre = false; +diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c +index 5aa6b998a1cb1..fc3a6eb1de741 100644 +--- a/drivers/gpu/drm/i915/display/intel_cdclk.c ++++ b/drivers/gpu/drm/i915/display/intel_cdclk.c +@@ -2453,7 +2453,8 @@ intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state) + intel_atomic_get_old_cdclk_state(state); + const struct intel_cdclk_state *new_cdclk_state = + intel_atomic_get_new_cdclk_state(state); +- enum pipe pipe = new_cdclk_state->pipe; ++ struct intel_cdclk_config cdclk_config; ++ enum pipe pipe; + + if (!intel_cdclk_changed(&old_cdclk_state->actual, + &new_cdclk_state->actual)) +@@ -2462,12 +2463,25 @@ intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state) + if (IS_DG2(i915)) + intel_cdclk_pcode_pre_notify(state); + +- if (new_cdclk_state->disable_pipes || +- old_cdclk_state->actual.cdclk <= new_cdclk_state->actual.cdclk) { +- drm_WARN_ON(&i915->drm, !new_cdclk_state->base.changed); ++ if (new_cdclk_state->disable_pipes) { ++ cdclk_config = new_cdclk_state->actual; ++ pipe = INVALID_PIPE; ++ } else { ++ if (new_cdclk_state->actual.cdclk >= old_cdclk_state->actual.cdclk) { ++ cdclk_config = new_cdclk_state->actual; ++ pipe = new_cdclk_state->pipe; ++ } else { ++ cdclk_config = old_cdclk_state->actual; ++ pipe = INVALID_PIPE; ++ } + +- intel_set_cdclk(i915, &new_cdclk_state->actual, pipe); ++ cdclk_config.voltage_level = max(new_cdclk_state->actual.voltage_level, ++ old_cdclk_state->actual.voltage_level); + } ++ ++ drm_WARN_ON(&i915->drm, !new_cdclk_state->base.changed); ++ ++ intel_set_cdclk(i915, &cdclk_config, pipe); + } + + /** +@@ -2485,7 +2499,7 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state) + intel_atomic_get_old_cdclk_state(state); + const struct intel_cdclk_state *new_cdclk_state = + intel_atomic_get_new_cdclk_state(state); +- enum pipe pipe = new_cdclk_state->pipe; ++ enum pipe pipe; + + if (!intel_cdclk_changed(&old_cdclk_state->actual, + &new_cdclk_state->actual)) +@@ -2495,11 +2509,14 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state) + intel_cdclk_pcode_post_notify(state); + + if (!new_cdclk_state->disable_pipes && +- old_cdclk_state->actual.cdclk > new_cdclk_state->actual.cdclk) { +- drm_WARN_ON(&i915->drm, !new_cdclk_state->base.changed); ++ new_cdclk_state->actual.cdclk < old_cdclk_state->actual.cdclk) ++ pipe = new_cdclk_state->pipe; ++ else ++ pipe = INVALID_PIPE; + +- intel_set_cdclk(i915, &new_cdclk_state->actual, pipe); +- } ++ drm_WARN_ON(&i915->drm, !new_cdclk_state->base.changed); ++ ++ intel_set_cdclk(i915, &new_cdclk_state->actual, pipe); + } + + static int intel_pixel_rate_to_cdclk(const struct intel_crtc_state *crtc_state) +diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c +index 182c6dd64f47c..cfbfbfed3f5e6 100644 +--- a/drivers/gpu/drm/i915/display/intel_crtc.c ++++ b/drivers/gpu/drm/i915/display/intel_crtc.c +@@ -468,9 +468,56 @@ static int intel_mode_vblank_start(const struct drm_display_mode *mode) + return vblank_start; + } + ++static void intel_crtc_vblank_evade_scanlines(struct intel_atomic_state *state, ++ struct intel_crtc *crtc, ++ int *min, int *max, int *vblank_start) ++{ ++ const struct intel_crtc_state *old_crtc_state = ++ intel_atomic_get_old_crtc_state(state, crtc); ++ const struct intel_crtc_state *new_crtc_state = ++ intel_atomic_get_new_crtc_state(state, crtc); ++ const struct intel_crtc_state *crtc_state; ++ const struct drm_display_mode *adjusted_mode; ++ ++ /* ++ * During fastsets/etc. the transcoder is still ++ * running with the old timings at this point. ++ * ++ * TODO: maybe just use the active timings here? ++ */ ++ if (intel_crtc_needs_modeset(new_crtc_state)) ++ crtc_state = new_crtc_state; ++ else ++ crtc_state = old_crtc_state; ++ ++ adjusted_mode = &crtc_state->hw.adjusted_mode; ++ ++ if (crtc->mode_flags & I915_MODE_FLAG_VRR) { ++ if (intel_vrr_is_push_sent(crtc_state)) ++ *vblank_start = intel_vrr_vmin_vblank_start(crtc_state); ++ else ++ *vblank_start = intel_vrr_vmax_vblank_start(crtc_state); ++ } else { ++ *vblank_start = intel_mode_vblank_start(adjusted_mode); ++ } ++ ++ /* FIXME needs to be calibrated sensibly */ ++ *min = *vblank_start - intel_usecs_to_scanlines(adjusted_mode, ++ VBLANK_EVASION_TIME_US); ++ *max = *vblank_start - 1; ++ ++ /* ++ * M/N is double buffered on the transcoder's undelayed vblank, ++ * so with seamless M/N we must evade both vblanks. ++ */ ++ if (new_crtc_state->update_m_n) ++ *min -= adjusted_mode->crtc_vblank_start - adjusted_mode->crtc_vdisplay; ++} ++ + /** + * intel_pipe_update_start() - start update of a set of display registers +- * @new_crtc_state: the new crtc state ++ * @state: the atomic state ++ * @crtc: the crtc + * + * Mark the start of an update to pipe registers that should be updated + * atomically regarding vblank. If the next vblank will happens within +@@ -480,11 +527,12 @@ static int intel_mode_vblank_start(const struct drm_display_mode *mode) + * until a subsequent call to intel_pipe_update_end(). That is done to + * avoid random delays. + */ +-void intel_pipe_update_start(struct intel_crtc_state *new_crtc_state) ++void intel_pipe_update_start(struct intel_atomic_state *state, ++ struct intel_crtc *crtc) + { +- struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->uapi.crtc); + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); +- const struct drm_display_mode *adjusted_mode = &new_crtc_state->hw.adjusted_mode; ++ struct intel_crtc_state *new_crtc_state = ++ intel_atomic_get_new_crtc_state(state, crtc); + long timeout = msecs_to_jiffies_timeout(1); + int scanline, min, max, vblank_start; + wait_queue_head_t *wq = drm_crtc_vblank_waitqueue(&crtc->base); +@@ -500,27 +548,7 @@ void intel_pipe_update_start(struct intel_crtc_state *new_crtc_state) + if (intel_crtc_needs_vblank_work(new_crtc_state)) + intel_crtc_vblank_work_init(new_crtc_state); + +- if (new_crtc_state->vrr.enable) { +- if (intel_vrr_is_push_sent(new_crtc_state)) +- vblank_start = intel_vrr_vmin_vblank_start(new_crtc_state); +- else +- vblank_start = intel_vrr_vmax_vblank_start(new_crtc_state); +- } else { +- vblank_start = intel_mode_vblank_start(adjusted_mode); +- } +- +- /* FIXME needs to be calibrated sensibly */ +- min = vblank_start - intel_usecs_to_scanlines(adjusted_mode, +- VBLANK_EVASION_TIME_US); +- max = vblank_start - 1; +- +- /* +- * M/N is double buffered on the transcoder's undelayed vblank, +- * so with seamless M/N we must evade both vblanks. +- */ +- if (new_crtc_state->seamless_m_n && intel_crtc_needs_fastset(new_crtc_state)) +- min -= adjusted_mode->crtc_vblank_start - adjusted_mode->crtc_vdisplay; +- ++ intel_crtc_vblank_evade_scanlines(state, crtc, &min, &max, &vblank_start); + if (min <= 0 || max <= 0) + goto irq_disable; + +@@ -631,15 +659,18 @@ static void dbg_vblank_evade(struct intel_crtc *crtc, ktime_t end) {} + + /** + * intel_pipe_update_end() - end update of a set of display registers +- * @new_crtc_state: the new crtc state ++ * @state: the atomic state ++ * @crtc: the crtc + * + * Mark the end of an update started with intel_pipe_update_start(). This + * re-enables interrupts and verifies the update was actually completed + * before a vblank. + */ +-void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state) ++void intel_pipe_update_end(struct intel_atomic_state *state, ++ struct intel_crtc *crtc) + { +- struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->uapi.crtc); ++ struct intel_crtc_state *new_crtc_state = ++ intel_atomic_get_new_crtc_state(state, crtc); + enum pipe pipe = crtc->pipe; + int scanline_end = intel_get_crtc_scanline(crtc); + u32 end_vbl_count = intel_crtc_get_vblank_counter(crtc); +@@ -697,15 +728,6 @@ void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state) + */ + intel_vrr_send_push(new_crtc_state); + +- /* +- * Seamless M/N update may need to update frame timings. +- * +- * FIXME Should be synchronized with the start of vblank somehow... +- */ +- if (new_crtc_state->seamless_m_n && intel_crtc_needs_fastset(new_crtc_state)) +- intel_crtc_update_active_timings(new_crtc_state, +- new_crtc_state->vrr.enable); +- + local_irq_enable(); + + if (intel_vgpu_active(dev_priv)) +diff --git a/drivers/gpu/drm/i915/display/intel_crtc.h b/drivers/gpu/drm/i915/display/intel_crtc.h +index 51a4c8df9e657..22d7993d1f0ba 100644 +--- a/drivers/gpu/drm/i915/display/intel_crtc.h ++++ b/drivers/gpu/drm/i915/display/intel_crtc.h +@@ -36,8 +36,10 @@ void intel_crtc_state_reset(struct intel_crtc_state *crtc_state, + u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc); + void intel_crtc_vblank_on(const struct intel_crtc_state *crtc_state); + void intel_crtc_vblank_off(const struct intel_crtc_state *crtc_state); +-void intel_pipe_update_start(struct intel_crtc_state *new_crtc_state); +-void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state); ++void intel_pipe_update_start(struct intel_atomic_state *state, ++ struct intel_crtc *crtc); ++void intel_pipe_update_end(struct intel_atomic_state *state, ++ struct intel_crtc *crtc); + void intel_wait_for_vblank_workers(struct intel_atomic_state *state); + struct intel_crtc *intel_first_crtc(struct drm_i915_private *i915); + struct intel_crtc *intel_crtc_for_pipe(struct drm_i915_private *i915, +diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c +index a072fbb9872aa..1a59fca40252c 100644 +--- a/drivers/gpu/drm/i915/display/intel_display.c ++++ b/drivers/gpu/drm/i915/display/intel_display.c +@@ -5215,7 +5215,7 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, + PIPE_CONF_CHECK_X(lane_lat_optim_mask); + + if (HAS_DOUBLE_BUFFERED_M_N(dev_priv)) { +- if (!fastset || !pipe_config->seamless_m_n) ++ if (!fastset || !pipe_config->update_m_n) + PIPE_CONF_CHECK_M_N(dp_m_n); + } else { + PIPE_CONF_CHECK_M_N(dp_m_n); +@@ -5353,7 +5353,7 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, + if (IS_G4X(dev_priv) || DISPLAY_VER(dev_priv) >= 5) + PIPE_CONF_CHECK_I(pipe_bpp); + +- if (!fastset || !pipe_config->seamless_m_n) { ++ if (!fastset || !pipe_config->update_m_n) { + PIPE_CONF_CHECK_I(hw.pipe_mode.crtc_clock); + PIPE_CONF_CHECK_I(hw.adjusted_mode.crtc_clock); + } +@@ -5448,6 +5448,7 @@ int intel_modeset_all_pipes(struct intel_atomic_state *state, + + crtc_state->uapi.mode_changed = true; + crtc_state->update_pipe = false; ++ crtc_state->update_m_n = false; + + ret = drm_atomic_add_affected_connectors(&state->base, + &crtc->base); +@@ -5565,13 +5566,14 @@ static void intel_crtc_check_fastset(const struct intel_crtc_state *old_crtc_sta + { + struct drm_i915_private *i915 = to_i915(old_crtc_state->uapi.crtc->dev); + +- if (!intel_pipe_config_compare(old_crtc_state, new_crtc_state, true)) { ++ if (!intel_pipe_config_compare(old_crtc_state, new_crtc_state, true)) + drm_dbg_kms(&i915->drm, "fastset requirement not met, forcing full modeset\n"); ++ else ++ new_crtc_state->uapi.mode_changed = false; + +- return; +- } ++ if (intel_crtc_needs_modeset(new_crtc_state)) ++ new_crtc_state->update_m_n = false; + +- new_crtc_state->uapi.mode_changed = false; + if (!intel_crtc_needs_modeset(new_crtc_state)) + new_crtc_state->update_pipe = true; + } +@@ -6297,6 +6299,7 @@ int intel_atomic_check(struct drm_device *dev, + if (intel_cpu_transcoders_need_modeset(state, BIT(master))) { + new_crtc_state->uapi.mode_changed = true; + new_crtc_state->update_pipe = false; ++ new_crtc_state->update_m_n = false; + } + } + +@@ -6309,6 +6312,7 @@ int intel_atomic_check(struct drm_device *dev, + if (intel_cpu_transcoders_need_modeset(state, trans)) { + new_crtc_state->uapi.mode_changed = true; + new_crtc_state->update_pipe = false; ++ new_crtc_state->update_m_n = false; + } + } + +@@ -6316,6 +6320,7 @@ int intel_atomic_check(struct drm_device *dev, + if (intel_pipes_need_modeset(state, new_crtc_state->bigjoiner_pipes)) { + new_crtc_state->uapi.mode_changed = true; + new_crtc_state->update_pipe = false; ++ new_crtc_state->update_m_n = false; + } + } + } +@@ -6494,7 +6499,7 @@ static void intel_pipe_fastset(const struct intel_crtc_state *old_crtc_state, + IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv)) + hsw_set_linetime_wm(new_crtc_state); + +- if (new_crtc_state->seamless_m_n) ++ if (new_crtc_state->update_m_n) + intel_cpu_transcoder_set_m1_n1(crtc, new_crtc_state->cpu_transcoder, + &new_crtc_state->dp_m_n); + } +@@ -6533,6 +6538,8 @@ static void commit_pipe_post_planes(struct intel_atomic_state *state, + struct intel_crtc *crtc) + { + struct drm_i915_private *dev_priv = to_i915(state->base.dev); ++ const struct intel_crtc_state *old_crtc_state = ++ intel_atomic_get_old_crtc_state(state, crtc); + const struct intel_crtc_state *new_crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + +@@ -6544,6 +6551,9 @@ static void commit_pipe_post_planes(struct intel_atomic_state *state, + if (DISPLAY_VER(dev_priv) >= 9 && + !intel_crtc_needs_modeset(new_crtc_state)) + skl_detach_scalers(new_crtc_state); ++ ++ if (vrr_enabling(old_crtc_state, new_crtc_state)) ++ intel_vrr_enable(new_crtc_state); + } + + static void intel_enable_crtc(struct intel_atomic_state *state, +@@ -6584,12 +6594,6 @@ static void intel_update_crtc(struct intel_atomic_state *state, + intel_dpt_configure(crtc); + } + +- if (vrr_enabling(old_crtc_state, new_crtc_state)) { +- intel_vrr_enable(new_crtc_state); +- intel_crtc_update_active_timings(new_crtc_state, +- new_crtc_state->vrr.enable); +- } +- + if (!modeset) { + if (new_crtc_state->preload_luts && + intel_crtc_needs_color_update(new_crtc_state)) +@@ -6616,7 +6620,7 @@ static void intel_update_crtc(struct intel_atomic_state *state, + intel_crtc_planes_update_noarm(state, crtc); + + /* Perform vblank evasion around commit operation */ +- intel_pipe_update_start(new_crtc_state); ++ intel_pipe_update_start(state, crtc); + + commit_pipe_pre_planes(state, crtc); + +@@ -6624,7 +6628,16 @@ static void intel_update_crtc(struct intel_atomic_state *state, + + commit_pipe_post_planes(state, crtc); + +- intel_pipe_update_end(new_crtc_state); ++ intel_pipe_update_end(state, crtc); ++ ++ /* ++ * VRR/Seamless M/N update may need to update frame timings. ++ * ++ * FIXME Should be synchronized with the start of vblank somehow... ++ */ ++ if (vrr_enabling(old_crtc_state, new_crtc_state) || new_crtc_state->update_m_n) ++ intel_crtc_update_active_timings(new_crtc_state, ++ new_crtc_state->vrr.enable); + + /* + * We usually enable FIFO underrun interrupts as part of the +diff --git a/drivers/gpu/drm/i915/display/intel_display_device.h b/drivers/gpu/drm/i915/display/intel_display_device.h +index 215e682bd8b7a..5fd07c1817766 100644 +--- a/drivers/gpu/drm/i915/display/intel_display_device.h ++++ b/drivers/gpu/drm/i915/display/intel_display_device.h +@@ -46,6 +46,7 @@ struct drm_printer; + #define HAS_DPT(i915) (DISPLAY_VER(i915) >= 13) + #define HAS_DSB(i915) (DISPLAY_INFO(i915)->has_dsb) + #define HAS_DSC(__i915) (DISPLAY_RUNTIME_INFO(__i915)->has_dsc) ++#define HAS_DSC_MST(__i915) (DISPLAY_VER(__i915) >= 12 && HAS_DSC(__i915)) + #define HAS_FBC(i915) (DISPLAY_RUNTIME_INFO(i915)->fbc_mask != 0) + #define HAS_FPGA_DBG_UNCLAIMED(i915) (DISPLAY_INFO(i915)->has_fpga_dbg) + #define HAS_FW_BLC(i915) (DISPLAY_VER(i915) > 2) +diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h +index 8b0dc2b75da4a..1c23b186aff20 100644 +--- a/drivers/gpu/drm/i915/display/intel_display_types.h ++++ b/drivers/gpu/drm/i915/display/intel_display_types.h +@@ -1084,6 +1084,7 @@ struct intel_crtc_state { + + unsigned fb_bits; /* framebuffers to flip */ + bool update_pipe; /* can a fast modeset be performed? */ ++ bool update_m_n; /* update M/N seamlessly during fastset? */ + bool disable_cxsr; + bool update_wm_pre, update_wm_post; /* watermarks are updated */ + bool fifo_changed; /* FIFO split is changed */ +@@ -1196,7 +1197,6 @@ struct intel_crtc_state { + /* m2_n2 for eDP downclock */ + struct intel_link_m_n dp_m2_n2; + bool has_drrs; +- bool seamless_m_n; + + /* PSR is supported but might not be enabled due the lack of enabled planes */ + bool has_psr; +diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c +index 18ee4f2a87f9e..ccc47cf4d15d8 100644 +--- a/drivers/gpu/drm/i915/display/intel_dp.c ++++ b/drivers/gpu/drm/i915/display/intel_dp.c +@@ -1310,13 +1310,14 @@ bool intel_dp_has_hdmi_sink(struct intel_dp *intel_dp) + static bool intel_dp_source_supports_fec(struct intel_dp *intel_dp, + const struct intel_crtc_state *pipe_config) + { ++ struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); + +- /* On TGL, FEC is supported on all Pipes */ + if (DISPLAY_VER(dev_priv) >= 12) + return true; + +- if (DISPLAY_VER(dev_priv) == 11 && pipe_config->cpu_transcoder != TRANSCODER_A) ++ if (DISPLAY_VER(dev_priv) == 11 && encoder->port != PORT_A && ++ !intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST)) + return true; + + return false; +@@ -2147,8 +2148,12 @@ intel_dp_drrs_compute_config(struct intel_connector *connector, + intel_panel_downclock_mode(connector, &pipe_config->hw.adjusted_mode); + int pixel_clock; + +- if (has_seamless_m_n(connector)) +- pipe_config->seamless_m_n = true; ++ /* ++ * FIXME all joined pipes share the same transcoder. ++ * Need to account for that when updating M/N live. ++ */ ++ if (has_seamless_m_n(connector) && !pipe_config->bigjoiner_pipes) ++ pipe_config->update_m_n = true; + + if (!can_enable_drrs(connector, pipe_config, downclock_mode)) { + if (intel_cpu_transcoder_has_m2_n2(i915, pipe_config->cpu_transcoder)) +diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c +index f104bd7f8c2a6..d2f8f20722d92 100644 +--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c ++++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c +@@ -964,7 +964,7 @@ intel_dp_mst_mode_valid_ctx(struct drm_connector *connector, + return 0; + } + +- if (DISPLAY_VER(dev_priv) >= 10 && ++ if (HAS_DSC_MST(dev_priv) && + drm_dp_sink_supports_dsc(intel_dp->dsc_dpcd)) { + /* + * TBD pass the connector BPC, +diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c +index 6f180ee138531..46e4a45e3c72a 100644 +--- a/drivers/gpu/drm/i915/i915_vma.c ++++ b/drivers/gpu/drm/i915/i915_vma.c +@@ -33,6 +33,7 @@ + #include "gt/intel_engine.h" + #include "gt/intel_engine_heartbeat.h" + #include "gt/intel_gt.h" ++#include "gt/intel_gt_pm.h" + #include "gt/intel_gt_requests.h" + #include "gt/intel_tlb.h" + +@@ -102,12 +103,34 @@ static inline struct i915_vma *active_to_vma(struct i915_active *ref) + + static int __i915_vma_active(struct i915_active *ref) + { +- return i915_vma_tryget(active_to_vma(ref)) ? 0 : -ENOENT; ++ struct i915_vma *vma = active_to_vma(ref); ++ ++ if (!i915_vma_tryget(vma)) ++ return -ENOENT; ++ ++ /* ++ * Exclude global GTT VMA from holding a GT wakeref ++ * while active, otherwise GPU never goes idle. ++ */ ++ if (!i915_vma_is_ggtt(vma)) ++ intel_gt_pm_get(vma->vm->gt); ++ ++ return 0; + } + + static void __i915_vma_retire(struct i915_active *ref) + { +- i915_vma_put(active_to_vma(ref)); ++ struct i915_vma *vma = active_to_vma(ref); ++ ++ if (!i915_vma_is_ggtt(vma)) { ++ /* ++ * Since we can be called from atomic contexts, ++ * use an async variant of intel_gt_pm_put(). ++ */ ++ intel_gt_pm_put_async(vma->vm->gt); ++ } ++ ++ i915_vma_put(vma); + } + + static struct i915_vma * +@@ -1403,7 +1426,7 @@ int i915_vma_pin_ww(struct i915_vma *vma, struct i915_gem_ww_ctx *ww, + struct i915_vma_work *work = NULL; + struct dma_fence *moving = NULL; + struct i915_vma_resource *vma_res = NULL; +- intel_wakeref_t wakeref = 0; ++ intel_wakeref_t wakeref; + unsigned int bound; + int err; + +@@ -1423,8 +1446,14 @@ int i915_vma_pin_ww(struct i915_vma *vma, struct i915_gem_ww_ctx *ww, + if (err) + return err; + +- if (flags & PIN_GLOBAL) +- wakeref = intel_runtime_pm_get(&vma->vm->i915->runtime_pm); ++ /* ++ * In case of a global GTT, we must hold a runtime-pm wakeref ++ * while global PTEs are updated. In other cases, we hold ++ * the rpm reference while the VMA is active. Since runtime ++ * resume may require allocations, which are forbidden inside ++ * vm->mutex, get the first rpm wakeref outside of the mutex. ++ */ ++ wakeref = intel_runtime_pm_get(&vma->vm->i915->runtime_pm); + + if (flags & vma->vm->bind_async_flags) { + /* lock VM */ +@@ -1560,8 +1589,7 @@ int i915_vma_pin_ww(struct i915_vma *vma, struct i915_gem_ww_ctx *ww, + if (work) + dma_fence_work_commit_imm(&work->base); + err_rpm: +- if (wakeref) +- intel_runtime_pm_put(&vma->vm->i915->runtime_pm, wakeref); ++ intel_runtime_pm_put(&vma->vm->i915->runtime_pm, wakeref); + + if (moving) + dma_fence_put(moving); +diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h +index 99acaf917e430..f0c3804f42587 100644 +--- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h ++++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_0_sm8150.h +@@ -77,7 +77,7 @@ static const struct dpu_sspp_cfg sm8150_sspp[] = { + .name = "sspp_0", .id = SSPP_VIG0, + .base = 0x4000, .len = 0x1f0, + .features = VIG_SDM845_MASK, +- .sblk = &sdm845_vig_sblk_0, ++ .sblk = &sm8150_vig_sblk_0, + .xin_id = 0, + .type = SSPP_TYPE_VIG, + .clk_ctrl = DPU_CLK_CTRL_VIG0, +@@ -85,7 +85,7 @@ static const struct dpu_sspp_cfg sm8150_sspp[] = { + .name = "sspp_1", .id = SSPP_VIG1, + .base = 0x6000, .len = 0x1f0, + .features = VIG_SDM845_MASK, +- .sblk = &sdm845_vig_sblk_1, ++ .sblk = &sm8150_vig_sblk_1, + .xin_id = 4, + .type = SSPP_TYPE_VIG, + .clk_ctrl = DPU_CLK_CTRL_VIG1, +@@ -93,7 +93,7 @@ static const struct dpu_sspp_cfg sm8150_sspp[] = { + .name = "sspp_2", .id = SSPP_VIG2, + .base = 0x8000, .len = 0x1f0, + .features = VIG_SDM845_MASK, +- .sblk = &sdm845_vig_sblk_2, ++ .sblk = &sm8150_vig_sblk_2, + .xin_id = 8, + .type = SSPP_TYPE_VIG, + .clk_ctrl = DPU_CLK_CTRL_VIG2, +@@ -101,7 +101,7 @@ static const struct dpu_sspp_cfg sm8150_sspp[] = { + .name = "sspp_3", .id = SSPP_VIG3, + .base = 0xa000, .len = 0x1f0, + .features = VIG_SDM845_MASK, +- .sblk = &sdm845_vig_sblk_3, ++ .sblk = &sm8150_vig_sblk_3, + .xin_id = 12, + .type = SSPP_TYPE_VIG, + .clk_ctrl = DPU_CLK_CTRL_VIG3, +diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h +index c92fbf24fbac1..47de71e71e310 100644 +--- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h ++++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_5_1_sc8180x.h +@@ -76,7 +76,7 @@ static const struct dpu_sspp_cfg sc8180x_sspp[] = { + .name = "sspp_0", .id = SSPP_VIG0, + .base = 0x4000, .len = 0x1f0, + .features = VIG_SDM845_MASK, +- .sblk = &sdm845_vig_sblk_0, ++ .sblk = &sm8150_vig_sblk_0, + .xin_id = 0, + .type = SSPP_TYPE_VIG, + .clk_ctrl = DPU_CLK_CTRL_VIG0, +@@ -84,7 +84,7 @@ static const struct dpu_sspp_cfg sc8180x_sspp[] = { + .name = "sspp_1", .id = SSPP_VIG1, + .base = 0x6000, .len = 0x1f0, + .features = VIG_SDM845_MASK, +- .sblk = &sdm845_vig_sblk_1, ++ .sblk = &sm8150_vig_sblk_1, + .xin_id = 4, + .type = SSPP_TYPE_VIG, + .clk_ctrl = DPU_CLK_CTRL_VIG1, +@@ -92,7 +92,7 @@ static const struct dpu_sspp_cfg sc8180x_sspp[] = { + .name = "sspp_2", .id = SSPP_VIG2, + .base = 0x8000, .len = 0x1f0, + .features = VIG_SDM845_MASK, +- .sblk = &sdm845_vig_sblk_2, ++ .sblk = &sm8150_vig_sblk_2, + .xin_id = 8, + .type = SSPP_TYPE_VIG, + .clk_ctrl = DPU_CLK_CTRL_VIG2, +@@ -100,7 +100,7 @@ static const struct dpu_sspp_cfg sc8180x_sspp[] = { + .name = "sspp_3", .id = SSPP_VIG3, + .base = 0xa000, .len = 0x1f0, + .features = VIG_SDM845_MASK, +- .sblk = &sdm845_vig_sblk_3, ++ .sblk = &sm8150_vig_sblk_3, + .xin_id = 12, + .type = SSPP_TYPE_VIG, + .clk_ctrl = DPU_CLK_CTRL_VIG3, +diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h +index 8a19cfa274dea..72a1726371cae 100644 +--- a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h ++++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_8_1_sm8450.h +@@ -77,7 +77,7 @@ static const struct dpu_sspp_cfg sm8450_sspp[] = { + .name = "sspp_0", .id = SSPP_VIG0, + .base = 0x4000, .len = 0x32c, + .features = VIG_SC7180_MASK, +- .sblk = &sm8250_vig_sblk_0, ++ .sblk = &sm8450_vig_sblk_0, + .xin_id = 0, + .type = SSPP_TYPE_VIG, + .clk_ctrl = DPU_CLK_CTRL_VIG0, +@@ -85,7 +85,7 @@ static const struct dpu_sspp_cfg sm8450_sspp[] = { + .name = "sspp_1", .id = SSPP_VIG1, + .base = 0x6000, .len = 0x32c, + .features = VIG_SC7180_MASK, +- .sblk = &sm8250_vig_sblk_1, ++ .sblk = &sm8450_vig_sblk_1, + .xin_id = 4, + .type = SSPP_TYPE_VIG, + .clk_ctrl = DPU_CLK_CTRL_VIG1, +@@ -93,7 +93,7 @@ static const struct dpu_sspp_cfg sm8450_sspp[] = { + .name = "sspp_2", .id = SSPP_VIG2, + .base = 0x8000, .len = 0x32c, + .features = VIG_SC7180_MASK, +- .sblk = &sm8250_vig_sblk_2, ++ .sblk = &sm8450_vig_sblk_2, + .xin_id = 8, + .type = SSPP_TYPE_VIG, + .clk_ctrl = DPU_CLK_CTRL_VIG2, +@@ -101,7 +101,7 @@ static const struct dpu_sspp_cfg sm8450_sspp[] = { + .name = "sspp_3", .id = SSPP_VIG3, + .base = 0xa000, .len = 0x32c, + .features = VIG_SC7180_MASK, +- .sblk = &sm8250_vig_sblk_3, ++ .sblk = &sm8450_vig_sblk_3, + .xin_id = 12, + .type = SSPP_TYPE_VIG, + .clk_ctrl = DPU_CLK_CTRL_VIG3, +diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +index 713dfc0797181..77d09f961d866 100644 +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +@@ -250,14 +250,17 @@ static const uint32_t wb2_formats[] = { + * SSPP sub blocks config + *************************************************************/ + ++#define SSPP_SCALER_VER(maj, min) (((maj) << 16) | (min)) ++ + /* SSPP common configuration */ +-#define _VIG_SBLK(sdma_pri, qseed_ver) \ ++#define _VIG_SBLK(sdma_pri, qseed_ver, scaler_ver) \ + { \ + .maxdwnscale = MAX_DOWNSCALE_RATIO, \ + .maxupscale = MAX_UPSCALE_RATIO, \ + .smart_dma_priority = sdma_pri, \ + .scaler_blk = {.name = "scaler", \ + .id = qseed_ver, \ ++ .version = scaler_ver, \ + .base = 0xa00, .len = 0xa0,}, \ + .csc_blk = {.name = "csc", \ + .id = DPU_SSPP_CSC_10BIT, \ +@@ -269,13 +272,14 @@ static const uint32_t wb2_formats[] = { + .rotation_cfg = NULL, \ + } + +-#define _VIG_SBLK_ROT(sdma_pri, qseed_ver, rot_cfg) \ ++#define _VIG_SBLK_ROT(sdma_pri, qseed_ver, scaler_ver, rot_cfg) \ + { \ + .maxdwnscale = MAX_DOWNSCALE_RATIO, \ + .maxupscale = MAX_UPSCALE_RATIO, \ + .smart_dma_priority = sdma_pri, \ + .scaler_blk = {.name = "scaler", \ + .id = qseed_ver, \ ++ .version = scaler_ver, \ + .base = 0xa00, .len = 0xa0,}, \ + .csc_blk = {.name = "csc", \ + .id = DPU_SSPP_CSC_10BIT, \ +@@ -299,13 +303,17 @@ static const uint32_t wb2_formats[] = { + } + + static const struct dpu_sspp_sub_blks msm8998_vig_sblk_0 = +- _VIG_SBLK(0, DPU_SSPP_SCALER_QSEED3); ++ _VIG_SBLK(0, DPU_SSPP_SCALER_QSEED3, ++ SSPP_SCALER_VER(1, 2)); + static const struct dpu_sspp_sub_blks msm8998_vig_sblk_1 = +- _VIG_SBLK(0, DPU_SSPP_SCALER_QSEED3); ++ _VIG_SBLK(0, DPU_SSPP_SCALER_QSEED3, ++ SSPP_SCALER_VER(1, 2)); + static const struct dpu_sspp_sub_blks msm8998_vig_sblk_2 = +- _VIG_SBLK(0, DPU_SSPP_SCALER_QSEED3); ++ _VIG_SBLK(0, DPU_SSPP_SCALER_QSEED3, ++ SSPP_SCALER_VER(1, 2)); + static const struct dpu_sspp_sub_blks msm8998_vig_sblk_3 = +- _VIG_SBLK(0, DPU_SSPP_SCALER_QSEED3); ++ _VIG_SBLK(0, DPU_SSPP_SCALER_QSEED3, ++ SSPP_SCALER_VER(1, 2)); + + static const struct dpu_rotation_cfg dpu_rot_sc7280_cfg_v2 = { + .rot_maxheight = 1088, +@@ -314,13 +322,30 @@ static const struct dpu_rotation_cfg dpu_rot_sc7280_cfg_v2 = { + }; + + static const struct dpu_sspp_sub_blks sdm845_vig_sblk_0 = +- _VIG_SBLK(5, DPU_SSPP_SCALER_QSEED3); ++ _VIG_SBLK(5, DPU_SSPP_SCALER_QSEED3, ++ SSPP_SCALER_VER(1, 3)); + static const struct dpu_sspp_sub_blks sdm845_vig_sblk_1 = +- _VIG_SBLK(6, DPU_SSPP_SCALER_QSEED3); ++ _VIG_SBLK(6, DPU_SSPP_SCALER_QSEED3, ++ SSPP_SCALER_VER(1, 3)); + static const struct dpu_sspp_sub_blks sdm845_vig_sblk_2 = +- _VIG_SBLK(7, DPU_SSPP_SCALER_QSEED3); ++ _VIG_SBLK(7, DPU_SSPP_SCALER_QSEED3, ++ SSPP_SCALER_VER(1, 3)); + static const struct dpu_sspp_sub_blks sdm845_vig_sblk_3 = +- _VIG_SBLK(8, DPU_SSPP_SCALER_QSEED3); ++ _VIG_SBLK(8, DPU_SSPP_SCALER_QSEED3, ++ SSPP_SCALER_VER(1, 3)); ++ ++static const struct dpu_sspp_sub_blks sm8150_vig_sblk_0 = ++ _VIG_SBLK(5, DPU_SSPP_SCALER_QSEED3, ++ SSPP_SCALER_VER(1, 4)); ++static const struct dpu_sspp_sub_blks sm8150_vig_sblk_1 = ++ _VIG_SBLK(6, DPU_SSPP_SCALER_QSEED3, ++ SSPP_SCALER_VER(1, 4)); ++static const struct dpu_sspp_sub_blks sm8150_vig_sblk_2 = ++ _VIG_SBLK(7, DPU_SSPP_SCALER_QSEED3, ++ SSPP_SCALER_VER(1, 4)); ++static const struct dpu_sspp_sub_blks sm8150_vig_sblk_3 = ++ _VIG_SBLK(8, DPU_SSPP_SCALER_QSEED3, ++ SSPP_SCALER_VER(1, 4)); + + static const struct dpu_sspp_sub_blks sdm845_dma_sblk_0 = _DMA_SBLK(1); + static const struct dpu_sspp_sub_blks sdm845_dma_sblk_1 = _DMA_SBLK(2); +@@ -328,34 +353,60 @@ static const struct dpu_sspp_sub_blks sdm845_dma_sblk_2 = _DMA_SBLK(3); + static const struct dpu_sspp_sub_blks sdm845_dma_sblk_3 = _DMA_SBLK(4); + + static const struct dpu_sspp_sub_blks sc7180_vig_sblk_0 = +- _VIG_SBLK(4, DPU_SSPP_SCALER_QSEED4); ++ _VIG_SBLK(4, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 0)); + + static const struct dpu_sspp_sub_blks sc7280_vig_sblk_0 = +- _VIG_SBLK_ROT(4, DPU_SSPP_SCALER_QSEED4, &dpu_rot_sc7280_cfg_v2); ++ _VIG_SBLK_ROT(4, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 0), ++ &dpu_rot_sc7280_cfg_v2); + + static const struct dpu_sspp_sub_blks sm6115_vig_sblk_0 = +- _VIG_SBLK(2, DPU_SSPP_SCALER_QSEED4); ++ _VIG_SBLK(2, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 0)); + + static const struct dpu_sspp_sub_blks sm6125_vig_sblk_0 = +- _VIG_SBLK(3, DPU_SSPP_SCALER_QSEED3LITE); ++ _VIG_SBLK(3, DPU_SSPP_SCALER_QSEED3LITE, ++ SSPP_SCALER_VER(2, 4)); + + static const struct dpu_sspp_sub_blks sm8250_vig_sblk_0 = +- _VIG_SBLK(5, DPU_SSPP_SCALER_QSEED4); ++ _VIG_SBLK(5, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 0)); + static const struct dpu_sspp_sub_blks sm8250_vig_sblk_1 = +- _VIG_SBLK(6, DPU_SSPP_SCALER_QSEED4); ++ _VIG_SBLK(6, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 0)); + static const struct dpu_sspp_sub_blks sm8250_vig_sblk_2 = +- _VIG_SBLK(7, DPU_SSPP_SCALER_QSEED4); ++ _VIG_SBLK(7, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 0)); + static const struct dpu_sspp_sub_blks sm8250_vig_sblk_3 = +- _VIG_SBLK(8, DPU_SSPP_SCALER_QSEED4); ++ _VIG_SBLK(8, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 0)); ++ ++static const struct dpu_sspp_sub_blks sm8450_vig_sblk_0 = ++ _VIG_SBLK(5, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 1)); ++static const struct dpu_sspp_sub_blks sm8450_vig_sblk_1 = ++ _VIG_SBLK(6, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 1)); ++static const struct dpu_sspp_sub_blks sm8450_vig_sblk_2 = ++ _VIG_SBLK(7, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 1)); ++static const struct dpu_sspp_sub_blks sm8450_vig_sblk_3 = ++ _VIG_SBLK(8, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 1)); + + static const struct dpu_sspp_sub_blks sm8550_vig_sblk_0 = +- _VIG_SBLK(7, DPU_SSPP_SCALER_QSEED4); ++ _VIG_SBLK(7, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 2)); + static const struct dpu_sspp_sub_blks sm8550_vig_sblk_1 = +- _VIG_SBLK(8, DPU_SSPP_SCALER_QSEED4); ++ _VIG_SBLK(8, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 2)); + static const struct dpu_sspp_sub_blks sm8550_vig_sblk_2 = +- _VIG_SBLK(9, DPU_SSPP_SCALER_QSEED4); ++ _VIG_SBLK(9, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 2)); + static const struct dpu_sspp_sub_blks sm8550_vig_sblk_3 = +- _VIG_SBLK(10, DPU_SSPP_SCALER_QSEED4); ++ _VIG_SBLK(10, DPU_SSPP_SCALER_QSEED4, ++ SSPP_SCALER_VER(3, 2)); + static const struct dpu_sspp_sub_blks sm8550_dma_sblk_4 = _DMA_SBLK(5); + static const struct dpu_sspp_sub_blks sm8550_dma_sblk_5 = _DMA_SBLK(6); + +diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +index 6c9634209e9fc..3f82d84bd1c90 100644 +--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h ++++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +@@ -269,7 +269,8 @@ enum { + /** + * struct dpu_scaler_blk: Scaler information + * @info: HW register and features supported by this sub-blk +- * @version: qseed block revision ++ * @version: qseed block revision, on QSEED3+ platforms this is the value of ++ * scaler_blk.base + QSEED3_HW_VERSION registers. + */ + struct dpu_scaler_blk { + DPU_HW_SUBBLK_INFO; +diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c +index 189903b65edc9..48cf593383b34 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_bios.c ++++ b/drivers/gpu/drm/nouveau/nouveau_bios.c +@@ -23,6 +23,7 @@ + */ + + #include "nouveau_drv.h" ++#include "nouveau_bios.h" + #include "nouveau_reg.h" + #include "dispnv04/hw.h" + #include "nouveau_encoder.h" +@@ -1675,7 +1676,7 @@ apply_dcb_encoder_quirks(struct drm_device *dev, int idx, u32 *conn, u32 *conf) + */ + if (nv_match_device(dev, 0x0201, 0x1462, 0x8851)) { + if (*conn == 0xf2005014 && *conf == 0xffffffff) { +- fabricate_dcb_output(dcb, DCB_OUTPUT_TMDS, 1, 1, 1); ++ fabricate_dcb_output(dcb, DCB_OUTPUT_TMDS, 1, 1, DCB_OUTPUT_B); + return false; + } + } +@@ -1761,26 +1762,26 @@ fabricate_dcb_encoder_table(struct drm_device *dev, struct nvbios *bios) + #ifdef __powerpc__ + /* Apple iMac G4 NV17 */ + if (of_machine_is_compatible("PowerMac4,5")) { +- fabricate_dcb_output(dcb, DCB_OUTPUT_TMDS, 0, all_heads, 1); +- fabricate_dcb_output(dcb, DCB_OUTPUT_ANALOG, 1, all_heads, 2); ++ fabricate_dcb_output(dcb, DCB_OUTPUT_TMDS, 0, all_heads, DCB_OUTPUT_B); ++ fabricate_dcb_output(dcb, DCB_OUTPUT_ANALOG, 1, all_heads, DCB_OUTPUT_C); + return; + } + #endif + + /* Make up some sane defaults */ + fabricate_dcb_output(dcb, DCB_OUTPUT_ANALOG, +- bios->legacy.i2c_indices.crt, 1, 1); ++ bios->legacy.i2c_indices.crt, 1, DCB_OUTPUT_B); + + if (nv04_tv_identify(dev, bios->legacy.i2c_indices.tv) >= 0) + fabricate_dcb_output(dcb, DCB_OUTPUT_TV, + bios->legacy.i2c_indices.tv, +- all_heads, 0); ++ all_heads, DCB_OUTPUT_A); + + else if (bios->tmds.output0_script_ptr || + bios->tmds.output1_script_ptr) + fabricate_dcb_output(dcb, DCB_OUTPUT_TMDS, + bios->legacy.i2c_indices.panel, +- all_heads, 1); ++ all_heads, DCB_OUTPUT_B); + } + + static int +diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c +index 4b2d7465d22f7..f4989f0526ecb 100644 +--- a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c ++++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c +@@ -221,8 +221,11 @@ nv50_instobj_acquire(struct nvkm_memory *memory) + void __iomem *map = NULL; + + /* Already mapped? */ +- if (refcount_inc_not_zero(&iobj->maps)) ++ if (refcount_inc_not_zero(&iobj->maps)) { ++ /* read barrier match the wmb on refcount set */ ++ smp_rmb(); + return iobj->map; ++ } + + /* Take the lock, and re-check that another thread hasn't + * already mapped the object in the meantime. +@@ -249,6 +252,8 @@ nv50_instobj_acquire(struct nvkm_memory *memory) + iobj->base.memory.ptrs = &nv50_instobj_fast; + else + iobj->base.memory.ptrs = &nv50_instobj_slow; ++ /* barrier to ensure the ptrs are written before refcount is set */ ++ smp_wmb(); + refcount_set(&iobj->maps, 1); + } + +diff --git a/drivers/gpu/drm/panel/panel-visionox-rm69299.c b/drivers/gpu/drm/panel/panel-visionox-rm69299.c +index c2806e4fd553b..6e946e5a036ee 100644 +--- a/drivers/gpu/drm/panel/panel-visionox-rm69299.c ++++ b/drivers/gpu/drm/panel/panel-visionox-rm69299.c +@@ -261,8 +261,6 @@ static void visionox_rm69299_remove(struct mipi_dsi_device *dsi) + struct visionox_rm69299 *ctx = mipi_dsi_get_drvdata(dsi); + + mipi_dsi_detach(ctx->dsi); +- mipi_dsi_device_unregister(ctx->dsi); +- + drm_panel_remove(&ctx->panel); + } + +diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c +index 85c4bb186203c..061396e7fa0f9 100644 +--- a/drivers/gpu/drm/radeon/radeon_atombios.c ++++ b/drivers/gpu/drm/radeon/radeon_atombios.c +@@ -922,8 +922,12 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct + max_device = ATOM_MAX_SUPPORTED_DEVICE_INFO; + + for (i = 0; i < max_device; i++) { +- ATOM_CONNECTOR_INFO_I2C ci = +- supported_devices->info.asConnInfo[i]; ++ ATOM_CONNECTOR_INFO_I2C ci; ++ ++ if (frev > 1) ++ ci = supported_devices->info_2d1.asConnInfo[i]; ++ else ++ ci = supported_devices->info.asConnInfo[i]; + + bios_connectors[i].valid = false; + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c +index c52c7bf1485b1..717d624e9a052 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c +@@ -456,8 +456,10 @@ int vmw_bo_cpu_blit(struct ttm_buffer_object *dst, + .no_wait_gpu = false + }; + u32 j, initial_line = dst_offset / dst_stride; +- struct vmw_bo_blit_line_data d; ++ struct vmw_bo_blit_line_data d = {0}; + int ret = 0; ++ struct page **dst_pages = NULL; ++ struct page **src_pages = NULL; + + /* Buffer objects need to be either pinned or reserved: */ + if (!(dst->pin_count)) +@@ -477,12 +479,35 @@ int vmw_bo_cpu_blit(struct ttm_buffer_object *dst, + return ret; + } + ++ if (!src->ttm->pages && src->ttm->sg) { ++ src_pages = kvmalloc_array(src->ttm->num_pages, ++ sizeof(struct page *), GFP_KERNEL); ++ if (!src_pages) ++ return -ENOMEM; ++ ret = drm_prime_sg_to_page_array(src->ttm->sg, src_pages, ++ src->ttm->num_pages); ++ if (ret) ++ goto out; ++ } ++ if (!dst->ttm->pages && dst->ttm->sg) { ++ dst_pages = kvmalloc_array(dst->ttm->num_pages, ++ sizeof(struct page *), GFP_KERNEL); ++ if (!dst_pages) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ret = drm_prime_sg_to_page_array(dst->ttm->sg, dst_pages, ++ dst->ttm->num_pages); ++ if (ret) ++ goto out; ++ } ++ + d.mapped_dst = 0; + d.mapped_src = 0; + d.dst_addr = NULL; + d.src_addr = NULL; +- d.dst_pages = dst->ttm->pages; +- d.src_pages = src->ttm->pages; ++ d.dst_pages = dst->ttm->pages ? dst->ttm->pages : dst_pages; ++ d.src_pages = src->ttm->pages ? src->ttm->pages : src_pages; + d.dst_num_pages = PFN_UP(dst->resource->size); + d.src_num_pages = PFN_UP(src->resource->size); + d.dst_prot = ttm_io_prot(dst, dst->resource, PAGE_KERNEL); +@@ -504,6 +529,10 @@ int vmw_bo_cpu_blit(struct ttm_buffer_object *dst, + kunmap_atomic(d.src_addr); + if (d.dst_addr) + kunmap_atomic(d.dst_addr); ++ if (src_pages) ++ kvfree(src_pages); ++ if (dst_pages) ++ kvfree(dst_pages); + + return ret; + } +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c +index 2bfac3aad7b7d..4aac88cc5f913 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c +@@ -377,7 +377,8 @@ static int vmw_bo_init(struct vmw_private *dev_priv, + { + struct ttm_operation_ctx ctx = { + .interruptible = params->bo_type != ttm_bo_type_kernel, +- .no_wait_gpu = false ++ .no_wait_gpu = false, ++ .resv = params->resv, + }; + struct ttm_device *bdev = &dev_priv->bdev; + struct drm_device *vdev = &dev_priv->drm; +@@ -394,8 +395,8 @@ static int vmw_bo_init(struct vmw_private *dev_priv, + + vmw_bo_placement_set(vmw_bo, params->domain, params->busy_domain); + ret = ttm_bo_init_reserved(bdev, &vmw_bo->tbo, params->bo_type, +- &vmw_bo->placement, 0, &ctx, NULL, +- NULL, destroy); ++ &vmw_bo->placement, 0, &ctx, ++ params->sg, params->resv, destroy); + if (unlikely(ret)) + return ret; + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h +index 0d496dc9c6af7..f349642e6190d 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h +@@ -55,6 +55,8 @@ struct vmw_bo_params { + enum ttm_bo_type bo_type; + size_t size; + bool pin; ++ struct dma_resv *resv; ++ struct sg_table *sg; + }; + + /** +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +index 0a304706e0132..58fb40c93100a 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +@@ -1628,6 +1628,7 @@ static const struct drm_driver driver = { + + .prime_fd_to_handle = vmw_prime_fd_to_handle, + .prime_handle_to_fd = vmw_prime_handle_to_fd, ++ .gem_prime_import_sg_table = vmw_prime_import_sg_table, + + .fops = &vmwgfx_driver_fops, + .name = VMWGFX_DRIVER_NAME, +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +index 3cd5090dedfc5..6acc7ad0e9eb8 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +@@ -1131,6 +1131,9 @@ extern int vmw_prime_handle_to_fd(struct drm_device *dev, + struct drm_file *file_priv, + uint32_t handle, uint32_t flags, + int *prime_fd); ++struct drm_gem_object *vmw_prime_import_sg_table(struct drm_device *dev, ++ struct dma_buf_attachment *attach, ++ struct sg_table *table); + + /* + * MemoryOBject management - vmwgfx_mob.c +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c +index 12787bb9c111d..d6bcaf078b1f4 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c +@@ -149,6 +149,38 @@ int vmw_gem_object_create_with_handle(struct vmw_private *dev_priv, + return ret; + } + ++struct drm_gem_object *vmw_prime_import_sg_table(struct drm_device *dev, ++ struct dma_buf_attachment *attach, ++ struct sg_table *table) ++{ ++ int ret; ++ struct vmw_private *dev_priv = vmw_priv(dev); ++ struct drm_gem_object *gem = NULL; ++ struct vmw_bo *vbo; ++ struct vmw_bo_params params = { ++ .domain = (dev_priv->has_mob) ? VMW_BO_DOMAIN_SYS : VMW_BO_DOMAIN_VRAM, ++ .busy_domain = VMW_BO_DOMAIN_SYS, ++ .bo_type = ttm_bo_type_sg, ++ .size = attach->dmabuf->size, ++ .pin = false, ++ .resv = attach->dmabuf->resv, ++ .sg = table, ++ ++ }; ++ ++ dma_resv_lock(params.resv, NULL); ++ ++ ret = vmw_bo_create(dev_priv, ¶ms, &vbo); ++ if (ret != 0) ++ goto out_no_bo; ++ ++ vbo->tbo.base.funcs = &vmw_gem_object_funcs; ++ ++ gem = &vbo->tbo.base; ++out_no_bo: ++ dma_resv_unlock(params.resv); ++ return gem; ++} + + int vmw_gem_object_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +index 5681a1b42aa24..a884072851322 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +@@ -926,6 +926,7 @@ int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, + int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state) + { ++ struct vmw_private *vmw = vmw_priv(crtc->dev); + struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state, + crtc); + struct vmw_display_unit *du = vmw_crtc_to_du(new_state->crtc); +@@ -933,9 +934,13 @@ int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, + bool has_primary = new_state->plane_mask & + drm_plane_mask(crtc->primary); + +- /* We always want to have an active plane with an active CRTC */ +- if (has_primary != new_state->enable) +- return -EINVAL; ++ /* ++ * This is fine in general, but broken userspace might expect ++ * some actual rendering so give a clue as why it's blank. ++ */ ++ if (new_state->enable && !has_primary) ++ drm_dbg_driver(&vmw->drm, ++ "CRTC without a primary plane will be blank.\n"); + + + if (new_state->connector_mask != connector_mask && +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +index db81e635dc061..9fda4f4ec7a97 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +@@ -243,10 +243,10 @@ struct vmw_framebuffer_bo { + + + static const uint32_t __maybe_unused vmw_primary_plane_formats[] = { +- DRM_FORMAT_XRGB1555, +- DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, ++ DRM_FORMAT_RGB565, ++ DRM_FORMAT_XRGB1555, + }; + + static const uint32_t __maybe_unused vmw_cursor_plane_formats[] = { +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_prime.c b/drivers/gpu/drm/vmwgfx/vmwgfx_prime.c +index 2d72a5ee7c0c7..c99cad4449915 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_prime.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_prime.c +@@ -75,8 +75,12 @@ int vmw_prime_fd_to_handle(struct drm_device *dev, + int fd, u32 *handle) + { + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; ++ int ret = ttm_prime_fd_to_handle(tfile, fd, handle); + +- return ttm_prime_fd_to_handle(tfile, fd, handle); ++ if (ret) ++ ret = drm_gem_prime_fd_to_handle(dev, file_priv, fd, handle); ++ ++ return ret; + } + + int vmw_prime_handle_to_fd(struct drm_device *dev, +@@ -85,5 +89,12 @@ int vmw_prime_handle_to_fd(struct drm_device *dev, + int *prime_fd) + { + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; +- return ttm_prime_handle_to_fd(tfile, handle, flags, prime_fd); ++ int ret; ++ ++ if (handle > VMWGFX_NUM_MOB) ++ ret = ttm_prime_handle_to_fd(tfile, handle, flags, prime_fd); ++ else ++ ret = drm_gem_prime_handle_to_fd(dev, file_priv, handle, flags, prime_fd); ++ ++ return ret; + } +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c +index af8562c95cc35..fcb87d83760ef 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c +@@ -220,13 +220,18 @@ static int vmw_ttm_map_dma(struct vmw_ttm_tt *vmw_tt) + switch (dev_priv->map_mode) { + case vmw_dma_map_bind: + case vmw_dma_map_populate: +- vsgt->sgt = &vmw_tt->sgt; +- ret = sg_alloc_table_from_pages_segment( +- &vmw_tt->sgt, vsgt->pages, vsgt->num_pages, 0, +- (unsigned long)vsgt->num_pages << PAGE_SHIFT, +- dma_get_max_seg_size(dev_priv->drm.dev), GFP_KERNEL); +- if (ret) +- goto out_sg_alloc_fail; ++ if (vmw_tt->dma_ttm.page_flags & TTM_TT_FLAG_EXTERNAL) { ++ vsgt->sgt = vmw_tt->dma_ttm.sg; ++ } else { ++ vsgt->sgt = &vmw_tt->sgt; ++ ret = sg_alloc_table_from_pages_segment(&vmw_tt->sgt, ++ vsgt->pages, vsgt->num_pages, 0, ++ (unsigned long)vsgt->num_pages << PAGE_SHIFT, ++ dma_get_max_seg_size(dev_priv->drm.dev), ++ GFP_KERNEL); ++ if (ret) ++ goto out_sg_alloc_fail; ++ } + + ret = vmw_ttm_map_for_dma(vmw_tt); + if (unlikely(ret != 0)) +@@ -241,8 +246,9 @@ static int vmw_ttm_map_dma(struct vmw_ttm_tt *vmw_tt) + return 0; + + out_map_fail: +- sg_free_table(vmw_tt->vsgt.sgt); +- vmw_tt->vsgt.sgt = NULL; ++ drm_warn(&dev_priv->drm, "VSG table map failed!"); ++ sg_free_table(vsgt->sgt); ++ vsgt->sgt = NULL; + out_sg_alloc_fail: + return ret; + } +@@ -388,15 +394,17 @@ static void vmw_ttm_destroy(struct ttm_device *bdev, struct ttm_tt *ttm) + static int vmw_ttm_populate(struct ttm_device *bdev, + struct ttm_tt *ttm, struct ttm_operation_ctx *ctx) + { +- int ret; ++ bool external = (ttm->page_flags & TTM_TT_FLAG_EXTERNAL) != 0; + +- /* TODO: maybe completely drop this ? */ + if (ttm_tt_is_populated(ttm)) + return 0; + +- ret = ttm_pool_alloc(&bdev->pool, ttm, ctx); ++ if (external && ttm->sg) ++ return drm_prime_sg_to_dma_addr_array(ttm->sg, ++ ttm->dma_address, ++ ttm->num_pages); + +- return ret; ++ return ttm_pool_alloc(&bdev->pool, ttm, ctx); + } + + static void vmw_ttm_unpopulate(struct ttm_device *bdev, +@@ -404,6 +412,10 @@ static void vmw_ttm_unpopulate(struct ttm_device *bdev, + { + struct vmw_ttm_tt *vmw_tt = container_of(ttm, struct vmw_ttm_tt, + dma_ttm); ++ bool external = (ttm->page_flags & TTM_TT_FLAG_EXTERNAL) != 0; ++ ++ if (external) ++ return; + + vmw_ttm_unbind(bdev, ttm); + +@@ -422,6 +434,7 @@ static struct ttm_tt *vmw_ttm_tt_create(struct ttm_buffer_object *bo, + { + struct vmw_ttm_tt *vmw_be; + int ret; ++ bool external = bo->type == ttm_bo_type_sg; + + vmw_be = kzalloc(sizeof(*vmw_be), GFP_KERNEL); + if (!vmw_be) +@@ -430,7 +443,10 @@ static struct ttm_tt *vmw_ttm_tt_create(struct ttm_buffer_object *bo, + vmw_be->dev_priv = vmw_priv_from_ttm(bo->bdev); + vmw_be->mob = NULL; + +- if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) ++ if (external) ++ page_flags |= TTM_TT_FLAG_EXTERNAL | TTM_TT_FLAG_EXTERNAL_MAPPABLE; ++ ++ if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent || external) + ret = ttm_sg_tt_init(&vmw_be->dma_ttm, bo, page_flags, + ttm_cached); + else +diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c +index bf0df6ee4f785..07fb8d3c037f0 100644 +--- a/drivers/infiniband/core/cm.c ++++ b/drivers/infiniband/core/cm.c +@@ -1026,23 +1026,26 @@ static void cm_reset_to_idle(struct cm_id_private *cm_id_priv) + } + } + +-static noinline void cm_destroy_id_wait_timeout(struct ib_cm_id *cm_id) ++static noinline void cm_destroy_id_wait_timeout(struct ib_cm_id *cm_id, ++ enum ib_cm_state old_state) + { + struct cm_id_private *cm_id_priv; + + cm_id_priv = container_of(cm_id, struct cm_id_private, id); +- pr_err("%s: cm_id=%p timed out. state=%d refcnt=%d\n", __func__, +- cm_id, cm_id->state, refcount_read(&cm_id_priv->refcount)); ++ pr_err("%s: cm_id=%p timed out. state %d -> %d, refcnt=%d\n", __func__, ++ cm_id, old_state, cm_id->state, refcount_read(&cm_id_priv->refcount)); + } + + static void cm_destroy_id(struct ib_cm_id *cm_id, int err) + { + struct cm_id_private *cm_id_priv; ++ enum ib_cm_state old_state; + struct cm_work *work; + int ret; + + cm_id_priv = container_of(cm_id, struct cm_id_private, id); + spin_lock_irq(&cm_id_priv->lock); ++ old_state = cm_id->state; + retest: + switch (cm_id->state) { + case IB_CM_LISTEN: +@@ -1151,7 +1154,7 @@ static void cm_destroy_id(struct ib_cm_id *cm_id, int err) + msecs_to_jiffies( + CM_DESTROY_ID_WAIT_TIMEOUT)); + if (!ret) /* timeout happened */ +- cm_destroy_id_wait_timeout(cm_id); ++ cm_destroy_id_wait_timeout(cm_id, old_state); + } while (!ret); + + while ((work = cm_dequeue_work(cm_id_priv)) != NULL) +diff --git a/drivers/infiniband/hw/mlx5/mad.c b/drivers/infiniband/hw/mlx5/mad.c +index 8102ef113b7e0..64dae68c43e62 100644 +--- a/drivers/infiniband/hw/mlx5/mad.c ++++ b/drivers/infiniband/hw/mlx5/mad.c +@@ -188,7 +188,8 @@ static int process_pma_cmd(struct mlx5_ib_dev *dev, u32 port_num, + mdev = dev->mdev; + mdev_port_num = 1; + } +- if (MLX5_CAP_GEN(dev->mdev, num_ports) == 1) { ++ if (MLX5_CAP_GEN(dev->mdev, num_ports) == 1 && ++ !mlx5_core_mp_enabled(mdev)) { + /* set local port to one for Function-Per-Port HCA. */ + mdev = dev->mdev; + mdev_port_num = 1; +diff --git a/drivers/infiniband/sw/rxe/rxe.c b/drivers/infiniband/sw/rxe/rxe.c +index 54c723a6eddac..6f9ec8db014c7 100644 +--- a/drivers/infiniband/sw/rxe/rxe.c ++++ b/drivers/infiniband/sw/rxe/rxe.c +@@ -33,6 +33,8 @@ void rxe_dealloc(struct ib_device *ib_dev) + + if (rxe->tfm) + crypto_free_shash(rxe->tfm); ++ ++ mutex_destroy(&rxe->usdev_lock); + } + + /* initialize rxe device parameters */ +diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c +index 50bac2d79d9b5..68edb07d4443e 100644 +--- a/drivers/interconnect/core.c ++++ b/drivers/interconnect/core.c +@@ -176,6 +176,8 @@ static struct icc_path *path_init(struct device *dev, struct icc_node *dst, + + path->num_nodes = num_nodes; + ++ mutex_lock(&icc_bw_lock); ++ + for (i = num_nodes - 1; i >= 0; i--) { + node->provider->users++; + hlist_add_head(&path->reqs[i].req_node, &node->req_list); +@@ -186,6 +188,8 @@ static struct icc_path *path_init(struct device *dev, struct icc_node *dst, + node = node->reverse; + } + ++ mutex_unlock(&icc_bw_lock); ++ + return path; + } + +@@ -792,12 +796,16 @@ void icc_put(struct icc_path *path) + pr_err("%s: error (%d)\n", __func__, ret); + + mutex_lock(&icc_lock); ++ mutex_lock(&icc_bw_lock); ++ + for (i = 0; i < path->num_nodes; i++) { + node = path->reqs[i].node; + hlist_del(&path->reqs[i].req_node); + if (!WARN_ON(!node->provider->users)) + node->provider->users--; + } ++ ++ mutex_unlock(&icc_bw_lock); + mutex_unlock(&icc_lock); + + kfree_const(path->name); +diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c +index cf6727d9c81f3..468191438849e 100644 +--- a/drivers/media/common/videobuf2/videobuf2-core.c ++++ b/drivers/media/common/videobuf2/videobuf2-core.c +@@ -2648,9 +2648,14 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) + return -EBUSY; + + /* +- * Start with count 1, driver can increase it in queue_setup() ++ * Start with q->min_buffers_needed + 1, driver can increase it in ++ * queue_setup() ++ * ++ * 'min_buffers_needed' buffers need to be queued up before you ++ * can start streaming, plus 1 for userspace (or in this case, ++ * kernelspace) processing. + */ +- count = 1; ++ count = max(2, q->min_buffers_needed + 1); + + dprintk(q, 3, "setting up file io: mode %s, count %d, read_once %d, write_immediately %d\n", + (read) ? "read" : "write", count, q->fileio_read_once, +diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c +index 8cf636c540322..bd4e3df44865e 100644 +--- a/drivers/misc/mei/pci-me.c ++++ b/drivers/misc/mei/pci-me.c +@@ -116,7 +116,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { + {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_P, MEI_ME_PCH15_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ADP_N, MEI_ME_PCH15_CFG)}, + +- {MEI_PCI_DEVICE(MEI_DEV_ID_RPL_S, MEI_ME_PCH15_CFG)}, ++ {MEI_PCI_DEVICE(MEI_DEV_ID_RPL_S, MEI_ME_PCH15_SPS_CFG)}, + + {MEI_PCI_DEVICE(MEI_DEV_ID_MTL_M, MEI_ME_PCH15_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ARL_S, MEI_ME_PCH15_CFG)}, +diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c +index 88f081672f6fb..53ead0989777f 100644 +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1948,14 +1948,16 @@ mt7530_port_vlan_del(struct dsa_switch *ds, int port, + + static int mt753x_mirror_port_get(unsigned int id, u32 val) + { +- return (id == ID_MT7531) ? MT7531_MIRROR_PORT_GET(val) : +- MIRROR_PORT(val); ++ return (id == ID_MT7531 || id == ID_MT7988) ? ++ MT7531_MIRROR_PORT_GET(val) : ++ MIRROR_PORT(val); + } + + static int mt753x_mirror_port_set(unsigned int id, u32 val) + { +- return (id == ID_MT7531) ? MT7531_MIRROR_PORT_SET(val) : +- MIRROR_PORT(val); ++ return (id == ID_MT7531 || id == ID_MT7988) ? ++ MT7531_MIRROR_PORT_SET(val) : ++ MIRROR_PORT(val); + } + + static int mt753x_port_mirror_add(struct dsa_switch *ds, int port, +@@ -2470,8 +2472,6 @@ mt7530_setup(struct dsa_switch *ds) + SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | + SYS_CTRL_REG_RST); + +- mt7530_pll_setup(priv); +- + /* Lower Tx driving for TRGMII path */ + for (i = 0; i < NUM_TRGMII_CTRL; i++) + mt7530_write(priv, MT7530_TRGMII_TD_ODT(i), +@@ -2489,6 +2489,9 @@ mt7530_setup(struct dsa_switch *ds) + + priv->p6_interface = PHY_INTERFACE_MODE_NA; + ++ if ((val & HWTRAP_XTAL_MASK) == HWTRAP_XTAL_40MHZ) ++ mt7530_pll_setup(priv); ++ + mt753x_trap_frames(priv); + + /* Enable and reset MIB counters */ +@@ -2518,6 +2521,9 @@ mt7530_setup(struct dsa_switch *ds) + PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); + } + ++ /* Allow mirroring frames received on the local port (monitor port). */ ++ mt7530_set(priv, MT753X_AGC, LOCAL_EN); ++ + /* Setup VLAN ID 0 for VLAN-unaware bridges */ + ret = mt7530_setup_vlan0(priv); + if (ret) +@@ -2626,6 +2632,9 @@ mt7531_setup_common(struct dsa_switch *ds) + PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); + } + ++ /* Allow mirroring frames received on the local port (monitor port). */ ++ mt7530_set(priv, MT753X_AGC, LOCAL_EN); ++ + /* Flush the FDB table */ + ret = mt7530_fdb_cmd(priv, MT7530_FDB_FLUSH, NULL); + if (ret < 0) +@@ -2704,18 +2713,25 @@ mt7531_setup(struct dsa_switch *ds) + priv->p5_interface = PHY_INTERFACE_MODE_NA; + priv->p6_interface = PHY_INTERFACE_MODE_NA; + +- /* Enable PHY core PLL, since phy_device has not yet been created +- * provided for phy_[read,write]_mmd_indirect is called, we provide +- * our own mt7531_ind_mmd_phy_[read,write] to complete this +- * function. ++ /* Enable Energy-Efficient Ethernet (EEE) and PHY core PLL, since ++ * phy_device has not yet been created provided for ++ * phy_[read,write]_mmd_indirect is called, we provide our own ++ * mt7531_ind_mmd_phy_[read,write] to complete this function. + */ + val = mt7531_ind_c45_phy_read(priv, MT753X_CTRL_PHY_ADDR, + MDIO_MMD_VEND2, CORE_PLL_GROUP4); +- val |= MT7531_PHY_PLL_BYPASS_MODE; ++ val |= MT7531_RG_SYSPLL_DMY2 | MT7531_PHY_PLL_BYPASS_MODE; + val &= ~MT7531_PHY_PLL_OFF; + mt7531_ind_c45_phy_write(priv, MT753X_CTRL_PHY_ADDR, MDIO_MMD_VEND2, + CORE_PLL_GROUP4, val); + ++ /* Disable EEE advertisement on the switch PHYs. */ ++ for (i = MT753X_CTRL_PHY_ADDR; ++ i < MT753X_CTRL_PHY_ADDR + MT7530_NUM_PHYS; i++) { ++ mt7531_ind_c45_phy_write(priv, i, MDIO_MMD_AN, MDIO_AN_EEE_ADV, ++ 0); ++ } ++ + mt7531_setup_common(ds); + + /* Setup VLAN ID 0 for VLAN-unaware bridges */ +diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h +index ddefeb69afda1..0ad52d3cbfebb 100644 +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -32,6 +32,10 @@ enum mt753x_id { + #define SYSC_REG_RSTCTRL 0x34 + #define RESET_MCM BIT(2) + ++/* Register for ARL global control */ ++#define MT753X_AGC 0xc ++#define LOCAL_EN BIT(7) ++ + /* Registers to mac forward control for unknown frames */ + #define MT7530_MFC 0x10 + #define BC_FFP(x) (((x) & 0xff) << 24) +@@ -630,6 +634,7 @@ enum mt7531_clk_skew { + #define RG_SYSPLL_DDSFBK_EN BIT(12) + #define RG_SYSPLL_BIAS_EN BIT(11) + #define RG_SYSPLL_BIAS_LPF_EN BIT(10) ++#define MT7531_RG_SYSPLL_DMY2 BIT(6) + #define MT7531_PHY_PLL_OFF BIT(5) + #define MT7531_PHY_PLL_BYPASS_MODE BIT(4) + +diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c b/drivers/net/ethernet/intel/ice/ice_tc_lib.c +index dd03cb69ad26b..76ad5930c0102 100644 +--- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c ++++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c +@@ -28,6 +28,8 @@ ice_tc_count_lkups(u32 flags, struct ice_tc_flower_lyr_2_4_hdrs *headers, + * - ICE_TC_FLWR_FIELD_VLAN_TPID (present if specified) + * - Tunnel flag (present if tunnel) + */ ++ if (fltr->direction == ICE_ESWITCH_FLTR_EGRESS) ++ lkups_cnt++; + + if (flags & ICE_TC_FLWR_FIELD_TENANT_ID) + lkups_cnt++; +@@ -363,6 +365,11 @@ ice_tc_fill_rules(struct ice_hw *hw, u32 flags, + /* Always add direction metadata */ + ice_rule_add_direction_metadata(&list[ICE_TC_METADATA_LKUP_IDX]); + ++ if (tc_fltr->direction == ICE_ESWITCH_FLTR_EGRESS) { ++ ice_rule_add_src_vsi_metadata(&list[i]); ++ i++; ++ } ++ + rule_info->tun_type = ice_sw_type_from_tunnel(tc_fltr->tunnel_type); + if (tc_fltr->tunnel_type != TNL_LAST) { + i = ice_tc_fill_tunnel_outer(flags, tc_fltr, list, i); +@@ -731,7 +738,7 @@ ice_eswitch_add_tc_fltr(struct ice_vsi *vsi, struct ice_tc_flower_fltr *fltr) + int ret; + int i; + +- if (!flags || (flags & ICE_TC_FLWR_FIELD_ENC_SRC_L4_PORT)) { ++ if (flags & ICE_TC_FLWR_FIELD_ENC_SRC_L4_PORT) { + NL_SET_ERR_MSG_MOD(fltr->extack, "Unsupported encap field(s)"); + return -EOPNOTSUPP; + } +@@ -779,6 +786,7 @@ ice_eswitch_add_tc_fltr(struct ice_vsi *vsi, struct ice_tc_flower_fltr *fltr) + + /* specify the cookie as filter_rule_id */ + rule_info.fltr_rule_id = fltr->cookie; ++ rule_info.src_vsi = vsi->idx; + + ret = ice_add_adv_rule(hw, list, lkups_cnt, &rule_info, &rule_added); + if (ret == -EEXIST) { +@@ -1440,7 +1448,10 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi, + (BIT_ULL(FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_ENC_KEYID) | +- BIT_ULL(FLOW_DISSECTOR_KEY_ENC_PORTS))) { ++ BIT_ULL(FLOW_DISSECTOR_KEY_ENC_PORTS) | ++ BIT_ULL(FLOW_DISSECTOR_KEY_ENC_IP) | ++ BIT_ULL(FLOW_DISSECTOR_KEY_ENC_OPTS) | ++ BIT_ULL(FLOW_DISSECTOR_KEY_ENC_CONTROL))) { + NL_SET_ERR_MSG_MOD(fltr->extack, "Tunnel key used, but device isn't a tunnel"); + return -EOPNOTSUPP; + } else { +diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +index 423ce54eaea69..46bdbee9d38ad 100644 +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +@@ -588,6 +588,7 @@ static int otx2_tc_prepare_flow(struct otx2_nic *nic, struct otx2_tc_flow *node, + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { + struct flow_match_control match; ++ u32 val; + + flow_rule_match_control(rule, &match); + if (match.mask->flags & FLOW_DIS_FIRST_FRAG) { +@@ -596,12 +597,14 @@ static int otx2_tc_prepare_flow(struct otx2_nic *nic, struct otx2_tc_flow *node, + } + + if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) { ++ val = match.key->flags & FLOW_DIS_IS_FRAGMENT; + if (ntohs(flow_spec->etype) == ETH_P_IP) { +- flow_spec->ip_flag = IPV4_FLAG_MORE; ++ flow_spec->ip_flag = val ? IPV4_FLAG_MORE : 0; + flow_mask->ip_flag = IPV4_FLAG_MORE; + req->features |= BIT_ULL(NPC_IPFRAG_IPV4); + } else if (ntohs(flow_spec->etype) == ETH_P_IPV6) { +- flow_spec->next_header = IPPROTO_FRAGMENT; ++ flow_spec->next_header = val ? ++ IPPROTO_FRAGMENT : 0; + flow_mask->next_header = 0xff; + req->features |= BIT_ULL(NPC_IPFRAG_IPV6); + } else { +diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c +index 94376aa2b34c5..c7196055c8c98 100644 +--- a/drivers/net/ethernet/mediatek/mtk_wed.c ++++ b/drivers/net/ethernet/mediatek/mtk_wed.c +@@ -598,13 +598,13 @@ mtk_wed_dma_disable(struct mtk_wed_device *dev) + static void + mtk_wed_stop(struct mtk_wed_device *dev) + { ++ mtk_wed_dma_disable(dev); + mtk_wed_set_ext_int(dev, false); + + wed_w32(dev, MTK_WED_WPDMA_INT_TRIGGER, 0); + wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, 0); + wdma_w32(dev, MTK_WDMA_INT_MASK, 0); + wdma_w32(dev, MTK_WDMA_INT_GRP2, 0); +- wed_w32(dev, MTK_WED_WPDMA_INT_MASK, 0); + + if (dev->hw->version == 1) + return; +@@ -617,7 +617,6 @@ static void + mtk_wed_deinit(struct mtk_wed_device *dev) + { + mtk_wed_stop(dev); +- mtk_wed_dma_disable(dev); + + wed_clr(dev, MTK_WED_CTRL, + MTK_WED_CTRL_WDMA_INT_AGENT_EN | +@@ -1703,9 +1702,6 @@ mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask) + static void + mtk_wed_irq_set_mask(struct mtk_wed_device *dev, u32 mask) + { +- if (!dev->running) +- return; +- + mtk_wed_set_ext_int(dev, !!mask); + wed_w32(dev, MTK_WED_INT_MASK, mask); + } +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +index e66f486faafe1..415fec7763bd2 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/en_arfs.c +@@ -45,6 +45,10 @@ struct arfs_table { + struct hlist_head rules_hash[ARFS_HASH_SIZE]; + }; + ++enum { ++ MLX5E_ARFS_STATE_ENABLED, ++}; ++ + enum arfs_type { + ARFS_IPV4_TCP, + ARFS_IPV6_TCP, +@@ -59,6 +63,7 @@ struct mlx5e_arfs_tables { + spinlock_t arfs_lock; + int last_filter_id; + struct workqueue_struct *wq; ++ unsigned long state; + }; + + struct arfs_tuple { +@@ -169,6 +174,8 @@ int mlx5e_arfs_enable(struct mlx5e_flow_steering *fs) + return err; + } + } ++ set_bit(MLX5E_ARFS_STATE_ENABLED, &arfs->state); ++ + return 0; + } + +@@ -454,6 +461,8 @@ static void arfs_del_rules(struct mlx5e_flow_steering *fs) + int i; + int j; + ++ clear_bit(MLX5E_ARFS_STATE_ENABLED, &arfs->state); ++ + spin_lock_bh(&arfs->arfs_lock); + mlx5e_for_each_arfs_rule(rule, htmp, arfs->arfs_tables, i, j) { + hlist_del_init(&rule->hlist); +@@ -626,17 +635,8 @@ static void arfs_handle_work(struct work_struct *work) + struct mlx5_flow_handle *rule; + + arfs = mlx5e_fs_get_arfs(priv->fs); +- mutex_lock(&priv->state_lock); +- if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { +- spin_lock_bh(&arfs->arfs_lock); +- hlist_del(&arfs_rule->hlist); +- spin_unlock_bh(&arfs->arfs_lock); +- +- mutex_unlock(&priv->state_lock); +- kfree(arfs_rule); +- goto out; +- } +- mutex_unlock(&priv->state_lock); ++ if (!test_bit(MLX5E_ARFS_STATE_ENABLED, &arfs->state)) ++ return; + + if (!arfs_rule->rule) { + rule = arfs_add_rule(priv, arfs_rule); +@@ -752,6 +752,11 @@ int mlx5e_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, + return -EPROTONOSUPPORT; + + spin_lock_bh(&arfs->arfs_lock); ++ if (!test_bit(MLX5E_ARFS_STATE_ENABLED, &arfs->state)) { ++ spin_unlock_bh(&arfs->arfs_lock); ++ return -EPERM; ++ } ++ + arfs_rule = arfs_find_rule(arfs_t, &fk); + if (arfs_rule) { + if (arfs_rule->rxq == rxq_index || work_busy(&arfs_rule->arfs_work)) { +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +index 3047d7015c525..1789800faaeb6 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +@@ -1868,6 +1868,7 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) + if (err) + goto abort; + ++ dev->priv.eswitch = esw; + err = esw_offloads_init(esw); + if (err) + goto reps_err; +@@ -1892,11 +1893,6 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) + esw->offloads.encap = DEVLINK_ESWITCH_ENCAP_MODE_BASIC; + else + esw->offloads.encap = DEVLINK_ESWITCH_ENCAP_MODE_NONE; +- if (MLX5_ESWITCH_MANAGER(dev) && +- mlx5_esw_vport_match_metadata_supported(esw)) +- esw->flags |= MLX5_ESWITCH_VPORT_MATCH_METADATA; +- +- dev->priv.eswitch = esw; + BLOCKING_INIT_NOTIFIER_HEAD(&esw->n_head); + + esw_info(dev, +@@ -1908,6 +1904,7 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) + + reps_err: + mlx5_esw_vports_cleanup(esw); ++ dev->priv.eswitch = NULL; + abort: + if (esw->work_queue) + destroy_workqueue(esw->work_queue); +@@ -1926,7 +1923,6 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) + + esw_info(esw->dev, "cleanup\n"); + +- esw->dev->priv.eswitch = NULL; + destroy_workqueue(esw->work_queue); + WARN_ON(refcount_read(&esw->qos.refcnt)); + mutex_destroy(&esw->state_lock); +@@ -1937,6 +1933,7 @@ void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) + mutex_destroy(&esw->offloads.encap_tbl_lock); + mutex_destroy(&esw->offloads.decap_tbl_lock); + esw_offloads_cleanup(esw); ++ esw->dev->priv.eswitch = NULL; + mlx5_esw_vports_cleanup(esw); + debugfs_remove_recursive(esw->debugfs_root); + devl_params_unregister(priv_to_devlink(esw->dev), mlx5_eswitch_params, +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +index baaae628b0a0f..e3cce110e52fd 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +@@ -2476,6 +2476,10 @@ int esw_offloads_init(struct mlx5_eswitch *esw) + if (err) + return err; + ++ if (MLX5_ESWITCH_MANAGER(esw->dev) && ++ mlx5_esw_vport_match_metadata_supported(esw)) ++ esw->flags |= MLX5_ESWITCH_VPORT_MATCH_METADATA; ++ + err = devl_params_register(priv_to_devlink(esw->dev), + esw_devlink_params, + ARRAY_SIZE(esw_devlink_params)); +diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +index af3fac090b828..e51cac1e1811e 100644 +--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c ++++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c +@@ -703,8 +703,10 @@ int mlx5_deactivate_lag(struct mlx5_lag *ldev) + return err; + } + +- if (test_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, &flags)) ++ if (test_bit(MLX5_LAG_MODE_FLAG_HASH_BASED, &flags)) { + mlx5_lag_port_sel_destroy(ldev); ++ ldev->buckets = 1; ++ } + if (mlx5_lag_has_drop_rule(ldev)) + mlx5_lag_drop_rule_cleanup(ldev); + +diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c +index 523e0c470894f..55f255a3c9db6 100644 +--- a/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c ++++ b/drivers/net/ethernet/microchip/sparx5/sparx5_tc_flower.c +@@ -36,6 +36,27 @@ struct sparx5_tc_flower_template { + u16 l3_proto; /* protocol specified in the template */ + }; + ++/* SparX-5 VCAP fragment types: ++ * 0 = no fragment, 1 = initial fragment, ++ * 2 = suspicious fragment, 3 = valid follow-up fragment ++ */ ++enum { /* key / mask */ ++ FRAG_NOT = 0x03, /* 0 / 3 */ ++ FRAG_SOME = 0x11, /* 1 / 1 */ ++ FRAG_FIRST = 0x13, /* 1 / 3 */ ++ FRAG_LATER = 0x33, /* 3 / 3 */ ++ FRAG_INVAL = 0xff, /* invalid */ ++}; ++ ++/* Flower fragment flag to VCAP fragment type mapping */ ++static const u8 sparx5_vcap_frag_map[4][4] = { /* is_frag */ ++ { FRAG_INVAL, FRAG_INVAL, FRAG_INVAL, FRAG_FIRST }, /* 0/0 */ ++ { FRAG_NOT, FRAG_NOT, FRAG_INVAL, FRAG_INVAL }, /* 0/1 */ ++ { FRAG_INVAL, FRAG_INVAL, FRAG_INVAL, FRAG_INVAL }, /* 1/0 */ ++ { FRAG_SOME, FRAG_LATER, FRAG_INVAL, FRAG_FIRST } /* 1/1 */ ++ /* 0/0 0/1 1/0 1/1 <-- first_frag */ ++}; ++ + static int + sparx5_tc_flower_es0_tpid(struct vcap_tc_flower_parse_usage *st) + { +@@ -145,29 +166,27 @@ sparx5_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st) + flow_rule_match_control(st->frule, &mt); + + if (mt.mask->flags) { +- if (mt.mask->flags & FLOW_DIS_FIRST_FRAG) { +- if (mt.key->flags & FLOW_DIS_FIRST_FRAG) { +- value = 1; /* initial fragment */ +- mask = 0x3; +- } else { +- if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { +- value = 3; /* follow up fragment */ +- mask = 0x3; +- } else { +- value = 0; /* no fragment */ +- mask = 0x3; +- } +- } +- } else { +- if (mt.mask->flags & FLOW_DIS_IS_FRAGMENT) { +- value = 3; /* follow up fragment */ +- mask = 0x3; +- } else { +- value = 0; /* no fragment */ +- mask = 0x3; +- } ++ u8 is_frag_key = !!(mt.key->flags & FLOW_DIS_IS_FRAGMENT); ++ u8 is_frag_mask = !!(mt.mask->flags & FLOW_DIS_IS_FRAGMENT); ++ u8 is_frag_idx = (is_frag_key << 1) | is_frag_mask; ++ ++ u8 first_frag_key = !!(mt.key->flags & FLOW_DIS_FIRST_FRAG); ++ u8 first_frag_mask = !!(mt.mask->flags & FLOW_DIS_FIRST_FRAG); ++ u8 first_frag_idx = (first_frag_key << 1) | first_frag_mask; ++ ++ /* Lookup verdict based on the 2 + 2 input bits */ ++ u8 vdt = sparx5_vcap_frag_map[is_frag_idx][first_frag_idx]; ++ ++ if (vdt == FRAG_INVAL) { ++ NL_SET_ERR_MSG_MOD(st->fco->common.extack, ++ "Match on invalid fragment flag combination"); ++ return -EINVAL; + } + ++ /* Extract VCAP fragment key and mask from verdict */ ++ value = (vdt >> 4) & 0x3; ++ mask = vdt & 0x3; ++ + err = vcap_rule_add_key_u32(st->vrule, + VCAP_KF_L3_FRAGMENT_TYPE, + value, mask); +diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h +index b0dd8adce3560..4dbc076f72d65 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/common.h ++++ b/drivers/net/ethernet/stmicro/stmmac/common.h +@@ -550,6 +550,7 @@ extern const struct stmmac_hwtimestamp stmmac_ptp; + extern const struct stmmac_mode_ops dwmac4_ring_mode_ops; + + struct mac_link { ++ u32 caps; + u32 speed_mask; + u32 speed10; + u32 speed100; +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +index 51f121f867457..63998d65fef8e 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +@@ -1096,6 +1096,8 @@ static struct mac_device_info *sun8i_dwmac_setup(void *ppriv) + + priv->dev->priv_flags |= IFF_UNICAST_FLT; + ++ mac->link.caps = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_10 | MAC_100 | MAC_1000; + /* The loopback bit seems to be re-set when link change + * Simply mask it each time + * Speed 10/100/1000 are set in BIT(2)/BIT(3) +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +index 3927609abc441..8555299443f4e 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +@@ -539,6 +539,8 @@ int dwmac1000_setup(struct stmmac_priv *priv) + if (mac->multicast_filter_bins) + mac->mcast_bits_log2 = ilog2(mac->multicast_filter_bins); + ++ mac->link.caps = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_10 | MAC_100 | MAC_1000; + mac->link.duplex = GMAC_CONTROL_DM; + mac->link.speed10 = GMAC_CONTROL_PS; + mac->link.speed100 = GMAC_CONTROL_PS | GMAC_CONTROL_FES; +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +index a6e8d7bd95886..7667d103cd0eb 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +@@ -175,6 +175,8 @@ int dwmac100_setup(struct stmmac_priv *priv) + dev_info(priv->device, "\tDWMAC100\n"); + + mac->pcsr = priv->ioaddr; ++ mac->link.caps = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_10 | MAC_100; + mac->link.duplex = MAC_CONTROL_F; + mac->link.speed10 = 0; + mac->link.speed100 = 0; +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +index 683c34e609638..4ead0ddf43a7a 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +@@ -70,7 +70,10 @@ static void dwmac4_core_init(struct mac_device_info *hw, + + static void dwmac4_phylink_get_caps(struct stmmac_priv *priv) + { +- priv->phylink_config.mac_capabilities |= MAC_2500FD; ++ if (priv->plat->tx_queues_to_use > 1) ++ priv->hw->link.caps &= ~(MAC_10HD | MAC_100HD | MAC_1000HD); ++ else ++ priv->hw->link.caps |= (MAC_10HD | MAC_100HD | MAC_1000HD); + } + + static void dwmac4_rx_queue_enable(struct mac_device_info *hw, +@@ -1347,6 +1350,8 @@ int dwmac4_setup(struct stmmac_priv *priv) + if (mac->multicast_filter_bins) + mac->mcast_bits_log2 = ilog2(mac->multicast_filter_bins); + ++ mac->link.caps = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD; + mac->link.duplex = GMAC_CONFIG_DM; + mac->link.speed10 = GMAC_CONFIG_PS; + mac->link.speed100 = GMAC_CONFIG_FES | GMAC_CONFIG_PS; +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +index 24c53b7255a2e..8bc317d2f7a61 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +@@ -47,14 +47,6 @@ static void dwxgmac2_core_init(struct mac_device_info *hw, + writel(XGMAC_INT_DEFAULT_EN, ioaddr + XGMAC_INT_EN); + } + +-static void xgmac_phylink_get_caps(struct stmmac_priv *priv) +-{ +- priv->phylink_config.mac_capabilities |= MAC_2500FD | MAC_5000FD | +- MAC_10000FD | MAC_25000FD | +- MAC_40000FD | MAC_50000FD | +- MAC_100000FD; +-} +- + static void dwxgmac2_set_mac(void __iomem *ioaddr, bool enable) + { + u32 tx = readl(ioaddr + XGMAC_TX_CONFIG); +@@ -1591,7 +1583,6 @@ static void dwxgmac3_fpe_configure(void __iomem *ioaddr, struct stmmac_fpe_cfg * + + const struct stmmac_ops dwxgmac210_ops = { + .core_init = dwxgmac2_core_init, +- .phylink_get_caps = xgmac_phylink_get_caps, + .set_mac = dwxgmac2_set_mac, + .rx_ipc = dwxgmac2_rx_ipc, + .rx_queue_enable = dwxgmac2_rx_queue_enable, +@@ -1653,7 +1644,6 @@ static void dwxlgmac2_rx_queue_enable(struct mac_device_info *hw, u8 mode, + + const struct stmmac_ops dwxlgmac2_ops = { + .core_init = dwxgmac2_core_init, +- .phylink_get_caps = xgmac_phylink_get_caps, + .set_mac = dwxgmac2_set_mac, + .rx_ipc = dwxgmac2_rx_ipc, + .rx_queue_enable = dwxlgmac2_rx_queue_enable, +@@ -1714,6 +1704,9 @@ int dwxgmac2_setup(struct stmmac_priv *priv) + if (mac->multicast_filter_bins) + mac->mcast_bits_log2 = ilog2(mac->multicast_filter_bins); + ++ mac->link.caps = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_1000FD | MAC_2500FD | MAC_5000FD | ++ MAC_10000FD; + mac->link.duplex = 0; + mac->link.speed10 = XGMAC_CONFIG_SS_10_MII; + mac->link.speed100 = XGMAC_CONFIG_SS_100_MII; +@@ -1751,6 +1744,11 @@ int dwxlgmac2_setup(struct stmmac_priv *priv) + if (mac->multicast_filter_bins) + mac->mcast_bits_log2 = ilog2(mac->multicast_filter_bins); + ++ mac->link.caps = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | ++ MAC_1000FD | MAC_2500FD | MAC_5000FD | ++ MAC_10000FD | MAC_25000FD | ++ MAC_40000FD | MAC_50000FD | ++ MAC_100000FD; + mac->link.duplex = 0; + mac->link.speed1000 = XLGMAC_CONFIG_SS_1000; + mac->link.speed2500 = XLGMAC_CONFIG_SS_2500; +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index d1adb102a1d49..19c58ad8df345 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -1198,17 +1198,6 @@ static int stmmac_init_phy(struct net_device *dev) + return ret; + } + +-static void stmmac_set_half_duplex(struct stmmac_priv *priv) +-{ +- /* Half-Duplex can only work with single tx queue */ +- if (priv->plat->tx_queues_to_use > 1) +- priv->phylink_config.mac_capabilities &= +- ~(MAC_10HD | MAC_100HD | MAC_1000HD); +- else +- priv->phylink_config.mac_capabilities |= +- (MAC_10HD | MAC_100HD | MAC_1000HD); +-} +- + static int stmmac_phy_setup(struct stmmac_priv *priv) + { + struct stmmac_mdio_bus_data *mdio_bus_data; +@@ -1236,15 +1225,11 @@ static int stmmac_phy_setup(struct stmmac_priv *priv) + xpcs_get_interfaces(priv->hw->xpcs, + priv->phylink_config.supported_interfaces); + +- priv->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | +- MAC_10FD | MAC_100FD | +- MAC_1000FD; +- +- stmmac_set_half_duplex(priv); +- + /* Get the MAC specific capabilities */ + stmmac_mac_phylink_get_caps(priv); + ++ priv->phylink_config.mac_capabilities = priv->hw->link.caps; ++ + max_speed = priv->plat->max_speed; + if (max_speed) + phylink_limit_mac_speed(&priv->phylink_config, max_speed); +@@ -7184,6 +7169,7 @@ int stmmac_reinit_queues(struct net_device *dev, u32 rx_cnt, u32 tx_cnt) + { + struct stmmac_priv *priv = netdev_priv(dev); + int ret = 0, i; ++ int max_speed; + + if (netif_running(dev)) + stmmac_release(dev); +@@ -7197,7 +7183,14 @@ int stmmac_reinit_queues(struct net_device *dev, u32 rx_cnt, u32 tx_cnt) + priv->rss.table[i] = ethtool_rxfh_indir_default(i, + rx_cnt); + +- stmmac_set_half_duplex(priv); ++ stmmac_mac_phylink_get_caps(priv); ++ ++ priv->phylink_config.mac_capabilities = priv->hw->link.caps; ++ ++ max_speed = priv->plat->max_speed; ++ if (max_speed) ++ phylink_limit_mac_speed(&priv->phylink_config, max_speed); ++ + stmmac_napi_add(dev); + + if (netif_running(dev)) +diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c +index c62b0f99f2bc4..d556e705ec000 100644 +--- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c ++++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c +@@ -2716,6 +2716,8 @@ static void am65_cpsw_unregister_devlink(struct am65_cpsw_common *common) + + static int am65_cpsw_nuss_register_ndevs(struct am65_cpsw_common *common) + { ++ struct am65_cpsw_rx_chn *rx_chan = &common->rx_chns; ++ struct am65_cpsw_tx_chn *tx_chan = common->tx_chns; + struct device *dev = common->dev; + struct am65_cpsw_port *port; + int ret = 0, i; +@@ -2728,6 +2730,22 @@ static int am65_cpsw_nuss_register_ndevs(struct am65_cpsw_common *common) + if (ret) + return ret; + ++ /* The DMA Channels are not guaranteed to be in a clean state. ++ * Reset and disable them to ensure that they are back to the ++ * clean state and ready to be used. ++ */ ++ for (i = 0; i < common->tx_ch_num; i++) { ++ k3_udma_glue_reset_tx_chn(tx_chan[i].tx_chn, &tx_chan[i], ++ am65_cpsw_nuss_tx_cleanup); ++ k3_udma_glue_disable_tx_chn(tx_chan[i].tx_chn); ++ } ++ ++ for (i = 0; i < AM65_CPSW_MAX_RX_FLOWS; i++) ++ k3_udma_glue_reset_rx_chn(rx_chan->rx_chn, i, rx_chan, ++ am65_cpsw_nuss_rx_cleanup, !!i); ++ ++ k3_udma_glue_disable_rx_chn(rx_chan->rx_chn); ++ + ret = am65_cpsw_nuss_register_devlink(common); + if (ret) + return ret; +diff --git a/drivers/net/tun.c b/drivers/net/tun.c +index 8f95a562b8d0c..86515f0c2b6c1 100644 +--- a/drivers/net/tun.c ++++ b/drivers/net/tun.c +@@ -2132,14 +2132,16 @@ static ssize_t tun_put_user(struct tun_struct *tun, + tun_is_little_endian(tun), true, + vlan_hlen)) { + struct skb_shared_info *sinfo = skb_shinfo(skb); +- pr_err("unexpected GSO type: " +- "0x%x, gso_size %d, hdr_len %d\n", +- sinfo->gso_type, tun16_to_cpu(tun, gso.gso_size), +- tun16_to_cpu(tun, gso.hdr_len)); +- print_hex_dump(KERN_ERR, "tun: ", +- DUMP_PREFIX_NONE, +- 16, 1, skb->head, +- min((int)tun16_to_cpu(tun, gso.hdr_len), 64), true); ++ ++ if (net_ratelimit()) { ++ netdev_err(tun->dev, "unexpected GSO type: 0x%x, gso_size %d, hdr_len %d\n", ++ sinfo->gso_type, tun16_to_cpu(tun, gso.gso_size), ++ tun16_to_cpu(tun, gso.hdr_len)); ++ print_hex_dump(KERN_ERR, "tun: ", ++ DUMP_PREFIX_NONE, ++ 16, 1, skb->head, ++ min((int)tun16_to_cpu(tun, gso.hdr_len), 64), true); ++ } + WARN_ON_ONCE(1); + return -EINVAL; + } +diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c +index e0e9b4c53cb02..3078511f76083 100644 +--- a/drivers/net/usb/ax88179_178a.c ++++ b/drivers/net/usb/ax88179_178a.c +@@ -1317,6 +1317,8 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) + + netif_set_tso_max_size(dev->net, 16384); + ++ ax88179_reset(dev); ++ + return 0; + } + +@@ -1695,7 +1697,6 @@ static const struct driver_info ax88179_info = { + .unbind = ax88179_unbind, + .status = ax88179_status, + .link_reset = ax88179_link_reset, +- .reset = ax88179_reset, + .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, +@@ -1708,7 +1709,6 @@ static const struct driver_info ax88178a_info = { + .unbind = ax88179_unbind, + .status = ax88179_status, + .link_reset = ax88179_link_reset, +- .reset = ax88179_reset, + .stop = ax88179_stop, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, +diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c +index 7cb0548d17a3f..56cbe00126bb1 100644 +--- a/drivers/net/virtio_net.c ++++ b/drivers/net/virtio_net.c +@@ -3570,19 +3570,34 @@ static int virtnet_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, u8 *hfu + static int virtnet_set_rxfh(struct net_device *dev, const u32 *indir, const u8 *key, const u8 hfunc) + { + struct virtnet_info *vi = netdev_priv(dev); ++ bool update = false; + int i; + + if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) + return -EOPNOTSUPP; + + if (indir) { ++ if (!vi->has_rss) ++ return -EOPNOTSUPP; ++ + for (i = 0; i < vi->rss_indir_table_size; ++i) + vi->ctrl->rss.indirection_table[i] = indir[i]; ++ update = true; + } +- if (key) ++ if (key) { ++ /* If either _F_HASH_REPORT or _F_RSS are negotiated, the ++ * device provides hash calculation capabilities, that is, ++ * hash_key is configured. ++ */ ++ if (!vi->has_rss && !vi->has_rss_hash_report) ++ return -EOPNOTSUPP; ++ + memcpy(vi->ctrl->rss.key, key, vi->rss_key_size); ++ update = true; ++ } + +- virtnet_commit_rss_command(vi); ++ if (update) ++ virtnet_commit_rss_command(vi); + + return 0; + } +@@ -4491,13 +4506,15 @@ static int virtnet_probe(struct virtio_device *vdev) + if (virtio_has_feature(vdev, VIRTIO_NET_F_HASH_REPORT)) + vi->has_rss_hash_report = true; + +- if (virtio_has_feature(vdev, VIRTIO_NET_F_RSS)) ++ if (virtio_has_feature(vdev, VIRTIO_NET_F_RSS)) { + vi->has_rss = true; + +- if (vi->has_rss || vi->has_rss_hash_report) { + vi->rss_indir_table_size = + virtio_cread16(vdev, offsetof(struct virtio_net_config, + rss_max_indirection_table_length)); ++ } ++ ++ if (vi->has_rss || vi->has_rss_hash_report) { + vi->rss_key_size = + virtio_cread8(vdev, offsetof(struct virtio_net_config, rss_max_key_size)); + +diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c +index 9c2137dae429a..826b5016a1010 100644 +--- a/drivers/pci/bus.c ++++ b/drivers/pci/bus.c +@@ -386,21 +386,8 @@ void pci_bus_add_devices(const struct pci_bus *bus) + } + EXPORT_SYMBOL(pci_bus_add_devices); + +-/** pci_walk_bus - walk devices on/under bus, calling callback. +- * @top bus whose devices should be walked +- * @cb callback to be called for each device found +- * @userdata arbitrary pointer to be passed to callback. +- * +- * Walk the given bus, including any bridged devices +- * on buses under this bus. Call the provided callback +- * on each device found. +- * +- * We check the return of @cb each time. If it returns anything +- * other than 0, we break out. +- * +- */ +-void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), +- void *userdata) ++static void __pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), ++ void *userdata, bool locked) + { + struct pci_dev *dev; + struct pci_bus *bus; +@@ -408,7 +395,8 @@ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), + int retval; + + bus = top; +- down_read(&pci_bus_sem); ++ if (!locked) ++ down_read(&pci_bus_sem); + next = top->devices.next; + for (;;) { + if (next == &bus->devices) { +@@ -431,10 +419,37 @@ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), + if (retval) + break; + } +- up_read(&pci_bus_sem); ++ if (!locked) ++ up_read(&pci_bus_sem); ++} ++ ++/** ++ * pci_walk_bus - walk devices on/under bus, calling callback. ++ * @top: bus whose devices should be walked ++ * @cb: callback to be called for each device found ++ * @userdata: arbitrary pointer to be passed to callback ++ * ++ * Walk the given bus, including any bridged devices ++ * on buses under this bus. Call the provided callback ++ * on each device found. ++ * ++ * We check the return of @cb each time. If it returns anything ++ * other than 0, we break out. ++ */ ++void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata) ++{ ++ __pci_walk_bus(top, cb, userdata, false); + } + EXPORT_SYMBOL_GPL(pci_walk_bus); + ++void pci_walk_bus_locked(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata) ++{ ++ lockdep_assert_held(&pci_bus_sem); ++ ++ __pci_walk_bus(top, cb, userdata, true); ++} ++EXPORT_SYMBOL_GPL(pci_walk_bus_locked); ++ + struct pci_bus *pci_bus_get(struct pci_bus *bus) + { + if (bus) +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index 06fc6f532d6c4..dddd30deea32b 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -1291,6 +1291,7 @@ int pci_power_up(struct pci_dev *dev) + /** + * pci_set_full_power_state - Put a PCI device into D0 and update its state + * @dev: PCI device to power up ++ * @locked: whether pci_bus_sem is held + * + * Call pci_power_up() to put @dev into D0, read from its PCI_PM_CTRL register + * to confirm the state change, restore its BARs if they might be lost and +@@ -1300,7 +1301,7 @@ int pci_power_up(struct pci_dev *dev) + * to D0, it is more efficient to use pci_power_up() directly instead of this + * function. + */ +-static int pci_set_full_power_state(struct pci_dev *dev) ++static int pci_set_full_power_state(struct pci_dev *dev, bool locked) + { + u16 pmcsr; + int ret; +@@ -1336,7 +1337,7 @@ static int pci_set_full_power_state(struct pci_dev *dev) + } + + if (dev->bus->self) +- pcie_aspm_pm_state_change(dev->bus->self); ++ pcie_aspm_pm_state_change(dev->bus->self, locked); + + return 0; + } +@@ -1365,10 +1366,22 @@ void pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state) + pci_walk_bus(bus, __pci_dev_set_current_state, &state); + } + ++static void __pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state, bool locked) ++{ ++ if (!bus) ++ return; ++ ++ if (locked) ++ pci_walk_bus_locked(bus, __pci_dev_set_current_state, &state); ++ else ++ pci_walk_bus(bus, __pci_dev_set_current_state, &state); ++} ++ + /** + * pci_set_low_power_state - Put a PCI device into a low-power state. + * @dev: PCI device to handle. + * @state: PCI power state (D1, D2, D3hot) to put the device into. ++ * @locked: whether pci_bus_sem is held + * + * Use the device's PCI_PM_CTRL register to put it into a low-power state. + * +@@ -1379,7 +1392,7 @@ void pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state) + * 0 if device already is in the requested state. + * 0 if device's power state has been successfully changed. + */ +-static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state) ++static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state, bool locked) + { + u16 pmcsr; + +@@ -1433,29 +1446,12 @@ static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state) + pci_power_name(state)); + + if (dev->bus->self) +- pcie_aspm_pm_state_change(dev->bus->self); ++ pcie_aspm_pm_state_change(dev->bus->self, locked); + + return 0; + } + +-/** +- * pci_set_power_state - Set the power state of a PCI device +- * @dev: PCI device to handle. +- * @state: PCI power state (D0, D1, D2, D3hot) to put the device into. +- * +- * Transition a device to a new power state, using the platform firmware and/or +- * the device's PCI PM registers. +- * +- * RETURN VALUE: +- * -EINVAL if the requested state is invalid. +- * -EIO if device does not support PCI PM or its PM capabilities register has a +- * wrong version, or device doesn't support the requested state. +- * 0 if the transition is to D1 or D2 but D1 and D2 are not supported. +- * 0 if device already is in the requested state. +- * 0 if the transition is to D3 but D3 is not supported. +- * 0 if device's power state has been successfully changed. +- */ +-int pci_set_power_state(struct pci_dev *dev, pci_power_t state) ++static int __pci_set_power_state(struct pci_dev *dev, pci_power_t state, bool locked) + { + int error; + +@@ -1479,7 +1475,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) + return 0; + + if (state == PCI_D0) +- return pci_set_full_power_state(dev); ++ return pci_set_full_power_state(dev, locked); + + /* + * This device is quirked not to be put into D3, so don't put it in +@@ -1493,16 +1489,16 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) + * To put the device in D3cold, put it into D3hot in the native + * way, then put it into D3cold using platform ops. + */ +- error = pci_set_low_power_state(dev, PCI_D3hot); ++ error = pci_set_low_power_state(dev, PCI_D3hot, locked); + + if (pci_platform_power_transition(dev, PCI_D3cold)) + return error; + + /* Powering off a bridge may power off the whole hierarchy */ + if (dev->current_state == PCI_D3cold) +- pci_bus_set_current_state(dev->subordinate, PCI_D3cold); ++ __pci_bus_set_current_state(dev->subordinate, PCI_D3cold, locked); + } else { +- error = pci_set_low_power_state(dev, state); ++ error = pci_set_low_power_state(dev, state, locked); + + if (pci_platform_power_transition(dev, state)) + return error; +@@ -1510,8 +1506,38 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) + + return 0; + } ++ ++/** ++ * pci_set_power_state - Set the power state of a PCI device ++ * @dev: PCI device to handle. ++ * @state: PCI power state (D0, D1, D2, D3hot) to put the device into. ++ * ++ * Transition a device to a new power state, using the platform firmware and/or ++ * the device's PCI PM registers. ++ * ++ * RETURN VALUE: ++ * -EINVAL if the requested state is invalid. ++ * -EIO if device does not support PCI PM or its PM capabilities register has a ++ * wrong version, or device doesn't support the requested state. ++ * 0 if the transition is to D1 or D2 but D1 and D2 are not supported. ++ * 0 if device already is in the requested state. ++ * 0 if the transition is to D3 but D3 is not supported. ++ * 0 if device's power state has been successfully changed. ++ */ ++int pci_set_power_state(struct pci_dev *dev, pci_power_t state) ++{ ++ return __pci_set_power_state(dev, state, false); ++} + EXPORT_SYMBOL(pci_set_power_state); + ++int pci_set_power_state_locked(struct pci_dev *dev, pci_power_t state) ++{ ++ lockdep_assert_held(&pci_bus_sem); ++ ++ return __pci_set_power_state(dev, state, true); ++} ++EXPORT_SYMBOL(pci_set_power_state_locked); ++ + #define PCI_EXP_SAVE_REGS 7 + + static struct pci_cap_saved_state *_pci_find_saved_cap(struct pci_dev *pci_dev, +diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h +index 1b4f941829724..2cc032e8cbb92 100644 +--- a/drivers/pci/pci.h ++++ b/drivers/pci/pci.h +@@ -561,12 +561,12 @@ int pcie_retrain_link(struct pci_dev *pdev, bool use_lt); + #ifdef CONFIG_PCIEASPM + void pcie_aspm_init_link_state(struct pci_dev *pdev); + void pcie_aspm_exit_link_state(struct pci_dev *pdev); +-void pcie_aspm_pm_state_change(struct pci_dev *pdev); ++void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked); + void pcie_aspm_powersave_config_link(struct pci_dev *pdev); + #else + static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) { } + static inline void pcie_aspm_exit_link_state(struct pci_dev *pdev) { } +-static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev) { } ++static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked) { } + static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev) { } + #endif + +diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c +index 7e3b342215e5b..0aef6dc055b92 100644 +--- a/drivers/pci/pcie/aspm.c ++++ b/drivers/pci/pcie/aspm.c +@@ -689,10 +689,10 @@ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state) + * in pcie_config_aspm_link(). + */ + if (enable_req & (ASPM_STATE_L1_1 | ASPM_STATE_L1_2)) { +- pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL, +- PCI_EXP_LNKCTL_ASPM_L1, 0); +- pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL, +- PCI_EXP_LNKCTL_ASPM_L1, 0); ++ pcie_capability_clear_word(child, PCI_EXP_LNKCTL, ++ PCI_EXP_LNKCTL_ASPM_L1); ++ pcie_capability_clear_word(parent, PCI_EXP_LNKCTL, ++ PCI_EXP_LNKCTL_ASPM_L1); + } + + val = 0; +@@ -1001,8 +1001,11 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) + up_read(&pci_bus_sem); + } + +-/* @pdev: the root port or switch downstream port */ +-void pcie_aspm_pm_state_change(struct pci_dev *pdev) ++/* ++ * @pdev: the root port or switch downstream port ++ * @locked: whether pci_bus_sem is held ++ */ ++void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked) + { + struct pcie_link_state *link = pdev->link_state; + +@@ -1012,12 +1015,14 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev) + * Devices changed PM state, we should recheck if latency + * meets all functions' requirement + */ +- down_read(&pci_bus_sem); ++ if (!locked) ++ down_read(&pci_bus_sem); + mutex_lock(&aspm_lock); + pcie_update_aspm_capable(link->root); + pcie_config_aspm_path(link); + mutex_unlock(&aspm_lock); +- up_read(&pci_bus_sem); ++ if (!locked) ++ up_read(&pci_bus_sem); + } + + void pcie_aspm_powersave_config_link(struct pci_dev *pdev) +diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c +index b4818007788f9..a5cec2a4e057d 100644 +--- a/drivers/pci/pcie/dpc.c ++++ b/drivers/pci/pcie/dpc.c +@@ -9,6 +9,7 @@ + #define dev_fmt(fmt) "DPC: " fmt + + #include ++#include + #include + #include + #include +@@ -202,7 +203,7 @@ static void dpc_process_rp_pio_error(struct pci_dev *pdev) + + /* Get First Error Pointer */ + pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &dpc_status); +- first_error = (dpc_status & 0x1f00) >> 8; ++ first_error = FIELD_GET(PCI_EXP_DPC_RP_PIO_FEP, dpc_status); + + for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) { + if ((status & ~mask) & (1 << i)) +@@ -338,7 +339,7 @@ void pci_dpc_init(struct pci_dev *pdev) + /* Quirks may set dpc_rp_log_size if device or firmware is buggy */ + if (!pdev->dpc_rp_log_size) { + pdev->dpc_rp_log_size = +- (cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8; ++ FIELD_GET(PCI_EXP_DPC_RP_PIO_LOG_SIZE, cap); + if (pdev->dpc_rp_log_size < 4 || pdev->dpc_rp_log_size > 9) { + pci_err(pdev, "RP PIO log size %u is invalid\n", + pdev->dpc_rp_log_size); +diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c +index b3976dcb71f10..ec4277d7835b2 100644 +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -4571,9 +4571,9 @@ static void quirk_disable_root_port_attributes(struct pci_dev *pdev) + + pci_info(root_port, "Disabling No Snoop/Relaxed Ordering Attributes to avoid PCIe Completion erratum in %s\n", + dev_name(&pdev->dev)); +- pcie_capability_clear_and_set_word(root_port, PCI_EXP_DEVCTL, +- PCI_EXP_DEVCTL_RELAX_EN | +- PCI_EXP_DEVCTL_NOSNOOP_EN, 0); ++ pcie_capability_clear_word(root_port, PCI_EXP_DEVCTL, ++ PCI_EXP_DEVCTL_RELAX_EN | ++ PCI_EXP_DEVCTL_NOSNOOP_EN); + } + + /* +@@ -6198,7 +6198,7 @@ static void dpc_log_size(struct pci_dev *dev) + if (!(val & PCI_EXP_DPC_CAP_RP_EXT)) + return; + +- if (!((val & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8)) { ++ if (FIELD_GET(PCI_EXP_DPC_RP_PIO_LOG_SIZE, val) == 0) { + pci_info(dev, "Overriding RP PIO Log Size to 4\n"); + dev->dpc_rp_log_size = 4; + } +diff --git a/drivers/platform/x86/amd/pmc/pmc-quirks.c b/drivers/platform/x86/amd/pmc/pmc-quirks.c +index b456370166b6b..b4f49720c87f6 100644 +--- a/drivers/platform/x86/amd/pmc/pmc-quirks.c ++++ b/drivers/platform/x86/amd/pmc/pmc-quirks.c +@@ -208,6 +208,15 @@ static const struct dmi_system_id fwbug_list[] = { + DMI_MATCH(DMI_BIOS_VERSION, "03.03"), + } + }, ++ { ++ .ident = "Framework Laptop 13 (Phoenix)", ++ .driver_data = &quirk_spurious_8042, ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Framework"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Laptop 13 (AMD Ryzen 7040Series)"), ++ DMI_MATCH(DMI_BIOS_VERSION, "03.05"), ++ } ++ }, + {} + }; + +diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c +index 4ca5adce91079..57e0050dbaa53 100644 +--- a/drivers/s390/cio/device.c ++++ b/drivers/s390/cio/device.c +@@ -363,10 +363,8 @@ int ccw_device_set_online(struct ccw_device *cdev) + + spin_lock_irq(cdev->ccwlock); + ret = ccw_device_online(cdev); +- spin_unlock_irq(cdev->ccwlock); +- if (ret == 0) +- wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); +- else { ++ if (ret) { ++ spin_unlock_irq(cdev->ccwlock); + CIO_MSG_EVENT(0, "ccw_device_online returned %d, " + "device 0.%x.%04x\n", + ret, cdev->private->dev_id.ssid, +@@ -375,7 +373,12 @@ int ccw_device_set_online(struct ccw_device *cdev) + put_device(&cdev->dev); + return ret; + } +- spin_lock_irq(cdev->ccwlock); ++ /* Wait until a final state is reached */ ++ while (!dev_fsm_final_state(cdev)) { ++ spin_unlock_irq(cdev->ccwlock); ++ wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev)); ++ spin_lock_irq(cdev->ccwlock); ++ } + /* Check if online processing was successful */ + if ((cdev->private->state != DEV_STATE_ONLINE) && + (cdev->private->state != DEV_STATE_W4SENSE)) { +diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c +index 9cde55730b65a..ebcb535809882 100644 +--- a/drivers/s390/cio/qdio_main.c ++++ b/drivers/s390/cio/qdio_main.c +@@ -722,8 +722,8 @@ static void qdio_handle_activate_check(struct qdio_irq *irq_ptr, + lgr_info_log(); + } + +-static void qdio_establish_handle_irq(struct qdio_irq *irq_ptr, int cstat, +- int dstat) ++static int qdio_establish_handle_irq(struct qdio_irq *irq_ptr, int cstat, ++ int dstat, int dcc) + { + DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq"); + +@@ -731,15 +731,18 @@ static void qdio_establish_handle_irq(struct qdio_irq *irq_ptr, int cstat, + goto error; + if (dstat & ~(DEV_STAT_DEV_END | DEV_STAT_CHN_END)) + goto error; ++ if (dcc == 1) ++ return -EAGAIN; + if (!(dstat & DEV_STAT_DEV_END)) + goto error; + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED); +- return; ++ return 0; + + error: + DBF_ERROR("%4x EQ:error", irq_ptr->schid.sch_no); + DBF_ERROR("ds: %2x cs:%2x", dstat, cstat); + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); ++ return -EIO; + } + + /* qdio interrupt handler */ +@@ -748,7 +751,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, + { + struct qdio_irq *irq_ptr = cdev->private->qdio_data; + struct subchannel_id schid; +- int cstat, dstat; ++ int cstat, dstat, rc, dcc; + + if (!intparm || !irq_ptr) { + ccw_device_get_schid(cdev, &schid); +@@ -768,10 +771,12 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, + qdio_irq_check_sense(irq_ptr, irb); + cstat = irb->scsw.cmd.cstat; + dstat = irb->scsw.cmd.dstat; ++ dcc = scsw_cmd_is_valid_cc(&irb->scsw) ? irb->scsw.cmd.cc : 0; ++ rc = 0; + + switch (irq_ptr->state) { + case QDIO_IRQ_STATE_INACTIVE: +- qdio_establish_handle_irq(irq_ptr, cstat, dstat); ++ rc = qdio_establish_handle_irq(irq_ptr, cstat, dstat, dcc); + break; + case QDIO_IRQ_STATE_CLEANUP: + qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); +@@ -785,12 +790,25 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, + if (cstat || dstat) + qdio_handle_activate_check(irq_ptr, intparm, cstat, + dstat); ++ else if (dcc == 1) ++ rc = -EAGAIN; + break; + case QDIO_IRQ_STATE_STOPPED: + break; + default: + WARN_ON_ONCE(1); + } ++ ++ if (rc == -EAGAIN) { ++ DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qint retry"); ++ rc = ccw_device_start(cdev, irq_ptr->ccw, intparm, 0, 0); ++ if (!rc) ++ return; ++ DBF_ERROR("%4x RETRY ERR", irq_ptr->schid.sch_no); ++ DBF_ERROR("rc:%4x", rc); ++ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); ++ } ++ + wake_up(&cdev->private->wait_q); + } + +diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c +index 81aabbfbbe2ca..622a61f8a3b84 100644 +--- a/drivers/s390/net/ism_drv.c ++++ b/drivers/s390/net/ism_drv.c +@@ -291,13 +291,16 @@ static int ism_read_local_gid(struct ism_dev *ism) + static void ism_free_dmb(struct ism_dev *ism, struct ism_dmb *dmb) + { + clear_bit(dmb->sba_idx, ism->sba_bitmap); +- dma_free_coherent(&ism->pdev->dev, dmb->dmb_len, +- dmb->cpu_addr, dmb->dma_addr); ++ dma_unmap_page(&ism->pdev->dev, dmb->dma_addr, dmb->dmb_len, ++ DMA_FROM_DEVICE); ++ folio_put(virt_to_folio(dmb->cpu_addr)); + } + + static int ism_alloc_dmb(struct ism_dev *ism, struct ism_dmb *dmb) + { ++ struct folio *folio; + unsigned long bit; ++ int rc; + + if (PAGE_ALIGN(dmb->dmb_len) > dma_get_max_seg_size(&ism->pdev->dev)) + return -EINVAL; +@@ -314,14 +317,30 @@ static int ism_alloc_dmb(struct ism_dev *ism, struct ism_dmb *dmb) + test_and_set_bit(dmb->sba_idx, ism->sba_bitmap)) + return -EINVAL; + +- dmb->cpu_addr = dma_alloc_coherent(&ism->pdev->dev, dmb->dmb_len, +- &dmb->dma_addr, +- GFP_KERNEL | __GFP_NOWARN | +- __GFP_NOMEMALLOC | __GFP_NORETRY); +- if (!dmb->cpu_addr) +- clear_bit(dmb->sba_idx, ism->sba_bitmap); ++ folio = folio_alloc(GFP_KERNEL | __GFP_NOWARN | __GFP_NOMEMALLOC | ++ __GFP_NORETRY, get_order(dmb->dmb_len)); + +- return dmb->cpu_addr ? 0 : -ENOMEM; ++ if (!folio) { ++ rc = -ENOMEM; ++ goto out_bit; ++ } ++ ++ dmb->cpu_addr = folio_address(folio); ++ dmb->dma_addr = dma_map_page(&ism->pdev->dev, ++ virt_to_page(dmb->cpu_addr), 0, ++ dmb->dmb_len, DMA_FROM_DEVICE); ++ if (dma_mapping_error(&ism->pdev->dev, dmb->dma_addr)) { ++ rc = -ENOMEM; ++ goto out_free; ++ } ++ ++ return 0; ++ ++out_free: ++ kfree(dmb->cpu_addr); ++out_bit: ++ clear_bit(dmb->sba_idx, ism->sba_bitmap); ++ return rc; + } + + int ism_register_dmb(struct ism_dev *ism, struct ism_dmb *dmb, +diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c +index 552809bca3507..97def2619ecf2 100644 +--- a/drivers/scsi/scsi_lib.c ++++ b/drivers/scsi/scsi_lib.c +@@ -543,10 +543,9 @@ static bool scsi_end_request(struct request *req, blk_status_t error, + if (blk_queue_add_random(q)) + add_disk_randomness(req->q->disk); + +- if (!blk_rq_is_passthrough(req)) { +- WARN_ON_ONCE(!(cmd->flags & SCMD_INITIALIZED)); +- cmd->flags &= ~SCMD_INITIALIZED; +- } ++ WARN_ON_ONCE(!blk_rq_is_passthrough(req) && ++ !(cmd->flags & SCMD_INITIALIZED)); ++ cmd->flags = 0; + + /* + * Calling rcu_barrier() is not necessary here because the +diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c +index ec7b5f65804e4..31f3da4e6a08d 100644 +--- a/drivers/thunderbolt/domain.c ++++ b/drivers/thunderbolt/domain.c +@@ -423,6 +423,7 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize + /** + * tb_domain_add() - Add domain to the system + * @tb: Domain to add ++ * @reset: Issue reset to the host router + * + * Starts the domain and adds it to the system. Hotplugging devices will + * work after this has been returned successfully. In order to remove +@@ -431,7 +432,7 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize + * + * Return: %0 in case of success and negative errno in case of error + */ +-int tb_domain_add(struct tb *tb) ++int tb_domain_add(struct tb *tb, bool reset) + { + int ret; + +@@ -460,7 +461,7 @@ int tb_domain_add(struct tb *tb) + + /* Start the domain */ + if (tb->cm_ops->start) { +- ret = tb->cm_ops->start(tb); ++ ret = tb->cm_ops->start(tb, reset); + if (ret) + goto err_domain_del; + } +diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c +index d8b9c734abd36..623aa81a88337 100644 +--- a/drivers/thunderbolt/icm.c ++++ b/drivers/thunderbolt/icm.c +@@ -2144,7 +2144,7 @@ static int icm_runtime_resume(struct tb *tb) + return 0; + } + +-static int icm_start(struct tb *tb) ++static int icm_start(struct tb *tb, bool not_used) + { + struct icm *icm = tb_priv(tb); + int ret; +diff --git a/drivers/thunderbolt/lc.c b/drivers/thunderbolt/lc.c +index 633970fbe9b05..63cb4b6afb718 100644 +--- a/drivers/thunderbolt/lc.c ++++ b/drivers/thunderbolt/lc.c +@@ -6,6 +6,8 @@ + * Author: Mika Westerberg + */ + ++#include ++ + #include "tb.h" + + /** +@@ -45,6 +47,49 @@ static int find_port_lc_cap(struct tb_port *port) + return sw->cap_lc + start + phys * size; + } + ++/** ++ * tb_lc_reset_port() - Trigger downstream port reset through LC ++ * @port: Port that is reset ++ * ++ * Triggers downstream port reset through link controller registers. ++ * Returns %0 in case of success negative errno otherwise. Only supports ++ * non-USB4 routers with link controller (that's Thunderbolt 2 and ++ * Thunderbolt 3). ++ */ ++int tb_lc_reset_port(struct tb_port *port) ++{ ++ struct tb_switch *sw = port->sw; ++ int cap, ret; ++ u32 mode; ++ ++ if (sw->generation < 2) ++ return -EINVAL; ++ ++ cap = find_port_lc_cap(port); ++ if (cap < 0) ++ return cap; ++ ++ ret = tb_sw_read(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1); ++ if (ret) ++ return ret; ++ ++ mode |= TB_LC_PORT_MODE_DPR; ++ ++ ret = tb_sw_write(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1); ++ if (ret) ++ return ret; ++ ++ fsleep(10000); ++ ++ ret = tb_sw_read(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1); ++ if (ret) ++ return ret; ++ ++ mode &= ~TB_LC_PORT_MODE_DPR; ++ ++ return tb_sw_write(sw, &mode, TB_CFG_SWITCH, cap + TB_LC_PORT_MODE, 1); ++} ++ + static int tb_lc_set_port_configured(struct tb_port *port, bool configured) + { + bool upstream = tb_is_upstream_port(port); +diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c +index 4b7bec74e89fb..1ec6f9c82aef0 100644 +--- a/drivers/thunderbolt/nhi.c ++++ b/drivers/thunderbolt/nhi.c +@@ -1221,7 +1221,7 @@ static void nhi_check_iommu(struct tb_nhi *nhi) + str_enabled_disabled(port_ok)); + } + +-static void nhi_reset(struct tb_nhi *nhi) ++static bool nhi_reset(struct tb_nhi *nhi) + { + ktime_t timeout; + u32 val; +@@ -1229,11 +1229,11 @@ static void nhi_reset(struct tb_nhi *nhi) + val = ioread32(nhi->iobase + REG_CAPS); + /* Reset only v2 and later routers */ + if (FIELD_GET(REG_CAPS_VERSION_MASK, val) < REG_CAPS_VERSION_2) +- return; ++ return false; + + if (!host_reset) { + dev_dbg(&nhi->pdev->dev, "skipping host router reset\n"); +- return; ++ return false; + } + + iowrite32(REG_RESET_HRR, nhi->iobase + REG_RESET); +@@ -1244,12 +1244,14 @@ static void nhi_reset(struct tb_nhi *nhi) + val = ioread32(nhi->iobase + REG_RESET); + if (!(val & REG_RESET_HRR)) { + dev_warn(&nhi->pdev->dev, "host router reset successful\n"); +- return; ++ return true; + } + usleep_range(10, 20); + } while (ktime_before(ktime_get(), timeout)); + + dev_warn(&nhi->pdev->dev, "timeout resetting host router\n"); ++ ++ return false; + } + + static int nhi_init_msi(struct tb_nhi *nhi) +@@ -1331,6 +1333,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) + struct device *dev = &pdev->dev; + struct tb_nhi *nhi; + struct tb *tb; ++ bool reset; + int res; + + if (!nhi_imr_valid(pdev)) +@@ -1365,7 +1368,11 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) + nhi_check_quirks(nhi); + nhi_check_iommu(nhi); + +- nhi_reset(nhi); ++ /* ++ * Only USB4 v2 hosts support host reset so if we already did ++ * that then don't do it again when the domain is initialized. ++ */ ++ reset = nhi_reset(nhi) ? false : host_reset; + + res = nhi_init_msi(nhi); + if (res) +@@ -1392,7 +1399,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) + + dev_dbg(dev, "NHI initialized, starting thunderbolt\n"); + +- res = tb_domain_add(tb); ++ res = tb_domain_add(tb, reset); + if (res) { + /* + * At this point the RX/TX rings might already have been +diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c +index ee03fd75a4728..17f7647e6a722 100644 +--- a/drivers/thunderbolt/path.c ++++ b/drivers/thunderbolt/path.c +@@ -446,6 +446,19 @@ static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index, + return -ETIMEDOUT; + } + ++/** ++ * tb_path_deactivate_hop() - Deactivate one path in path config space ++ * @port: Lane or protocol adapter ++ * @hop_index: HopID of the path to be cleared ++ * ++ * This deactivates or clears a single path config space entry at ++ * @hop_index. Returns %0 in success and negative errno otherwise. ++ */ ++int tb_path_deactivate_hop(struct tb_port *port, int hop_index) ++{ ++ return __tb_path_deactivate_hop(port, hop_index, true); ++} ++ + static void __tb_path_deactivate_hops(struct tb_path *path, int first_hop) + { + int i, res; +diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c +index 509b99af5087b..de0f9cd44c231 100644 +--- a/drivers/thunderbolt/switch.c ++++ b/drivers/thunderbolt/switch.c +@@ -675,6 +675,13 @@ int tb_port_disable(struct tb_port *port) + return __tb_port_enable(port, false); + } + ++static int tb_port_reset(struct tb_port *port) ++{ ++ if (tb_switch_is_usb4(port->sw)) ++ return port->cap_usb4 ? usb4_port_reset(port) : 0; ++ return tb_lc_reset_port(port); ++} ++ + /* + * tb_init_port() - initialize a port + * +@@ -1485,29 +1492,124 @@ static void tb_dump_switch(const struct tb *tb, const struct tb_switch *sw) + regs->__unknown1, regs->__unknown4); + } + ++static int tb_switch_reset_host(struct tb_switch *sw) ++{ ++ if (sw->generation > 1) { ++ struct tb_port *port; ++ ++ tb_switch_for_each_port(sw, port) { ++ int i, ret; ++ ++ /* ++ * For lane adapters we issue downstream port ++ * reset and clear up path config spaces. ++ * ++ * For protocol adapters we disable the path and ++ * clear path config space one by one (from 8 to ++ * Max Input HopID of the adapter). ++ */ ++ if (tb_port_is_null(port) && !tb_is_upstream_port(port)) { ++ ret = tb_port_reset(port); ++ if (ret) ++ return ret; ++ } else if (tb_port_is_usb3_down(port) || ++ tb_port_is_usb3_up(port)) { ++ tb_usb3_port_enable(port, false); ++ } else if (tb_port_is_dpin(port) || ++ tb_port_is_dpout(port)) { ++ tb_dp_port_enable(port, false); ++ } else if (tb_port_is_pcie_down(port) || ++ tb_port_is_pcie_up(port)) { ++ tb_pci_port_enable(port, false); ++ } else { ++ continue; ++ } ++ ++ /* Cleanup path config space of protocol adapter */ ++ for (i = TB_PATH_MIN_HOPID; ++ i <= port->config.max_in_hop_id; i++) { ++ ret = tb_path_deactivate_hop(port, i); ++ if (ret) ++ return ret; ++ } ++ } ++ } else { ++ struct tb_cfg_result res; ++ ++ /* Thunderbolt 1 uses the "reset" config space packet */ ++ res.err = tb_sw_write(sw, ((u32 *) &sw->config) + 2, ++ TB_CFG_SWITCH, 2, 2); ++ if (res.err) ++ return res.err; ++ res = tb_cfg_reset(sw->tb->ctl, tb_route(sw)); ++ if (res.err > 0) ++ return -EIO; ++ else if (res.err < 0) ++ return res.err; ++ } ++ ++ return 0; ++} ++ ++static int tb_switch_reset_device(struct tb_switch *sw) ++{ ++ return tb_port_reset(tb_switch_downstream_port(sw)); ++} ++ ++static bool tb_switch_enumerated(struct tb_switch *sw) ++{ ++ u32 val; ++ int ret; ++ ++ /* ++ * Read directly from the hardware because we use this also ++ * during system sleep where sw->config.enabled is already set ++ * by us. ++ */ ++ ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_3, 1); ++ if (ret) ++ return false; ++ ++ return !!(val & ROUTER_CS_3_V); ++} ++ + /** +- * tb_switch_reset() - reconfigure route, enable and send TB_CFG_PKG_RESET +- * @sw: Switch to reset ++ * tb_switch_reset() - Perform reset to the router ++ * @sw: Router to reset + * +- * Return: Returns 0 on success or an error code on failure. ++ * Issues reset to the router @sw. Can be used for any router. For host ++ * routers, resets all the downstream ports and cleans up path config ++ * spaces accordingly. For device routers issues downstream port reset ++ * through the parent router, so as side effect there will be unplug ++ * soon after this is finished. ++ * ++ * If the router is not enumerated does nothing. ++ * ++ * Returns %0 on success or negative errno in case of failure. + */ + int tb_switch_reset(struct tb_switch *sw) + { +- struct tb_cfg_result res; ++ int ret; + +- if (sw->generation > 1) ++ /* ++ * We cannot access the port config spaces unless the router is ++ * already enumerated. If the router is not enumerated it is ++ * equal to being reset so we can skip that here. ++ */ ++ if (!tb_switch_enumerated(sw)) + return 0; + +- tb_sw_dbg(sw, "resetting switch\n"); ++ tb_sw_dbg(sw, "resetting\n"); ++ ++ if (tb_route(sw)) ++ ret = tb_switch_reset_device(sw); ++ else ++ ret = tb_switch_reset_host(sw); ++ ++ if (ret) ++ tb_sw_warn(sw, "failed to reset\n"); + +- res.err = tb_sw_write(sw, ((u32 *) &sw->config) + 2, +- TB_CFG_SWITCH, 2, 2); +- if (res.err) +- return res.err; +- res = tb_cfg_reset(sw->tb->ctl, tb_route(sw)); +- if (res.err > 0) +- return -EIO; +- return res.err; ++ return ret; + } + + /** +@@ -2847,22 +2949,29 @@ void tb_switch_unconfigure_link(struct tb_switch *sw) + { + struct tb_port *up, *down; + +- if (sw->is_unplugged) +- return; + if (!tb_route(sw) || tb_switch_is_icm(sw)) + return; + ++ /* ++ * Unconfigure downstream port so that wake-on-connect can be ++ * configured after router unplug. No need to unconfigure upstream port ++ * since its router is unplugged. ++ */ + up = tb_upstream_port(sw); +- if (tb_switch_is_usb4(up->sw)) +- usb4_port_unconfigure(up); +- else +- tb_lc_unconfigure_port(up); +- + down = up->remote; + if (tb_switch_is_usb4(down->sw)) + usb4_port_unconfigure(down); + else + tb_lc_unconfigure_port(down); ++ ++ if (sw->is_unplugged) ++ return; ++ ++ up = tb_upstream_port(sw); ++ if (tb_switch_is_usb4(up->sw)) ++ usb4_port_unconfigure(up); ++ else ++ tb_lc_unconfigure_port(up); + } + + static void tb_switch_credits_init(struct tb_switch *sw) +@@ -3106,7 +3215,26 @@ static int tb_switch_set_wake(struct tb_switch *sw, unsigned int flags) + return tb_lc_set_wake(sw, flags); + } + +-int tb_switch_resume(struct tb_switch *sw) ++static void tb_switch_check_wakes(struct tb_switch *sw) ++{ ++ if (device_may_wakeup(&sw->dev)) { ++ if (tb_switch_is_usb4(sw)) ++ usb4_switch_check_wakes(sw); ++ } ++} ++ ++/** ++ * tb_switch_resume() - Resume a switch after sleep ++ * @sw: Switch to resume ++ * @runtime: Is this resume from runtime suspend or system sleep ++ * ++ * Resumes and re-enumerates router (and all its children), if still plugged ++ * after suspend. Don't enumerate device router whose UID was changed during ++ * suspend. If this is resume from system sleep, notifies PM core about the ++ * wakes occurred during suspend. Disables all wakes, except USB4 wake of ++ * upstream port for USB4 routers that shall be always enabled. ++ */ ++int tb_switch_resume(struct tb_switch *sw, bool runtime) + { + struct tb_port *port; + int err; +@@ -3155,6 +3283,9 @@ int tb_switch_resume(struct tb_switch *sw) + if (err) + return err; + ++ if (!runtime) ++ tb_switch_check_wakes(sw); ++ + /* Disable wakes */ + tb_switch_set_wake(sw, 0); + +@@ -3184,7 +3315,8 @@ int tb_switch_resume(struct tb_switch *sw) + */ + if (tb_port_unlock(port)) + tb_port_warn(port, "failed to unlock port\n"); +- if (port->remote && tb_switch_resume(port->remote->sw)) { ++ if (port->remote && ++ tb_switch_resume(port->remote->sw, runtime)) { + tb_port_warn(port, + "lost during suspend, disconnecting\n"); + tb_sw_set_unplugged(port->remote->sw); +diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c +index ecfb5714e822d..c5e10c1d4c383 100644 +--- a/drivers/thunderbolt/tb.c ++++ b/drivers/thunderbolt/tb.c +@@ -2131,7 +2131,7 @@ static int tb_scan_finalize_switch(struct device *dev, void *data) + return 0; + } + +-static int tb_start(struct tb *tb) ++static int tb_start(struct tb *tb, bool reset) + { + struct tb_cm *tcm = tb_priv(tb); + int ret; +@@ -2172,12 +2172,24 @@ static int tb_start(struct tb *tb) + tb_switch_tmu_configure(tb->root_switch, TB_SWITCH_TMU_MODE_LOWRES); + /* Enable TMU if it is off */ + tb_switch_tmu_enable(tb->root_switch); +- /* Full scan to discover devices added before the driver was loaded. */ +- tb_scan_switch(tb->root_switch); +- /* Find out tunnels created by the boot firmware */ +- tb_discover_tunnels(tb); +- /* Add DP resources from the DP tunnels created by the boot firmware */ +- tb_discover_dp_resources(tb); ++ ++ /* ++ * Boot firmware might have created tunnels of its own. Since we ++ * cannot be sure they are usable for us, tear them down and ++ * reset the ports to handle it as new hotplug for USB4 v1 ++ * routers (for USB4 v2 and beyond we already do host reset). ++ */ ++ if (reset && usb4_switch_version(tb->root_switch) == 1) { ++ tb_switch_reset(tb->root_switch); ++ } else { ++ /* Full scan to discover devices added before the driver was loaded. */ ++ tb_scan_switch(tb->root_switch); ++ /* Find out tunnels created by the boot firmware */ ++ tb_discover_tunnels(tb); ++ /* Add DP resources from the DP tunnels created by the boot firmware */ ++ tb_discover_dp_resources(tb); ++ } ++ + /* + * If the boot firmware did not create USB 3.x tunnels create them + * now for the whole topology. +@@ -2247,10 +2259,14 @@ static int tb_resume_noirq(struct tb *tb) + + tb_dbg(tb, "resuming...\n"); + +- /* remove any pci devices the firmware might have setup */ +- tb_switch_reset(tb->root_switch); ++ /* ++ * For non-USB4 hosts (Apple systems) remove any PCIe devices ++ * the firmware might have setup. ++ */ ++ if (!tb_switch_is_usb4(tb->root_switch)) ++ tb_switch_reset(tb->root_switch); + +- tb_switch_resume(tb->root_switch); ++ tb_switch_resume(tb->root_switch, false); + tb_free_invalid_tunnels(tb); + tb_free_unplugged_children(tb->root_switch); + tb_restore_children(tb->root_switch); +@@ -2376,7 +2392,7 @@ static int tb_runtime_resume(struct tb *tb) + struct tb_tunnel *tunnel, *n; + + mutex_lock(&tb->lock); +- tb_switch_resume(tb->root_switch); ++ tb_switch_resume(tb->root_switch, true); + tb_free_invalid_tunnels(tb); + tb_restore_children(tb->root_switch); + list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) +diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h +index 4893d2c7ac968..8a75aabb9ce8e 100644 +--- a/drivers/thunderbolt/tb.h ++++ b/drivers/thunderbolt/tb.h +@@ -488,7 +488,7 @@ struct tb_path { + */ + struct tb_cm_ops { + int (*driver_ready)(struct tb *tb); +- int (*start)(struct tb *tb); ++ int (*start)(struct tb *tb, bool reset); + void (*stop)(struct tb *tb); + int (*suspend_noirq)(struct tb *tb); + int (*resume_noirq)(struct tb *tb); +@@ -735,7 +735,7 @@ int tb_xdomain_init(void); + void tb_xdomain_exit(void); + + struct tb *tb_domain_alloc(struct tb_nhi *nhi, int timeout_msec, size_t privsize); +-int tb_domain_add(struct tb *tb); ++int tb_domain_add(struct tb *tb, bool reset); + void tb_domain_remove(struct tb *tb); + int tb_domain_suspend_noirq(struct tb *tb); + int tb_domain_resume_noirq(struct tb *tb); +@@ -802,7 +802,7 @@ int tb_switch_configuration_valid(struct tb_switch *sw); + int tb_switch_add(struct tb_switch *sw); + void tb_switch_remove(struct tb_switch *sw); + void tb_switch_suspend(struct tb_switch *sw, bool runtime); +-int tb_switch_resume(struct tb_switch *sw); ++int tb_switch_resume(struct tb_switch *sw, bool runtime); + int tb_switch_reset(struct tb_switch *sw); + int tb_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit, + u32 value, int timeout_msec); +@@ -1100,6 +1100,7 @@ struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid, + void tb_path_free(struct tb_path *path); + int tb_path_activate(struct tb_path *path); + void tb_path_deactivate(struct tb_path *path); ++int tb_path_deactivate_hop(struct tb_port *port, int hop_index); + bool tb_path_is_invalid(struct tb_path *path); + bool tb_path_port_on_path(const struct tb_path *path, + const struct tb_port *port); +@@ -1119,6 +1120,7 @@ int tb_drom_read(struct tb_switch *sw); + int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid); + + int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid); ++int tb_lc_reset_port(struct tb_port *port); + int tb_lc_configure_port(struct tb_port *port); + void tb_lc_unconfigure_port(struct tb_port *port); + int tb_lc_configure_xdomain(struct tb_port *port); +@@ -1222,6 +1224,7 @@ static inline bool tb_switch_is_usb4(const struct tb_switch *sw) + return usb4_switch_version(sw) > 0; + } + ++void usb4_switch_check_wakes(struct tb_switch *sw); + int usb4_switch_setup(struct tb_switch *sw); + int usb4_switch_configuration_valid(struct tb_switch *sw); + int usb4_switch_read_uid(struct tb_switch *sw, u64 *uid); +@@ -1251,6 +1254,7 @@ void usb4_switch_remove_ports(struct tb_switch *sw); + + int usb4_port_unlock(struct tb_port *port); + int usb4_port_hotplug_enable(struct tb_port *port); ++int usb4_port_reset(struct tb_port *port); + int usb4_port_configure(struct tb_port *port); + void usb4_port_unconfigure(struct tb_port *port); + int usb4_port_configure_xdomain(struct tb_port *port, struct tb_xdomain *xd); +diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h +index 580277dc91152..736e28beac119 100644 +--- a/drivers/thunderbolt/tb_regs.h ++++ b/drivers/thunderbolt/tb_regs.h +@@ -194,6 +194,8 @@ struct tb_regs_switch_header { + #define USB4_VERSION_MAJOR_MASK GENMASK(7, 5) + + #define ROUTER_CS_1 0x01 ++#define ROUTER_CS_3 0x03 ++#define ROUTER_CS_3_V BIT(31) + #define ROUTER_CS_4 0x04 + /* Used with the router cmuv field */ + #define ROUTER_CS_4_CMUV_V1 0x10 +@@ -383,6 +385,7 @@ struct tb_regs_port_header { + #define PORT_CS_18_WODS BIT(17) + #define PORT_CS_18_WOU4S BIT(18) + #define PORT_CS_19 0x13 ++#define PORT_CS_19_DPR BIT(0) + #define PORT_CS_19_PC BIT(3) + #define PORT_CS_19_PID BIT(4) + #define PORT_CS_19_WOC BIT(16) +@@ -579,6 +582,9 @@ struct tb_regs_hop { + #define TB_LC_POWER 0x740 + + /* Link controller registers */ ++#define TB_LC_PORT_MODE 0x26 ++#define TB_LC_PORT_MODE_DPR BIT(0) ++ + #define TB_LC_CS_42 0x2a + #define TB_LC_CS_42_USB_PLUGGED BIT(31) + +diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c +index 13c779e23011b..3aa32d7f9f6a1 100644 +--- a/drivers/thunderbolt/usb4.c ++++ b/drivers/thunderbolt/usb4.c +@@ -155,7 +155,13 @@ static inline int usb4_switch_op_data(struct tb_switch *sw, u16 opcode, + tx_dwords, rx_data, rx_dwords); + } + +-static void usb4_switch_check_wakes(struct tb_switch *sw) ++/** ++ * usb4_switch_check_wakes() - Check for wakes and notify PM core about them ++ * @sw: Router whose wakes to check ++ * ++ * Checks wakes occurred during suspend and notify the PM core about them. ++ */ ++void usb4_switch_check_wakes(struct tb_switch *sw) + { + bool wakeup_usb4 = false; + struct usb4_port *usb4; +@@ -163,9 +169,6 @@ static void usb4_switch_check_wakes(struct tb_switch *sw) + bool wakeup = false; + u32 val; + +- if (!device_may_wakeup(&sw->dev)) +- return; +- + if (tb_route(sw)) { + if (tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_6, 1)) + return; +@@ -244,8 +247,6 @@ int usb4_switch_setup(struct tb_switch *sw) + u32 val = 0; + int ret; + +- usb4_switch_check_wakes(sw); +- + if (!tb_route(sw)) + return 0; + +@@ -1113,6 +1114,45 @@ int usb4_port_hotplug_enable(struct tb_port *port) + return tb_port_write(port, &val, TB_CFG_PORT, ADP_CS_5, 1); + } + ++/** ++ * usb4_port_reset() - Issue downstream port reset ++ * @port: USB4 port to reset ++ * ++ * Issues downstream port reset to @port. ++ */ ++int usb4_port_reset(struct tb_port *port) ++{ ++ int ret; ++ u32 val; ++ ++ if (!port->cap_usb4) ++ return -EINVAL; ++ ++ ret = tb_port_read(port, &val, TB_CFG_PORT, ++ port->cap_usb4 + PORT_CS_19, 1); ++ if (ret) ++ return ret; ++ ++ val |= PORT_CS_19_DPR; ++ ++ ret = tb_port_write(port, &val, TB_CFG_PORT, ++ port->cap_usb4 + PORT_CS_19, 1); ++ if (ret) ++ return ret; ++ ++ fsleep(10000); ++ ++ ret = tb_port_read(port, &val, TB_CFG_PORT, ++ port->cap_usb4 + PORT_CS_19, 1); ++ if (ret) ++ return ret; ++ ++ val &= ~PORT_CS_19_DPR; ++ ++ return tb_port_write(port, &val, TB_CFG_PORT, ++ port->cap_usb4 + PORT_CS_19, 1); ++} ++ + static int usb4_port_set_configured(struct tb_port *port, bool configured) + { + int ret; +diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c +index 380a8b0590e34..777dc8a0aa835 100644 +--- a/drivers/tty/serial/mxs-auart.c ++++ b/drivers/tty/serial/mxs-auart.c +@@ -1080,11 +1080,13 @@ static void mxs_auart_set_ldisc(struct uart_port *port, + + static irqreturn_t mxs_auart_irq_handle(int irq, void *context) + { +- u32 istat; ++ u32 istat, stat; + struct mxs_auart_port *s = context; + u32 mctrl_temp = s->mctrl_prev; +- u32 stat = mxs_read(s, REG_STAT); + ++ uart_port_lock(&s->port); ++ ++ stat = mxs_read(s, REG_STAT); + istat = mxs_read(s, REG_INTR); + + /* ack irq */ +@@ -1120,6 +1122,8 @@ static irqreturn_t mxs_auart_irq_handle(int irq, void *context) + istat &= ~AUART_INTR_TXIS; + } + ++ uart_port_unlock(&s->port); ++ + return IRQ_HANDLED; + } + +diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c +index 13668ffdb1e7d..29bc80d39e8b7 100644 +--- a/drivers/tty/serial/pmac_zilog.c ++++ b/drivers/tty/serial/pmac_zilog.c +@@ -210,7 +210,6 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) + { + struct tty_port *port; + unsigned char ch, r1, drop, flag; +- int loops = 0; + + /* Sanity check, make sure the old bug is no longer happening */ + if (uap->port.state == NULL) { +@@ -291,24 +290,11 @@ static bool pmz_receive_chars(struct uart_pmac_port *uap) + if (r1 & Rx_OVR) + tty_insert_flip_char(port, 0, TTY_OVERRUN); + next_char: +- /* We can get stuck in an infinite loop getting char 0 when the +- * line is in a wrong HW state, we break that here. +- * When that happens, I disable the receive side of the driver. +- * Note that what I've been experiencing is a real irq loop where +- * I'm getting flooded regardless of the actual port speed. +- * Something strange is going on with the HW +- */ +- if ((++loops) > 1000) +- goto flood; + ch = read_zsreg(uap, R0); + if (!(ch & Rx_CH_AV)) + break; + } + +- return true; +- flood: +- pmz_interrupt_control(uap, 0); +- pmz_error("pmz: rx irq flood !\n"); + return true; + } + +diff --git a/drivers/tty/serial/serial_base.h b/drivers/tty/serial/serial_base.h +index c74c548f0db62..b6c38d2edfd40 100644 +--- a/drivers/tty/serial/serial_base.h ++++ b/drivers/tty/serial/serial_base.h +@@ -22,6 +22,7 @@ struct serial_ctrl_device { + struct serial_port_device { + struct device dev; + struct uart_port *port; ++ unsigned int tx_enabled:1; + }; + + int serial_base_ctrl_init(void); +@@ -30,6 +31,9 @@ void serial_base_ctrl_exit(void); + int serial_base_port_init(void); + void serial_base_port_exit(void); + ++void serial_base_port_startup(struct uart_port *port); ++void serial_base_port_shutdown(struct uart_port *port); ++ + int serial_base_driver_register(struct device_driver *driver); + void serial_base_driver_unregister(struct device_driver *driver); + +diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c +index 4c81210ad9b3a..2eceef54e0b30 100644 +--- a/drivers/tty/serial/serial_core.c ++++ b/drivers/tty/serial/serial_core.c +@@ -323,16 +323,26 @@ static int uart_startup(struct tty_struct *tty, struct uart_state *state, + bool init_hw) + { + struct tty_port *port = &state->port; ++ struct uart_port *uport; + int retval; + + if (tty_port_initialized(port)) +- return 0; ++ goto out_base_port_startup; + + retval = uart_port_startup(tty, state, init_hw); +- if (retval) ++ if (retval) { + set_bit(TTY_IO_ERROR, &tty->flags); ++ return retval; ++ } + +- return retval; ++out_base_port_startup: ++ uport = uart_port_check(state); ++ if (!uport) ++ return -EIO; ++ ++ serial_base_port_startup(uport); ++ ++ return 0; + } + + /* +@@ -355,6 +365,9 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); + ++ if (uport) ++ serial_base_port_shutdown(uport); ++ + if (tty_port_initialized(port)) { + tty_port_set_initialized(port, false); + +@@ -1769,6 +1782,7 @@ static void uart_tty_port_shutdown(struct tty_port *port) + uport->ops->stop_rx(uport); + spin_unlock_irq(&uport->lock); + ++ serial_base_port_shutdown(uport); + uart_port_shutdown(port); + + /* +@@ -1782,6 +1796,7 @@ static void uart_tty_port_shutdown(struct tty_port *port) + * Free the transmit buffer. + */ + spin_lock_irq(&uport->lock); ++ uart_circ_clear(&state->xmit); + buf = state->xmit.buf; + state->xmit.buf = NULL; + spin_unlock_irq(&uport->lock); +diff --git a/drivers/tty/serial/serial_port.c b/drivers/tty/serial/serial_port.c +index 0b3cef3c323b8..d622a9297f651 100644 +--- a/drivers/tty/serial/serial_port.c ++++ b/drivers/tty/serial/serial_port.c +@@ -36,8 +36,12 @@ static int serial_port_runtime_resume(struct device *dev) + + /* Flush any pending TX for the port */ + spin_lock_irqsave(&port->lock, flags); ++ if (!port_dev->tx_enabled) ++ goto unlock; + if (__serial_port_busy(port)) + port->ops->start_tx(port); ++ ++unlock: + spin_unlock_irqrestore(&port->lock, flags); + + out: +@@ -57,6 +61,11 @@ static int serial_port_runtime_suspend(struct device *dev) + return 0; + + uart_port_lock_irqsave(port, &flags); ++ if (!port_dev->tx_enabled) { ++ uart_port_unlock_irqrestore(port, flags); ++ return 0; ++ } ++ + busy = __serial_port_busy(port); + if (busy) + port->ops->start_tx(port); +@@ -68,6 +77,31 @@ static int serial_port_runtime_suspend(struct device *dev) + return busy ? -EBUSY : 0; + } + ++static void serial_base_port_set_tx(struct uart_port *port, ++ struct serial_port_device *port_dev, ++ bool enabled) ++{ ++ unsigned long flags; ++ ++ uart_port_lock_irqsave(port, &flags); ++ port_dev->tx_enabled = enabled; ++ uart_port_unlock_irqrestore(port, flags); ++} ++ ++void serial_base_port_startup(struct uart_port *port) ++{ ++ struct serial_port_device *port_dev = port->port_dev; ++ ++ serial_base_port_set_tx(port, port_dev, true); ++} ++ ++void serial_base_port_shutdown(struct uart_port *port) ++{ ++ struct serial_port_device *port_dev = port->port_dev; ++ ++ serial_base_port_set_tx(port, port_dev, false); ++} ++ + static DEFINE_RUNTIME_DEV_PM_OPS(serial_port_pm, + serial_port_runtime_suspend, + serial_port_runtime_resume, NULL); +diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c +index e5f933beb6c05..9ef90bb30a47e 100644 +--- a/drivers/tty/serial/stm32-usart.c ++++ b/drivers/tty/serial/stm32-usart.c +@@ -857,6 +857,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) + const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + u32 sr; + unsigned int size; ++ irqreturn_t ret = IRQ_NONE; + + sr = readl_relaxed(port->membase + ofs->isr); + +@@ -865,11 +866,14 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) + (sr & USART_SR_TC)) { + stm32_usart_tc_interrupt_disable(port); + stm32_usart_rs485_rts_disable(port); ++ ret = IRQ_HANDLED; + } + +- if ((sr & USART_SR_RTOF) && ofs->icr != UNDEF_REG) ++ if ((sr & USART_SR_RTOF) && ofs->icr != UNDEF_REG) { + writel_relaxed(USART_ICR_RTOCF, + port->membase + ofs->icr); ++ ret = IRQ_HANDLED; ++ } + + if ((sr & USART_SR_WUF) && ofs->icr != UNDEF_REG) { + /* Clear wake up flag and disable wake up interrupt */ +@@ -878,6 +882,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) + stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_WUFIE); + if (irqd_is_wakeup_set(irq_get_irq_data(port->irq))) + pm_wakeup_event(tport->tty->dev, 0); ++ ret = IRQ_HANDLED; + } + + /* +@@ -892,6 +897,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) + uart_unlock_and_check_sysrq(port); + if (size) + tty_flip_buffer_push(tport); ++ ret = IRQ_HANDLED; + } + } + +@@ -899,6 +905,7 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) + spin_lock(&port->lock); + stm32_usart_transmit_chars(port); + spin_unlock(&port->lock); ++ ret = IRQ_HANDLED; + } + + /* Receiver timeout irq for DMA RX */ +@@ -908,9 +915,10 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr) + uart_unlock_and_check_sysrq(port); + if (size) + tty_flip_buffer_push(tport); ++ ret = IRQ_HANDLED; + } + +- return IRQ_HANDLED; ++ return ret; + } + + static void stm32_usart_set_mctrl(struct uart_port *port, unsigned int mctrl) +@@ -1069,6 +1077,7 @@ static int stm32_usart_startup(struct uart_port *port) + val |= USART_CR2_SWAP; + writel_relaxed(val, port->membase + ofs->cr2); + } ++ stm32_port->throttled = false; + + /* RX FIFO Flush */ + if (ofs->rqr != UNDEF_REG) +diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c +index 0cbe14aca8774..797219db026bc 100644 +--- a/drivers/ufs/host/ufs-qcom.c ++++ b/drivers/ufs/host/ufs-qcom.c +@@ -47,7 +47,7 @@ enum { + TSTBUS_MAX, + }; + +-#define QCOM_UFS_MAX_GEAR 4 ++#define QCOM_UFS_MAX_GEAR 5 + #define QCOM_UFS_MAX_LANE 2 + + enum { +@@ -67,26 +67,32 @@ static const struct __ufs_qcom_bw_table { + [MODE_PWM][UFS_PWM_G2][UFS_LANE_1] = { 1844, 1000 }, + [MODE_PWM][UFS_PWM_G3][UFS_LANE_1] = { 3688, 1000 }, + [MODE_PWM][UFS_PWM_G4][UFS_LANE_1] = { 7376, 1000 }, ++ [MODE_PWM][UFS_PWM_G5][UFS_LANE_1] = { 14752, 1000 }, + [MODE_PWM][UFS_PWM_G1][UFS_LANE_2] = { 1844, 1000 }, + [MODE_PWM][UFS_PWM_G2][UFS_LANE_2] = { 3688, 1000 }, + [MODE_PWM][UFS_PWM_G3][UFS_LANE_2] = { 7376, 1000 }, + [MODE_PWM][UFS_PWM_G4][UFS_LANE_2] = { 14752, 1000 }, ++ [MODE_PWM][UFS_PWM_G5][UFS_LANE_2] = { 29504, 1000 }, + [MODE_HS_RA][UFS_HS_G1][UFS_LANE_1] = { 127796, 1000 }, + [MODE_HS_RA][UFS_HS_G2][UFS_LANE_1] = { 255591, 1000 }, + [MODE_HS_RA][UFS_HS_G3][UFS_LANE_1] = { 1492582, 102400 }, + [MODE_HS_RA][UFS_HS_G4][UFS_LANE_1] = { 2915200, 204800 }, ++ [MODE_HS_RA][UFS_HS_G5][UFS_LANE_1] = { 5836800, 409600 }, + [MODE_HS_RA][UFS_HS_G1][UFS_LANE_2] = { 255591, 1000 }, + [MODE_HS_RA][UFS_HS_G2][UFS_LANE_2] = { 511181, 1000 }, + [MODE_HS_RA][UFS_HS_G3][UFS_LANE_2] = { 1492582, 204800 }, + [MODE_HS_RA][UFS_HS_G4][UFS_LANE_2] = { 2915200, 409600 }, ++ [MODE_HS_RA][UFS_HS_G5][UFS_LANE_2] = { 5836800, 819200 }, + [MODE_HS_RB][UFS_HS_G1][UFS_LANE_1] = { 149422, 1000 }, + [MODE_HS_RB][UFS_HS_G2][UFS_LANE_1] = { 298189, 1000 }, + [MODE_HS_RB][UFS_HS_G3][UFS_LANE_1] = { 1492582, 102400 }, + [MODE_HS_RB][UFS_HS_G4][UFS_LANE_1] = { 2915200, 204800 }, ++ [MODE_HS_RB][UFS_HS_G5][UFS_LANE_1] = { 5836800, 409600 }, + [MODE_HS_RB][UFS_HS_G1][UFS_LANE_2] = { 298189, 1000 }, + [MODE_HS_RB][UFS_HS_G2][UFS_LANE_2] = { 596378, 1000 }, + [MODE_HS_RB][UFS_HS_G3][UFS_LANE_2] = { 1492582, 204800 }, + [MODE_HS_RB][UFS_HS_G4][UFS_LANE_2] = { 2915200, 409600 }, ++ [MODE_HS_RB][UFS_HS_G5][UFS_LANE_2] = { 5836800, 819200 }, + [MODE_MAX][0][0] = { 7643136, 307200 }, + }; + +diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c +index c8262e2f29177..c553decb54610 100644 +--- a/drivers/usb/class/cdc-wdm.c ++++ b/drivers/usb/class/cdc-wdm.c +@@ -485,7 +485,6 @@ static ssize_t wdm_write + static int service_outstanding_interrupt(struct wdm_device *desc) + { + int rv = 0; +- int used; + + /* submit read urb only if the device is waiting for it */ + if (!desc->resp_count || !--desc->resp_count) +@@ -500,10 +499,7 @@ static int service_outstanding_interrupt(struct wdm_device *desc) + goto out; + } + +- used = test_and_set_bit(WDM_RESPONDING, &desc->flags); +- if (used) +- goto out; +- ++ set_bit(WDM_RESPONDING, &desc->flags); + spin_unlock_irq(&desc->iuspin); + rv = usb_submit_urb(desc->response, GFP_KERNEL); + spin_lock_irq(&desc->iuspin); +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +index 9a3da5d7fe1bc..7417972202b8b 100644 +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -60,6 +60,12 @@ + #define USB_PING_RESPONSE_TIME 400 /* ns */ + #define USB_REDUCE_FRAME_INTR_BINTERVAL 9 + ++/* ++ * The SET_ADDRESS request timeout will be 500 ms when ++ * USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT quirk flag is set. ++ */ ++#define USB_SHORT_SET_ADDRESS_REQ_TIMEOUT 500 /* ms */ ++ + /* Protect struct usb_device->state and ->children members + * Note: Both are also protected by ->dev.sem, except that ->state can + * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ +@@ -4663,7 +4669,12 @@ EXPORT_SYMBOL_GPL(usb_ep0_reinit); + static int hub_set_address(struct usb_device *udev, int devnum) + { + int retval; ++ unsigned int timeout_ms = USB_CTRL_SET_TIMEOUT; + struct usb_hcd *hcd = bus_to_hcd(udev->bus); ++ struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); ++ ++ if (hub->hdev->quirks & USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT) ++ timeout_ms = USB_SHORT_SET_ADDRESS_REQ_TIMEOUT; + + /* + * The host controller will choose the device address, +@@ -4676,11 +4687,11 @@ static int hub_set_address(struct usb_device *udev, int devnum) + if (udev->state != USB_STATE_DEFAULT) + return -EINVAL; + if (hcd->driver->address_device) +- retval = hcd->driver->address_device(hcd, udev); ++ retval = hcd->driver->address_device(hcd, udev, timeout_ms); + else + retval = usb_control_msg(udev, usb_sndaddr0pipe(), + USB_REQ_SET_ADDRESS, 0, devnum, 0, +- NULL, 0, USB_CTRL_SET_TIMEOUT); ++ NULL, 0, timeout_ms); + if (retval == 0) { + update_devnum(udev, devnum); + /* Device now using proper address. */ +diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c +index 10f2b1e499b8a..76e00bfedc25c 100644 +--- a/drivers/usb/core/port.c ++++ b/drivers/usb/core/port.c +@@ -448,8 +448,10 @@ static void usb_port_shutdown(struct device *dev) + { + struct usb_port *port_dev = to_usb_port(dev); + +- if (port_dev->child) ++ if (port_dev->child) { + usb_disable_usb2_hardware_lpm(port_dev->child); ++ usb_unlocked_disable_lpm(port_dev->child); ++ } + } + + static const struct dev_pm_ops usb_port_pm_ops = { +diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c +index 15e9bd180a1d2..b4783574b8e66 100644 +--- a/drivers/usb/core/quirks.c ++++ b/drivers/usb/core/quirks.c +@@ -138,6 +138,9 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp) + case 'o': + flags |= USB_QUIRK_HUB_SLOW_RESET; + break; ++ case 'p': ++ flags |= USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT; ++ break; + /* Ignore unrecognized flag characters */ + } + } +@@ -527,6 +530,10 @@ static const struct usb_device_id usb_quirk_list[] = { + + { USB_DEVICE(0x2386, 0x350e), .driver_info = USB_QUIRK_NO_LPM }, + ++ /* APTIV AUTOMOTIVE HUB */ ++ { USB_DEVICE(0x2c48, 0x0132), .driver_info = ++ USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT }, ++ + /* DJI CineSSD */ + { USB_DEVICE(0x2ca3, 0x0031), .driver_info = USB_QUIRK_NO_LPM }, + +diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c +index 79582b102c7ed..994a78ad084b1 100644 +--- a/drivers/usb/dwc2/hcd_ddma.c ++++ b/drivers/usb/dwc2/hcd_ddma.c +@@ -867,13 +867,15 @@ static int dwc2_cmpl_host_isoc_dma_desc(struct dwc2_hsotg *hsotg, + struct dwc2_dma_desc *dma_desc; + struct dwc2_hcd_iso_packet_desc *frame_desc; + u16 frame_desc_idx; +- struct urb *usb_urb = qtd->urb->priv; ++ struct urb *usb_urb; + u16 remain = 0; + int rc = 0; + + if (!qtd->urb) + return -EINVAL; + ++ usb_urb = qtd->urb->priv; ++ + dma_sync_single_for_cpu(hsotg->dev, qh->desc_list_dma + (idx * + sizeof(struct dwc2_dma_desc)), + sizeof(struct dwc2_dma_desc), +diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c +index f3456b8bf4152..f5731d465cd7b 100644 +--- a/drivers/usb/gadget/function/f_ncm.c ++++ b/drivers/usb/gadget/function/f_ncm.c +@@ -869,7 +869,7 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) + if (alt > 1) + goto fail; + +- if (ncm->port.in_ep->enabled) { ++ if (ncm->netdev) { + DBG(cdev, "reset ncm\n"); + ncm->netdev = NULL; + gether_disconnect(&ncm->port); +@@ -1354,7 +1354,7 @@ static void ncm_disable(struct usb_function *f) + + DBG(cdev, "ncm deactivated\n"); + +- if (ncm->port.in_ep->enabled) { ++ if (ncm->netdev) { + ncm->netdev = NULL; + gether_disconnect(&ncm->port); + } +diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c +index 0a37f0d511cf5..b1e3fa54c6397 100644 +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -1729,6 +1729,8 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, + } + + command->status = 0; ++ /* set default timeout to 5000 ms */ ++ command->timeout_ms = XHCI_CMD_DEFAULT_TIMEOUT; + INIT_LIST_HEAD(&command->cmd_list); + return command; + } +diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c +index 3ec1a2617ed7e..c959d9144baa5 100644 +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -372,9 +372,10 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci) + readl(&xhci->dba->doorbell[0]); + } + +-static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay) ++static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci) + { +- return mod_delayed_work(system_wq, &xhci->cmd_timer, delay); ++ return mod_delayed_work(system_wq, &xhci->cmd_timer, ++ msecs_to_jiffies(xhci->current_cmd->timeout_ms)); + } + + static struct xhci_command *xhci_next_queued_cmd(struct xhci_hcd *xhci) +@@ -418,7 +419,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, + if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) && + !(xhci->xhc_state & XHCI_STATE_DYING)) { + xhci->current_cmd = cur_cmd; +- xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); ++ xhci_mod_cmd_timer(xhci); + xhci_ring_cmd_db(xhci); + } + } +@@ -1792,7 +1793,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, + if (!list_is_singular(&xhci->cmd_list)) { + xhci->current_cmd = list_first_entry(&cmd->cmd_list, + struct xhci_command, cmd_list); +- xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); ++ xhci_mod_cmd_timer(xhci); + } else if (xhci->current_cmd == cmd) { + xhci->current_cmd = NULL; + } +@@ -4359,7 +4360,7 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, + /* if there are no other commands queued we start the timeout timer */ + if (list_empty(&xhci->cmd_list)) { + xhci->current_cmd = cmd; +- xhci_mod_cmd_timer(xhci, XHCI_CMD_DEFAULT_TIMEOUT); ++ xhci_mod_cmd_timer(xhci); + } + + list_add_tail(&cmd->cmd_list, &xhci->cmd_list); +diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c +index c4c733d724bd8..573b5784d1c3d 100644 +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -4007,12 +4007,18 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) + return 0; + } + +-/* +- * Issue an Address Device command and optionally send a corresponding +- * SetAddress request to the device. ++/** ++ * xhci_setup_device - issues an Address Device command to assign a unique ++ * USB bus address. ++ * @hcd: USB host controller data structure. ++ * @udev: USB dev structure representing the connected device. ++ * @setup: Enum specifying setup mode: address only or with context. ++ * @timeout_ms: Max wait time (ms) for the command operation to complete. ++ * ++ * Return: 0 if successful; otherwise, negative error code. + */ + static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, +- enum xhci_setup_dev setup) ++ enum xhci_setup_dev setup, unsigned int timeout_ms) + { + const char *act = setup == SETUP_CONTEXT_ONLY ? "context" : "address"; + unsigned long flags; +@@ -4069,6 +4075,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, + } + + command->in_ctx = virt_dev->in_ctx; ++ command->timeout_ms = timeout_ms; + + slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); + ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx); +@@ -4195,14 +4202,16 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, + return ret; + } + +-static int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) ++static int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev, ++ unsigned int timeout_ms) + { +- return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS); ++ return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS, timeout_ms); + } + + static int xhci_enable_device(struct usb_hcd *hcd, struct usb_device *udev) + { +- return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ONLY); ++ return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ONLY, ++ XHCI_CMD_DEFAULT_TIMEOUT); + } + + /* +diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h +index 31088602c0708..be480d6ac8586 100644 +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -818,6 +818,8 @@ struct xhci_command { + struct completion *completion; + union xhci_trb *command_trb; + struct list_head cmd_list; ++ /* xHCI command response timeout in milliseconds */ ++ unsigned int timeout_ms; + }; + + /* drop context bitmasks */ +@@ -1577,8 +1579,11 @@ struct xhci_td { + unsigned int num_trbs; + }; + +-/* xHCI command default timeout value */ +-#define XHCI_CMD_DEFAULT_TIMEOUT (5 * HZ) ++/* ++ * xHCI command default timeout value in milliseconds. ++ * USB 3.2 spec, section 9.2.6.1 ++ */ ++#define XHCI_CMD_DEFAULT_TIMEOUT 5000 + + /* command descriptor */ + struct xhci_cd { +diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c +index 55a65d941ccbf..8a5846d4adf67 100644 +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -255,6 +255,10 @@ static void option_instat_callback(struct urb *urb); + #define QUECTEL_PRODUCT_EM061K_LMS 0x0124 + #define QUECTEL_PRODUCT_EC25 0x0125 + #define QUECTEL_PRODUCT_EM060K_128 0x0128 ++#define QUECTEL_PRODUCT_EM060K_129 0x0129 ++#define QUECTEL_PRODUCT_EM060K_12a 0x012a ++#define QUECTEL_PRODUCT_EM060K_12b 0x012b ++#define QUECTEL_PRODUCT_EM060K_12c 0x012c + #define QUECTEL_PRODUCT_EG91 0x0191 + #define QUECTEL_PRODUCT_EG95 0x0195 + #define QUECTEL_PRODUCT_BG96 0x0296 +@@ -1218,6 +1222,18 @@ static const struct usb_device_id option_ids[] = { + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_128, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_128, 0xff, 0x00, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_128, 0xff, 0xff, 0x40) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_129, 0xff, 0xff, 0x30) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_129, 0xff, 0x00, 0x40) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_129, 0xff, 0xff, 0x40) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_12a, 0xff, 0xff, 0x30) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_12a, 0xff, 0x00, 0x40) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_12a, 0xff, 0xff, 0x40) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_12b, 0xff, 0xff, 0x30) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_12b, 0xff, 0x00, 0x40) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_12b, 0xff, 0xff, 0x40) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_12c, 0xff, 0xff, 0x30) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_12c, 0xff, 0x00, 0x40) }, ++ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K_12c, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LCN, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LCN, 0xff, 0x00, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM061K_LCN, 0xff, 0xff, 0x40) }, +@@ -1360,6 +1376,12 @@ static const struct usb_device_id option_ids[] = { + .driver_info = NCTRL(2) | RSVD(3) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1083, 0xff), /* Telit FE990 (ECM) */ + .driver_info = NCTRL(0) | RSVD(1) }, ++ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a0, 0xff), /* Telit FN20C04 (rmnet) */ ++ .driver_info = RSVD(0) | NCTRL(3) }, ++ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a4, 0xff), /* Telit FN20C04 (rmnet) */ ++ .driver_info = RSVD(0) | NCTRL(3) }, ++ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a9, 0xff), /* Telit FN20C04 (rmnet) */ ++ .driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910), + .driver_info = NCTRL(0) | RSVD(1) | RSVD(3) }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM), +@@ -2052,6 +2074,10 @@ static const struct usb_device_id option_ids[] = { + .driver_info = RSVD(3) }, + { USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9803, 0xff), + .driver_info = RSVD(4) }, ++ { USB_DEVICE(LONGCHEER_VENDOR_ID, 0x9b05), /* Longsung U8300 */ ++ .driver_info = RSVD(4) | RSVD(5) }, ++ { USB_DEVICE(LONGCHEER_VENDOR_ID, 0x9b3c), /* Longsung U9300 */ ++ .driver_info = RSVD(0) | RSVD(4) }, + { USB_DEVICE(LONGCHEER_VENDOR_ID, ZOOM_PRODUCT_4597) }, + { USB_DEVICE(LONGCHEER_VENDOR_ID, IBALL_3_5G_CONNECT) }, + { USB_DEVICE(HAIER_VENDOR_ID, HAIER_PRODUCT_CE100) }, +@@ -2272,15 +2298,29 @@ static const struct usb_device_id option_ids[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x2cb7, 0x010b, 0xff, 0xff, 0x30) }, /* Fibocom FG150 Diag */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2cb7, 0x010b, 0xff, 0, 0) }, /* Fibocom FG150 AT */ + { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0111, 0xff) }, /* Fibocom FM160 (MBIM mode) */ ++ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0115, 0xff), /* Fibocom FM135 (laptop MBIM) */ ++ .driver_info = RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a0, 0xff) }, /* Fibocom NL668-AM/NL652-EU (laptop MBIM) */ + { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a2, 0xff) }, /* Fibocom FM101-GL (laptop MBIM) */ + { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a3, 0xff) }, /* Fibocom FM101-GL (laptop MBIM) */ + { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a4, 0xff), /* Fibocom FM101-GL (laptop MBIM) */ + .driver_info = RSVD(4) }, ++ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0a04, 0xff) }, /* Fibocom FM650-CN (ECM mode) */ ++ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0a05, 0xff) }, /* Fibocom FM650-CN (NCM mode) */ ++ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0a06, 0xff) }, /* Fibocom FM650-CN (RNDIS mode) */ ++ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0a07, 0xff) }, /* Fibocom FM650-CN (MBIM mode) */ + { USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */ + { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */ + { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */ + { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1406, 0xff) }, /* GosunCn GM500 ECM/NCM */ ++ { USB_DEVICE(0x33f8, 0x0104), /* Rolling RW101-GL (laptop RMNET) */ ++ .driver_info = RSVD(4) | RSVD(5) }, ++ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x01a2, 0xff) }, /* Rolling RW101-GL (laptop MBIM) */ ++ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x01a3, 0xff) }, /* Rolling RW101-GL (laptop MBIM) */ ++ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x01a4, 0xff), /* Rolling RW101-GL (laptop MBIM) */ ++ .driver_info = RSVD(4) }, ++ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0115, 0xff), /* Rolling RW135-GL (laptop MBIM) */ ++ .driver_info = RSVD(5) }, + { USB_DEVICE_AND_INTERFACE_INFO(OPPO_VENDOR_ID, OPPO_PRODUCT_R11, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0xff, 0x40) }, +diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c +index f4863078f7fe5..da64bb7325dbc 100644 +--- a/fs/ceph/addr.c ++++ b/fs/ceph/addr.c +@@ -229,7 +229,7 @@ static void ceph_netfs_expand_readahead(struct netfs_io_request *rreq) + static bool ceph_netfs_clamp_length(struct netfs_io_subrequest *subreq) + { + struct inode *inode = subreq->rreq->inode; +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_inode_info *ci = ceph_inode(inode); + u64 objno, objoff; + u32 xlen; +@@ -244,7 +244,7 @@ static bool ceph_netfs_clamp_length(struct netfs_io_subrequest *subreq) + static void finish_netfs_read(struct ceph_osd_request *req) + { + struct inode *inode = req->r_inode; +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_osd_data *osd_data = osd_req_op_extent_osd_data(req, 0); + struct netfs_io_subrequest *subreq = req->r_priv; + struct ceph_osd_req_op *op = &req->r_ops[0]; +@@ -348,7 +348,7 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq) + struct netfs_io_request *rreq = subreq->rreq; + struct inode *inode = rreq->inode; + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_osd_request *req = NULL; + struct ceph_vino vino = ceph_vino(inode); + struct iov_iter iter; +@@ -658,7 +658,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) + struct folio *folio = page_folio(page); + struct inode *inode = page->mapping->host; + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_snap_context *snapc, *oldest; + loff_t page_off = page_offset(page); + int err; +@@ -803,8 +803,10 @@ static int ceph_writepage(struct page *page, struct writeback_control *wbc) + ihold(inode); + + if (wbc->sync_mode == WB_SYNC_NONE && +- ceph_inode_to_client(inode)->write_congested) ++ ceph_inode_to_fs_client(inode)->write_congested) { ++ redirty_page_for_writepage(wbc, page); + return AOP_WRITEPAGE_ACTIVATE; ++ } + + wait_on_page_fscache(page); + +@@ -836,7 +838,7 @@ static void writepages_finish(struct ceph_osd_request *req) + int rc = req->r_result; + struct ceph_snap_context *snapc = req->r_snapc; + struct address_space *mapping = inode->i_mapping; +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + unsigned int len = 0; + bool remove_page; + +@@ -926,7 +928,7 @@ static int ceph_writepages_start(struct address_space *mapping, + { + struct inode *inode = mapping->host; + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_vino vino = ceph_vino(inode); + pgoff_t index, start_index, end = -1; + struct ceph_snap_context *snapc = NULL, *last_snapc = NULL, *pgsnapc; +@@ -1823,7 +1825,7 @@ int ceph_uninline_data(struct file *file) + { + struct inode *inode = file_inode(file); + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_osd_request *req = NULL; + struct ceph_cap_flush *prealloc_cf = NULL; + struct folio *folio = NULL; +@@ -1977,7 +1979,7 @@ enum { + static int __ceph_pool_perm_get(struct ceph_inode_info *ci, + s64 pool, struct ceph_string *pool_ns) + { +- struct ceph_fs_client *fsc = ceph_inode_to_client(&ci->netfs.inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(&ci->netfs.inode); + struct ceph_mds_client *mdsc = fsc->mdsc; + struct ceph_osd_request *rd_req = NULL, *wr_req = NULL; + struct rb_node **p, *parent; +@@ -2168,7 +2170,7 @@ int ceph_pool_perm_check(struct inode *inode, int need) + return 0; + } + +- if (ceph_test_mount_opt(ceph_inode_to_client(inode), ++ if (ceph_test_mount_opt(ceph_inode_to_fs_client(inode), + NOPOOLPERM)) + return 0; + +diff --git a/fs/ceph/cache.c b/fs/ceph/cache.c +index de1dee46d3df7..930fbd54d2c8c 100644 +--- a/fs/ceph/cache.c ++++ b/fs/ceph/cache.c +@@ -15,7 +15,7 @@ + void ceph_fscache_register_inode_cookie(struct inode *inode) + { + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + + /* No caching for filesystem? */ + if (!fsc->fscache) +diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c +index fc9f8f1a9036d..00045b8eadd14 100644 +--- a/fs/ceph/caps.c ++++ b/fs/ceph/caps.c +@@ -635,7 +635,7 @@ void ceph_add_cap(struct inode *inode, + unsigned seq, unsigned mseq, u64 realmino, int flags, + struct ceph_cap **new_cap) + { +- struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_inode_to_fs_client(inode)->mdsc; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_cap *cap; + int mds = session->s_mds; +@@ -922,7 +922,7 @@ int __ceph_caps_issued_mask(struct ceph_inode_info *ci, int mask, int touch) + int __ceph_caps_issued_mask_metric(struct ceph_inode_info *ci, int mask, + int touch) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(ci->netfs.inode.i_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(ci->netfs.inode.i_sb); + int r; + + r = __ceph_caps_issued_mask(ci, mask, touch); +@@ -996,7 +996,7 @@ int __ceph_caps_file_wanted(struct ceph_inode_info *ci) + const int WR_SHIFT = ffs(CEPH_FILE_MODE_WR); + const int LAZY_SHIFT = ffs(CEPH_FILE_MODE_LAZY); + struct ceph_mount_options *opt = +- ceph_inode_to_client(&ci->netfs.inode)->mount_options; ++ ceph_inode_to_fs_client(&ci->netfs.inode)->mount_options; + unsigned long used_cutoff = jiffies - opt->caps_wanted_delay_max * HZ; + unsigned long idle_cutoff = jiffies - opt->caps_wanted_delay_min * HZ; + +@@ -1121,7 +1121,7 @@ void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release) + + dout("__ceph_remove_cap %p from %p\n", cap, &ci->netfs.inode); + +- mdsc = ceph_inode_to_client(&ci->netfs.inode)->mdsc; ++ mdsc = ceph_inode_to_fs_client(&ci->netfs.inode)->mdsc; + + /* remove from inode's cap rbtree, and clear auth cap */ + rb_erase(&cap->ci_node, &ci->i_caps); +@@ -1178,7 +1178,8 @@ void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release) + } + } + +-void ceph_remove_cap(struct ceph_cap *cap, bool queue_release) ++void ceph_remove_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, ++ bool queue_release) + { + struct ceph_inode_info *ci = cap->ci; + struct ceph_fs_client *fsc; +@@ -1191,7 +1192,7 @@ void ceph_remove_cap(struct ceph_cap *cap, bool queue_release) + + lockdep_assert_held(&ci->i_ceph_lock); + +- fsc = ceph_inode_to_client(&ci->netfs.inode); ++ fsc = ceph_inode_to_fs_client(&ci->netfs.inode); + WARN_ON_ONCE(ci->i_auth_cap == cap && + !list_empty(&ci->i_dirty_item) && + !fsc->blocklisted && +@@ -1342,6 +1343,8 @@ static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg) + */ + void __ceph_remove_caps(struct ceph_inode_info *ci) + { ++ struct inode *inode = &ci->netfs.inode; ++ struct ceph_mds_client *mdsc = ceph_inode_to_fs_client(inode)->mdsc; + struct rb_node *p; + + /* lock i_ceph_lock, because ceph_d_revalidate(..., LOOKUP_RCU) +@@ -1351,7 +1354,7 @@ void __ceph_remove_caps(struct ceph_inode_info *ci) + while (p) { + struct ceph_cap *cap = rb_entry(p, struct ceph_cap, ci_node); + p = rb_next(p); +- ceph_remove_cap(cap, true); ++ ceph_remove_cap(mdsc, cap, true); + } + spin_unlock(&ci->i_ceph_lock); + } +@@ -1686,7 +1689,7 @@ void ceph_flush_snaps(struct ceph_inode_info *ci, + struct ceph_mds_session **psession) + { + struct inode *inode = &ci->netfs.inode; +- struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_inode_to_fs_client(inode)->mdsc; + struct ceph_mds_session *session = NULL; + bool need_put = false; + int mds; +@@ -1751,7 +1754,7 @@ int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask, + struct ceph_cap_flush **pcf) + { + struct ceph_mds_client *mdsc = +- ceph_sb_to_client(ci->netfs.inode.i_sb)->mdsc; ++ ceph_sb_to_fs_client(ci->netfs.inode.i_sb)->mdsc; + struct inode *inode = &ci->netfs.inode; + int was = ci->i_dirty_caps; + int dirty = 0; +@@ -1874,7 +1877,7 @@ static u64 __mark_caps_flushing(struct inode *inode, + struct ceph_mds_session *session, bool wake, + u64 *oldest_flush_tid) + { +- struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_cap_flush *cf = NULL; + int flushing; +@@ -2233,7 +2236,7 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags) + */ + static int try_flush_caps(struct inode *inode, u64 *ptid) + { +- struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; + struct ceph_inode_info *ci = ceph_inode(inode); + int flushing = 0; + u64 flush_tid = 0, oldest_flush_tid = 0; +@@ -2311,7 +2314,7 @@ static int caps_are_flushed(struct inode *inode, u64 flush_tid) + */ + static int flush_mdlog_and_wait_inode_unsafe_requests(struct inode *inode) + { +- struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_mds_request *req1 = NULL, *req2 = NULL; + int ret, err = 0; +@@ -2494,7 +2497,7 @@ int ceph_write_inode(struct inode *inode, struct writeback_control *wbc) + caps_are_flushed(inode, flush_tid)); + } else { + struct ceph_mds_client *mdsc = +- ceph_sb_to_client(inode->i_sb)->mdsc; ++ ceph_sb_to_fs_client(inode->i_sb)->mdsc; + + spin_lock(&ci->i_ceph_lock); + if (__ceph_caps_dirty(ci)) +@@ -2747,7 +2750,7 @@ static int try_get_cap_refs(struct inode *inode, int need, int want, + loff_t endoff, int flags, int *got) + { + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_inode_to_fs_client(inode)->mdsc; + int ret = 0; + int have, implemented; + bool snap_rwsem_locked = false; +@@ -2965,7 +2968,7 @@ int __ceph_get_caps(struct inode *inode, struct ceph_file_info *fi, int need, + int want, loff_t endoff, int *got) + { + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + int ret, _got, flags; + + ret = ceph_pool_perm_check(inode, need); +@@ -3728,7 +3731,7 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid, + __releases(ci->i_ceph_lock) + { + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; + struct ceph_cap_flush *cf, *tmp_cf; + LIST_HEAD(to_remove); + unsigned seq = le32_to_cpu(m->seq); +@@ -3834,7 +3837,7 @@ void __ceph_remove_capsnap(struct inode *inode, struct ceph_cap_snap *capsnap, + bool *wake_ci, bool *wake_mdsc) + { + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; + bool ret; + + lockdep_assert_held(&ci->i_ceph_lock); +@@ -3878,7 +3881,7 @@ static void handle_cap_flushsnap_ack(struct inode *inode, u64 flush_tid, + struct ceph_mds_session *session) + { + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; + u64 follows = le64_to_cpu(m->snap_follows); + struct ceph_cap_snap *capsnap = NULL, *iter; + bool wake_ci = false; +@@ -3970,7 +3973,7 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex, + struct ceph_mds_cap_peer *ph, + struct ceph_mds_session *session) + { +- struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_inode_to_fs_client(inode)->mdsc; + struct ceph_mds_session *tsession = NULL; + struct ceph_cap *cap, *tcap, *new_cap = NULL; + struct ceph_inode_info *ci = ceph_inode(inode); +@@ -4000,7 +4003,7 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex, + goto out_unlock; + + if (target < 0) { +- ceph_remove_cap(cap, false); ++ ceph_remove_cap(mdsc, cap, false); + goto out_unlock; + } + +@@ -4035,7 +4038,7 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex, + change_auth_cap_ses(ci, tcap->session); + } + } +- ceph_remove_cap(cap, false); ++ ceph_remove_cap(mdsc, cap, false); + goto out_unlock; + } else if (tsession) { + /* add placeholder for the export tagert */ +@@ -4052,7 +4055,7 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex, + spin_unlock(&mdsc->cap_dirty_lock); + } + +- ceph_remove_cap(cap, false); ++ ceph_remove_cap(mdsc, cap, false); + goto out_unlock; + } + +@@ -4165,7 +4168,7 @@ static void handle_cap_import(struct ceph_mds_client *mdsc, + ocap->mseq, mds, le32_to_cpu(ph->seq), + le32_to_cpu(ph->mseq)); + } +- ceph_remove_cap(ocap, (ph->flags & CEPH_CAP_FLAG_RELEASE)); ++ ceph_remove_cap(mdsc, ocap, (ph->flags & CEPH_CAP_FLAG_RELEASE)); + } + + *old_issued = issued; +@@ -4673,7 +4676,7 @@ int ceph_drop_caps_for_unlink(struct inode *inode) + + if (__ceph_caps_dirty(ci)) { + struct ceph_mds_client *mdsc = +- ceph_inode_to_client(inode)->mdsc; ++ ceph_inode_to_fs_client(inode)->mdsc; + __cap_delay_requeue_front(mdsc, ci); + } + } +@@ -4853,7 +4856,7 @@ static int remove_capsnaps(struct ceph_mds_client *mdsc, struct inode *inode) + + int ceph_purge_inode_cap(struct inode *inode, struct ceph_cap *cap, bool *invalidate) + { +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_mds_client *mdsc = fsc->mdsc; + struct ceph_inode_info *ci = ceph_inode(inode); + bool is_auth; +diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c +index 5b5112c784629..08c3856107316 100644 +--- a/fs/ceph/crypto.c ++++ b/fs/ceph/crypto.c +@@ -129,7 +129,7 @@ static bool ceph_crypt_empty_dir(struct inode *inode) + + static const union fscrypt_policy *ceph_get_dummy_policy(struct super_block *sb) + { +- return ceph_sb_to_client(sb)->fsc_dummy_enc_policy.policy; ++ return ceph_sb_to_fs_client(sb)->fsc_dummy_enc_policy.policy; + } + + static struct fscrypt_operations ceph_fscrypt_ops = { +diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c +index 3904333fa6c38..2f1e7498cd745 100644 +--- a/fs/ceph/debugfs.c ++++ b/fs/ceph/debugfs.c +@@ -81,7 +81,7 @@ static int mdsc_show(struct seq_file *s, void *p) + if (req->r_inode) { + seq_printf(s, " #%llx", ceph_ino(req->r_inode)); + } else if (req->r_dentry) { +- path = ceph_mdsc_build_path(req->r_dentry, &pathlen, ++ path = ceph_mdsc_build_path(mdsc, req->r_dentry, &pathlen, + &pathbase, 0); + if (IS_ERR(path)) + path = NULL; +@@ -100,7 +100,7 @@ static int mdsc_show(struct seq_file *s, void *p) + } + + if (req->r_old_dentry) { +- path = ceph_mdsc_build_path(req->r_old_dentry, &pathlen, ++ path = ceph_mdsc_build_path(mdsc, req->r_old_dentry, &pathlen, + &pathbase, 0); + if (IS_ERR(path)) + path = NULL; +diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c +index 854cbdd666619..1395b71df5ccc 100644 +--- a/fs/ceph/dir.c ++++ b/fs/ceph/dir.c +@@ -310,7 +310,7 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx) + struct ceph_dir_file_info *dfi = file->private_data; + struct inode *inode = file_inode(file); + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_mds_client *mdsc = fsc->mdsc; + int i; + int err; +@@ -703,7 +703,7 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int whence) + struct dentry *ceph_handle_snapdir(struct ceph_mds_request *req, + struct dentry *dentry) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(dentry->d_sb); + struct inode *parent = d_inode(dentry->d_parent); /* we hold i_rwsem */ + + /* .snap dir? */ +@@ -771,7 +771,7 @@ static bool is_root_ceph_dentry(struct inode *inode, struct dentry *dentry) + static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(dir->i_sb); + struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); + struct ceph_mds_request *req; + int op; +@@ -1199,7 +1199,7 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc, + struct ceph_mds_request *req) + { + struct dentry *dentry = req->r_dentry; +- struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(dentry->d_sb); + struct ceph_dentry_info *di = ceph_dentry(dentry); + int result = req->r_err ? req->r_err : + le32_to_cpu(req->r_reply_info.head->result); +@@ -1226,7 +1226,7 @@ static void ceph_async_unlink_cb(struct ceph_mds_client *mdsc, + if (result) { + int pathlen = 0; + u64 base = 0; +- char *path = ceph_mdsc_build_path(dentry, &pathlen, ++ char *path = ceph_mdsc_build_path(mdsc, dentry, &pathlen, + &base, 0); + + /* mark error on parent + clear complete */ +@@ -1290,7 +1290,7 @@ static int get_caps_for_async_unlink(struct inode *dir, struct dentry *dentry) + */ + static int ceph_unlink(struct inode *dir, struct dentry *dentry) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(dir->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; + struct inode *inode = d_inode(dentry); + struct ceph_mds_request *req; +@@ -1469,7 +1469,7 @@ void __ceph_dentry_lease_touch(struct ceph_dentry_info *di) + return; + } + +- mdsc = ceph_sb_to_client(dn->d_sb)->mdsc; ++ mdsc = ceph_sb_to_fs_client(dn->d_sb)->mdsc; + spin_lock(&mdsc->dentry_list_lock); + list_move_tail(&di->lease_list, &mdsc->dentry_leases); + spin_unlock(&mdsc->dentry_list_lock); +@@ -1516,7 +1516,7 @@ void __ceph_dentry_dir_lease_touch(struct ceph_dentry_info *di) + return; + } + +- mdsc = ceph_sb_to_client(dn->d_sb)->mdsc; ++ mdsc = ceph_sb_to_fs_client(dn->d_sb)->mdsc; + spin_lock(&mdsc->dentry_list_lock); + __dentry_dir_lease_touch(mdsc, di), + spin_unlock(&mdsc->dentry_list_lock); +@@ -1530,7 +1530,7 @@ static void __dentry_lease_unlist(struct ceph_dentry_info *di) + if (list_empty(&di->lease_list)) + return; + +- mdsc = ceph_sb_to_client(di->dentry->d_sb)->mdsc; ++ mdsc = ceph_sb_to_fs_client(di->dentry->d_sb)->mdsc; + spin_lock(&mdsc->dentry_list_lock); + list_del_init(&di->lease_list); + spin_unlock(&mdsc->dentry_list_lock); +@@ -1888,7 +1888,7 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags) + dentry, inode, ceph_dentry(dentry)->offset, + !!(dentry->d_flags & DCACHE_NOKEY_NAME)); + +- mdsc = ceph_sb_to_client(dir->i_sb)->mdsc; ++ mdsc = ceph_sb_to_fs_client(dir->i_sb)->mdsc; + + /* always trust cached snapped dentries, snapdir dentry */ + if (ceph_snap(dir) != CEPH_NOSNAP) { +@@ -1995,7 +1995,7 @@ static int ceph_d_delete(const struct dentry *dentry) + static void ceph_d_release(struct dentry *dentry) + { + struct ceph_dentry_info *di = ceph_dentry(dentry); +- struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(dentry->d_sb); + + dout("d_release %p\n", dentry); + +@@ -2064,7 +2064,7 @@ static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size, + int left; + const int bufsize = 1024; + +- if (!ceph_test_mount_opt(ceph_sb_to_client(inode->i_sb), DIRSTAT)) ++ if (!ceph_test_mount_opt(ceph_sb_to_fs_client(inode->i_sb), DIRSTAT)) + return -EISDIR; + + if (!dfi->dir_info) { +diff --git a/fs/ceph/export.c b/fs/ceph/export.c +index 8559990a59a5c..52c4daf2447d3 100644 +--- a/fs/ceph/export.c ++++ b/fs/ceph/export.c +@@ -123,7 +123,7 @@ static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len, + + static struct inode *__lookup_inode(struct super_block *sb, u64 ino) + { +- struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(sb)->mdsc; + struct inode *inode; + struct ceph_vino vino; + int err; +@@ -205,7 +205,7 @@ static struct dentry *__snapfh_to_dentry(struct super_block *sb, + struct ceph_nfs_snapfh *sfh, + bool want_parent) + { +- struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(sb)->mdsc; + struct ceph_mds_request *req; + struct inode *inode; + struct ceph_vino vino; +@@ -317,7 +317,7 @@ static struct dentry *ceph_fh_to_dentry(struct super_block *sb, + static struct dentry *__get_parent(struct super_block *sb, + struct dentry *child, u64 ino) + { +- struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(sb)->mdsc; + struct ceph_mds_request *req; + struct inode *inode; + int mask; +@@ -439,7 +439,7 @@ static int __get_snap_name(struct dentry *parent, char *name, + { + struct inode *inode = d_inode(child); + struct inode *dir = d_inode(parent); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_mds_request *req = NULL; + char *last_name = NULL; + unsigned next_offset = 2; +@@ -544,7 +544,7 @@ static int ceph_get_name(struct dentry *parent, char *name, + if (ceph_snap(inode) != CEPH_NOSNAP) + return __get_snap_name(parent, name, child); + +- mdsc = ceph_inode_to_client(inode)->mdsc; ++ mdsc = ceph_inode_to_fs_client(inode)->mdsc; + req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPNAME, + USE_ANY_MDS); + if (IS_ERR(req)) +diff --git a/fs/ceph/file.c b/fs/ceph/file.c +index bdd0a3b894b7b..1e0497295662a 100644 +--- a/fs/ceph/file.c ++++ b/fs/ceph/file.c +@@ -200,7 +200,7 @@ static int ceph_init_file_info(struct inode *inode, struct file *file, + { + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_mount_options *opt = +- ceph_inode_to_client(&ci->netfs.inode)->mount_options; ++ ceph_inode_to_fs_client(&ci->netfs.inode)->mount_options; + struct ceph_file_info *fi; + int ret; + +@@ -234,7 +234,7 @@ static int ceph_init_file_info(struct inode *inode, struct file *file, + + spin_lock_init(&fi->rw_contexts_lock); + INIT_LIST_HEAD(&fi->rw_contexts); +- fi->filp_gen = READ_ONCE(ceph_inode_to_client(inode)->filp_gen); ++ fi->filp_gen = READ_ONCE(ceph_inode_to_fs_client(inode)->filp_gen); + + if ((file->f_mode & FMODE_WRITE) && ceph_has_inline_data(ci)) { + ret = ceph_uninline_data(file); +@@ -352,7 +352,7 @@ int ceph_renew_caps(struct inode *inode, int fmode) + int ceph_open(struct inode *inode, struct file *file) + { + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_fs_client *fsc = ceph_sb_to_client(inode->i_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(inode->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; + struct ceph_mds_request *req; + struct ceph_file_info *fi = file->private_data; +@@ -574,7 +574,7 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc, + if (result) { + int pathlen = 0; + u64 base = 0; +- char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen, ++ char *path = ceph_mdsc_build_path(mdsc, req->r_dentry, &pathlen, + &base, 0); + + pr_warn("async create failure path=(%llx)%s result=%d!\n", +@@ -730,7 +730,7 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode, + int ceph_atomic_open(struct inode *dir, struct dentry *dentry, + struct file *file, unsigned flags, umode_t mode) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(dir->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; + struct ceph_mds_request *req; + struct inode *new_inode = NULL; +@@ -962,7 +962,7 @@ ssize_t __ceph_sync_read(struct inode *inode, loff_t *ki_pos, + u64 *last_objver) + { + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_osd_client *osdc = &fsc->client->osdc; + ssize_t ret; + u64 off = *ki_pos; +@@ -1259,7 +1259,7 @@ static void ceph_aio_complete_req(struct ceph_osd_request *req) + if (aio_work) { + INIT_WORK(&aio_work->work, ceph_aio_retry_work); + aio_work->req = req; +- queue_work(ceph_inode_to_client(inode)->inode_wq, ++ queue_work(ceph_inode_to_fs_client(inode)->inode_wq, + &aio_work->work); + return; + } +@@ -1389,7 +1389,7 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_client_metric *metric = &fsc->mdsc->metric; + struct ceph_vino vino; + struct ceph_osd_request *req; +@@ -1613,7 +1613,7 @@ ceph_sync_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos, + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_osd_client *osdc = &fsc->client->osdc; + struct ceph_osd_request *req; + struct page **pages; +@@ -2231,7 +2231,7 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from) + struct ceph_file_info *fi = file->private_data; + struct inode *inode = file_inode(file); + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_osd_client *osdc = &fsc->client->osdc; + struct ceph_cap_flush *prealloc_cf; + ssize_t count, written = 0; +@@ -2465,7 +2465,7 @@ static int ceph_zero_partial_object(struct inode *inode, + loff_t offset, loff_t *length) + { + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_osd_request *req; + int ret = 0; + loff_t zero = 0; +@@ -2848,7 +2848,7 @@ static ssize_t __ceph_copy_file_range(struct file *src_file, loff_t src_off, + struct ceph_inode_info *src_ci = ceph_inode(src_inode); + struct ceph_inode_info *dst_ci = ceph_inode(dst_inode); + struct ceph_cap_flush *prealloc_cf; +- struct ceph_fs_client *src_fsc = ceph_inode_to_client(src_inode); ++ struct ceph_fs_client *src_fsc = ceph_inode_to_fs_client(src_inode); + loff_t size; + ssize_t ret = -EIO, bytes; + u64 src_objnum, dst_objnum, src_objoff, dst_objoff; +@@ -2856,7 +2856,7 @@ static ssize_t __ceph_copy_file_range(struct file *src_file, loff_t src_off, + int src_got = 0, dst_got = 0, err, dirty; + + if (src_inode->i_sb != dst_inode->i_sb) { +- struct ceph_fs_client *dst_fsc = ceph_inode_to_client(dst_inode); ++ struct ceph_fs_client *dst_fsc = ceph_inode_to_fs_client(dst_inode); + + if (ceph_fsid_compare(&src_fsc->client->fsid, + &dst_fsc->client->fsid)) { +diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c +index b79100f720b38..db6977c15c282 100644 +--- a/fs/ceph/inode.c ++++ b/fs/ceph/inode.c +@@ -1489,7 +1489,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) + struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info; + struct inode *in = NULL; + struct ceph_vino tvino, dvino; +- struct ceph_fs_client *fsc = ceph_sb_to_client(sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(sb); + int err = 0; + + dout("fill_trace %p is_dentry %d is_target %d\n", req, +@@ -2079,7 +2079,7 @@ bool ceph_inode_set_size(struct inode *inode, loff_t size) + + void ceph_queue_inode_work(struct inode *inode, int work_bit) + { +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + struct ceph_inode_info *ci = ceph_inode(inode); + set_bit(work_bit, &ci->i_work_mask); + +@@ -2427,7 +2427,7 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr, + struct ceph_inode_info *ci = ceph_inode(inode); + unsigned int ia_valid = attr->ia_valid; + struct ceph_mds_request *req; +- struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; + struct ceph_cap_flush *prealloc_cf; + loff_t isize = i_size_read(inode); + int issued; +@@ -2740,7 +2740,7 @@ int ceph_setattr(struct mnt_idmap *idmap, struct dentry *dentry, + struct iattr *attr) + { + struct inode *inode = d_inode(dentry); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + int err; + + if (ceph_snap(inode) != CEPH_NOSNAP) +@@ -2810,7 +2810,7 @@ int ceph_try_to_choose_auth_mds(struct inode *inode, int mask) + int __ceph_do_getattr(struct inode *inode, struct page *locked_page, + int mask, bool force) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(inode->i_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(inode->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; + struct ceph_mds_request *req; + int mode; +@@ -2856,7 +2856,7 @@ int __ceph_do_getattr(struct inode *inode, struct page *locked_page, + int ceph_do_getvxattr(struct inode *inode, const char *name, void *value, + size_t size) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(inode->i_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(inode->i_sb); + struct ceph_mds_client *mdsc = fsc->mdsc; + struct ceph_mds_request *req; + int mode = USE_AUTH_MDS; +@@ -3001,7 +3001,7 @@ int ceph_getattr(struct mnt_idmap *idmap, const struct path *path, + stat->dev = ci->i_snapid_map ? ci->i_snapid_map->dev : 0; + + if (S_ISDIR(inode->i_mode)) { +- if (ceph_test_mount_opt(ceph_sb_to_client(sb), RBYTES)) { ++ if (ceph_test_mount_opt(ceph_sb_to_fs_client(sb), RBYTES)) { + stat->size = ci->i_rbytes; + } else if (ceph_snap(inode) == CEPH_SNAPDIR) { + struct ceph_inode_info *pci; +diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c +index 91a84917d203c..3f617146e4ad3 100644 +--- a/fs/ceph/ioctl.c ++++ b/fs/ceph/ioctl.c +@@ -65,7 +65,7 @@ static long __validate_layout(struct ceph_mds_client *mdsc, + static long ceph_ioctl_set_layout(struct file *file, void __user *arg) + { + struct inode *inode = file_inode(file); +- struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; + struct ceph_mds_request *req; + struct ceph_ioctl_layout l; + struct ceph_inode_info *ci = ceph_inode(file_inode(file)); +@@ -140,7 +140,7 @@ static long ceph_ioctl_set_layout_policy (struct file *file, void __user *arg) + struct ceph_mds_request *req; + struct ceph_ioctl_layout l; + int err; +- struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; + + /* copy and validate */ + if (copy_from_user(&l, arg, sizeof(l))) +@@ -183,7 +183,7 @@ static long ceph_ioctl_get_dataloc(struct file *file, void __user *arg) + struct inode *inode = file_inode(file); + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_osd_client *osdc = +- &ceph_sb_to_client(inode->i_sb)->client->osdc; ++ &ceph_sb_to_fs_client(inode->i_sb)->client->osdc; + struct ceph_object_locator oloc; + CEPH_DEFINE_OID_ONSTACK(oid); + u32 xlen; +@@ -244,7 +244,7 @@ static long ceph_ioctl_lazyio(struct file *file) + struct ceph_file_info *fi = file->private_data; + struct inode *inode = file_inode(file); + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_inode_to_fs_client(inode)->mdsc; + + if ((fi->fmode & CEPH_FILE_MODE_LAZY) == 0) { + spin_lock(&ci->i_ceph_lock); +diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c +index 6d76fd0f704a6..11289ce8a8cc8 100644 +--- a/fs/ceph/mds_client.c ++++ b/fs/ceph/mds_client.c +@@ -830,7 +830,7 @@ static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info) + */ + int ceph_wait_on_conflict_unlink(struct dentry *dentry) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(dentry->d_sb); + struct dentry *pdentry = dentry->d_parent; + struct dentry *udentry, *found = NULL; + struct ceph_dentry_info *di; +@@ -2126,6 +2126,7 @@ static bool drop_negative_children(struct dentry *dentry) + */ + static int trim_caps_cb(struct inode *inode, int mds, void *arg) + { ++ struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb); + int *remaining = arg; + struct ceph_inode_info *ci = ceph_inode(inode); + int used, wanted, oissued, mine; +@@ -2173,7 +2174,7 @@ static int trim_caps_cb(struct inode *inode, int mds, void *arg) + + if (oissued) { + /* we aren't the only cap.. just remove us */ +- ceph_remove_cap(cap, true); ++ ceph_remove_cap(mdsc, cap, true); + (*remaining)--; + } else { + struct dentry *dentry; +@@ -2588,6 +2589,7 @@ static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen) + + /** + * ceph_mdsc_build_path - build a path string to a given dentry ++ * @mdsc: mds client + * @dentry: dentry to which path should be built + * @plen: returned length of string + * @pbase: returned base inode number +@@ -2607,8 +2609,8 @@ static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen) + * Encode hidden .snap dirs as a double /, i.e. + * foo/.snap/bar -> foo//bar + */ +-char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase, +- int for_wire) ++char *ceph_mdsc_build_path(struct ceph_mds_client *mdsc, struct dentry *dentry, ++ int *plen, u64 *pbase, int for_wire) + { + struct dentry *cur; + struct inode *inode; +@@ -2726,9 +2728,9 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase, + return path + pos; + } + +-static int build_dentry_path(struct dentry *dentry, struct inode *dir, +- const char **ppath, int *ppathlen, u64 *pino, +- bool *pfreepath, bool parent_locked) ++static int build_dentry_path(struct ceph_mds_client *mdsc, struct dentry *dentry, ++ struct inode *dir, const char **ppath, int *ppathlen, ++ u64 *pino, bool *pfreepath, bool parent_locked) + { + char *path; + +@@ -2744,7 +2746,7 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir, + return 0; + } + rcu_read_unlock(); +- path = ceph_mdsc_build_path(dentry, ppathlen, pino, 1); ++ path = ceph_mdsc_build_path(mdsc, dentry, ppathlen, pino, 1); + if (IS_ERR(path)) + return PTR_ERR(path); + *ppath = path; +@@ -2756,6 +2758,7 @@ static int build_inode_path(struct inode *inode, + const char **ppath, int *ppathlen, u64 *pino, + bool *pfreepath) + { ++ struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb); + struct dentry *dentry; + char *path; + +@@ -2765,7 +2768,7 @@ static int build_inode_path(struct inode *inode, + return 0; + } + dentry = d_find_alias(inode); +- path = ceph_mdsc_build_path(dentry, ppathlen, pino, 1); ++ path = ceph_mdsc_build_path(mdsc, dentry, ppathlen, pino, 1); + dput(dentry); + if (IS_ERR(path)) + return PTR_ERR(path); +@@ -2778,10 +2781,11 @@ static int build_inode_path(struct inode *inode, + * request arguments may be specified via an inode *, a dentry *, or + * an explicit ino+path. + */ +-static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry, +- struct inode *rdiri, const char *rpath, +- u64 rino, const char **ppath, int *pathlen, +- u64 *ino, bool *freepath, bool parent_locked) ++static int set_request_path_attr(struct ceph_mds_client *mdsc, struct inode *rinode, ++ struct dentry *rdentry, struct inode *rdiri, ++ const char *rpath, u64 rino, const char **ppath, ++ int *pathlen, u64 *ino, bool *freepath, ++ bool parent_locked) + { + int r = 0; + +@@ -2790,7 +2794,7 @@ static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry, + dout(" inode %p %llx.%llx\n", rinode, ceph_ino(rinode), + ceph_snap(rinode)); + } else if (rdentry) { +- r = build_dentry_path(rdentry, rdiri, ppath, pathlen, ino, ++ r = build_dentry_path(mdsc, rdentry, rdiri, ppath, pathlen, ino, + freepath, parent_locked); + dout(" dentry %p %llx/%.*s\n", rdentry, *ino, *pathlen, + *ppath); +@@ -2877,7 +2881,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session, + bool old_version = !test_bit(CEPHFS_FEATURE_32BITS_RETRY_FWD, + &session->s_features); + +- ret = set_request_path_attr(req->r_inode, req->r_dentry, ++ ret = set_request_path_attr(mdsc, req->r_inode, req->r_dentry, + req->r_parent, req->r_path1, req->r_ino1.ino, + &path1, &pathlen1, &ino1, &freepath1, + test_bit(CEPH_MDS_R_PARENT_LOCKED, +@@ -2891,7 +2895,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session, + if (req->r_old_dentry && + !(req->r_old_dentry->d_flags & DCACHE_DISCONNECTED)) + old_dentry = req->r_old_dentry; +- ret = set_request_path_attr(NULL, old_dentry, ++ ret = set_request_path_attr(mdsc, NULL, old_dentry, + req->r_old_dentry_dir, + req->r_path2, req->r_ino2.ino, + &path2, &pathlen2, &ino2, &freepath2, true); +@@ -4290,6 +4294,7 @@ static struct dentry* d_find_primary(struct inode *inode) + */ + static int reconnect_caps_cb(struct inode *inode, int mds, void *arg) + { ++ struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb); + union { + struct ceph_mds_cap_reconnect v2; + struct ceph_mds_cap_reconnect_v1 v1; +@@ -4307,7 +4312,7 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg) + dentry = d_find_primary(inode); + if (dentry) { + /* set pathbase to parent dir when msg_version >= 2 */ +- path = ceph_mdsc_build_path(dentry, &pathlen, &pathbase, ++ path = ceph_mdsc_build_path(mdsc, dentry, &pathlen, &pathbase, + recon_state->msg_version >= 2); + dput(dentry); + if (IS_ERR(path)) { +@@ -5662,7 +5667,7 @@ void ceph_mdsc_handle_mdsmap(struct ceph_mds_client *mdsc, struct ceph_msg *msg) + return; + } + +- newmap = ceph_mdsmap_decode(&p, end, ceph_msgr2(mdsc->fsc->client)); ++ newmap = ceph_mdsmap_decode(mdsc, &p, end, ceph_msgr2(mdsc->fsc->client)); + if (IS_ERR(newmap)) { + err = PTR_ERR(newmap); + goto bad_unlock; +diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h +index 5a3714bdd64a8..d930eb79dc380 100644 +--- a/fs/ceph/mds_client.h ++++ b/fs/ceph/mds_client.h +@@ -581,7 +581,8 @@ static inline void ceph_mdsc_free_path(char *path, int len) + __putname(path - (PATH_MAX - 1 - len)); + } + +-extern char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base, ++extern char *ceph_mdsc_build_path(struct ceph_mds_client *mdsc, ++ struct dentry *dentry, int *plen, u64 *base, + int for_wire); + + extern void __ceph_mdsc_drop_dentry_lease(struct dentry *dentry); +diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c +index 3bb3b610d403e..66afb18df76b2 100644 +--- a/fs/ceph/mdsmap.c ++++ b/fs/ceph/mdsmap.c +@@ -114,7 +114,8 @@ static int __decode_and_drop_compat_set(void **p, void* end) + * Ignore any fields we don't care about (there are quite a few of + * them). + */ +-struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end, bool msgr2) ++struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p, ++ void *end, bool msgr2) + { + struct ceph_mdsmap *m; + const void *start = *p; +diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c +index 813f21add992c..d0d3612f28f0e 100644 +--- a/fs/ceph/snap.c ++++ b/fs/ceph/snap.c +@@ -329,7 +329,8 @@ static int cmpu64_rev(const void *a, const void *b) + /* + * build the snap context for a given realm. + */ +-static int build_snap_context(struct ceph_snap_realm *realm, ++static int build_snap_context(struct ceph_mds_client *mdsc, ++ struct ceph_snap_realm *realm, + struct list_head *realm_queue, + struct list_head *dirty_realms) + { +@@ -425,7 +426,8 @@ static int build_snap_context(struct ceph_snap_realm *realm, + /* + * rebuild snap context for the given realm and all of its children. + */ +-static void rebuild_snap_realms(struct ceph_snap_realm *realm, ++static void rebuild_snap_realms(struct ceph_mds_client *mdsc, ++ struct ceph_snap_realm *realm, + struct list_head *dirty_realms) + { + LIST_HEAD(realm_queue); +@@ -451,7 +453,8 @@ static void rebuild_snap_realms(struct ceph_snap_realm *realm, + continue; + } + +- last = build_snap_context(_realm, &realm_queue, dirty_realms); ++ last = build_snap_context(mdsc, _realm, &realm_queue, ++ dirty_realms); + dout("%s %llx %p, %s\n", __func__, _realm->ino, _realm, + last > 0 ? "is deferred" : !last ? "succeeded" : "failed"); + +@@ -708,7 +711,8 @@ int __ceph_finish_cap_snap(struct ceph_inode_info *ci, + * Queue cap_snaps for snap writeback for this realm and its children. + * Called under snap_rwsem, so realm topology won't change. + */ +-static void queue_realm_cap_snaps(struct ceph_snap_realm *realm) ++static void queue_realm_cap_snaps(struct ceph_mds_client *mdsc, ++ struct ceph_snap_realm *realm) + { + struct ceph_inode_info *ci; + struct inode *lastinode = NULL; +@@ -855,7 +859,7 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc, + + /* rebuild_snapcs when we reach the _end_ (root) of the trace */ + if (realm_to_rebuild && p >= e) +- rebuild_snap_realms(realm_to_rebuild, &dirty_realms); ++ rebuild_snap_realms(mdsc, realm_to_rebuild, &dirty_realms); + + if (!first_realm) + first_realm = realm; +@@ -873,7 +877,7 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc, + realm = list_first_entry(&dirty_realms, struct ceph_snap_realm, + dirty_item); + list_del_init(&realm->dirty_item); +- queue_realm_cap_snaps(realm); ++ queue_realm_cap_snaps(mdsc, realm); + } + + if (realm_ret) +@@ -960,7 +964,7 @@ static void flush_snaps(struct ceph_mds_client *mdsc) + void ceph_change_snap_realm(struct inode *inode, struct ceph_snap_realm *realm) + { + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_inode_to_fs_client(inode)->mdsc; + struct ceph_snap_realm *oldrealm = ci->i_snap_realm; + + lockdep_assert_held(&ci->i_ceph_lock); +diff --git a/fs/ceph/super.c b/fs/ceph/super.c +index 2d7f5a8d4a926..52af90beab000 100644 +--- a/fs/ceph/super.c ++++ b/fs/ceph/super.c +@@ -44,7 +44,7 @@ static LIST_HEAD(ceph_fsc_list); + */ + static void ceph_put_super(struct super_block *s) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(s); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(s); + + dout("put_super\n"); + ceph_fscrypt_free_dummy_policy(fsc); +@@ -53,7 +53,7 @@ static void ceph_put_super(struct super_block *s) + + static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf) + { +- struct ceph_fs_client *fsc = ceph_inode_to_client(d_inode(dentry)); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(d_inode(dentry)); + struct ceph_mon_client *monc = &fsc->client->monc; + struct ceph_statfs st; + int i, err; +@@ -118,7 +118,7 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf) + + static int ceph_sync_fs(struct super_block *sb, int wait) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(sb); + + if (!wait) { + dout("sync_fs (non-blocking)\n"); +@@ -684,7 +684,7 @@ static int compare_mount_options(struct ceph_mount_options *new_fsopt, + */ + static int ceph_show_options(struct seq_file *m, struct dentry *root) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(root->d_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(root->d_sb); + struct ceph_mount_options *fsopt = fsc->mount_options; + size_t pos; + int ret; +@@ -1015,7 +1015,7 @@ static void __ceph_umount_begin(struct ceph_fs_client *fsc) + */ + void ceph_umount_begin(struct super_block *sb) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(sb); + + dout("ceph_umount_begin - starting forced umount\n"); + if (!fsc) +@@ -1226,7 +1226,7 @@ static int ceph_compare_super(struct super_block *sb, struct fs_context *fc) + struct ceph_fs_client *new = fc->s_fs_info; + struct ceph_mount_options *fsopt = new->mount_options; + struct ceph_options *opt = new->client->options; +- struct ceph_fs_client *fsc = ceph_sb_to_client(sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(sb); + + dout("ceph_compare_super %p\n", sb); + +@@ -1322,9 +1322,9 @@ static int ceph_get_tree(struct fs_context *fc) + goto out; + } + +- if (ceph_sb_to_client(sb) != fsc) { ++ if (ceph_sb_to_fs_client(sb) != fsc) { + destroy_fs_client(fsc); +- fsc = ceph_sb_to_client(sb); ++ fsc = ceph_sb_to_fs_client(sb); + dout("get_sb got existing client %p\n", fsc); + } else { + dout("get_sb using new client %p\n", fsc); +@@ -1377,7 +1377,7 @@ static int ceph_reconfigure_fc(struct fs_context *fc) + struct ceph_parse_opts_ctx *pctx = fc->fs_private; + struct ceph_mount_options *fsopt = pctx->opts; + struct super_block *sb = fc->root->d_sb; +- struct ceph_fs_client *fsc = ceph_sb_to_client(sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(sb); + + err = ceph_apply_test_dummy_encryption(sb, fc, fsopt); + if (err) +@@ -1516,7 +1516,7 @@ void ceph_dec_osd_stopping_blocker(struct ceph_mds_client *mdsc) + + static void ceph_kill_sb(struct super_block *s) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(s); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(s); + struct ceph_mds_client *mdsc = fsc->mdsc; + bool wait; + +@@ -1578,7 +1578,7 @@ MODULE_ALIAS_FS("ceph"); + + int ceph_force_reconnect(struct super_block *sb) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(sb); + int err = 0; + + fsc->mount_state = CEPH_MOUNT_RECOVER; +diff --git a/fs/ceph/super.h b/fs/ceph/super.h +index 51c7f2b14f6f8..8efd4ba607744 100644 +--- a/fs/ceph/super.h ++++ b/fs/ceph/super.h +@@ -488,13 +488,13 @@ ceph_inode(const struct inode *inode) + } + + static inline struct ceph_fs_client * +-ceph_inode_to_client(const struct inode *inode) ++ceph_inode_to_fs_client(const struct inode *inode) + { + return (struct ceph_fs_client *)inode->i_sb->s_fs_info; + } + + static inline struct ceph_fs_client * +-ceph_sb_to_client(const struct super_block *sb) ++ceph_sb_to_fs_client(const struct super_block *sb) + { + return (struct ceph_fs_client *)sb->s_fs_info; + } +@@ -502,7 +502,7 @@ ceph_sb_to_client(const struct super_block *sb) + static inline struct ceph_mds_client * + ceph_sb_to_mdsc(const struct super_block *sb) + { +- return (struct ceph_mds_client *)ceph_sb_to_client(sb)->mdsc; ++ return (struct ceph_mds_client *)ceph_sb_to_fs_client(sb)->mdsc; + } + + static inline struct ceph_vino +@@ -558,7 +558,7 @@ static inline u64 ceph_snap(struct inode *inode) + */ + static inline u64 ceph_present_ino(struct super_block *sb, u64 ino) + { +- if (unlikely(ceph_test_mount_opt(ceph_sb_to_client(sb), INO32))) ++ if (unlikely(ceph_test_mount_opt(ceph_sb_to_fs_client(sb), INO32))) + return ceph_ino_to_ino32(ino); + return ino; + } +@@ -1106,7 +1106,7 @@ void ceph_inode_shutdown(struct inode *inode); + static inline bool ceph_inode_is_shutdown(struct inode *inode) + { + unsigned long flags = READ_ONCE(ceph_inode(inode)->i_ceph_flags); +- struct ceph_fs_client *fsc = ceph_inode_to_client(inode); ++ struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode); + int state = READ_ONCE(fsc->mount_state); + + return (flags & CEPH_I_SHUTDOWN) || state >= CEPH_MOUNT_SHUTDOWN; +@@ -1223,7 +1223,8 @@ extern void ceph_add_cap(struct inode *inode, + unsigned cap, unsigned seq, u64 realmino, int flags, + struct ceph_cap **new_cap); + extern void __ceph_remove_cap(struct ceph_cap *cap, bool queue_release); +-extern void ceph_remove_cap(struct ceph_cap *cap, bool queue_release); ++extern void ceph_remove_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, ++ bool queue_release); + extern void __ceph_remove_caps(struct ceph_inode_info *ci); + extern void ceph_put_cap(struct ceph_mds_client *mdsc, + struct ceph_cap *cap); +diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c +index 0deae4a0f5f16..558f64554b591 100644 +--- a/fs/ceph/xattr.c ++++ b/fs/ceph/xattr.c +@@ -57,7 +57,7 @@ static bool ceph_vxattrcb_layout_exists(struct ceph_inode_info *ci) + static ssize_t ceph_vxattrcb_layout(struct ceph_inode_info *ci, char *val, + size_t size) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(ci->netfs.inode.i_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(ci->netfs.inode.i_sb); + struct ceph_osd_client *osdc = &fsc->client->osdc; + struct ceph_string *pool_ns; + s64 pool = ci->i_layout.pool_id; +@@ -161,7 +161,7 @@ static ssize_t ceph_vxattrcb_layout_pool(struct ceph_inode_info *ci, + char *val, size_t size) + { + ssize_t ret; +- struct ceph_fs_client *fsc = ceph_sb_to_client(ci->netfs.inode.i_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(ci->netfs.inode.i_sb); + struct ceph_osd_client *osdc = &fsc->client->osdc; + s64 pool = ci->i_layout.pool_id; + const char *pool_name; +@@ -313,7 +313,7 @@ static ssize_t ceph_vxattrcb_snap_btime(struct ceph_inode_info *ci, char *val, + static ssize_t ceph_vxattrcb_cluster_fsid(struct ceph_inode_info *ci, + char *val, size_t size) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(ci->netfs.inode.i_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(ci->netfs.inode.i_sb); + + return ceph_fmt_xattr(val, size, "%pU", &fsc->client->fsid); + } +@@ -321,7 +321,7 @@ static ssize_t ceph_vxattrcb_cluster_fsid(struct ceph_inode_info *ci, + static ssize_t ceph_vxattrcb_client_id(struct ceph_inode_info *ci, + char *val, size_t size) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(ci->netfs.inode.i_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(ci->netfs.inode.i_sb); + + return ceph_fmt_xattr(val, size, "client%lld", + ceph_client_gid(fsc->client)); +@@ -1094,7 +1094,7 @@ ssize_t ceph_listxattr(struct dentry *dentry, char *names, size_t size) + static int ceph_sync_setxattr(struct inode *inode, const char *name, + const char *value, size_t size, int flags) + { +- struct ceph_fs_client *fsc = ceph_sb_to_client(inode->i_sb); ++ struct ceph_fs_client *fsc = ceph_sb_to_fs_client(inode->i_sb); + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_mds_request *req; + struct ceph_mds_client *mdsc = fsc->mdsc; +@@ -1164,7 +1164,7 @@ int __ceph_setxattr(struct inode *inode, const char *name, + { + struct ceph_vxattr *vxattr; + struct ceph_inode_info *ci = ceph_inode(inode); +- struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; ++ struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(inode->i_sb)->mdsc; + struct ceph_cap_flush *prealloc_cf = NULL; + struct ceph_buffer *old_blob = NULL; + int issued; +diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c +index a8a7fc0e17547..c1703d2c4bf9a 100644 +--- a/fs/fuse/dir.c ++++ b/fs/fuse/dir.c +@@ -1317,6 +1317,7 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file, + err = fuse_do_statx(inode, file, stat); + if (err == -ENOSYS) { + fc->no_statx = 1; ++ err = 0; + goto retry; + } + } else { +diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c +index bce734b68f08e..929edc0b101a0 100644 +--- a/fs/nilfs2/dir.c ++++ b/fs/nilfs2/dir.c +@@ -243,7 +243,7 @@ nilfs_filetype_table[NILFS_FT_MAX] = { + + #define S_SHIFT 12 + static unsigned char +-nilfs_type_by_mode[S_IFMT >> S_SHIFT] = { ++nilfs_type_by_mode[(S_IFMT >> S_SHIFT) + 1] = { + [S_IFREG >> S_SHIFT] = NILFS_FT_REG_FILE, + [S_IFDIR >> S_SHIFT] = NILFS_FT_DIR, + [S_IFCHR >> S_SHIFT] = NILFS_FT_CHRDEV, +diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c +index 6c85edb8635d0..c53d516459fc4 100644 +--- a/fs/smb/client/cifs_debug.c ++++ b/fs/smb/client/cifs_debug.c +@@ -663,6 +663,7 @@ static ssize_t cifs_stats_proc_write(struct file *file, + spin_lock(&tcon->stat_lock); + tcon->bytes_read = 0; + tcon->bytes_written = 0; ++ tcon->stats_from_time = ktime_get_real_seconds(); + spin_unlock(&tcon->stat_lock); + if (server->ops->clear_stats) + server->ops->clear_stats(tcon); +@@ -743,8 +744,9 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) + seq_printf(m, "\n%d) %s", i, tcon->tree_name); + if (tcon->need_reconnect) + seq_puts(m, "\tDISCONNECTED "); +- seq_printf(m, "\nSMBs: %d", +- atomic_read(&tcon->num_smbs_sent)); ++ seq_printf(m, "\nSMBs: %d since %ptTs UTC", ++ atomic_read(&tcon->num_smbs_sent), ++ &tcon->stats_from_time); + if (server->ops->print_stats) + server->ops->print_stats(m, tcon); + } +diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h +index a878b1e5aa313..68fd61a564089 100644 +--- a/fs/smb/client/cifsglob.h ++++ b/fs/smb/client/cifsglob.h +@@ -1208,6 +1208,7 @@ struct cifs_tcon { + __u64 bytes_read; + __u64 bytes_written; + spinlock_t stat_lock; /* protects the two fields above */ ++ time64_t stats_from_time; + FILE_SYSTEM_DEVICE_INFO fsDevInfo; + FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */ + FILE_SYSTEM_UNIX_INFO fsUnixInfo; +@@ -1252,7 +1253,6 @@ struct cifs_tcon { + struct cached_fids *cfids; + /* BB add field for back pointer to sb struct(s)? */ + #ifdef CONFIG_CIFS_DFS_UPCALL +- struct list_head dfs_ses_list; + struct delayed_work dfs_cache_work; + #endif + struct delayed_work query_interfaces; /* query interfaces workqueue job */ +@@ -1774,7 +1774,6 @@ struct cifs_mount_ctx { + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct cifs_tcon *tcon; +- struct list_head dfs_ses_list; + }; + + static inline void __free_dfs_info_param(struct dfs_info3_param *param) +diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h +index ed257612bf0bc..1bdad33580b57 100644 +--- a/fs/smb/client/cifsproto.h ++++ b/fs/smb/client/cifsproto.h +@@ -716,31 +716,31 @@ struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon); + void cifs_put_tcon_super(struct super_block *sb); + int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry); + +-/* Put references of @ses and @ses->dfs_root_ses */ ++/* Put references of @ses and its children */ + static inline void cifs_put_smb_ses(struct cifs_ses *ses) + { +- struct cifs_ses *rses = ses->dfs_root_ses; ++ struct cifs_ses *next; + +- __cifs_put_smb_ses(ses); +- if (rses) +- __cifs_put_smb_ses(rses); ++ do { ++ next = ses->dfs_root_ses; ++ __cifs_put_smb_ses(ses); ++ } while ((ses = next)); + } + +-/* Get an active reference of @ses and @ses->dfs_root_ses. ++/* Get an active reference of @ses and its children. + * + * NOTE: make sure to call this function when incrementing reference count of + * @ses to ensure that any DFS root session attached to it (@ses->dfs_root_ses) + * will also get its reference count incremented. + * +- * cifs_put_smb_ses() will put both references, so call it when you're done. ++ * cifs_put_smb_ses() will put all references, so call it when you're done. + */ + static inline void cifs_smb_ses_inc_refcount(struct cifs_ses *ses) + { + lockdep_assert_held(&cifs_tcp_ses_lock); + +- ses->ses_count++; +- if (ses->dfs_root_ses) +- ses->dfs_root_ses->ses_count++; ++ for (; ses; ses = ses->dfs_root_ses) ++ ses->ses_count++; + } + + static inline bool dfs_src_pathname_equal(const char *s1, const char *s2) +diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c +index 97776dd12b6b8..c5705de7f9de2 100644 +--- a/fs/smb/client/connect.c ++++ b/fs/smb/client/connect.c +@@ -237,7 +237,13 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server, + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) { +- /* check if iface is still active */ ++ spin_lock(&ses->ses_lock); ++ if (ses->ses_status == SES_EXITING) { ++ spin_unlock(&ses->ses_lock); ++ continue; ++ } ++ spin_unlock(&ses->ses_lock); ++ + spin_lock(&ses->chan_lock); + if (cifs_ses_get_chan_index(ses, server) == + CIFS_INVAL_CHAN_INDEX) { +@@ -1857,6 +1863,9 @@ static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx) + ctx->sectype != ses->sectype) + return 0; + ++ if (ctx->dfs_root_ses != ses->dfs_root_ses) ++ return 0; ++ + /* + * If an existing session is limited to less channels than + * requested, it should not be reused +@@ -1960,31 +1969,6 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx) + return rc; + } + +-/** +- * cifs_free_ipc - helper to release the session IPC tcon +- * @ses: smb session to unmount the IPC from +- * +- * Needs to be called everytime a session is destroyed. +- * +- * On session close, the IPC is closed and the server must release all tcons of the session. +- * No need to send a tree disconnect here. +- * +- * Besides, it will make the server to not close durable and resilient files on session close, as +- * specified in MS-SMB2 3.3.5.6 Receiving an SMB2 LOGOFF Request. +- */ +-static int +-cifs_free_ipc(struct cifs_ses *ses) +-{ +- struct cifs_tcon *tcon = ses->tcon_ipc; +- +- if (tcon == NULL) +- return 0; +- +- tconInfoFree(tcon); +- ses->tcon_ipc = NULL; +- return 0; +-} +- + static struct cifs_ses * + cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) + { +@@ -2015,68 +1999,67 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) + + void __cifs_put_smb_ses(struct cifs_ses *ses) + { +- unsigned int rc, xid; +- unsigned int chan_count; + struct TCP_Server_Info *server = ses->server; ++ struct cifs_tcon *tcon; ++ unsigned int xid; ++ size_t i; ++ bool do_logoff; ++ int rc; + ++ spin_lock(&cifs_tcp_ses_lock); + spin_lock(&ses->ses_lock); +- if (ses->ses_status == SES_EXITING) { ++ cifs_dbg(FYI, "%s: id=0x%llx ses_count=%d ses_status=%u ipc=%s\n", ++ __func__, ses->Suid, ses->ses_count, ses->ses_status, ++ ses->tcon_ipc ? ses->tcon_ipc->tree_name : "none"); ++ if (ses->ses_status == SES_EXITING || --ses->ses_count > 0) { + spin_unlock(&ses->ses_lock); ++ spin_unlock(&cifs_tcp_ses_lock); + return; + } +- spin_unlock(&ses->ses_lock); ++ /* ses_count can never go negative */ ++ WARN_ON(ses->ses_count < 0); + +- cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count); +- cifs_dbg(FYI, +- "%s: ses ipc: %s\n", __func__, ses->tcon_ipc ? ses->tcon_ipc->tree_name : "NONE"); ++ spin_lock(&ses->chan_lock); ++ cifs_chan_clear_need_reconnect(ses, server); ++ spin_unlock(&ses->chan_lock); + +- spin_lock(&cifs_tcp_ses_lock); +- if (--ses->ses_count > 0) { +- spin_unlock(&cifs_tcp_ses_lock); +- return; +- } +- spin_lock(&ses->ses_lock); +- if (ses->ses_status == SES_GOOD) +- ses->ses_status = SES_EXITING; ++ do_logoff = ses->ses_status == SES_GOOD && server->ops->logoff; ++ ses->ses_status = SES_EXITING; ++ tcon = ses->tcon_ipc; ++ ses->tcon_ipc = NULL; + spin_unlock(&ses->ses_lock); + spin_unlock(&cifs_tcp_ses_lock); + +- /* ses_count can never go negative */ +- WARN_ON(ses->ses_count < 0); +- +- spin_lock(&ses->ses_lock); +- if (ses->ses_status == SES_EXITING && server->ops->logoff) { +- spin_unlock(&ses->ses_lock); +- cifs_free_ipc(ses); ++ /* ++ * On session close, the IPC is closed and the server must release all ++ * tcons of the session. No need to send a tree disconnect here. ++ * ++ * Besides, it will make the server to not close durable and resilient ++ * files on session close, as specified in MS-SMB2 3.3.5.6 Receiving an ++ * SMB2 LOGOFF Request. ++ */ ++ tconInfoFree(tcon); ++ if (do_logoff) { + xid = get_xid(); + rc = server->ops->logoff(xid, ses); + if (rc) + cifs_server_dbg(VFS, "%s: Session Logoff failure rc=%d\n", + __func__, rc); + _free_xid(xid); +- } else { +- spin_unlock(&ses->ses_lock); +- cifs_free_ipc(ses); + } + + spin_lock(&cifs_tcp_ses_lock); + list_del_init(&ses->smb_ses_list); + spin_unlock(&cifs_tcp_ses_lock); + +- chan_count = ses->chan_count; +- + /* close any extra channels */ +- if (chan_count > 1) { +- int i; +- +- for (i = 1; i < chan_count; i++) { +- if (ses->chans[i].iface) { +- kref_put(&ses->chans[i].iface->refcount, release_iface); +- ses->chans[i].iface = NULL; +- } +- cifs_put_tcp_session(ses->chans[i].server, 0); +- ses->chans[i].server = NULL; ++ for (i = 1; i < ses->chan_count; i++) { ++ if (ses->chans[i].iface) { ++ kref_put(&ses->chans[i].iface->refcount, release_iface); ++ ses->chans[i].iface = NULL; + } ++ cifs_put_tcp_session(ses->chans[i].server, 0); ++ ses->chans[i].server = NULL; + } + + /* we now account for primary channel in iface->refcount */ +@@ -2375,9 +2358,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) + * need to lock before changing something in the session. + */ + spin_lock(&cifs_tcp_ses_lock); ++ if (ctx->dfs_root_ses) ++ cifs_smb_ses_inc_refcount(ctx->dfs_root_ses); + ses->dfs_root_ses = ctx->dfs_root_ses; +- if (ses->dfs_root_ses) +- ses->dfs_root_ses->ses_count++; + list_add(&ses->smb_ses_list, &server->smb_ses_list); + spin_unlock(&cifs_tcp_ses_lock); + +@@ -3321,6 +3304,9 @@ void cifs_mount_put_conns(struct cifs_mount_ctx *mnt_ctx) + cifs_put_smb_ses(mnt_ctx->ses); + else if (mnt_ctx->server) + cifs_put_tcp_session(mnt_ctx->server, 0); ++ mnt_ctx->ses = NULL; ++ mnt_ctx->tcon = NULL; ++ mnt_ctx->server = NULL; + mnt_ctx->cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS; + free_xid(mnt_ctx->xid); + } +@@ -3599,8 +3585,6 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) + bool isdfs; + int rc; + +- INIT_LIST_HEAD(&mnt_ctx.dfs_ses_list); +- + rc = dfs_mount_share(&mnt_ctx, &isdfs); + if (rc) + goto error; +@@ -3631,7 +3615,6 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) + return rc; + + error: +- dfs_put_root_smb_sessions(&mnt_ctx.dfs_ses_list); + cifs_mount_put_conns(&mnt_ctx); + return rc; + } +@@ -3646,6 +3629,18 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) + goto error; + + rc = cifs_mount_get_tcon(&mnt_ctx); ++ if (!rc) { ++ /* ++ * Prevent superblock from being created with any missing ++ * connections. ++ */ ++ if (WARN_ON(!mnt_ctx.server)) ++ rc = -EHOSTDOWN; ++ else if (WARN_ON(!mnt_ctx.ses)) ++ rc = -EACCES; ++ else if (WARN_ON(!mnt_ctx.tcon)) ++ rc = -ENOENT; ++ } + if (rc) + goto error; + +diff --git a/fs/smb/client/dfs.c b/fs/smb/client/dfs.c +index 449c59830039b..3ec965547e3d4 100644 +--- a/fs/smb/client/dfs.c ++++ b/fs/smb/client/dfs.c +@@ -66,33 +66,20 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path) + } + + /* +- * Track individual DFS referral servers used by new DFS mount. +- * +- * On success, their lifetime will be shared by final tcon (dfs_ses_list). +- * Otherwise, they will be put by dfs_put_root_smb_sessions() in cifs_mount(). ++ * Get an active reference of @ses so that next call to cifs_put_tcon() won't ++ * release it as any new DFS referrals must go through its IPC tcon. + */ +-static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx) ++static void add_root_smb_session(struct cifs_mount_ctx *mnt_ctx) + { + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; +- struct dfs_root_ses *root_ses; + struct cifs_ses *ses = mnt_ctx->ses; + + if (ses) { +- root_ses = kmalloc(sizeof(*root_ses), GFP_KERNEL); +- if (!root_ses) +- return -ENOMEM; +- +- INIT_LIST_HEAD(&root_ses->list); +- + spin_lock(&cifs_tcp_ses_lock); + cifs_smb_ses_inc_refcount(ses); + spin_unlock(&cifs_tcp_ses_lock); +- root_ses->ses = ses; +- list_add_tail(&root_ses->list, &mnt_ctx->dfs_ses_list); + } +- /* Select new DFS referral server so that new referrals go through it */ + ctx->dfs_root_ses = ses; +- return 0; + } + + static inline int parse_dfs_target(struct smb3_fs_context *ctx, +@@ -185,11 +172,8 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx, + continue; + } + +- if (is_refsrv) { +- rc = add_root_smb_session(mnt_ctx); +- if (rc) +- goto out; +- } ++ if (is_refsrv) ++ add_root_smb_session(mnt_ctx); + + rc = ref_walk_advance(rw); + if (!rc) { +@@ -232,6 +216,7 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + struct cifs_tcon *tcon; + char *origin_fullpath; ++ bool new_tcon = true; + int rc; + + origin_fullpath = dfs_get_path(cifs_sb, ctx->source); +@@ -239,6 +224,18 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) + return PTR_ERR(origin_fullpath); + + rc = dfs_referral_walk(mnt_ctx); ++ if (!rc) { ++ /* ++ * Prevent superblock from being created with any missing ++ * connections. ++ */ ++ if (WARN_ON(!mnt_ctx->server)) ++ rc = -EHOSTDOWN; ++ else if (WARN_ON(!mnt_ctx->ses)) ++ rc = -EACCES; ++ else if (WARN_ON(!mnt_ctx->tcon)) ++ rc = -ENOENT; ++ } + if (rc) + goto out; + +@@ -247,15 +244,14 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx) + if (!tcon->origin_fullpath) { + tcon->origin_fullpath = origin_fullpath; + origin_fullpath = NULL; ++ } else { ++ new_tcon = false; + } + spin_unlock(&tcon->tc_lock); + +- if (list_empty(&tcon->dfs_ses_list)) { +- list_replace_init(&mnt_ctx->dfs_ses_list, &tcon->dfs_ses_list); ++ if (new_tcon) { + queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, + dfs_cache_get_ttl() * HZ); +- } else { +- dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list); + } + + out: +@@ -298,7 +294,6 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) + if (rc) + return rc; + +- ctx->dfs_root_ses = mnt_ctx->ses; + /* + * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally + * try to get an DFS referral (even cached) to determine whether it is an DFS mount. +@@ -324,7 +319,9 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs) + + *isdfs = true; + add_root_smb_session(mnt_ctx); +- return __dfs_mount_share(mnt_ctx); ++ rc = __dfs_mount_share(mnt_ctx); ++ dfs_put_root_smb_sessions(mnt_ctx); ++ return rc; + } + + /* Update dfs referral path of superblock */ +diff --git a/fs/smb/client/dfs.h b/fs/smb/client/dfs.h +index 875ab7ae57fcd..e5c4dcf837503 100644 +--- a/fs/smb/client/dfs.h ++++ b/fs/smb/client/dfs.h +@@ -7,7 +7,9 @@ + #define _CIFS_DFS_H + + #include "cifsglob.h" ++#include "cifsproto.h" + #include "fs_context.h" ++#include "dfs_cache.h" + #include "cifs_unicode.h" + #include + +@@ -114,11 +116,6 @@ static inline void ref_walk_set_tgt_hint(struct dfs_ref_walk *rw) + ref_walk_tit(rw)); + } + +-struct dfs_root_ses { +- struct list_head list; +- struct cifs_ses *ses; +-}; +- + int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref, + struct smb3_fs_context *ctx); + int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs); +@@ -133,20 +130,32 @@ static inline int dfs_get_referral(struct cifs_mount_ctx *mnt_ctx, const char *p + { + struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; ++ struct cifs_ses *rses = ctx->dfs_root_ses ?: mnt_ctx->ses; + +- return dfs_cache_find(mnt_ctx->xid, ctx->dfs_root_ses, cifs_sb->local_nls, ++ return dfs_cache_find(mnt_ctx->xid, rses, cifs_sb->local_nls, + cifs_remap(cifs_sb), path, ref, tl); + } + +-static inline void dfs_put_root_smb_sessions(struct list_head *head) ++/* ++ * cifs_get_smb_ses() already guarantees an active reference of ++ * @ses->dfs_root_ses when a new session is created, so we need to put extra ++ * references of all DFS root sessions that were used across the mount process ++ * in dfs_mount_share(). ++ */ ++static inline void dfs_put_root_smb_sessions(struct cifs_mount_ctx *mnt_ctx) + { +- struct dfs_root_ses *root, *tmp; ++ const struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; ++ struct cifs_ses *ses = ctx->dfs_root_ses; ++ struct cifs_ses *cur; ++ ++ if (!ses) ++ return; + +- list_for_each_entry_safe(root, tmp, head, list) { +- list_del_init(&root->list); +- cifs_put_smb_ses(root->ses); +- kfree(root); ++ for (cur = ses; cur; cur = cur->dfs_root_ses) { ++ if (cur->dfs_root_ses) ++ cifs_put_smb_ses(cur->dfs_root_ses); + } ++ cifs_put_smb_ses(ses); + } + + #endif /* _CIFS_DFS_H */ +diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c +index 508d831fabe37..11c8efecf7aa1 100644 +--- a/fs/smb/client/dfs_cache.c ++++ b/fs/smb/client/dfs_cache.c +@@ -1172,8 +1172,8 @@ static bool is_ses_good(struct cifs_ses *ses) + return ret; + } + +-/* Refresh dfs referral of tcon and mark it for reconnect if needed */ +-static int __refresh_tcon(const char *path, struct cifs_ses *ses, bool force_refresh) ++/* Refresh dfs referral of @ses and mark it for reconnect if needed */ ++static void __refresh_ses_referral(struct cifs_ses *ses, bool force_refresh) + { + struct TCP_Server_Info *server = ses->server; + DFS_CACHE_TGT_LIST(old_tl); +@@ -1181,10 +1181,21 @@ static int __refresh_tcon(const char *path, struct cifs_ses *ses, bool force_ref + bool needs_refresh = false; + struct cache_entry *ce; + unsigned int xid; ++ char *path = NULL; + int rc = 0; + + xid = get_xid(); + ++ mutex_lock(&server->refpath_lock); ++ if (server->leaf_fullpath) { ++ path = kstrdup(server->leaf_fullpath + 1, GFP_ATOMIC); ++ if (!path) ++ rc = -ENOMEM; ++ } ++ mutex_unlock(&server->refpath_lock); ++ if (!path) ++ goto out; ++ + down_read(&htable_rw_lock); + ce = lookup_cache_entry(path); + needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce); +@@ -1218,19 +1229,17 @@ static int __refresh_tcon(const char *path, struct cifs_ses *ses, bool force_ref + free_xid(xid); + dfs_cache_free_tgts(&old_tl); + dfs_cache_free_tgts(&new_tl); +- return rc; ++ kfree(path); + } + +-static int refresh_tcon(struct cifs_tcon *tcon, bool force_refresh) ++static inline void refresh_ses_referral(struct cifs_ses *ses) + { +- struct TCP_Server_Info *server = tcon->ses->server; +- struct cifs_ses *ses = tcon->ses; ++ __refresh_ses_referral(ses, false); ++} + +- mutex_lock(&server->refpath_lock); +- if (server->leaf_fullpath) +- __refresh_tcon(server->leaf_fullpath + 1, ses, force_refresh); +- mutex_unlock(&server->refpath_lock); +- return 0; ++static inline void force_refresh_ses_referral(struct cifs_ses *ses) ++{ ++ __refresh_ses_referral(ses, true); + } + + /** +@@ -1271,34 +1280,20 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) + */ + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + +- return refresh_tcon(tcon, true); ++ force_refresh_ses_referral(tcon->ses); ++ return 0; + } + + /* Refresh all DFS referrals related to DFS tcon */ + void dfs_cache_refresh(struct work_struct *work) + { +- struct TCP_Server_Info *server; +- struct dfs_root_ses *rses; + struct cifs_tcon *tcon; + struct cifs_ses *ses; + + tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work); +- ses = tcon->ses; +- server = ses->server; + +- mutex_lock(&server->refpath_lock); +- if (server->leaf_fullpath) +- __refresh_tcon(server->leaf_fullpath + 1, ses, false); +- mutex_unlock(&server->refpath_lock); +- +- list_for_each_entry(rses, &tcon->dfs_ses_list, list) { +- ses = rses->ses; +- server = ses->server; +- mutex_lock(&server->refpath_lock); +- if (server->leaf_fullpath) +- __refresh_tcon(server->leaf_fullpath + 1, ses, false); +- mutex_unlock(&server->refpath_lock); +- } ++ for (ses = tcon->ses; ses; ses = ses->dfs_root_ses) ++ refresh_ses_referral(ses); + + queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, + atomic_read(&dfs_cache_ttl) * HZ); +diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c +index ef573e3f8e52a..74627d647818a 100644 +--- a/fs/smb/client/misc.c ++++ b/fs/smb/client/misc.c +@@ -140,9 +140,7 @@ tcon_info_alloc(bool dir_leases_enabled) + spin_lock_init(&ret_buf->stat_lock); + atomic_set(&ret_buf->num_local_opens, 0); + atomic_set(&ret_buf->num_remote_opens, 0); +-#ifdef CONFIG_CIFS_DFS_UPCALL +- INIT_LIST_HEAD(&ret_buf->dfs_ses_list); +-#endif ++ ret_buf->stats_from_time = ktime_get_real_seconds(); + + return ret_buf; + } +@@ -158,9 +156,6 @@ tconInfoFree(struct cifs_tcon *tcon) + atomic_dec(&tconInfoAllocCount); + kfree(tcon->nativeFileSystem); + kfree_sensitive(tcon->password); +-#ifdef CONFIG_CIFS_DFS_UPCALL +- dfs_put_root_smb_sessions(&tcon->dfs_ses_list); +-#endif + kfree(tcon->origin_fullpath); + kfree(tcon); + } +diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h +index 63f969a8e44d7..22c94dea52116 100644 +--- a/fs/smb/common/smb2pdu.h ++++ b/fs/smb/common/smb2pdu.h +@@ -700,7 +700,7 @@ struct smb2_close_rsp { + __le16 StructureSize; /* 60 */ + __le16 Flags; + __le32 Reserved; +- struct_group(network_open_info, ++ struct_group_attr(network_open_info, __packed, + __le64 CreationTime; + __le64 LastAccessTime; + __le64 LastWriteTime; +diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c +index 3079e607c5fe6..2bbc3c3316f0f 100644 +--- a/fs/smb/server/server.c ++++ b/fs/smb/server/server.c +@@ -167,20 +167,17 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, + int rc; + bool is_chained = false; + +- if (conn->ops->allocate_rsp_buf(work)) +- return; +- + if (conn->ops->is_transform_hdr && + conn->ops->is_transform_hdr(work->request_buf)) { + rc = conn->ops->decrypt_req(work); +- if (rc < 0) { +- conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); +- goto send; +- } +- ++ if (rc < 0) ++ return; + work->encrypted = true; + } + ++ if (conn->ops->allocate_rsp_buf(work)) ++ return; ++ + rc = conn->ops->init_rsp_hdr(work); + if (rc) { + /* either uid or tid is not correct */ +diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c +index 924f08326eef4..fb9eea631069e 100644 +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -535,6 +535,10 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work) + if (cmd == SMB2_QUERY_INFO_HE) { + struct smb2_query_info_req *req; + ++ if (get_rfc1002_len(work->request_buf) < ++ offsetof(struct smb2_query_info_req, OutputBufferLength)) ++ return -EINVAL; ++ + req = smb2_get_msg(work->request_buf); + if ((req->InfoType == SMB2_O_INFO_FILE && + (req->FileInfoClass == FILE_FULL_EA_INFORMATION || +diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c +index a8936aba7710e..626406b0cf4ac 100644 +--- a/fs/smb/server/vfs.c ++++ b/fs/smb/server/vfs.c +@@ -745,10 +745,15 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, + goto out4; + } + ++ /* ++ * explicitly handle file overwrite case, for compatibility with ++ * filesystems that may not support rename flags (e.g: fuse) ++ */ + if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry)) { + err = -EEXIST; + goto out4; + } ++ flags &= ~(RENAME_NOREPLACE); + + if (old_child == trap) { + err = -EINVAL; +diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c +index a12ac0356c69c..f21e73d107249 100644 +--- a/fs/sysfs/file.c ++++ b/fs/sysfs/file.c +@@ -450,6 +450,8 @@ struct kernfs_node *sysfs_break_active_protection(struct kobject *kobj, + kn = kernfs_find_and_get(kobj->sd, attr->name); + if (kn) + kernfs_break_active_protection(kn); ++ else ++ kobject_put(kobj); + return kn; + } + EXPORT_SYMBOL_GPL(sysfs_break_active_protection); +diff --git a/include/asm-generic/barrier.h b/include/asm-generic/barrier.h +index 961f4d88f9ef7..1985c22d90ca4 100644 +--- a/include/asm-generic/barrier.h ++++ b/include/asm-generic/barrier.h +@@ -296,5 +296,13 @@ do { \ + #define io_stop_wc() do { } while (0) + #endif + ++/* ++ * Architectures that guarantee an implicit smp_mb() in switch_mm() ++ * can override smp_mb__after_switch_mm. ++ */ ++#ifndef smp_mb__after_switch_mm ++# define smp_mb__after_switch_mm() smp_mb() ++#endif ++ + #endif /* !__ASSEMBLY__ */ + #endif /* __ASM_GENERIC_BARRIER_H */ +diff --git a/include/linux/bootconfig.h b/include/linux/bootconfig.h +index ca73940e26df8..4195444ec45d1 100644 +--- a/include/linux/bootconfig.h ++++ b/include/linux/bootconfig.h +@@ -287,7 +287,12 @@ int __init xbc_init(const char *buf, size_t size, const char **emsg, int *epos); + int __init xbc_get_info(int *node_size, size_t *data_size); + + /* XBC cleanup data structures */ +-void __init xbc_exit(void); ++void __init _xbc_exit(bool early); ++ ++static inline void xbc_exit(void) ++{ ++ _xbc_exit(false); ++} + + /* XBC embedded bootconfig data in kernel */ + #ifdef CONFIG_BOOT_CONFIG_EMBED +diff --git a/include/linux/ceph/mdsmap.h b/include/linux/ceph/mdsmap.h +index fcc95bff72a57..1f2171dd01bfa 100644 +--- a/include/linux/ceph/mdsmap.h ++++ b/include/linux/ceph/mdsmap.h +@@ -5,6 +5,8 @@ + #include + #include + ++struct ceph_mds_client; ++ + /* + * mds map - describe servers in the mds cluster. + * +@@ -69,7 +71,8 @@ static inline bool ceph_mdsmap_is_laggy(struct ceph_mdsmap *m, int w) + } + + extern int ceph_mdsmap_get_random_mds(struct ceph_mdsmap *m); +-struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end, bool msgr2); ++struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p, ++ void *end, bool msgr2); + extern void ceph_mdsmap_destroy(struct ceph_mdsmap *m); + extern bool ceph_mdsmap_is_cluster_available(struct ceph_mdsmap *m); + +diff --git a/include/linux/gpio/property.h b/include/linux/gpio/property.h +index 6c75c8bd44a0b..1a14e239221f7 100644 +--- a/include/linux/gpio/property.h ++++ b/include/linux/gpio/property.h +@@ -2,7 +2,6 @@ + #ifndef __LINUX_GPIO_PROPERTY_H + #define __LINUX_GPIO_PROPERTY_H + +-#include /* for GPIO_* flags */ + #include + + #define PROPERTY_ENTRY_GPIO(_name_, _chip_node_, _idx_, _flags_) \ +diff --git a/include/linux/pci.h b/include/linux/pci.h +index b548d5646a86d..ee89a69817aaf 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1391,6 +1391,7 @@ int pci_load_and_free_saved_state(struct pci_dev *dev, + struct pci_saved_state **state); + int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state); + int pci_set_power_state(struct pci_dev *dev, pci_power_t state); ++int pci_set_power_state_locked(struct pci_dev *dev, pci_power_t state); + pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); + bool pci_pme_capable(struct pci_dev *dev, pci_power_t state); + void pci_pme_active(struct pci_dev *dev, bool enable); +@@ -1594,6 +1595,8 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, + + void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), + void *userdata); ++void pci_walk_bus_locked(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), ++ void *userdata); + int pci_cfg_space_size(struct pci_dev *dev); + unsigned char pci_bus_max_busnr(struct pci_bus *bus); + void pci_setup_bridge(struct pci_bus *bus); +@@ -1990,6 +1993,8 @@ static inline int pci_save_state(struct pci_dev *dev) { return 0; } + static inline void pci_restore_state(struct pci_dev *dev) { } + static inline int pci_set_power_state(struct pci_dev *dev, pci_power_t state) + { return 0; } ++static inline int pci_set_power_state_locked(struct pci_dev *dev, pci_power_t state) ++{ return 0; } + static inline int pci_wake_from_d3(struct pci_dev *dev, bool enable) + { return 0; } + static inline pci_power_t pci_choose_state(struct pci_dev *dev, +diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h +index fa99e68e5e776..134c686c8676c 100644 +--- a/include/linux/shmem_fs.h ++++ b/include/linux/shmem_fs.h +@@ -110,8 +110,17 @@ extern struct page *shmem_read_mapping_page_gfp(struct address_space *mapping, + extern void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end); + int shmem_unuse(unsigned int type); + ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE + extern bool shmem_is_huge(struct inode *inode, pgoff_t index, bool shmem_huge_force, + struct mm_struct *mm, unsigned long vm_flags); ++#else ++static __always_inline bool shmem_is_huge(struct inode *inode, pgoff_t index, bool shmem_huge_force, ++ struct mm_struct *mm, unsigned long vm_flags) ++{ ++ return false; ++} ++#endif ++ + #ifdef CONFIG_SHMEM + extern unsigned long shmem_swap_usage(struct vm_area_struct *vma); + #else +diff --git a/include/linux/swapops.h b/include/linux/swapops.h +index bff1e8d97de0e..925c84653af5e 100644 +--- a/include/linux/swapops.h ++++ b/include/linux/swapops.h +@@ -390,6 +390,35 @@ static inline bool is_migration_entry_dirty(swp_entry_t entry) + } + #endif /* CONFIG_MIGRATION */ + ++#ifdef CONFIG_MEMORY_FAILURE ++ ++/* ++ * Support for hardware poisoned pages ++ */ ++static inline swp_entry_t make_hwpoison_entry(struct page *page) ++{ ++ BUG_ON(!PageLocked(page)); ++ return swp_entry(SWP_HWPOISON, page_to_pfn(page)); ++} ++ ++static inline int is_hwpoison_entry(swp_entry_t entry) ++{ ++ return swp_type(entry) == SWP_HWPOISON; ++} ++ ++#else ++ ++static inline swp_entry_t make_hwpoison_entry(struct page *page) ++{ ++ return swp_entry(0, 0); ++} ++ ++static inline int is_hwpoison_entry(swp_entry_t swp) ++{ ++ return 0; ++} ++#endif ++ + typedef unsigned long pte_marker; + + #define PTE_MARKER_UFFD_WP BIT(0) +@@ -470,8 +499,9 @@ static inline struct page *pfn_swap_entry_to_page(swp_entry_t entry) + + /* + * A pfn swap entry is a special type of swap entry that always has a pfn stored +- * in the swap offset. They are used to represent unaddressable device memory +- * and to restrict access to a page undergoing migration. ++ * in the swap offset. They can either be used to represent unaddressable device ++ * memory, to restrict access to a page undergoing migration or to represent a ++ * pfn which has been hwpoisoned and unmapped. + */ + static inline bool is_pfn_swap_entry(swp_entry_t entry) + { +@@ -479,7 +509,7 @@ static inline bool is_pfn_swap_entry(swp_entry_t entry) + BUILD_BUG_ON(SWP_TYPE_SHIFT < SWP_PFN_BITS); + + return is_migration_entry(entry) || is_device_private_entry(entry) || +- is_device_exclusive_entry(entry); ++ is_device_exclusive_entry(entry) || is_hwpoison_entry(entry); + } + + struct page_vma_mapped_walk; +@@ -548,35 +578,6 @@ static inline int is_pmd_migration_entry(pmd_t pmd) + } + #endif /* CONFIG_ARCH_ENABLE_THP_MIGRATION */ + +-#ifdef CONFIG_MEMORY_FAILURE +- +-/* +- * Support for hardware poisoned pages +- */ +-static inline swp_entry_t make_hwpoison_entry(struct page *page) +-{ +- BUG_ON(!PageLocked(page)); +- return swp_entry(SWP_HWPOISON, page_to_pfn(page)); +-} +- +-static inline int is_hwpoison_entry(swp_entry_t entry) +-{ +- return swp_type(entry) == SWP_HWPOISON; +-} +- +-#else +- +-static inline swp_entry_t make_hwpoison_entry(struct page *page) +-{ +- return swp_entry(0, 0); +-} +- +-static inline int is_hwpoison_entry(swp_entry_t swp) +-{ +- return 0; +-} +-#endif +- + static inline int non_swap_entry(swp_entry_t entry) + { + return swp_type(entry) >= MAX_SWAPFILES; +diff --git a/include/linux/udp.h b/include/linux/udp.h +index 94e63b2695406..00790bb5cbde6 100644 +--- a/include/linux/udp.h ++++ b/include/linux/udp.h +@@ -105,7 +105,7 @@ struct udp_sock { + #define udp_assign_bit(nr, sk, val) \ + assign_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags, val) + +-#define UDP_MAX_SEGMENTS (1 << 6UL) ++#define UDP_MAX_SEGMENTS (1 << 7UL) + + #define udp_sk(ptr) container_of_const(ptr, struct udp_sock, inet.sk) + +diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h +index 61d4f0b793dcd..d0e19ac3ba6ce 100644 +--- a/include/linux/usb/hcd.h ++++ b/include/linux/usb/hcd.h +@@ -372,8 +372,9 @@ struct hc_driver { + * or bandwidth constraints. + */ + void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *); +- /* Returns the hardware-chosen device address */ +- int (*address_device)(struct usb_hcd *, struct usb_device *udev); ++ /* Set the hardware-chosen device address */ ++ int (*address_device)(struct usb_hcd *, struct usb_device *udev, ++ unsigned int timeout_ms); + /* prepares the hardware to send commands to the device */ + int (*enable_device)(struct usb_hcd *, struct usb_device *udev); + /* Notifies the HCD after a hub descriptor is fetched. +diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h +index eeb7c2157c72f..59409c1fc3dee 100644 +--- a/include/linux/usb/quirks.h ++++ b/include/linux/usb/quirks.h +@@ -72,4 +72,7 @@ + /* device has endpoints that should be ignored */ + #define USB_QUIRK_ENDPOINT_IGNORE BIT(15) + ++/* short SET_ADDRESS request timeout */ ++#define USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT BIT(16) ++ + #endif /* __LINUX_USB_QUIRKS_H */ +diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h +index 4a767b3d20b9d..df7775afb92b9 100644 +--- a/include/net/netfilter/nf_flow_table.h ++++ b/include/net/netfilter/nf_flow_table.h +@@ -335,7 +335,7 @@ int nf_flow_rule_route_ipv6(struct net *net, struct flow_offload *flow, + int nf_flow_table_offload_init(void); + void nf_flow_table_offload_exit(void); + +-static inline __be16 nf_flow_pppoe_proto(const struct sk_buff *skb) ++static inline __be16 __nf_flow_pppoe_proto(const struct sk_buff *skb) + { + __be16 proto; + +@@ -351,6 +351,16 @@ static inline __be16 nf_flow_pppoe_proto(const struct sk_buff *skb) + return 0; + } + ++static inline bool nf_flow_pppoe_proto(struct sk_buff *skb, __be16 *inner_proto) ++{ ++ if (!pskb_may_pull(skb, PPPOE_SES_HLEN)) ++ return false; ++ ++ *inner_proto = __nf_flow_pppoe_proto(skb); ++ ++ return true; ++} ++ + #define NF_FLOW_TABLE_STAT_INC(net, count) __this_cpu_inc((net)->ft.stat->count) + #define NF_FLOW_TABLE_STAT_DEC(net, count) __this_cpu_dec((net)->ft.stat->count) + #define NF_FLOW_TABLE_STAT_INC_ATOMIC(net, count) \ +diff --git a/include/trace/events/rpcgss.h b/include/trace/events/rpcgss.h +index ba2d96a1bc2f9..f50fcafc69de2 100644 +--- a/include/trace/events/rpcgss.h ++++ b/include/trace/events/rpcgss.h +@@ -609,7 +609,7 @@ TRACE_EVENT(rpcgss_context, + __field(unsigned int, timeout) + __field(u32, window_size) + __field(int, len) +- __string(acceptor, data) ++ __string_len(acceptor, data, len) + ), + + TP_fast_assign( +@@ -618,7 +618,7 @@ TRACE_EVENT(rpcgss_context, + __entry->timeout = timeout; + __entry->window_size = window_size; + __entry->len = len; +- strncpy(__get_str(acceptor), data, len); ++ __assign_str(acceptor, data); + ), + + TP_printk("win_size=%u expiry=%lu now=%lu timeout=%u acceptor=%.*s", +diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h +index e5f558d964939..ade8dabf62108 100644 +--- a/include/uapi/linux/pci_regs.h ++++ b/include/uapi/linux/pci_regs.h +@@ -1045,6 +1045,7 @@ + #define PCI_EXP_DPC_STATUS_INTERRUPT 0x0008 /* Interrupt Status */ + #define PCI_EXP_DPC_RP_BUSY 0x0010 /* Root Port Busy */ + #define PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT 0x0060 /* Trig Reason Extension */ ++#define PCI_EXP_DPC_RP_PIO_FEP 0x1f00 /* RP PIO First Err Ptr */ + + #define PCI_EXP_DPC_SOURCE_ID 0x0A /* DPC Source Identifier */ + +diff --git a/init/main.c b/init/main.c +index 9e6ab6d593bd8..b25c779e93ac4 100644 +--- a/init/main.c ++++ b/init/main.c +@@ -630,6 +630,8 @@ static void __init setup_command_line(char *command_line) + if (!saved_command_line) + panic("%s: Failed to allocate %zu bytes\n", __func__, len + ilen); + ++ len = xlen + strlen(command_line) + 1; ++ + static_command_line = memblock_alloc(len, SMP_CACHE_BYTES); + if (!static_command_line) + panic("%s: Failed to allocate %zu bytes\n", __func__, len); +diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c +index 62ff7cee5db5f..a5628d29b9b1f 100644 +--- a/io_uring/io_uring.c ++++ b/io_uring/io_uring.c +@@ -2559,19 +2559,6 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, + if (__io_cqring_events_user(ctx) >= min_events) + return 0; + +- if (sig) { +-#ifdef CONFIG_COMPAT +- if (in_compat_syscall()) +- ret = set_compat_user_sigmask((const compat_sigset_t __user *)sig, +- sigsz); +- else +-#endif +- ret = set_user_sigmask(sig, sigsz); +- +- if (ret) +- return ret; +- } +- + init_waitqueue_func_entry(&iowq.wq, io_wake_function); + iowq.wq.private = current; + INIT_LIST_HEAD(&iowq.wq.entry); +@@ -2588,6 +2575,19 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events, + iowq.timeout = ktime_add_ns(timespec64_to_ktime(ts), ktime_get_ns()); + } + ++ if (sig) { ++#ifdef CONFIG_COMPAT ++ if (in_compat_syscall()) ++ ret = set_compat_user_sigmask((const compat_sigset_t __user *)sig, ++ sigsz); ++ else ++#endif ++ ret = set_user_sigmask(sig, sigsz); ++ ++ if (ret) ++ return ret; ++ } ++ + trace_io_uring_cqring_wait(ctx, min_events); + do { + int nr_wait = (int) iowq.cq_tail - READ_ONCE(ctx->rings->cq.tail); +diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h +index 04846272409cc..35c38daa2d3e1 100644 +--- a/kernel/sched/sched.h ++++ b/kernel/sched/sched.h +@@ -88,6 +88,8 @@ + # include + #endif + ++#include ++ + #include "cpupri.h" + #include "cpudeadline.h" + +@@ -3500,13 +3502,19 @@ static inline void switch_mm_cid(struct rq *rq, + * between rq->curr store and load of {prev,next}->mm->pcpu_cid[cpu]. + * Provide it here. + */ +- if (!prev->mm) // from kernel ++ if (!prev->mm) { // from kernel + smp_mb(); +- /* +- * user -> user transition guarantees a memory barrier through +- * switch_mm() when current->mm changes. If current->mm is +- * unchanged, no barrier is needed. +- */ ++ } else { // from user ++ /* ++ * user->user transition relies on an implicit ++ * memory barrier in switch_mm() when ++ * current->mm changes. If the architecture ++ * switch_mm() does not have an implicit memory ++ * barrier, it is emitted here. If current->mm ++ * is unchanged, no barrier is needed. ++ */ ++ smp_mb__after_switch_mm(); ++ } + } + if (prev->mm_cid_active) { + mm_cid_snapshot_time(rq, prev->mm); +diff --git a/lib/bootconfig.c b/lib/bootconfig.c +index c59d26068a640..8841554432d5b 100644 +--- a/lib/bootconfig.c ++++ b/lib/bootconfig.c +@@ -61,9 +61,12 @@ static inline void * __init xbc_alloc_mem(size_t size) + return memblock_alloc(size, SMP_CACHE_BYTES); + } + +-static inline void __init xbc_free_mem(void *addr, size_t size) ++static inline void __init xbc_free_mem(void *addr, size_t size, bool early) + { +- memblock_free(addr, size); ++ if (early) ++ memblock_free(addr, size); ++ else if (addr) ++ memblock_free_late(__pa(addr), size); + } + + #else /* !__KERNEL__ */ +@@ -73,7 +76,7 @@ static inline void *xbc_alloc_mem(size_t size) + return malloc(size); + } + +-static inline void xbc_free_mem(void *addr, size_t size) ++static inline void xbc_free_mem(void *addr, size_t size, bool early) + { + free(addr); + } +@@ -904,13 +907,13 @@ static int __init xbc_parse_tree(void) + * If you need to reuse xbc_init() with new boot config, you can + * use this. + */ +-void __init xbc_exit(void) ++void __init _xbc_exit(bool early) + { +- xbc_free_mem(xbc_data, xbc_data_size); ++ xbc_free_mem(xbc_data, xbc_data_size, early); + xbc_data = NULL; + xbc_data_size = 0; + xbc_node_num = 0; +- xbc_free_mem(xbc_nodes, sizeof(struct xbc_node) * XBC_NODE_MAX); ++ xbc_free_mem(xbc_nodes, sizeof(struct xbc_node) * XBC_NODE_MAX, early); + xbc_nodes = NULL; + brace_index = 0; + } +@@ -963,7 +966,7 @@ int __init xbc_init(const char *data, size_t size, const char **emsg, int *epos) + if (!xbc_nodes) { + if (emsg) + *emsg = "Failed to allocate bootconfig nodes"; +- xbc_exit(); ++ _xbc_exit(true); + return -ENOMEM; + } + memset(xbc_nodes, 0, sizeof(struct xbc_node) * XBC_NODE_MAX); +@@ -977,7 +980,7 @@ int __init xbc_init(const char *data, size_t size, const char **emsg, int *epos) + *epos = xbc_err_pos; + if (emsg) + *emsg = xbc_err_msg; +- xbc_exit(); ++ _xbc_exit(true); + } else + ret = xbc_node_num; + +diff --git a/mm/hugetlb.c b/mm/hugetlb.c +index 5e6c4d367d33a..a17950160395d 100644 +--- a/mm/hugetlb.c ++++ b/mm/hugetlb.c +@@ -6653,9 +6653,13 @@ long hugetlb_change_protection(struct vm_area_struct *vma, + if (!pte_same(pte, newpte)) + set_huge_pte_at(mm, address, ptep, newpte, psize); + } else if (unlikely(is_pte_marker(pte))) { +- /* No other markers apply for now. */ +- WARN_ON_ONCE(!pte_marker_uffd_wp(pte)); +- if (uffd_wp_resolve) ++ /* ++ * Do nothing on a poison marker; page is ++ * corrupted, permissons do not apply. Here ++ * pte_marker_uffd_wp()==true implies !poison ++ * because they're mutual exclusive. ++ */ ++ if (pte_marker_uffd_wp(pte) && uffd_wp_resolve) + /* Safe to modify directly (non-present->none). */ + huge_pte_clear(mm, address, ptep, psize); + } else if (!huge_pte_none(pte)) { +diff --git a/mm/memory-failure.c b/mm/memory-failure.c +index 455093f73a70c..5378edad9df8f 100644 +--- a/mm/memory-failure.c ++++ b/mm/memory-failure.c +@@ -154,11 +154,23 @@ static int __page_handle_poison(struct page *page) + { + int ret; + +- zone_pcp_disable(page_zone(page)); ++ /* ++ * zone_pcp_disable() can't be used here. It will ++ * hold pcp_batch_high_lock and dissolve_free_huge_page() might hold ++ * cpu_hotplug_lock via static_key_slow_dec() when hugetlb vmemmap ++ * optimization is enabled. This will break current lock dependency ++ * chain and leads to deadlock. ++ * Disabling pcp before dissolving the page was a deterministic ++ * approach because we made sure that those pages cannot end up in any ++ * PCP list. Draining PCP lists expels those pages to the buddy system, ++ * but nothing guarantees that those pages do not get back to a PCP ++ * queue if we need to refill those. ++ */ + ret = dissolve_free_huge_page(page); +- if (!ret) ++ if (!ret) { ++ drain_all_pages(page_zone(page)); + ret = take_page_off_buddy(page); +- zone_pcp_enable(page_zone(page)); ++ } + + return ret; + } +diff --git a/mm/shmem.c b/mm/shmem.c +index 80c2666114b3e..f2023cb7f6f6e 100644 +--- a/mm/shmem.c ++++ b/mm/shmem.c +@@ -742,12 +742,6 @@ static long shmem_unused_huge_count(struct super_block *sb, + + #define shmem_huge SHMEM_HUGE_DENY + +-bool shmem_is_huge(struct inode *inode, pgoff_t index, bool shmem_huge_force, +- struct mm_struct *mm, unsigned long vm_flags) +-{ +- return false; +-} +- + static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo, + struct shrink_control *sc, unsigned long nr_to_split) + { +diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c +index c729528b5e85f..e09000e38d071 100644 +--- a/net/bridge/br_input.c ++++ b/net/bridge/br_input.c +@@ -30,7 +30,7 @@ br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb) + return netif_receive_skb(skb); + } + +-static int br_pass_frame_up(struct sk_buff *skb) ++static int br_pass_frame_up(struct sk_buff *skb, bool promisc) + { + struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev; + struct net_bridge *br = netdev_priv(brdev); +@@ -65,6 +65,8 @@ static int br_pass_frame_up(struct sk_buff *skb) + br_multicast_count(br, NULL, skb, br_multicast_igmp_type(skb), + BR_MCAST_DIR_TX); + ++ BR_INPUT_SKB_CB(skb)->promisc = promisc; ++ + return NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, + dev_net(indev), NULL, skb, indev, NULL, + br_netif_receive_skb); +@@ -82,6 +84,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb + struct net_bridge_mcast *brmctx; + struct net_bridge_vlan *vlan; + struct net_bridge *br; ++ bool promisc; + u16 vid = 0; + u8 state; + +@@ -137,7 +140,9 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb + if (p->flags & BR_LEARNING) + br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, 0); + +- local_rcv = !!(br->dev->flags & IFF_PROMISC); ++ promisc = !!(br->dev->flags & IFF_PROMISC); ++ local_rcv = promisc; ++ + if (is_multicast_ether_addr(eth_hdr(skb)->h_dest)) { + /* by definition the broadcast is also a multicast address */ + if (is_broadcast_ether_addr(eth_hdr(skb)->h_dest)) { +@@ -200,7 +205,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb + unsigned long now = jiffies; + + if (test_bit(BR_FDB_LOCAL, &dst->flags)) +- return br_pass_frame_up(skb); ++ return br_pass_frame_up(skb, false); + + if (now != dst->used) + dst->used = now; +@@ -213,7 +218,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb + } + + if (local_rcv) +- return br_pass_frame_up(skb); ++ return br_pass_frame_up(skb, promisc); + + out: + return 0; +@@ -386,6 +391,8 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb) + goto forward; + } + ++ BR_INPUT_SKB_CB(skb)->promisc = false; ++ + /* The else clause should be hit when nf_hook(): + * - returns < 0 (drop/error) + * - returns = 0 (stolen/nf_queue) +diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c +index 6ef67030b4db3..d848c84ed030d 100644 +--- a/net/bridge/br_netfilter_hooks.c ++++ b/net/bridge/br_netfilter_hooks.c +@@ -600,11 +600,17 @@ static unsigned int br_nf_local_in(void *priv, + struct sk_buff *skb, + const struct nf_hook_state *state) + { ++ bool promisc = BR_INPUT_SKB_CB(skb)->promisc; + struct nf_conntrack *nfct = skb_nfct(skb); + const struct nf_ct_hook *ct_hook; + struct nf_conn *ct; + int ret; + ++ if (promisc) { ++ nf_reset_ct(skb); ++ return NF_ACCEPT; ++ } ++ + if (!nfct || skb->pkt_type == PACKET_HOST) + return NF_ACCEPT; + +diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h +index 82e63908dce8a..e4f1a08322da9 100644 +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -583,6 +583,7 @@ struct br_input_skb_cb { + #endif + u8 proxyarp_replied:1; + u8 src_port_isolated:1; ++ u8 promisc:1; + #ifdef CONFIG_BRIDGE_VLAN_FILTERING + u8 vlan_filtered:1; + #endif +diff --git a/net/bridge/netfilter/nf_conntrack_bridge.c b/net/bridge/netfilter/nf_conntrack_bridge.c +index d32fce70d797d..6ef04f9fe481b 100644 +--- a/net/bridge/netfilter/nf_conntrack_bridge.c ++++ b/net/bridge/netfilter/nf_conntrack_bridge.c +@@ -294,18 +294,24 @@ static unsigned int nf_ct_bridge_pre(void *priv, struct sk_buff *skb, + static unsigned int nf_ct_bridge_in(void *priv, struct sk_buff *skb, + const struct nf_hook_state *state) + { +- enum ip_conntrack_info ctinfo; ++ bool promisc = BR_INPUT_SKB_CB(skb)->promisc; ++ struct nf_conntrack *nfct = skb_nfct(skb); + struct nf_conn *ct; + +- if (skb->pkt_type == PACKET_HOST) ++ if (promisc) { ++ nf_reset_ct(skb); ++ return NF_ACCEPT; ++ } ++ ++ if (!nfct || skb->pkt_type == PACKET_HOST) + return NF_ACCEPT; + + /* nf_conntrack_confirm() cannot handle concurrent clones, + * this happens for broad/multicast frames with e.g. macvlan on top + * of the bridge device. + */ +- ct = nf_ct_get(skb, &ctinfo); +- if (!ct || nf_ct_is_confirmed(ct) || nf_ct_is_template(ct)) ++ ct = container_of(nfct, struct nf_conn, ct_general); ++ if (nf_ct_is_confirmed(ct) || nf_ct_is_template(ct)) + return NF_ACCEPT; + + /* let inet prerouting call conntrack again */ +diff --git a/net/netfilter/nf_flow_table_inet.c b/net/netfilter/nf_flow_table_inet.c +index 9505f9d188ff2..6eef15648b7b0 100644 +--- a/net/netfilter/nf_flow_table_inet.c ++++ b/net/netfilter/nf_flow_table_inet.c +@@ -21,7 +21,8 @@ nf_flow_offload_inet_hook(void *priv, struct sk_buff *skb, + proto = veth->h_vlan_encapsulated_proto; + break; + case htons(ETH_P_PPP_SES): +- proto = nf_flow_pppoe_proto(skb); ++ if (!nf_flow_pppoe_proto(skb, &proto)) ++ return NF_ACCEPT; + break; + default: + proto = skb->protocol; +diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c +index e45fade764096..5383bed3d3e00 100644 +--- a/net/netfilter/nf_flow_table_ip.c ++++ b/net/netfilter/nf_flow_table_ip.c +@@ -157,7 +157,7 @@ static void nf_flow_tuple_encap(struct sk_buff *skb, + tuple->encap[i].proto = skb->protocol; + break; + case htons(ETH_P_PPP_SES): +- phdr = (struct pppoe_hdr *)skb_mac_header(skb); ++ phdr = (struct pppoe_hdr *)skb_network_header(skb); + tuple->encap[i].id = ntohs(phdr->sid); + tuple->encap[i].proto = skb->protocol; + break; +@@ -273,10 +273,11 @@ static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb, + return NF_STOLEN; + } + +-static bool nf_flow_skb_encap_protocol(const struct sk_buff *skb, __be16 proto, ++static bool nf_flow_skb_encap_protocol(struct sk_buff *skb, __be16 proto, + u32 *offset) + { + struct vlan_ethhdr *veth; ++ __be16 inner_proto; + + switch (skb->protocol) { + case htons(ETH_P_8021Q): +@@ -287,7 +288,8 @@ static bool nf_flow_skb_encap_protocol(const struct sk_buff *skb, __be16 proto, + } + break; + case htons(ETH_P_PPP_SES): +- if (nf_flow_pppoe_proto(skb) == proto) { ++ if (nf_flow_pppoe_proto(skb, &inner_proto) && ++ inner_proto == proto) { + *offset += PPPOE_SES_HLEN; + return true; + } +@@ -316,7 +318,7 @@ static void nf_flow_encap_pop(struct sk_buff *skb, + skb_reset_network_header(skb); + break; + case htons(ETH_P_PPP_SES): +- skb->protocol = nf_flow_pppoe_proto(skb); ++ skb->protocol = __nf_flow_pppoe_proto(skb); + skb_pull(skb, PPPOE_SES_HLEN); + skb_reset_network_header(skb); + break; +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index 2a4649df8f086..387eee416b0bf 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -3047,7 +3047,7 @@ static const struct nft_expr_type *__nft_expr_type_get(u8 family, + { + const struct nft_expr_type *type, *candidate = NULL; + +- list_for_each_entry(type, &nf_tables_expressions, list) { ++ list_for_each_entry_rcu(type, &nf_tables_expressions, list) { + if (!nla_strcmp(nla, type->name)) { + if (!type->family && !candidate) + candidate = type; +@@ -3079,9 +3079,13 @@ static const struct nft_expr_type *nft_expr_type_get(struct net *net, + if (nla == NULL) + return ERR_PTR(-EINVAL); + ++ rcu_read_lock(); + type = __nft_expr_type_get(family, nla); +- if (type != NULL && try_module_get(type->owner)) ++ if (type != NULL && try_module_get(type->owner)) { ++ rcu_read_unlock(); + return type; ++ } ++ rcu_read_unlock(); + + lockdep_nfnl_nft_mutex_not_held(); + #ifdef CONFIG_MODULES +@@ -7464,7 +7468,7 @@ static const struct nft_object_type *__nft_obj_type_get(u32 objtype, u8 family) + { + const struct nft_object_type *type; + +- list_for_each_entry(type, &nf_tables_objects, list) { ++ list_for_each_entry_rcu(type, &nf_tables_objects, list) { + if (type->family != NFPROTO_UNSPEC && + type->family != family) + continue; +@@ -7480,9 +7484,13 @@ nft_obj_type_get(struct net *net, u32 objtype, u8 family) + { + const struct nft_object_type *type; + ++ rcu_read_lock(); + type = __nft_obj_type_get(objtype, family); +- if (type != NULL && try_module_get(type->owner)) ++ if (type != NULL && try_module_get(type->owner)) { ++ rcu_read_unlock(); + return type; ++ } ++ rcu_read_unlock(); + + lockdep_nfnl_nft_mutex_not_held(); + #ifdef CONFIG_MODULES +diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c +index a890aa0abad58..69b02a3f1ff05 100644 +--- a/net/netfilter/nft_set_pipapo.c ++++ b/net/netfilter/nft_set_pipapo.c +@@ -1993,6 +1993,8 @@ static void nft_pipapo_remove(const struct net *net, const struct nft_set *set, + rules_fx = rules_f0; + + nft_pipapo_for_each_field(f, i, m) { ++ bool last = i == m->field_count - 1; ++ + if (!pipapo_match_field(f, start, rules_fx, + match_start, match_end)) + break; +@@ -2005,16 +2007,18 @@ static void nft_pipapo_remove(const struct net *net, const struct nft_set *set, + + match_start += NFT_PIPAPO_GROUPS_PADDED_SIZE(f); + match_end += NFT_PIPAPO_GROUPS_PADDED_SIZE(f); +- } + +- if (i == m->field_count) { +- priv->dirty = true; +- pipapo_drop(m, rulemap); +- return; ++ if (last && f->mt[rulemap[i].to].e == e) { ++ priv->dirty = true; ++ pipapo_drop(m, rulemap); ++ return; ++ } + } + + first_rule += rules_f0; + } ++ ++ WARN_ON_ONCE(1); /* elem_priv not found */ + } + + /** +diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c +index 918724844231e..6eab35a5e2f3b 100644 +--- a/net/unix/af_unix.c ++++ b/net/unix/af_unix.c +@@ -2587,7 +2587,9 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk, + WRITE_ONCE(u->oob_skb, NULL); + consume_skb(skb); + } +- } else if (!(flags & MSG_PEEK)) { ++ } else if (flags & MSG_PEEK) { ++ skb = NULL; ++ } else { + skb_unlink(skb, &sk->sk_receive_queue); + WRITE_ONCE(u->oob_skb, NULL); + if (!WARN_ON_ONCE(skb_unref(skb))) +@@ -2665,18 +2667,16 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, + last = skb = skb_peek(&sk->sk_receive_queue); + last_len = last ? last->len : 0; + ++again: + #if IS_ENABLED(CONFIG_AF_UNIX_OOB) + if (skb) { + skb = manage_oob(skb, sk, flags, copied); +- if (!skb) { ++ if (!skb && copied) { + unix_state_unlock(sk); +- if (copied) +- break; +- goto redo; ++ break; + } + } + #endif +-again: + if (skb == NULL) { + if (copied >= target) + goto unlock; +diff --git a/sound/core/seq/seq_ump_convert.c b/sound/core/seq/seq_ump_convert.c +index b141024830ecc..ee6ac649df836 100644 +--- a/sound/core/seq/seq_ump_convert.c ++++ b/sound/core/seq/seq_ump_convert.c +@@ -428,7 +428,7 @@ static int cvt_ump_midi2_to_midi1(struct snd_seq_client *dest, + midi1->note.group = midi2->note.group; + midi1->note.status = midi2->note.status; + midi1->note.channel = midi2->note.channel; +- switch (midi2->note.status << 4) { ++ switch (midi2->note.status) { + case UMP_MSG_STATUS_NOTE_ON: + case UMP_MSG_STATUS_NOTE_OFF: + midi1->note.note = midi2->note.note; +diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c +index 6841de9d423e3..057c207237cc2 100644 +--- a/sound/pci/hda/patch_realtek.c ++++ b/sound/pci/hda/patch_realtek.c +@@ -10098,6 +10098,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x152d, 0x1082, "Quanta NL3", ALC269_FIXUP_LIFEBOOK), ++ SND_PCI_QUIRK(0x152d, 0x1262, "Huawei NBLB-WAX9N", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1558, 0x0353, "Clevo V35[05]SN[CDE]Q", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x1323, "Clevo N130ZU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x1325, "Clevo N15[01][CW]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), +@@ -10203,6 +10204,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x222e, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2231, "Thinkpad T560", ALC292_FIXUP_TPT460), + SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460), ++ SND_PCI_QUIRK(0x17aa, 0x2234, "Thinkpad ICE-1", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x17aa, 0x2245, "Thinkpad T470", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2246, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2247, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), +@@ -10315,6 +10317,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1d05, 0x115c, "TongFang GMxTGxx", ALC269_FIXUP_NO_SHUTUP), + SND_PCI_QUIRK(0x1d05, 0x121b, "TongFang GMxAGxx", ALC269_FIXUP_NO_SHUTUP), + SND_PCI_QUIRK(0x1d05, 0x1387, "TongFang GMxIXxx", ALC2XX_FIXUP_HEADSET_MIC), ++ SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), + SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC), + SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC), +diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c +index 6188308223588..a3dec624132d4 100644 +--- a/sound/pci/hda/tas2781_hda_i2c.c ++++ b/sound/pci/hda/tas2781_hda_i2c.c +@@ -420,10 +420,10 @@ static const struct snd_kcontrol_new tas2781_dsp_conf_ctrl = { + static void tas2781_apply_calib(struct tasdevice_priv *tas_priv) + { + static const unsigned char page_array[CALIB_MAX] = { +- 0x17, 0x18, 0x18, 0x0d, 0x18 ++ 0x17, 0x18, 0x18, 0x13, 0x18, + }; + static const unsigned char rgno_array[CALIB_MAX] = { +- 0x74, 0x0c, 0x14, 0x3c, 0x7c ++ 0x74, 0x0c, 0x14, 0x70, 0x7c, + }; + unsigned char *data; + int i, j, rc; +diff --git a/sound/soc/ti/omap3pandora.c b/sound/soc/ti/omap3pandora.c +index a287e9747c2a1..fa92ed97dfe3b 100644 +--- a/sound/soc/ti/omap3pandora.c ++++ b/sound/soc/ti/omap3pandora.c +@@ -7,7 +7,7 @@ + + #include + #include +-#include ++#include + #include + #include + #include +@@ -21,12 +21,11 @@ + + #include "omap-mcbsp.h" + +-#define OMAP3_PANDORA_DAC_POWER_GPIO 118 +-#define OMAP3_PANDORA_AMP_POWER_GPIO 14 +- + #define PREFIX "ASoC omap3pandora: " + + static struct regulator *omap3pandora_dac_reg; ++static struct gpio_desc *dac_power_gpio; ++static struct gpio_desc *amp_power_gpio; + + static int omap3pandora_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +@@ -78,9 +77,9 @@ static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w, + return ret; + } + mdelay(1); +- gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1); ++ gpiod_set_value(dac_power_gpio, 1); + } else { +- gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0); ++ gpiod_set_value(dac_power_gpio, 0); + mdelay(1); + regulator_disable(omap3pandora_dac_reg); + } +@@ -92,9 +91,9 @@ static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) + { + if (SND_SOC_DAPM_EVENT_ON(event)) +- gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1); ++ gpiod_set_value(amp_power_gpio, 1); + else +- gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0); ++ gpiod_set_value(amp_power_gpio, 0); + + return 0; + } +@@ -229,35 +228,10 @@ static int __init omap3pandora_soc_init(void) + + pr_info("OMAP3 Pandora SoC init\n"); + +- ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power"); +- if (ret) { +- pr_err(PREFIX "Failed to get DAC power GPIO\n"); +- return ret; +- } +- +- ret = gpio_direction_output(OMAP3_PANDORA_DAC_POWER_GPIO, 0); +- if (ret) { +- pr_err(PREFIX "Failed to set DAC power GPIO direction\n"); +- goto fail0; +- } +- +- ret = gpio_request(OMAP3_PANDORA_AMP_POWER_GPIO, "amp_power"); +- if (ret) { +- pr_err(PREFIX "Failed to get amp power GPIO\n"); +- goto fail0; +- } +- +- ret = gpio_direction_output(OMAP3_PANDORA_AMP_POWER_GPIO, 0); +- if (ret) { +- pr_err(PREFIX "Failed to set amp power GPIO direction\n"); +- goto fail1; +- } +- + omap3pandora_snd_device = platform_device_alloc("soc-audio", -1); + if (omap3pandora_snd_device == NULL) { + pr_err(PREFIX "Platform device allocation failed\n"); +- ret = -ENOMEM; +- goto fail1; ++ return -ENOMEM; + } + + platform_set_drvdata(omap3pandora_snd_device, &snd_soc_card_omap3pandora); +@@ -268,6 +242,20 @@ static int __init omap3pandora_soc_init(void) + goto fail2; + } + ++ dac_power_gpio = devm_gpiod_get(&omap3pandora_snd_device->dev, ++ "dac", GPIOD_OUT_LOW); ++ if (IS_ERR(dac_power_gpio)) { ++ ret = PTR_ERR(dac_power_gpio); ++ goto fail3; ++ } ++ ++ amp_power_gpio = devm_gpiod_get(&omap3pandora_snd_device->dev, ++ "amp", GPIOD_OUT_LOW); ++ if (IS_ERR(amp_power_gpio)) { ++ ret = PTR_ERR(amp_power_gpio); ++ goto fail3; ++ } ++ + omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc"); + if (IS_ERR(omap3pandora_dac_reg)) { + pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n", +@@ -283,10 +271,7 @@ static int __init omap3pandora_soc_init(void) + platform_device_del(omap3pandora_snd_device); + fail2: + platform_device_put(omap3pandora_snd_device); +-fail1: +- gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO); +-fail0: +- gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO); ++ + return ret; + } + module_init(omap3pandora_soc_init); +@@ -295,8 +280,6 @@ static void __exit omap3pandora_soc_exit(void) + { + regulator_put(omap3pandora_dac_reg); + platform_device_unregister(omap3pandora_snd_device); +- gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO); +- gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO); + } + module_exit(omap3pandora_soc_exit); + +diff --git a/sound/usb/Makefile b/sound/usb/Makefile +index db5ff76d0e61f..8c657c2753c84 100644 +--- a/sound/usb/Makefile ++++ b/sound/usb/Makefile +@@ -12,7 +12,7 @@ snd-usb-audio-objs := card.o \ + mixer.o \ + mixer_quirks.o \ + mixer_scarlett.o \ +- mixer_scarlett_gen2.o \ ++ mixer_scarlett2.o \ + mixer_us16x08.o \ + mixer_s1810c.o \ + pcm.o \ +diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c +index 1f32e3ae3aa31..c8d48566e1759 100644 +--- a/sound/usb/mixer_quirks.c ++++ b/sound/usb/mixer_quirks.c +@@ -33,7 +33,7 @@ + #include "mixer.h" + #include "mixer_quirks.h" + #include "mixer_scarlett.h" +-#include "mixer_scarlett_gen2.h" ++#include "mixer_scarlett2.h" + #include "mixer_us16x08.h" + #include "mixer_s1810c.h" + #include "helper.h" +@@ -3447,8 +3447,13 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) + case USB_ID(0x1235, 0x8213): /* Focusrite Scarlett 8i6 3rd Gen */ + case USB_ID(0x1235, 0x8214): /* Focusrite Scarlett 18i8 3rd Gen */ + case USB_ID(0x1235, 0x8215): /* Focusrite Scarlett 18i20 3rd Gen */ ++ case USB_ID(0x1235, 0x8206): /* Focusrite Clarett 2Pre USB */ ++ case USB_ID(0x1235, 0x8207): /* Focusrite Clarett 4Pre USB */ ++ case USB_ID(0x1235, 0x8208): /* Focusrite Clarett 8Pre USB */ ++ case USB_ID(0x1235, 0x820a): /* Focusrite Clarett+ 2Pre */ ++ case USB_ID(0x1235, 0x820b): /* Focusrite Clarett+ 4Pre */ + case USB_ID(0x1235, 0x820c): /* Focusrite Clarett+ 8Pre */ +- err = snd_scarlett_gen2_init(mixer); ++ err = snd_scarlett2_init(mixer); + break; + + case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */ +diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c +new file mode 100644 +index 0000000000000..90480b9b9b089 +--- /dev/null ++++ b/sound/usb/mixer_scarlett2.c +@@ -0,0 +1,4391 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Focusrite Scarlett 2 Protocol Driver for ALSA ++ * (including Scarlett 2nd Gen, 3rd Gen, Clarett USB, and Clarett+ ++ * series products) ++ * ++ * Supported models: ++ * - 6i6/18i8/18i20 Gen 2 ++ * - Solo/2i2/4i4/8i6/18i8/18i20 Gen 3 ++ * - Clarett 2Pre/4Pre/8Pre USB ++ * - Clarett+ 2Pre/4Pre/8Pre ++ * ++ * Copyright (c) 2018-2023 by Geoffrey D. Bennett ++ * Copyright (c) 2020-2021 by Vladimir Sadovnikov ++ * Copyright (c) 2022 by Christian Colglazier ++ * ++ * Based on the Scarlett (Gen 1) Driver for ALSA: ++ * ++ * Copyright (c) 2013 by Tobias Hoffmann ++ * Copyright (c) 2013 by Robin Gareus ++ * Copyright (c) 2002 by Takashi Iwai ++ * Copyright (c) 2014 by Chris J Arges ++ * ++ * Many codes borrowed from audio.c by ++ * Alan Cox (alan at lxorguk.ukuu.org.uk) ++ * Thomas Sailer (sailer at ife.ee.ethz.ch) ++ * ++ * Code cleanup: ++ * David Henningsson ++ */ ++ ++/* The protocol was reverse engineered by looking at the communication ++ * between Focusrite Control 2.3.4 and the Focusrite(R) Scarlett 18i20 ++ * (firmware 1083) using usbmon in July-August 2018. ++ * ++ * Scarlett 18i8 support added in April 2019. ++ * ++ * Scarlett 6i6 support added in June 2019 (thanks to Martin Wittmann ++ * for providing usbmon output and testing). ++ * ++ * Scarlett 4i4/8i6 Gen 3 support added in May 2020 (thanks to Laurent ++ * Debricon for donating a 4i4 and to Fredrik Unger for providing 8i6 ++ * usbmon output and testing). ++ * ++ * Scarlett 18i8/18i20 Gen 3 support added in June 2020 (thanks to ++ * Darren Jaeckel, Alex Sedlack, and Clovis Lunel for providing usbmon ++ * output, protocol traces and testing). ++ * ++ * Support for loading mixer volume and mux configuration from the ++ * interface during driver initialisation added in May 2021 (thanks to ++ * Vladimir Sadovnikov for figuring out how). ++ * ++ * Support for Solo/2i2 Gen 3 added in May 2021 (thanks to Alexander ++ * Vorona for 2i2 protocol traces). ++ * ++ * Support for phantom power, direct monitoring, speaker switching, ++ * and talkback added in May-June 2021. ++ * ++ * Support for Clarett+ 8Pre added in Aug 2022 by Christian ++ * Colglazier. ++ * ++ * Support for Clarett 8Pre USB added in Sep 2023 (thanks to Philippe ++ * Perrot for confirmation). ++ * ++ * Support for Clarett+ 4Pre and 2Pre added in Sep 2023 (thanks to ++ * Gregory Rozzo for donating a 4Pre, and David Sherwood and Patrice ++ * Peterson for usbmon output). ++ * ++ * Support for Clarett 2Pre and 4Pre USB added in Oct 2023. ++ * ++ * This ALSA mixer gives access to (model-dependent): ++ * - input, output, mixer-matrix muxes ++ * - mixer-matrix gain stages ++ * - gain/volume/mute controls ++ * - level meters ++ * - line/inst level, pad, and air controls ++ * - phantom power, direct monitor, speaker switching, and talkback ++ * controls ++ * - disable/enable MSD mode ++ * - disable/enable standalone mode ++ * ++ * ++ * /--------------\ 18chn 20chn /--------------\ ++ * | Hardware in +--+------\ /-------------+--+ ALSA PCM out | ++ * \--------------/ | | | | \--------------/ ++ * | | | /-----\ | ++ * | | | | | | ++ * | v v v | | ++ * | +---------------+ | | ++ * | \ Matrix Mux / | | ++ * | +-----+-----+ | | ++ * | | | | ++ * | |18chn | | ++ * | | | | ++ * | | 10chn| | ++ * | v | | ++ * | +------------+ | | ++ * | | Mixer | | | ++ * | | Matrix | | | ++ * | | | | | ++ * | | 18x10 Gain | | | ++ * | | stages | | | ++ * | +-----+------+ | | ++ * | | | | ++ * |18chn |10chn | |20chn ++ * | | | | ++ * | +----------/ | ++ * | | | ++ * v v v ++ * =========================== ++ * +---------------+ +--—------------+ ++ * \ Output Mux / \ Capture Mux / ++ * +---+---+---+ +-----+-----+ ++ * | | | ++ * 10chn| | |18chn ++ * | | | ++ * /--------------\ | | | /--------------\ ++ * | S/PDIF, ADAT |<--/ |10chn \-->| ALSA PCM in | ++ * | Hardware out | | \--------------/ ++ * \--------------/ | ++ * v ++ * +-------------+ Software gain per channel. ++ * | Master Gain |<-- 18i20 only: Switch per channel ++ * +------+------+ to select HW or SW gain control. ++ * | ++ * |10chn ++ * /--------------\ | ++ * | Analogue |<------/ ++ * | Hardware out | ++ * \--------------/ ++ * ++ * ++ * Gen 3 devices have a Mass Storage Device (MSD) mode where a small ++ * disk with registration and driver download information is presented ++ * to the host. To access the full functionality of the device without ++ * proprietary software, MSD mode can be disabled by: ++ * - holding down the 48V button for five seconds while powering on ++ * the device, or ++ * - using this driver and alsamixer to change the "MSD Mode" setting ++ * to Off and power-cycling the device ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "usbaudio.h" ++#include "mixer.h" ++#include "helper.h" ++ ++#include "mixer_scarlett2.h" ++ ++/* device_setup value to allow turning MSD mode back on */ ++#define SCARLETT2_MSD_ENABLE 0x02 ++ ++/* device_setup value to disable this mixer driver */ ++#define SCARLETT2_DISABLE 0x04 ++ ++/* some gui mixers can't handle negative ctl values */ ++#define SCARLETT2_VOLUME_BIAS 127 ++ ++/* mixer range from -80dB to +6dB in 0.5dB steps */ ++#define SCARLETT2_MIXER_MIN_DB -80 ++#define SCARLETT2_MIXER_BIAS (-SCARLETT2_MIXER_MIN_DB * 2) ++#define SCARLETT2_MIXER_MAX_DB 6 ++#define SCARLETT2_MIXER_MAX_VALUE \ ++ ((SCARLETT2_MIXER_MAX_DB - SCARLETT2_MIXER_MIN_DB) * 2) ++#define SCARLETT2_MIXER_VALUE_COUNT (SCARLETT2_MIXER_MAX_VALUE + 1) ++ ++/* map from (dB + 80) * 2 to mixer value ++ * for dB in 0 .. 172: int(8192 * pow(10, ((dB - 160) / 2 / 20))) ++ */ ++static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = { ++ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, ++ 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, ++ 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, ++ 23, 24, 25, 27, 29, 30, 32, 34, 36, 38, 41, 43, 46, 48, 51, ++ 54, 57, 61, 65, 68, 73, 77, 81, 86, 91, 97, 103, 109, 115, ++ 122, 129, 137, 145, 154, 163, 173, 183, 194, 205, 217, 230, ++ 244, 259, 274, 290, 307, 326, 345, 365, 387, 410, 434, 460, ++ 487, 516, 547, 579, 614, 650, 689, 730, 773, 819, 867, 919, ++ 973, 1031, 1092, 1157, 1225, 1298, 1375, 1456, 1543, 1634, ++ 1731, 1833, 1942, 2057, 2179, 2308, 2445, 2590, 2744, 2906, ++ 3078, 3261, 3454, 3659, 3876, 4105, 4349, 4606, 4879, 5168, ++ 5475, 5799, 6143, 6507, 6892, 7301, 7733, 8192, 8677, 9191, ++ 9736, 10313, 10924, 11571, 12257, 12983, 13752, 14567, 15430, ++ 16345 ++}; ++ ++/* Maximum number of analogue outputs */ ++#define SCARLETT2_ANALOGUE_MAX 10 ++ ++/* Maximum number of level and pad switches */ ++#define SCARLETT2_LEVEL_SWITCH_MAX 2 ++#define SCARLETT2_PAD_SWITCH_MAX 8 ++#define SCARLETT2_AIR_SWITCH_MAX 8 ++#define SCARLETT2_PHANTOM_SWITCH_MAX 2 ++ ++/* Maximum number of inputs to the mixer */ ++#define SCARLETT2_INPUT_MIX_MAX 25 ++ ++/* Maximum number of outputs from the mixer */ ++#define SCARLETT2_OUTPUT_MIX_MAX 12 ++ ++/* Maximum size of the data in the USB mux assignment message: ++ * 20 inputs, 20 outputs, 25 matrix inputs, 12 spare ++ */ ++#define SCARLETT2_MUX_MAX 77 ++ ++/* Maximum number of meters (sum of output port counts) */ ++#define SCARLETT2_MAX_METERS 65 ++ ++/* There are three different sets of configuration parameters across ++ * the devices ++ */ ++enum { ++ SCARLETT2_CONFIG_SET_NO_MIXER = 0, ++ SCARLETT2_CONFIG_SET_GEN_2 = 1, ++ SCARLETT2_CONFIG_SET_GEN_3 = 2, ++ SCARLETT2_CONFIG_SET_CLARETT = 3, ++ SCARLETT2_CONFIG_SET_COUNT = 4 ++}; ++ ++/* Hardware port types: ++ * - None (no input to mux) ++ * - Analogue I/O ++ * - S/PDIF I/O ++ * - ADAT I/O ++ * - Mixer I/O ++ * - PCM I/O ++ */ ++enum { ++ SCARLETT2_PORT_TYPE_NONE = 0, ++ SCARLETT2_PORT_TYPE_ANALOGUE = 1, ++ SCARLETT2_PORT_TYPE_SPDIF = 2, ++ SCARLETT2_PORT_TYPE_ADAT = 3, ++ SCARLETT2_PORT_TYPE_MIX = 4, ++ SCARLETT2_PORT_TYPE_PCM = 5, ++ SCARLETT2_PORT_TYPE_COUNT = 6, ++}; ++ ++/* I/O count of each port type kept in struct scarlett2_ports */ ++enum { ++ SCARLETT2_PORT_IN = 0, ++ SCARLETT2_PORT_OUT = 1, ++ SCARLETT2_PORT_DIRNS = 2, ++}; ++ ++/* Dim/Mute buttons on the 18i20 */ ++enum { ++ SCARLETT2_BUTTON_MUTE = 0, ++ SCARLETT2_BUTTON_DIM = 1, ++ SCARLETT2_DIM_MUTE_COUNT = 2, ++}; ++ ++static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = { ++ "Mute Playback Switch", "Dim Playback Switch" ++}; ++ ++/* Description of each hardware port type: ++ * - id: hardware ID of this port type ++ * - src_descr: printf format string for mux input selections ++ * - src_num_offset: added to channel number for the fprintf ++ * - dst_descr: printf format string for mixer controls ++ */ ++struct scarlett2_port { ++ u16 id; ++ const char * const src_descr; ++ int src_num_offset; ++ const char * const dst_descr; ++}; ++ ++static const struct scarlett2_port scarlett2_ports[SCARLETT2_PORT_TYPE_COUNT] = { ++ [SCARLETT2_PORT_TYPE_NONE] = { ++ .id = 0x000, ++ .src_descr = "Off" ++ }, ++ [SCARLETT2_PORT_TYPE_ANALOGUE] = { ++ .id = 0x080, ++ .src_descr = "Analogue %d", ++ .src_num_offset = 1, ++ .dst_descr = "Analogue Output %02d Playback" ++ }, ++ [SCARLETT2_PORT_TYPE_SPDIF] = { ++ .id = 0x180, ++ .src_descr = "S/PDIF %d", ++ .src_num_offset = 1, ++ .dst_descr = "S/PDIF Output %d Playback" ++ }, ++ [SCARLETT2_PORT_TYPE_ADAT] = { ++ .id = 0x200, ++ .src_descr = "ADAT %d", ++ .src_num_offset = 1, ++ .dst_descr = "ADAT Output %d Playback" ++ }, ++ [SCARLETT2_PORT_TYPE_MIX] = { ++ .id = 0x300, ++ .src_descr = "Mix %c", ++ .src_num_offset = 'A', ++ .dst_descr = "Mixer Input %02d Capture" ++ }, ++ [SCARLETT2_PORT_TYPE_PCM] = { ++ .id = 0x600, ++ .src_descr = "PCM %d", ++ .src_num_offset = 1, ++ .dst_descr = "PCM %02d Capture" ++ }, ++}; ++ ++/* Number of mux tables: one for each band of sample rates ++ * (44.1/48kHz, 88.2/96kHz, and 176.4/176kHz) ++ */ ++#define SCARLETT2_MUX_TABLES 3 ++ ++/* Maximum number of entries in a mux table */ ++#define SCARLETT2_MAX_MUX_ENTRIES 10 ++ ++/* One entry within mux_assignment defines the port type and range of ++ * ports to add to the set_mux message. The end of the list is marked ++ * with count == 0. ++ */ ++struct scarlett2_mux_entry { ++ u8 port_type; ++ u8 start; ++ u8 count; ++}; ++ ++struct scarlett2_device_info { ++ /* Gen 3 devices have an internal MSD mode switch that needs ++ * to be disabled in order to access the full functionality of ++ * the device. ++ */ ++ u8 has_msd_mode; ++ ++ /* which set of configuration parameters the device uses */ ++ u8 config_set; ++ ++ /* line out hw volume is sw controlled */ ++ u8 line_out_hw_vol; ++ ++ /* support for main/alt speaker switching */ ++ u8 has_speaker_switching; ++ ++ /* support for talkback microphone */ ++ u8 has_talkback; ++ ++ /* the number of analogue inputs with a software switchable ++ * level control that can be set to line or instrument ++ */ ++ u8 level_input_count; ++ ++ /* the first input with a level control (0-based) */ ++ u8 level_input_first; ++ ++ /* the number of analogue inputs with a software switchable ++ * 10dB pad control ++ */ ++ u8 pad_input_count; ++ ++ /* the number of analogue inputs with a software switchable ++ * "air" control ++ */ ++ u8 air_input_count; ++ ++ /* the number of phantom (48V) software switchable controls */ ++ u8 phantom_count; ++ ++ /* the number of inputs each phantom switch controls */ ++ u8 inputs_per_phantom; ++ ++ /* the number of direct monitor options ++ * (0 = none, 1 = mono only, 2 = mono/stereo) ++ */ ++ u8 direct_monitor; ++ ++ /* remap analogue outputs; 18i8 Gen 3 has "line 3/4" connected ++ * internally to the analogue 7/8 outputs ++ */ ++ u8 line_out_remap_enable; ++ u8 line_out_remap[SCARLETT2_ANALOGUE_MAX]; ++ ++ /* additional description for the line out volume controls */ ++ const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX]; ++ ++ /* number of sources/destinations of each port type */ ++ const int port_count[SCARLETT2_PORT_TYPE_COUNT][SCARLETT2_PORT_DIRNS]; ++ ++ /* layout/order of the entries in the set_mux message */ ++ struct scarlett2_mux_entry mux_assignment[SCARLETT2_MUX_TABLES] ++ [SCARLETT2_MAX_MUX_ENTRIES]; ++}; ++ ++struct scarlett2_data { ++ struct usb_mixer_interface *mixer; ++ struct mutex usb_mutex; /* prevent sending concurrent USB requests */ ++ struct mutex data_mutex; /* lock access to this data */ ++ struct delayed_work work; ++ const struct scarlett2_device_info *info; ++ const char *series_name; ++ __u8 bInterfaceNumber; ++ __u8 bEndpointAddress; ++ __u16 wMaxPacketSize; ++ __u8 bInterval; ++ int num_mux_srcs; ++ int num_mux_dsts; ++ u16 scarlett2_seq; ++ u8 sync_updated; ++ u8 vol_updated; ++ u8 input_other_updated; ++ u8 monitor_other_updated; ++ u8 mux_updated; ++ u8 speaker_switching_switched; ++ u8 sync; ++ u8 master_vol; ++ u8 vol[SCARLETT2_ANALOGUE_MAX]; ++ u8 vol_sw_hw_switch[SCARLETT2_ANALOGUE_MAX]; ++ u8 mute_switch[SCARLETT2_ANALOGUE_MAX]; ++ u8 level_switch[SCARLETT2_LEVEL_SWITCH_MAX]; ++ u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX]; ++ u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT]; ++ u8 air_switch[SCARLETT2_AIR_SWITCH_MAX]; ++ u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX]; ++ u8 phantom_persistence; ++ u8 direct_monitor_switch; ++ u8 speaker_switching_switch; ++ u8 talkback_switch; ++ u8 talkback_map[SCARLETT2_OUTPUT_MIX_MAX]; ++ u8 msd_switch; ++ u8 standalone_switch; ++ struct snd_kcontrol *sync_ctl; ++ struct snd_kcontrol *master_vol_ctl; ++ struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX]; ++ struct snd_kcontrol *sw_hw_ctls[SCARLETT2_ANALOGUE_MAX]; ++ struct snd_kcontrol *mute_ctls[SCARLETT2_ANALOGUE_MAX]; ++ struct snd_kcontrol *dim_mute_ctls[SCARLETT2_DIM_MUTE_COUNT]; ++ struct snd_kcontrol *level_ctls[SCARLETT2_LEVEL_SWITCH_MAX]; ++ struct snd_kcontrol *pad_ctls[SCARLETT2_PAD_SWITCH_MAX]; ++ struct snd_kcontrol *air_ctls[SCARLETT2_AIR_SWITCH_MAX]; ++ struct snd_kcontrol *phantom_ctls[SCARLETT2_PHANTOM_SWITCH_MAX]; ++ struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX]; ++ struct snd_kcontrol *direct_monitor_ctl; ++ struct snd_kcontrol *speaker_switching_ctl; ++ struct snd_kcontrol *talkback_ctl; ++ u8 mux[SCARLETT2_MUX_MAX]; ++ u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX]; ++}; ++ ++/*** Model-specific data ***/ ++ ++static const struct scarlett2_device_info s6i6_gen2_info = { ++ .config_set = SCARLETT2_CONFIG_SET_GEN_2, ++ .level_input_count = 2, ++ .pad_input_count = 2, ++ ++ .line_out_descrs = { ++ "Headphones 1 L", ++ "Headphones 1 R", ++ "Headphones 2 L", ++ "Headphones 2 R", ++ }, ++ ++ .port_count = { ++ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, ++ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 4, 4 }, ++ [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, ++ [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, ++ [SCARLETT2_PORT_TYPE_PCM] = { 6, 6 }, ++ }, ++ ++ .mux_assignment = { { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, ++ { 0, 0, 0 }, ++ } }, ++}; ++ ++static const struct scarlett2_device_info s18i8_gen2_info = { ++ .config_set = SCARLETT2_CONFIG_SET_GEN_2, ++ .level_input_count = 2, ++ .pad_input_count = 4, ++ ++ .line_out_descrs = { ++ "Monitor L", ++ "Monitor R", ++ "Headphones 1 L", ++ "Headphones 1 R", ++ "Headphones 2 L", ++ "Headphones 2 R", ++ }, ++ ++ .port_count = { ++ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, ++ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 8, 6 }, ++ [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, ++ [SCARLETT2_PORT_TYPE_ADAT] = { 8, 0 }, ++ [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, ++ [SCARLETT2_PORT_TYPE_PCM] = { 8, 18 }, ++ }, ++ ++ .mux_assignment = { { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 14 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 4 }, ++ { 0, 0, 0 }, ++ } }, ++}; ++ ++static const struct scarlett2_device_info s18i20_gen2_info = { ++ .config_set = SCARLETT2_CONFIG_SET_GEN_2, ++ .line_out_hw_vol = 1, ++ ++ .line_out_descrs = { ++ "Monitor L", ++ "Monitor R", ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ "Headphones 1 L", ++ "Headphones 1 R", ++ "Headphones 2 L", ++ "Headphones 2 R", ++ }, ++ ++ .port_count = { ++ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, ++ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 8, 10 }, ++ [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, ++ [SCARLETT2_PORT_TYPE_ADAT] = { 8, 8 }, ++ [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, ++ [SCARLETT2_PORT_TYPE_PCM] = { 20, 18 }, ++ }, ++ ++ .mux_assignment = { { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_ADAT, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 14 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_ADAT, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 6 }, ++ { 0, 0, 0 }, ++ } }, ++}; ++ ++static const struct scarlett2_device_info solo_gen3_info = { ++ .has_msd_mode = 1, ++ .config_set = SCARLETT2_CONFIG_SET_NO_MIXER, ++ .level_input_count = 1, ++ .level_input_first = 1, ++ .air_input_count = 1, ++ .phantom_count = 1, ++ .inputs_per_phantom = 1, ++ .direct_monitor = 1, ++}; ++ ++static const struct scarlett2_device_info s2i2_gen3_info = { ++ .has_msd_mode = 1, ++ .config_set = SCARLETT2_CONFIG_SET_NO_MIXER, ++ .level_input_count = 2, ++ .air_input_count = 2, ++ .phantom_count = 1, ++ .inputs_per_phantom = 2, ++ .direct_monitor = 2, ++}; ++ ++static const struct scarlett2_device_info s4i4_gen3_info = { ++ .has_msd_mode = 1, ++ .config_set = SCARLETT2_CONFIG_SET_GEN_3, ++ .level_input_count = 2, ++ .pad_input_count = 2, ++ .air_input_count = 2, ++ .phantom_count = 1, ++ .inputs_per_phantom = 2, ++ ++ .line_out_descrs = { ++ "Monitor L", ++ "Monitor R", ++ "Headphones L", ++ "Headphones R", ++ }, ++ ++ .port_count = { ++ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, ++ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 4, 4 }, ++ [SCARLETT2_PORT_TYPE_MIX] = { 6, 8 }, ++ [SCARLETT2_PORT_TYPE_PCM] = { 4, 6 }, ++ }, ++ ++ .mux_assignment = { { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 16 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 16 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 16 }, ++ { 0, 0, 0 }, ++ } }, ++}; ++ ++static const struct scarlett2_device_info s8i6_gen3_info = { ++ .has_msd_mode = 1, ++ .config_set = SCARLETT2_CONFIG_SET_GEN_3, ++ .level_input_count = 2, ++ .pad_input_count = 2, ++ .air_input_count = 2, ++ .phantom_count = 1, ++ .inputs_per_phantom = 2, ++ ++ .line_out_descrs = { ++ "Headphones 1 L", ++ "Headphones 1 R", ++ "Headphones 2 L", ++ "Headphones 2 R", ++ }, ++ ++ .port_count = { ++ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, ++ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 6, 4 }, ++ [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, ++ [SCARLETT2_PORT_TYPE_MIX] = { 8, 8 }, ++ [SCARLETT2_PORT_TYPE_PCM] = { 6, 10 }, ++ }, ++ ++ .mux_assignment = { { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 18 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 18 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 18 }, ++ { 0, 0, 0 }, ++ } }, ++}; ++ ++static const struct scarlett2_device_info s18i8_gen3_info = { ++ .has_msd_mode = 1, ++ .config_set = SCARLETT2_CONFIG_SET_GEN_3, ++ .line_out_hw_vol = 1, ++ .has_speaker_switching = 1, ++ .level_input_count = 2, ++ .pad_input_count = 4, ++ .air_input_count = 4, ++ .phantom_count = 2, ++ .inputs_per_phantom = 2, ++ ++ .line_out_remap_enable = 1, ++ .line_out_remap = { 0, 1, 6, 7, 2, 3, 4, 5 }, ++ ++ .line_out_descrs = { ++ "Monitor L", ++ "Monitor R", ++ "Alt Monitor L", ++ "Alt Monitor R", ++ "Headphones 1 L", ++ "Headphones 1 R", ++ "Headphones 2 L", ++ "Headphones 2 R", ++ }, ++ ++ .port_count = { ++ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, ++ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 8, 8 }, ++ [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, ++ [SCARLETT2_PORT_TYPE_ADAT] = { 8, 0 }, ++ [SCARLETT2_PORT_TYPE_MIX] = { 10, 20 }, ++ [SCARLETT2_PORT_TYPE_PCM] = { 8, 20 }, ++ }, ++ ++ .mux_assignment = { { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_PCM, 12, 8 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 6, 2 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 2, 4 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_PCM, 10, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 20 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 10 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_PCM, 12, 4 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 6, 2 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 2, 4 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_PCM, 10, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 20 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 10 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 6, 2 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 2, 4 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 20 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 10 }, ++ { 0, 0, 0 }, ++ } }, ++}; ++ ++static const struct scarlett2_device_info s18i20_gen3_info = { ++ .has_msd_mode = 1, ++ .config_set = SCARLETT2_CONFIG_SET_GEN_3, ++ .line_out_hw_vol = 1, ++ .has_speaker_switching = 1, ++ .has_talkback = 1, ++ .level_input_count = 2, ++ .pad_input_count = 8, ++ .air_input_count = 8, ++ .phantom_count = 2, ++ .inputs_per_phantom = 4, ++ ++ .line_out_descrs = { ++ "Monitor 1 L", ++ "Monitor 1 R", ++ "Monitor 2 L", ++ "Monitor 2 R", ++ NULL, ++ NULL, ++ "Headphones 1 L", ++ "Headphones 1 R", ++ "Headphones 2 L", ++ "Headphones 2 R", ++ }, ++ ++ .port_count = { ++ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, ++ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 9, 10 }, ++ [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, ++ [SCARLETT2_PORT_TYPE_ADAT] = { 8, 8 }, ++ [SCARLETT2_PORT_TYPE_MIX] = { 12, 25 }, ++ [SCARLETT2_PORT_TYPE_PCM] = { 20, 20 }, ++ }, ++ ++ .mux_assignment = { { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_PCM, 10, 10 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_ADAT, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 25 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 12 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_PCM, 10, 8 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_ADAT, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 25 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 10 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 24 }, ++ { 0, 0, 0 }, ++ } }, ++}; ++ ++static const struct scarlett2_device_info clarett_2pre_info = { ++ .config_set = SCARLETT2_CONFIG_SET_CLARETT, ++ .line_out_hw_vol = 1, ++ .level_input_count = 2, ++ .air_input_count = 2, ++ ++ .line_out_descrs = { ++ "Monitor L", ++ "Monitor R", ++ "Headphones L", ++ "Headphones R", ++ }, ++ ++ .port_count = { ++ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, ++ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 2, 4 }, ++ [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 0 }, ++ [SCARLETT2_PORT_TYPE_ADAT] = { 8, 0 }, ++ [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, ++ [SCARLETT2_PORT_TYPE_PCM] = { 4, 12 }, ++ }, ++ ++ .mux_assignment = { { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 12 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 26 }, ++ { 0, 0, 0 }, ++ } }, ++}; ++ ++static const struct scarlett2_device_info clarett_4pre_info = { ++ .config_set = SCARLETT2_CONFIG_SET_CLARETT, ++ .line_out_hw_vol = 1, ++ .level_input_count = 2, ++ .air_input_count = 4, ++ ++ .line_out_descrs = { ++ "Monitor L", ++ "Monitor R", ++ "Headphones 1 L", ++ "Headphones 1 R", ++ "Headphones 2 L", ++ "Headphones 2 R", ++ }, ++ ++ .port_count = { ++ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, ++ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 8, 6 }, ++ [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, ++ [SCARLETT2_PORT_TYPE_ADAT] = { 8, 0 }, ++ [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, ++ [SCARLETT2_PORT_TYPE_PCM] = { 8, 18 }, ++ }, ++ ++ .mux_assignment = { { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 14 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 12 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 24 }, ++ { 0, 0, 0 }, ++ } }, ++}; ++ ++static const struct scarlett2_device_info clarett_8pre_info = { ++ .config_set = SCARLETT2_CONFIG_SET_CLARETT, ++ .line_out_hw_vol = 1, ++ .level_input_count = 2, ++ .air_input_count = 8, ++ ++ .line_out_descrs = { ++ "Monitor L", ++ "Monitor R", ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ "Headphones 1 L", ++ "Headphones 1 R", ++ "Headphones 2 L", ++ "Headphones 2 R", ++ }, ++ ++ .port_count = { ++ [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, ++ [SCARLETT2_PORT_TYPE_ANALOGUE] = { 8, 10 }, ++ [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, ++ [SCARLETT2_PORT_TYPE_ADAT] = { 8, 8 }, ++ [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, ++ [SCARLETT2_PORT_TYPE_PCM] = { 20, 18 }, ++ }, ++ ++ .mux_assignment = { { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_ADAT, 0, 8 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 14 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_ADAT, 0, 4 }, ++ { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, ++ { 0, 0, 0 }, ++ }, { ++ { SCARLETT2_PORT_TYPE_PCM, 0, 12 }, ++ { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, ++ { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, ++ { SCARLETT2_PORT_TYPE_NONE, 0, 22 }, ++ { 0, 0, 0 }, ++ } }, ++}; ++ ++struct scarlett2_device_entry { ++ const u32 usb_id; /* USB device identifier */ ++ const struct scarlett2_device_info *info; ++ const char *series_name; ++}; ++ ++static const struct scarlett2_device_entry scarlett2_devices[] = { ++ /* Supported Gen 2 devices */ ++ { USB_ID(0x1235, 0x8203), &s6i6_gen2_info, "Scarlett Gen 2" }, ++ { USB_ID(0x1235, 0x8204), &s18i8_gen2_info, "Scarlett Gen 2" }, ++ { USB_ID(0x1235, 0x8201), &s18i20_gen2_info, "Scarlett Gen 2" }, ++ ++ /* Supported Gen 3 devices */ ++ { USB_ID(0x1235, 0x8211), &solo_gen3_info, "Scarlett Gen 3" }, ++ { USB_ID(0x1235, 0x8210), &s2i2_gen3_info, "Scarlett Gen 3" }, ++ { USB_ID(0x1235, 0x8212), &s4i4_gen3_info, "Scarlett Gen 3" }, ++ { USB_ID(0x1235, 0x8213), &s8i6_gen3_info, "Scarlett Gen 3" }, ++ { USB_ID(0x1235, 0x8214), &s18i8_gen3_info, "Scarlett Gen 3" }, ++ { USB_ID(0x1235, 0x8215), &s18i20_gen3_info, "Scarlett Gen 3" }, ++ ++ /* Supported Clarett USB/Clarett+ devices */ ++ { USB_ID(0x1235, 0x8206), &clarett_2pre_info, "Clarett USB" }, ++ { USB_ID(0x1235, 0x8207), &clarett_4pre_info, "Clarett USB" }, ++ { USB_ID(0x1235, 0x8208), &clarett_8pre_info, "Clarett USB" }, ++ { USB_ID(0x1235, 0x820a), &clarett_2pre_info, "Clarett+" }, ++ { USB_ID(0x1235, 0x820b), &clarett_4pre_info, "Clarett+" }, ++ { USB_ID(0x1235, 0x820c), &clarett_8pre_info, "Clarett+" }, ++ ++ /* End of list */ ++ { 0, NULL }, ++}; ++ ++/* get the starting port index number for a given port type/direction */ ++static int scarlett2_get_port_start_num( ++ const int port_count[][SCARLETT2_PORT_DIRNS], ++ int direction, int port_type) ++{ ++ int i, num = 0; ++ ++ for (i = 0; i < port_type; i++) ++ num += port_count[i][direction]; ++ ++ return num; ++} ++ ++/*** USB Interactions ***/ ++ ++/* Notifications from the interface */ ++#define SCARLETT2_USB_NOTIFY_SYNC 0x00000008 ++#define SCARLETT2_USB_NOTIFY_DIM_MUTE 0x00200000 ++#define SCARLETT2_USB_NOTIFY_MONITOR 0x00400000 ++#define SCARLETT2_USB_NOTIFY_INPUT_OTHER 0x00800000 ++#define SCARLETT2_USB_NOTIFY_MONITOR_OTHER 0x01000000 ++ ++/* Commands for sending/receiving requests/responses */ ++#define SCARLETT2_USB_CMD_INIT 0 ++#define SCARLETT2_USB_CMD_REQ 2 ++#define SCARLETT2_USB_CMD_RESP 3 ++ ++#define SCARLETT2_USB_INIT_1 0x00000000 ++#define SCARLETT2_USB_INIT_2 0x00000002 ++#define SCARLETT2_USB_GET_METER 0x00001001 ++#define SCARLETT2_USB_GET_MIX 0x00002001 ++#define SCARLETT2_USB_SET_MIX 0x00002002 ++#define SCARLETT2_USB_GET_MUX 0x00003001 ++#define SCARLETT2_USB_SET_MUX 0x00003002 ++#define SCARLETT2_USB_GET_SYNC 0x00006004 ++#define SCARLETT2_USB_GET_DATA 0x00800000 ++#define SCARLETT2_USB_SET_DATA 0x00800001 ++#define SCARLETT2_USB_DATA_CMD 0x00800002 ++ ++#define SCARLETT2_USB_CONFIG_SAVE 6 ++ ++#define SCARLETT2_USB_VOLUME_STATUS_OFFSET 0x31 ++#define SCARLETT2_USB_METER_LEVELS_GET_MAGIC 1 ++ ++/* volume status is read together (matches scarlett2_config_items[1]) */ ++struct scarlett2_usb_volume_status { ++ /* dim/mute buttons */ ++ u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT]; ++ ++ u8 pad1; ++ ++ /* software volume setting */ ++ s16 sw_vol[SCARLETT2_ANALOGUE_MAX]; ++ ++ /* actual volume of output inc. dim (-18dB) */ ++ s16 hw_vol[SCARLETT2_ANALOGUE_MAX]; ++ ++ /* internal mute buttons */ ++ u8 mute_switch[SCARLETT2_ANALOGUE_MAX]; ++ ++ /* sw (0) or hw (1) controlled */ ++ u8 sw_hw_switch[SCARLETT2_ANALOGUE_MAX]; ++ ++ u8 pad3[6]; ++ ++ /* front panel volume knob */ ++ s16 master_vol; ++} __packed; ++ ++/* Configuration parameters that can be read and written */ ++enum { ++ SCARLETT2_CONFIG_DIM_MUTE = 0, ++ SCARLETT2_CONFIG_LINE_OUT_VOLUME = 1, ++ SCARLETT2_CONFIG_MUTE_SWITCH = 2, ++ SCARLETT2_CONFIG_SW_HW_SWITCH = 3, ++ SCARLETT2_CONFIG_LEVEL_SWITCH = 4, ++ SCARLETT2_CONFIG_PAD_SWITCH = 5, ++ SCARLETT2_CONFIG_MSD_SWITCH = 6, ++ SCARLETT2_CONFIG_AIR_SWITCH = 7, ++ SCARLETT2_CONFIG_STANDALONE_SWITCH = 8, ++ SCARLETT2_CONFIG_PHANTOM_SWITCH = 9, ++ SCARLETT2_CONFIG_PHANTOM_PERSISTENCE = 10, ++ SCARLETT2_CONFIG_DIRECT_MONITOR = 11, ++ SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH = 12, ++ SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE = 13, ++ SCARLETT2_CONFIG_TALKBACK_MAP = 14, ++ SCARLETT2_CONFIG_COUNT = 15 ++}; ++ ++/* Location, size, and activation command number for the configuration ++ * parameters. Size is in bits and may be 1, 8, or 16. ++ */ ++struct scarlett2_config { ++ u8 offset; ++ u8 size; ++ u8 activate; ++}; ++ ++static const struct scarlett2_config ++ scarlett2_config_items[SCARLETT2_CONFIG_SET_COUNT] ++ [SCARLETT2_CONFIG_COUNT] = ++ ++/* Devices without a mixer (Gen 3 Solo and 2i2) */ ++{ { ++ [SCARLETT2_CONFIG_MSD_SWITCH] = { ++ .offset = 0x04, .size = 8, .activate = 6 }, ++ ++ [SCARLETT2_CONFIG_PHANTOM_PERSISTENCE] = { ++ .offset = 0x05, .size = 8, .activate = 6 }, ++ ++ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { ++ .offset = 0x06, .size = 8, .activate = 3 }, ++ ++ [SCARLETT2_CONFIG_DIRECT_MONITOR] = { ++ .offset = 0x07, .size = 8, .activate = 4 }, ++ ++ [SCARLETT2_CONFIG_LEVEL_SWITCH] = { ++ .offset = 0x08, .size = 1, .activate = 7 }, ++ ++ [SCARLETT2_CONFIG_AIR_SWITCH] = { ++ .offset = 0x09, .size = 1, .activate = 8 }, ++ ++/* Gen 2 devices: 6i6, 18i8, 18i20 */ ++}, { ++ [SCARLETT2_CONFIG_DIM_MUTE] = { ++ .offset = 0x31, .size = 8, .activate = 2 }, ++ ++ [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = { ++ .offset = 0x34, .size = 16, .activate = 1 }, ++ ++ [SCARLETT2_CONFIG_MUTE_SWITCH] = { ++ .offset = 0x5c, .size = 8, .activate = 1 }, ++ ++ [SCARLETT2_CONFIG_SW_HW_SWITCH] = { ++ .offset = 0x66, .size = 8, .activate = 3 }, ++ ++ [SCARLETT2_CONFIG_LEVEL_SWITCH] = { ++ .offset = 0x7c, .size = 8, .activate = 7 }, ++ ++ [SCARLETT2_CONFIG_PAD_SWITCH] = { ++ .offset = 0x84, .size = 8, .activate = 8 }, ++ ++ [SCARLETT2_CONFIG_STANDALONE_SWITCH] = { ++ .offset = 0x8d, .size = 8, .activate = 6 }, ++ ++/* Gen 3 devices: 4i4, 8i6, 18i8, 18i20 */ ++}, { ++ [SCARLETT2_CONFIG_DIM_MUTE] = { ++ .offset = 0x31, .size = 8, .activate = 2 }, ++ ++ [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = { ++ .offset = 0x34, .size = 16, .activate = 1 }, ++ ++ [SCARLETT2_CONFIG_MUTE_SWITCH] = { ++ .offset = 0x5c, .size = 8, .activate = 1 }, ++ ++ [SCARLETT2_CONFIG_SW_HW_SWITCH] = { ++ .offset = 0x66, .size = 8, .activate = 3 }, ++ ++ [SCARLETT2_CONFIG_LEVEL_SWITCH] = { ++ .offset = 0x7c, .size = 8, .activate = 7 }, ++ ++ [SCARLETT2_CONFIG_PAD_SWITCH] = { ++ .offset = 0x84, .size = 8, .activate = 8 }, ++ ++ [SCARLETT2_CONFIG_AIR_SWITCH] = { ++ .offset = 0x8c, .size = 8, .activate = 8 }, ++ ++ [SCARLETT2_CONFIG_STANDALONE_SWITCH] = { ++ .offset = 0x95, .size = 8, .activate = 6 }, ++ ++ [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { ++ .offset = 0x9c, .size = 1, .activate = 8 }, ++ ++ [SCARLETT2_CONFIG_MSD_SWITCH] = { ++ .offset = 0x9d, .size = 8, .activate = 6 }, ++ ++ [SCARLETT2_CONFIG_PHANTOM_PERSISTENCE] = { ++ .offset = 0x9e, .size = 8, .activate = 6 }, ++ ++ [SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH] = { ++ .offset = 0x9f, .size = 1, .activate = 10 }, ++ ++ [SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE] = { ++ .offset = 0xa0, .size = 1, .activate = 10 }, ++ ++ [SCARLETT2_CONFIG_TALKBACK_MAP] = { ++ .offset = 0xb0, .size = 16, .activate = 10 }, ++ ++/* Clarett USB and Clarett+ devices: 2Pre, 4Pre, 8Pre */ ++}, { ++ [SCARLETT2_CONFIG_DIM_MUTE] = { ++ .offset = 0x31, .size = 8, .activate = 2 }, ++ ++ [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = { ++ .offset = 0x34, .size = 16, .activate = 1 }, ++ ++ [SCARLETT2_CONFIG_MUTE_SWITCH] = { ++ .offset = 0x5c, .size = 8, .activate = 1 }, ++ ++ [SCARLETT2_CONFIG_SW_HW_SWITCH] = { ++ .offset = 0x66, .size = 8, .activate = 3 }, ++ ++ [SCARLETT2_CONFIG_LEVEL_SWITCH] = { ++ .offset = 0x7c, .size = 8, .activate = 7 }, ++ ++ [SCARLETT2_CONFIG_AIR_SWITCH] = { ++ .offset = 0x95, .size = 8, .activate = 8 }, ++ ++ [SCARLETT2_CONFIG_STANDALONE_SWITCH] = { ++ .offset = 0x8d, .size = 8, .activate = 6 }, ++} }; ++ ++/* proprietary request/response format */ ++struct scarlett2_usb_packet { ++ __le32 cmd; ++ __le16 size; ++ __le16 seq; ++ __le32 error; ++ __le32 pad; ++ u8 data[]; ++}; ++ ++static void scarlett2_fill_request_header(struct scarlett2_data *private, ++ struct scarlett2_usb_packet *req, ++ u32 cmd, u16 req_size) ++{ ++ /* sequence must go up by 1 for each request */ ++ u16 seq = private->scarlett2_seq++; ++ ++ req->cmd = cpu_to_le32(cmd); ++ req->size = cpu_to_le16(req_size); ++ req->seq = cpu_to_le16(seq); ++ req->error = 0; ++ req->pad = 0; ++} ++ ++static int scarlett2_usb_tx(struct usb_device *dev, int interface, ++ void *buf, u16 size) ++{ ++ return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), ++ SCARLETT2_USB_CMD_REQ, ++ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, ++ 0, interface, buf, size); ++} ++ ++static int scarlett2_usb_rx(struct usb_device *dev, int interface, ++ u32 usb_req, void *buf, u16 size) ++{ ++ return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), ++ usb_req, ++ USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, ++ 0, interface, buf, size); ++} ++ ++/* Send a proprietary format request to the Scarlett interface */ ++static int scarlett2_usb( ++ struct usb_mixer_interface *mixer, u32 cmd, ++ void *req_data, u16 req_size, void *resp_data, u16 resp_size) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ struct usb_device *dev = mixer->chip->dev; ++ struct scarlett2_usb_packet *req, *resp = NULL; ++ size_t req_buf_size = struct_size(req, data, req_size); ++ size_t resp_buf_size = struct_size(resp, data, resp_size); ++ int err; ++ ++ req = kmalloc(req_buf_size, GFP_KERNEL); ++ if (!req) { ++ err = -ENOMEM; ++ goto error; ++ } ++ ++ resp = kmalloc(resp_buf_size, GFP_KERNEL); ++ if (!resp) { ++ err = -ENOMEM; ++ goto error; ++ } ++ ++ mutex_lock(&private->usb_mutex); ++ ++ /* build request message and send it */ ++ ++ scarlett2_fill_request_header(private, req, cmd, req_size); ++ ++ if (req_size) ++ memcpy(req->data, req_data, req_size); ++ ++ err = scarlett2_usb_tx(dev, private->bInterfaceNumber, ++ req, req_buf_size); ++ ++ if (err != req_buf_size) { ++ usb_audio_err( ++ mixer->chip, ++ "%s USB request result cmd %x was %d\n", ++ private->series_name, cmd, err); ++ err = -EINVAL; ++ goto unlock; ++ } ++ ++ /* send a second message to get the response */ ++ ++ err = scarlett2_usb_rx(dev, private->bInterfaceNumber, ++ SCARLETT2_USB_CMD_RESP, ++ resp, resp_buf_size); ++ ++ /* validate the response */ ++ ++ if (err != resp_buf_size) { ++ usb_audio_err( ++ mixer->chip, ++ "%s USB response result cmd %x was %d expected %zu\n", ++ private->series_name, cmd, err, resp_buf_size); ++ err = -EINVAL; ++ goto unlock; ++ } ++ ++ /* cmd/seq/size should match except when initialising ++ * seq sent = 1, response = 0 ++ */ ++ if (resp->cmd != req->cmd || ++ (resp->seq != req->seq && ++ (le16_to_cpu(req->seq) != 1 || resp->seq != 0)) || ++ resp_size != le16_to_cpu(resp->size) || ++ resp->error || ++ resp->pad) { ++ usb_audio_err( ++ mixer->chip, ++ "%s USB invalid response; " ++ "cmd tx/rx %d/%d seq %d/%d size %d/%d " ++ "error %d pad %d\n", ++ private->series_name, ++ le32_to_cpu(req->cmd), le32_to_cpu(resp->cmd), ++ le16_to_cpu(req->seq), le16_to_cpu(resp->seq), ++ resp_size, le16_to_cpu(resp->size), ++ le32_to_cpu(resp->error), ++ le32_to_cpu(resp->pad)); ++ err = -EINVAL; ++ goto unlock; ++ } ++ ++ if (resp_data && resp_size > 0) ++ memcpy(resp_data, resp->data, resp_size); ++ ++unlock: ++ mutex_unlock(&private->usb_mutex); ++error: ++ kfree(req); ++ kfree(resp); ++ return err; ++} ++ ++/* Send a USB message to get data; result placed in *buf */ ++static int scarlett2_usb_get( ++ struct usb_mixer_interface *mixer, ++ int offset, void *buf, int size) ++{ ++ struct { ++ __le32 offset; ++ __le32 size; ++ } __packed req; ++ ++ req.offset = cpu_to_le32(offset); ++ req.size = cpu_to_le32(size); ++ return scarlett2_usb(mixer, SCARLETT2_USB_GET_DATA, ++ &req, sizeof(req), buf, size); ++} ++ ++/* Send a USB message to get configuration parameters; result placed in *buf */ ++static int scarlett2_usb_get_config( ++ struct usb_mixer_interface *mixer, ++ int config_item_num, int count, void *buf) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const struct scarlett2_config *config_item = ++ &scarlett2_config_items[info->config_set][config_item_num]; ++ int size, err, i; ++ u8 *buf_8; ++ u8 value; ++ ++ /* For byte-sized parameters, retrieve directly into buf */ ++ if (config_item->size >= 8) { ++ size = config_item->size / 8 * count; ++ err = scarlett2_usb_get(mixer, config_item->offset, buf, size); ++ if (err < 0) ++ return err; ++ if (size == 2) { ++ u16 *buf_16 = buf; ++ ++ for (i = 0; i < count; i++, buf_16++) ++ *buf_16 = le16_to_cpu(*(__le16 *)buf_16); ++ } ++ return 0; ++ } ++ ++ /* For bit-sized parameters, retrieve into value */ ++ err = scarlett2_usb_get(mixer, config_item->offset, &value, 1); ++ if (err < 0) ++ return err; ++ ++ /* then unpack from value into buf[] */ ++ buf_8 = buf; ++ for (i = 0; i < 8 && i < count; i++, value >>= 1) ++ *buf_8++ = value & 1; ++ ++ return 0; ++} ++ ++/* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */ ++static void scarlett2_config_save(struct usb_mixer_interface *mixer) ++{ ++ __le32 req = cpu_to_le32(SCARLETT2_USB_CONFIG_SAVE); ++ ++ int err = scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD, ++ &req, sizeof(u32), ++ NULL, 0); ++ if (err < 0) ++ usb_audio_err(mixer->chip, "config save failed: %d\n", err); ++} ++ ++/* Delayed work to save config */ ++static void scarlett2_config_save_work(struct work_struct *work) ++{ ++ struct scarlett2_data *private = ++ container_of(work, struct scarlett2_data, work.work); ++ ++ scarlett2_config_save(private->mixer); ++} ++ ++/* Send a USB message to set a SCARLETT2_CONFIG_* parameter */ ++static int scarlett2_usb_set_config( ++ struct usb_mixer_interface *mixer, ++ int config_item_num, int index, int value) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const struct scarlett2_config *config_item = ++ &scarlett2_config_items[info->config_set][config_item_num]; ++ struct { ++ __le32 offset; ++ __le32 bytes; ++ __le32 value; ++ } __packed req; ++ __le32 req2; ++ int offset, size; ++ int err; ++ ++ /* Cancel any pending NVRAM save */ ++ cancel_delayed_work_sync(&private->work); ++ ++ /* Convert config_item->size in bits to size in bytes and ++ * calculate offset ++ */ ++ if (config_item->size >= 8) { ++ size = config_item->size / 8; ++ offset = config_item->offset + index * size; ++ ++ /* If updating a bit, retrieve the old value, set/clear the ++ * bit as needed, and update value ++ */ ++ } else { ++ u8 tmp; ++ ++ size = 1; ++ offset = config_item->offset; ++ ++ err = scarlett2_usb_get(mixer, offset, &tmp, 1); ++ if (err < 0) ++ return err; ++ ++ if (value) ++ tmp |= (1 << index); ++ else ++ tmp &= ~(1 << index); ++ ++ value = tmp; ++ } ++ ++ /* Send the configuration parameter data */ ++ req.offset = cpu_to_le32(offset); ++ req.bytes = cpu_to_le32(size); ++ req.value = cpu_to_le32(value); ++ err = scarlett2_usb(mixer, SCARLETT2_USB_SET_DATA, ++ &req, sizeof(u32) * 2 + size, ++ NULL, 0); ++ if (err < 0) ++ return err; ++ ++ /* Activate the change */ ++ req2 = cpu_to_le32(config_item->activate); ++ err = scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD, ++ &req2, sizeof(req2), NULL, 0); ++ if (err < 0) ++ return err; ++ ++ /* Schedule the change to be written to NVRAM */ ++ if (config_item->activate != SCARLETT2_USB_CONFIG_SAVE) ++ schedule_delayed_work(&private->work, msecs_to_jiffies(2000)); ++ ++ return 0; ++} ++ ++/* Send a USB message to get sync status; result placed in *sync */ ++static int scarlett2_usb_get_sync_status( ++ struct usb_mixer_interface *mixer, ++ u8 *sync) ++{ ++ __le32 data; ++ int err; ++ ++ err = scarlett2_usb(mixer, SCARLETT2_USB_GET_SYNC, ++ NULL, 0, &data, sizeof(data)); ++ if (err < 0) ++ return err; ++ ++ *sync = !!data; ++ return 0; ++} ++ ++/* Send a USB message to get volume status; result placed in *buf */ ++static int scarlett2_usb_get_volume_status( ++ struct usb_mixer_interface *mixer, ++ struct scarlett2_usb_volume_status *buf) ++{ ++ return scarlett2_usb_get(mixer, SCARLETT2_USB_VOLUME_STATUS_OFFSET, ++ buf, sizeof(*buf)); ++} ++ ++/* Send a USB message to get the volumes for all inputs of one mix ++ * and put the values into private->mix[] ++ */ ++static int scarlett2_usb_get_mix(struct usb_mixer_interface *mixer, ++ int mix_num) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ ++ int num_mixer_in = ++ info->port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT]; ++ int err, i, j, k; ++ ++ struct { ++ __le16 mix_num; ++ __le16 count; ++ } __packed req; ++ ++ __le16 data[SCARLETT2_INPUT_MIX_MAX]; ++ ++ req.mix_num = cpu_to_le16(mix_num); ++ req.count = cpu_to_le16(num_mixer_in); ++ ++ err = scarlett2_usb(mixer, SCARLETT2_USB_GET_MIX, ++ &req, sizeof(req), ++ data, num_mixer_in * sizeof(u16)); ++ if (err < 0) ++ return err; ++ ++ for (i = 0, j = mix_num * num_mixer_in; i < num_mixer_in; i++, j++) { ++ u16 mixer_value = le16_to_cpu(data[i]); ++ ++ for (k = 0; k < SCARLETT2_MIXER_VALUE_COUNT; k++) ++ if (scarlett2_mixer_values[k] >= mixer_value) ++ break; ++ if (k == SCARLETT2_MIXER_VALUE_COUNT) ++ k = SCARLETT2_MIXER_MAX_VALUE; ++ private->mix[j] = k; ++ } ++ ++ return 0; ++} ++ ++/* Send a USB message to set the volumes for all inputs of one mix ++ * (values obtained from private->mix[]) ++ */ ++static int scarlett2_usb_set_mix(struct usb_mixer_interface *mixer, ++ int mix_num) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ ++ struct { ++ __le16 mix_num; ++ __le16 data[SCARLETT2_INPUT_MIX_MAX]; ++ } __packed req; ++ ++ int i, j; ++ int num_mixer_in = ++ info->port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT]; ++ ++ req.mix_num = cpu_to_le16(mix_num); ++ ++ for (i = 0, j = mix_num * num_mixer_in; i < num_mixer_in; i++, j++) ++ req.data[i] = cpu_to_le16( ++ scarlett2_mixer_values[private->mix[j]] ++ ); ++ ++ return scarlett2_usb(mixer, SCARLETT2_USB_SET_MIX, ++ &req, (num_mixer_in + 1) * sizeof(u16), ++ NULL, 0); ++} ++ ++/* Convert a port number index (per info->port_count) to a hardware ID */ ++static u32 scarlett2_mux_src_num_to_id( ++ const int port_count[][SCARLETT2_PORT_DIRNS], int num) ++{ ++ int port_type; ++ ++ for (port_type = 0; ++ port_type < SCARLETT2_PORT_TYPE_COUNT; ++ port_type++) { ++ if (num < port_count[port_type][SCARLETT2_PORT_IN]) ++ return scarlett2_ports[port_type].id | num; ++ num -= port_count[port_type][SCARLETT2_PORT_IN]; ++ } ++ ++ /* Oops */ ++ return 0; ++} ++ ++/* Convert a hardware ID to a port number index */ ++static u32 scarlett2_mux_id_to_num( ++ const int port_count[][SCARLETT2_PORT_DIRNS], int direction, u32 id) ++{ ++ int port_type; ++ int port_num = 0; ++ ++ for (port_type = 0; ++ port_type < SCARLETT2_PORT_TYPE_COUNT; ++ port_type++) { ++ int base = scarlett2_ports[port_type].id; ++ int count = port_count[port_type][direction]; ++ ++ if (id >= base && id < base + count) ++ return port_num + id - base; ++ port_num += count; ++ } ++ ++ /* Oops */ ++ return -1; ++} ++ ++/* Convert one mux entry from the interface and load into private->mux[] */ ++static void scarlett2_usb_populate_mux(struct scarlett2_data *private, ++ u32 mux_entry) ++{ ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ ++ int dst_idx, src_idx; ++ ++ dst_idx = scarlett2_mux_id_to_num(port_count, SCARLETT2_PORT_OUT, ++ mux_entry & 0xFFF); ++ if (dst_idx < 0) ++ return; ++ ++ if (dst_idx >= private->num_mux_dsts) { ++ usb_audio_err(private->mixer->chip, ++ "BUG: scarlett2_mux_id_to_num(%06x, OUT): %d >= %d", ++ mux_entry, dst_idx, private->num_mux_dsts); ++ return; ++ } ++ ++ src_idx = scarlett2_mux_id_to_num(port_count, SCARLETT2_PORT_IN, ++ mux_entry >> 12); ++ if (src_idx < 0) ++ return; ++ ++ if (src_idx >= private->num_mux_srcs) { ++ usb_audio_err(private->mixer->chip, ++ "BUG: scarlett2_mux_id_to_num(%06x, IN): %d >= %d", ++ mux_entry, src_idx, private->num_mux_srcs); ++ return; ++ } ++ ++ private->mux[dst_idx] = src_idx; ++} ++ ++/* Send USB message to get mux inputs and then populate private->mux[] */ ++static int scarlett2_usb_get_mux(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ int count = private->num_mux_dsts; ++ int err, i; ++ ++ struct { ++ __le16 num; ++ __le16 count; ++ } __packed req; ++ ++ __le32 data[SCARLETT2_MUX_MAX]; ++ ++ private->mux_updated = 0; ++ ++ req.num = 0; ++ req.count = cpu_to_le16(count); ++ ++ err = scarlett2_usb(mixer, SCARLETT2_USB_GET_MUX, ++ &req, sizeof(req), ++ data, count * sizeof(u32)); ++ if (err < 0) ++ return err; ++ ++ for (i = 0; i < count; i++) ++ scarlett2_usb_populate_mux(private, le32_to_cpu(data[i])); ++ ++ return 0; ++} ++ ++/* Send USB messages to set mux inputs */ ++static int scarlett2_usb_set_mux(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ int table; ++ ++ struct { ++ __le16 pad; ++ __le16 num; ++ __le32 data[SCARLETT2_MUX_MAX]; ++ } __packed req; ++ ++ req.pad = 0; ++ ++ /* set mux settings for each rate */ ++ for (table = 0; table < SCARLETT2_MUX_TABLES; table++) { ++ const struct scarlett2_mux_entry *entry; ++ ++ /* i counts over the output array */ ++ int i = 0, err; ++ ++ req.num = cpu_to_le16(table); ++ ++ /* loop through each entry */ ++ for (entry = info->mux_assignment[table]; ++ entry->count; ++ entry++) { ++ int j; ++ int port_type = entry->port_type; ++ int port_idx = entry->start; ++ int mux_idx = scarlett2_get_port_start_num(port_count, ++ SCARLETT2_PORT_OUT, port_type) + port_idx; ++ int dst_id = scarlett2_ports[port_type].id + port_idx; ++ ++ /* Empty slots */ ++ if (!dst_id) { ++ for (j = 0; j < entry->count; j++) ++ req.data[i++] = 0; ++ continue; ++ } ++ ++ /* Non-empty mux slots use the lower 12 bits ++ * for the destination and next 12 bits for ++ * the source ++ */ ++ for (j = 0; j < entry->count; j++) { ++ int src_id = scarlett2_mux_src_num_to_id( ++ port_count, private->mux[mux_idx++]); ++ req.data[i++] = cpu_to_le32(dst_id | ++ src_id << 12); ++ dst_id++; ++ } ++ } ++ ++ err = scarlett2_usb(mixer, SCARLETT2_USB_SET_MUX, ++ &req, (i + 1) * sizeof(u32), ++ NULL, 0); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/* Send USB message to get meter levels */ ++static int scarlett2_usb_get_meter_levels(struct usb_mixer_interface *mixer, ++ u16 num_meters, u16 *levels) ++{ ++ struct { ++ __le16 pad; ++ __le16 num_meters; ++ __le32 magic; ++ } __packed req; ++ u32 resp[SCARLETT2_MAX_METERS]; ++ int i, err; ++ ++ req.pad = 0; ++ req.num_meters = cpu_to_le16(num_meters); ++ req.magic = cpu_to_le32(SCARLETT2_USB_METER_LEVELS_GET_MAGIC); ++ err = scarlett2_usb(mixer, SCARLETT2_USB_GET_METER, ++ &req, sizeof(req), resp, num_meters * sizeof(u32)); ++ if (err < 0) ++ return err; ++ ++ /* copy, convert to u16 */ ++ for (i = 0; i < num_meters; i++) ++ levels[i] = resp[i]; ++ ++ return 0; ++} ++ ++/*** Control Functions ***/ ++ ++/* helper function to create a new control */ ++static int scarlett2_add_new_ctl(struct usb_mixer_interface *mixer, ++ const struct snd_kcontrol_new *ncontrol, ++ int index, int channels, const char *name, ++ struct snd_kcontrol **kctl_return) ++{ ++ struct snd_kcontrol *kctl; ++ struct usb_mixer_elem_info *elem; ++ int err; ++ ++ elem = kzalloc(sizeof(*elem), GFP_KERNEL); ++ if (!elem) ++ return -ENOMEM; ++ ++ /* We set USB_MIXER_BESPOKEN type, so that the core USB mixer code ++ * ignores them for resume and other operations. ++ * Also, the head.id field is set to 0, as we don't use this field. ++ */ ++ elem->head.mixer = mixer; ++ elem->control = index; ++ elem->head.id = 0; ++ elem->channels = channels; ++ elem->val_type = USB_MIXER_BESPOKEN; ++ ++ kctl = snd_ctl_new1(ncontrol, elem); ++ if (!kctl) { ++ kfree(elem); ++ return -ENOMEM; ++ } ++ kctl->private_free = snd_usb_mixer_elem_free; ++ ++ strscpy(kctl->id.name, name, sizeof(kctl->id.name)); ++ ++ err = snd_usb_mixer_add_control(&elem->head, kctl); ++ if (err < 0) ++ return err; ++ ++ if (kctl_return) ++ *kctl_return = kctl; ++ ++ return 0; ++} ++ ++/*** Sync Control ***/ ++ ++/* Update sync control after receiving notification that the status ++ * has changed ++ */ ++static int scarlett2_update_sync(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ ++ private->sync_updated = 0; ++ return scarlett2_usb_get_sync_status(mixer, &private->sync); ++} ++ ++static int scarlett2_sync_ctl_info(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ static const char *texts[2] = { ++ "Unlocked", "Locked" ++ }; ++ return snd_ctl_enum_info(uinfo, 1, 2, texts); ++} ++ ++static int scarlett2_sync_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ if (private->sync_updated) { ++ err = scarlett2_update_sync(mixer); ++ if (err < 0) ++ goto unlock; ++ } ++ ucontrol->value.enumerated.item[0] = private->sync; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_sync_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READ, ++ .name = "", ++ .info = scarlett2_sync_ctl_info, ++ .get = scarlett2_sync_ctl_get ++}; ++ ++static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ ++ /* devices without a mixer also don't support reporting sync status */ ++ if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) ++ return 0; ++ ++ return scarlett2_add_new_ctl(mixer, &scarlett2_sync_ctl, ++ 0, 1, "Sync Status", &private->sync_ctl); ++} ++ ++/*** Analogue Line Out Volume Controls ***/ ++ ++/* Update hardware volume controls after receiving notification that ++ * they have changed ++ */ ++static int scarlett2_update_volumes(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ struct scarlett2_usb_volume_status volume_status; ++ int num_line_out = ++ port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; ++ int err, i; ++ int mute; ++ ++ private->vol_updated = 0; ++ ++ err = scarlett2_usb_get_volume_status(mixer, &volume_status); ++ if (err < 0) ++ return err; ++ ++ private->master_vol = clamp( ++ volume_status.master_vol + SCARLETT2_VOLUME_BIAS, ++ 0, SCARLETT2_VOLUME_BIAS); ++ ++ if (info->line_out_hw_vol) ++ for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) ++ private->dim_mute[i] = !!volume_status.dim_mute[i]; ++ ++ mute = private->dim_mute[SCARLETT2_BUTTON_MUTE]; ++ ++ for (i = 0; i < num_line_out; i++) ++ if (private->vol_sw_hw_switch[i]) { ++ private->vol[i] = private->master_vol; ++ private->mute_switch[i] = mute; ++ } ++ ++ return 0; ++} ++ ++static int scarlett2_volume_ctl_info(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = elem->channels; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = SCARLETT2_VOLUME_BIAS; ++ uinfo->value.integer.step = 1; ++ return 0; ++} ++ ++static int scarlett2_master_volume_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ if (private->vol_updated) { ++ err = scarlett2_update_volumes(mixer); ++ if (err < 0) ++ goto unlock; ++ } ++ ucontrol->value.integer.value[0] = private->master_vol; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static int line_out_remap(struct scarlett2_data *private, int index) ++{ ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ int line_out_count = ++ port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; ++ ++ if (!info->line_out_remap_enable) ++ return index; ++ ++ if (index >= line_out_count) ++ return index; ++ ++ return info->line_out_remap[index]; ++} ++ ++static int scarlett2_volume_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int index = line_out_remap(private, elem->control); ++ int err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ if (private->vol_updated) { ++ err = scarlett2_update_volumes(mixer); ++ if (err < 0) ++ goto unlock; ++ } ++ ucontrol->value.integer.value[0] = private->vol[index]; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static int scarlett2_volume_ctl_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int index = line_out_remap(private, elem->control); ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->vol[index]; ++ val = ucontrol->value.integer.value[0]; ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->vol[index] = val; ++ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME, ++ index, val - SCARLETT2_VOLUME_BIAS); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const DECLARE_TLV_DB_MINMAX( ++ db_scale_scarlett2_gain, -SCARLETT2_VOLUME_BIAS * 100, 0 ++); ++ ++static const struct snd_kcontrol_new scarlett2_master_volume_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READ | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "", ++ .info = scarlett2_volume_ctl_info, ++ .get = scarlett2_master_volume_ctl_get, ++ .private_value = 0, /* max value */ ++ .tlv = { .p = db_scale_scarlett2_gain } ++}; ++ ++static const struct snd_kcontrol_new scarlett2_line_out_volume_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "", ++ .info = scarlett2_volume_ctl_info, ++ .get = scarlett2_volume_ctl_get, ++ .put = scarlett2_volume_ctl_put, ++ .private_value = 0, /* max value */ ++ .tlv = { .p = db_scale_scarlett2_gain } ++}; ++ ++/*** Mute Switch Controls ***/ ++ ++static int scarlett2_mute_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int index = line_out_remap(private, elem->control); ++ int err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ if (private->vol_updated) { ++ err = scarlett2_update_volumes(mixer); ++ if (err < 0) ++ goto unlock; ++ } ++ ucontrol->value.integer.value[0] = private->mute_switch[index]; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static int scarlett2_mute_ctl_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int index = line_out_remap(private, elem->control); ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->mute_switch[index]; ++ val = !!ucontrol->value.integer.value[0]; ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->mute_switch[index] = val; ++ ++ /* Send mute change to the device */ ++ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_MUTE_SWITCH, ++ index, val); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_mute_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = snd_ctl_boolean_mono_info, ++ .get = scarlett2_mute_ctl_get, ++ .put = scarlett2_mute_ctl_put, ++}; ++ ++/*** HW/SW Volume Switch Controls ***/ ++ ++static void scarlett2_sw_hw_ctl_ro(struct scarlett2_data *private, int index) ++{ ++ private->sw_hw_ctls[index]->vd[0].access &= ++ ~SNDRV_CTL_ELEM_ACCESS_WRITE; ++} ++ ++static void scarlett2_sw_hw_ctl_rw(struct scarlett2_data *private, int index) ++{ ++ private->sw_hw_ctls[index]->vd[0].access |= ++ SNDRV_CTL_ELEM_ACCESS_WRITE; ++} ++ ++static int scarlett2_sw_hw_enum_ctl_info(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ static const char *const values[2] = { ++ "SW", "HW" ++ }; ++ ++ return snd_ctl_enum_info(uinfo, 1, 2, values); ++} ++ ++static int scarlett2_sw_hw_enum_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct scarlett2_data *private = elem->head.mixer->private_data; ++ int index = line_out_remap(private, elem->control); ++ ++ ucontrol->value.enumerated.item[0] = private->vol_sw_hw_switch[index]; ++ return 0; ++} ++ ++static void scarlett2_vol_ctl_set_writable(struct usb_mixer_interface *mixer, ++ int index, int value) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ struct snd_card *card = mixer->chip->card; ++ ++ /* Set/Clear write bits */ ++ if (value) { ++ private->vol_ctls[index]->vd[0].access |= ++ SNDRV_CTL_ELEM_ACCESS_WRITE; ++ private->mute_ctls[index]->vd[0].access |= ++ SNDRV_CTL_ELEM_ACCESS_WRITE; ++ } else { ++ private->vol_ctls[index]->vd[0].access &= ++ ~SNDRV_CTL_ELEM_ACCESS_WRITE; ++ private->mute_ctls[index]->vd[0].access &= ++ ~SNDRV_CTL_ELEM_ACCESS_WRITE; ++ } ++ ++ /* Notify of write bit and possible value change */ ++ snd_ctl_notify(card, ++ SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, ++ &private->vol_ctls[index]->id); ++ snd_ctl_notify(card, ++ SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, ++ &private->mute_ctls[index]->id); ++} ++ ++static int scarlett2_sw_hw_change(struct usb_mixer_interface *mixer, ++ int ctl_index, int val) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ int index = line_out_remap(private, ctl_index); ++ int err; ++ ++ private->vol_sw_hw_switch[index] = val; ++ ++ /* Change access mode to RO (hardware controlled volume) ++ * or RW (software controlled volume) ++ */ ++ scarlett2_vol_ctl_set_writable(mixer, ctl_index, !val); ++ ++ /* Reset volume/mute to master volume/mute */ ++ private->vol[index] = private->master_vol; ++ private->mute_switch[index] = private->dim_mute[SCARLETT2_BUTTON_MUTE]; ++ ++ /* Set SW volume to current HW volume */ ++ err = scarlett2_usb_set_config( ++ mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME, ++ index, private->master_vol - SCARLETT2_VOLUME_BIAS); ++ if (err < 0) ++ return err; ++ ++ /* Set SW mute to current HW mute */ ++ err = scarlett2_usb_set_config( ++ mixer, SCARLETT2_CONFIG_MUTE_SWITCH, ++ index, private->dim_mute[SCARLETT2_BUTTON_MUTE]); ++ if (err < 0) ++ return err; ++ ++ /* Send SW/HW switch change to the device */ ++ return scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SW_HW_SWITCH, ++ index, val); ++} ++ ++static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int ctl_index = elem->control; ++ int index = line_out_remap(private, ctl_index); ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->vol_sw_hw_switch[index]; ++ val = !!ucontrol->value.enumerated.item[0]; ++ ++ if (oval == val) ++ goto unlock; ++ ++ err = scarlett2_sw_hw_change(mixer, ctl_index, val); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_sw_hw_enum_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = scarlett2_sw_hw_enum_ctl_info, ++ .get = scarlett2_sw_hw_enum_ctl_get, ++ .put = scarlett2_sw_hw_enum_ctl_put, ++}; ++ ++/*** Line Level/Instrument Level Switch Controls ***/ ++ ++static int scarlett2_update_input_other(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ ++ private->input_other_updated = 0; ++ ++ if (info->level_input_count) { ++ int err = scarlett2_usb_get_config( ++ mixer, SCARLETT2_CONFIG_LEVEL_SWITCH, ++ info->level_input_count + info->level_input_first, ++ private->level_switch); ++ if (err < 0) ++ return err; ++ } ++ ++ if (info->pad_input_count) { ++ int err = scarlett2_usb_get_config( ++ mixer, SCARLETT2_CONFIG_PAD_SWITCH, ++ info->pad_input_count, private->pad_switch); ++ if (err < 0) ++ return err; ++ } ++ ++ if (info->air_input_count) { ++ int err = scarlett2_usb_get_config( ++ mixer, SCARLETT2_CONFIG_AIR_SWITCH, ++ info->air_input_count, private->air_switch); ++ if (err < 0) ++ return err; ++ } ++ ++ if (info->phantom_count) { ++ int err = scarlett2_usb_get_config( ++ mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH, ++ info->phantom_count, private->phantom_switch); ++ if (err < 0) ++ return err; ++ ++ err = scarlett2_usb_get_config( ++ mixer, SCARLETT2_CONFIG_PHANTOM_PERSISTENCE, ++ 1, &private->phantom_persistence); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int scarlett2_level_enum_ctl_info(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ static const char *const values[2] = { ++ "Line", "Inst" ++ }; ++ ++ return snd_ctl_enum_info(uinfo, 1, 2, values); ++} ++ ++static int scarlett2_level_enum_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ ++ int index = elem->control + info->level_input_first; ++ int err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ if (private->input_other_updated) { ++ err = scarlett2_update_input_other(mixer); ++ if (err < 0) ++ goto unlock; ++ } ++ ucontrol->value.enumerated.item[0] = private->level_switch[index]; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ ++ int index = elem->control + info->level_input_first; ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->level_switch[index]; ++ val = !!ucontrol->value.enumerated.item[0]; ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->level_switch[index] = val; ++ ++ /* Send switch change to the device */ ++ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LEVEL_SWITCH, ++ index, val); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_level_enum_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = scarlett2_level_enum_ctl_info, ++ .get = scarlett2_level_enum_ctl_get, ++ .put = scarlett2_level_enum_ctl_put, ++}; ++ ++/*** Pad Switch Controls ***/ ++ ++static int scarlett2_pad_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ if (private->input_other_updated) { ++ err = scarlett2_update_input_other(mixer); ++ if (err < 0) ++ goto unlock; ++ } ++ ucontrol->value.integer.value[0] = ++ private->pad_switch[elem->control]; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static int scarlett2_pad_ctl_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ ++ int index = elem->control; ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->pad_switch[index]; ++ val = !!ucontrol->value.integer.value[0]; ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->pad_switch[index] = val; ++ ++ /* Send switch change to the device */ ++ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PAD_SWITCH, ++ index, val); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_pad_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = snd_ctl_boolean_mono_info, ++ .get = scarlett2_pad_ctl_get, ++ .put = scarlett2_pad_ctl_put, ++}; ++ ++/*** Air Switch Controls ***/ ++ ++static int scarlett2_air_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ if (private->input_other_updated) { ++ err = scarlett2_update_input_other(mixer); ++ if (err < 0) ++ goto unlock; ++ } ++ ucontrol->value.integer.value[0] = private->air_switch[elem->control]; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ ++ int index = elem->control; ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->air_switch[index]; ++ val = !!ucontrol->value.integer.value[0]; ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->air_switch[index] = val; ++ ++ /* Send switch change to the device */ ++ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_AIR_SWITCH, ++ index, val); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_air_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = snd_ctl_boolean_mono_info, ++ .get = scarlett2_air_ctl_get, ++ .put = scarlett2_air_ctl_put, ++}; ++ ++/*** Phantom Switch Controls ***/ ++ ++static int scarlett2_phantom_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ if (private->input_other_updated) { ++ err = scarlett2_update_input_other(mixer); ++ if (err < 0) ++ goto unlock; ++ } ++ ucontrol->value.integer.value[0] = ++ private->phantom_switch[elem->control]; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ ++ int index = elem->control; ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->phantom_switch[index]; ++ val = !!ucontrol->value.integer.value[0]; ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->phantom_switch[index] = val; ++ ++ /* Send switch change to the device */ ++ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH, ++ index, val); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_phantom_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = snd_ctl_boolean_mono_info, ++ .get = scarlett2_phantom_ctl_get, ++ .put = scarlett2_phantom_ctl_put, ++}; ++ ++/*** Phantom Persistence Control ***/ ++ ++static int scarlett2_phantom_persistence_ctl_get( ++ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct scarlett2_data *private = elem->head.mixer->private_data; ++ ++ ucontrol->value.integer.value[0] = private->phantom_persistence; ++ return 0; ++} ++ ++static int scarlett2_phantom_persistence_ctl_put( ++ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ ++ int index = elem->control; ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->phantom_persistence; ++ val = !!ucontrol->value.integer.value[0]; ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->phantom_persistence = val; ++ ++ /* Send switch change to the device */ ++ err = scarlett2_usb_set_config( ++ mixer, SCARLETT2_CONFIG_PHANTOM_PERSISTENCE, index, val); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_phantom_persistence_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = snd_ctl_boolean_mono_info, ++ .get = scarlett2_phantom_persistence_ctl_get, ++ .put = scarlett2_phantom_persistence_ctl_put, ++}; ++ ++/*** Direct Monitor Control ***/ ++ ++static int scarlett2_update_monitor_other(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ int err; ++ ++ /* monitor_other_enable[0] enables speaker switching ++ * monitor_other_enable[1] enables talkback ++ */ ++ u8 monitor_other_enable[2]; ++ ++ /* monitor_other_switch[0] activates the alternate speakers ++ * monitor_other_switch[1] activates talkback ++ */ ++ u8 monitor_other_switch[2]; ++ ++ private->monitor_other_updated = 0; ++ ++ if (info->direct_monitor) ++ return scarlett2_usb_get_config( ++ mixer, SCARLETT2_CONFIG_DIRECT_MONITOR, ++ 1, &private->direct_monitor_switch); ++ ++ /* if it doesn't do speaker switching then it also doesn't do ++ * talkback ++ */ ++ if (!info->has_speaker_switching) ++ return 0; ++ ++ err = scarlett2_usb_get_config( ++ mixer, SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE, ++ 2, monitor_other_enable); ++ if (err < 0) ++ return err; ++ ++ err = scarlett2_usb_get_config( ++ mixer, SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH, ++ 2, monitor_other_switch); ++ if (err < 0) ++ return err; ++ ++ if (!monitor_other_enable[0]) ++ private->speaker_switching_switch = 0; ++ else ++ private->speaker_switching_switch = monitor_other_switch[0] + 1; ++ ++ if (info->has_talkback) { ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = ++ info->port_count; ++ int num_mixes = ++ port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; ++ u16 bitmap; ++ int i; ++ ++ if (!monitor_other_enable[1]) ++ private->talkback_switch = 0; ++ else ++ private->talkback_switch = monitor_other_switch[1] + 1; ++ ++ err = scarlett2_usb_get_config(mixer, ++ SCARLETT2_CONFIG_TALKBACK_MAP, ++ 1, &bitmap); ++ if (err < 0) ++ return err; ++ for (i = 0; i < num_mixes; i++, bitmap >>= 1) ++ private->talkback_map[i] = bitmap & 1; ++ } ++ ++ return 0; ++} ++ ++static int scarlett2_direct_monitor_ctl_get( ++ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = elem->head.mixer->private_data; ++ int err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ if (private->monitor_other_updated) { ++ err = scarlett2_update_monitor_other(mixer); ++ if (err < 0) ++ goto unlock; ++ } ++ ucontrol->value.enumerated.item[0] = private->direct_monitor_switch; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static int scarlett2_direct_monitor_ctl_put( ++ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ ++ int index = elem->control; ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->direct_monitor_switch; ++ val = min(ucontrol->value.enumerated.item[0], 2U); ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->direct_monitor_switch = val; ++ ++ /* Send switch change to the device */ ++ err = scarlett2_usb_set_config( ++ mixer, SCARLETT2_CONFIG_DIRECT_MONITOR, index, val); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static int scarlett2_direct_monitor_stereo_enum_ctl_info( ++ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) ++{ ++ static const char *const values[3] = { ++ "Off", "Mono", "Stereo" ++ }; ++ ++ return snd_ctl_enum_info(uinfo, 1, 3, values); ++} ++ ++/* Direct Monitor for Solo is mono-only and only needs a boolean control ++ * Direct Monitor for 2i2 is selectable between Off/Mono/Stereo ++ */ ++static const struct snd_kcontrol_new scarlett2_direct_monitor_ctl[2] = { ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = snd_ctl_boolean_mono_info, ++ .get = scarlett2_direct_monitor_ctl_get, ++ .put = scarlett2_direct_monitor_ctl_put, ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = scarlett2_direct_monitor_stereo_enum_ctl_info, ++ .get = scarlett2_direct_monitor_ctl_get, ++ .put = scarlett2_direct_monitor_ctl_put, ++ } ++}; ++ ++static int scarlett2_add_direct_monitor_ctl(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const char *s; ++ ++ if (!info->direct_monitor) ++ return 0; ++ ++ s = info->direct_monitor == 1 ++ ? "Direct Monitor Playback Switch" ++ : "Direct Monitor Playback Enum"; ++ ++ return scarlett2_add_new_ctl( ++ mixer, &scarlett2_direct_monitor_ctl[info->direct_monitor - 1], ++ 0, 1, s, &private->direct_monitor_ctl); ++} ++ ++/*** Speaker Switching Control ***/ ++ ++static int scarlett2_speaker_switch_enum_ctl_info( ++ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) ++{ ++ static const char *const values[3] = { ++ "Off", "Main", "Alt" ++ }; ++ ++ return snd_ctl_enum_info(uinfo, 1, 3, values); ++} ++ ++static int scarlett2_speaker_switch_enum_ctl_get( ++ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ if (private->monitor_other_updated) { ++ err = scarlett2_update_monitor_other(mixer); ++ if (err < 0) ++ goto unlock; ++ } ++ ucontrol->value.enumerated.item[0] = private->speaker_switching_switch; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++/* when speaker switching gets enabled, switch the main/alt speakers ++ * to HW volume and disable those controls ++ */ ++static int scarlett2_speaker_switch_enable(struct usb_mixer_interface *mixer) ++{ ++ struct snd_card *card = mixer->chip->card; ++ struct scarlett2_data *private = mixer->private_data; ++ int i, err; ++ ++ for (i = 0; i < 4; i++) { ++ int index = line_out_remap(private, i); ++ ++ /* switch the main/alt speakers to HW volume */ ++ if (!private->vol_sw_hw_switch[index]) { ++ err = scarlett2_sw_hw_change(private->mixer, i, 1); ++ if (err < 0) ++ return err; ++ } ++ ++ /* disable the line out SW/HW switch */ ++ scarlett2_sw_hw_ctl_ro(private, i); ++ snd_ctl_notify(card, ++ SNDRV_CTL_EVENT_MASK_VALUE | ++ SNDRV_CTL_EVENT_MASK_INFO, ++ &private->sw_hw_ctls[i]->id); ++ } ++ ++ /* when the next monitor-other notify comes in, update the mux ++ * configuration ++ */ ++ private->speaker_switching_switched = 1; ++ ++ return 0; ++} ++ ++/* when speaker switching gets disabled, reenable the hw/sw controls ++ * and invalidate the routing ++ */ ++static void scarlett2_speaker_switch_disable(struct usb_mixer_interface *mixer) ++{ ++ struct snd_card *card = mixer->chip->card; ++ struct scarlett2_data *private = mixer->private_data; ++ int i; ++ ++ /* enable the line out SW/HW switch */ ++ for (i = 0; i < 4; i++) { ++ scarlett2_sw_hw_ctl_rw(private, i); ++ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, ++ &private->sw_hw_ctls[i]->id); ++ } ++ ++ /* when the next monitor-other notify comes in, update the mux ++ * configuration ++ */ ++ private->speaker_switching_switched = 1; ++} ++ ++static int scarlett2_speaker_switch_enum_ctl_put( ++ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->speaker_switching_switch; ++ val = min(ucontrol->value.enumerated.item[0], 2U); ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->speaker_switching_switch = val; ++ ++ /* enable/disable speaker switching */ ++ err = scarlett2_usb_set_config( ++ mixer, SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE, ++ 0, !!val); ++ if (err < 0) ++ goto unlock; ++ ++ /* if speaker switching is enabled, select main or alt */ ++ err = scarlett2_usb_set_config( ++ mixer, SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH, ++ 0, val == 2); ++ if (err < 0) ++ goto unlock; ++ ++ /* update controls if speaker switching gets enabled or disabled */ ++ if (!oval && val) ++ err = scarlett2_speaker_switch_enable(mixer); ++ else if (oval && !val) ++ scarlett2_speaker_switch_disable(mixer); ++ ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_speaker_switch_enum_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = scarlett2_speaker_switch_enum_ctl_info, ++ .get = scarlett2_speaker_switch_enum_ctl_get, ++ .put = scarlett2_speaker_switch_enum_ctl_put, ++}; ++ ++static int scarlett2_add_speaker_switch_ctl( ++ struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ ++ if (!info->has_speaker_switching) ++ return 0; ++ ++ return scarlett2_add_new_ctl( ++ mixer, &scarlett2_speaker_switch_enum_ctl, ++ 0, 1, "Speaker Switching Playback Enum", ++ &private->speaker_switching_ctl); ++} ++ ++/*** Talkback and Talkback Map Controls ***/ ++ ++static int scarlett2_talkback_enum_ctl_info( ++ struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) ++{ ++ static const char *const values[3] = { ++ "Disabled", "Off", "On" ++ }; ++ ++ return snd_ctl_enum_info(uinfo, 1, 3, values); ++} ++ ++static int scarlett2_talkback_enum_ctl_get( ++ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ if (private->monitor_other_updated) { ++ err = scarlett2_update_monitor_other(mixer); ++ if (err < 0) ++ goto unlock; ++ } ++ ucontrol->value.enumerated.item[0] = private->talkback_switch; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static int scarlett2_talkback_enum_ctl_put( ++ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->talkback_switch; ++ val = min(ucontrol->value.enumerated.item[0], 2U); ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->talkback_switch = val; ++ ++ /* enable/disable talkback */ ++ err = scarlett2_usb_set_config( ++ mixer, SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE, ++ 1, !!val); ++ if (err < 0) ++ goto unlock; ++ ++ /* if talkback is enabled, select main or alt */ ++ err = scarlett2_usb_set_config( ++ mixer, SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH, ++ 1, val == 2); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_talkback_enum_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = scarlett2_talkback_enum_ctl_info, ++ .get = scarlett2_talkback_enum_ctl_get, ++ .put = scarlett2_talkback_enum_ctl_put, ++}; ++ ++static int scarlett2_talkback_map_ctl_get( ++ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int index = elem->control; ++ ++ ucontrol->value.integer.value[0] = private->talkback_map[index]; ++ ++ return 0; ++} ++ ++static int scarlett2_talkback_map_ctl_put( ++ struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = ++ private->info->port_count; ++ int num_mixes = port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; ++ ++ int index = elem->control; ++ int oval, val, err = 0, i; ++ u16 bitmap = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->talkback_map[index]; ++ val = !!ucontrol->value.integer.value[0]; ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->talkback_map[index] = val; ++ ++ for (i = 0; i < num_mixes; i++) ++ bitmap |= private->talkback_map[i] << i; ++ ++ /* Send updated bitmap to the device */ ++ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_TALKBACK_MAP, ++ 0, bitmap); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_talkback_map_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = snd_ctl_boolean_mono_info, ++ .get = scarlett2_talkback_map_ctl_get, ++ .put = scarlett2_talkback_map_ctl_put, ++}; ++ ++static int scarlett2_add_talkback_ctls( ++ struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ int num_mixes = port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; ++ int err, i; ++ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; ++ ++ if (!info->has_talkback) ++ return 0; ++ ++ err = scarlett2_add_new_ctl( ++ mixer, &scarlett2_talkback_enum_ctl, ++ 0, 1, "Talkback Playback Enum", ++ &private->talkback_ctl); ++ if (err < 0) ++ return err; ++ ++ for (i = 0; i < num_mixes; i++) { ++ snprintf(s, sizeof(s), ++ "Talkback Mix %c Playback Switch", i + 'A'); ++ err = scarlett2_add_new_ctl(mixer, &scarlett2_talkback_map_ctl, ++ i, 1, s, NULL); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/*** Dim/Mute Controls ***/ ++ ++static int scarlett2_dim_mute_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ if (private->vol_updated) { ++ err = scarlett2_update_volumes(mixer); ++ if (err < 0) ++ goto unlock; ++ } ++ ucontrol->value.integer.value[0] = private->dim_mute[elem->control]; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static int scarlett2_dim_mute_ctl_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ int num_line_out = ++ port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; ++ ++ int index = elem->control; ++ int oval, val, err = 0, i; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->dim_mute[index]; ++ val = !!ucontrol->value.integer.value[0]; ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->dim_mute[index] = val; ++ ++ /* Send switch change to the device */ ++ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_DIM_MUTE, ++ index, val); ++ if (err == 0) ++ err = 1; ++ ++ if (index == SCARLETT2_BUTTON_MUTE) ++ for (i = 0; i < num_line_out; i++) { ++ int line_index = line_out_remap(private, i); ++ ++ if (private->vol_sw_hw_switch[line_index]) { ++ private->mute_switch[line_index] = val; ++ snd_ctl_notify(mixer->chip->card, ++ SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->mute_ctls[i]->id); ++ } ++ } ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_dim_mute_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = snd_ctl_boolean_mono_info, ++ .get = scarlett2_dim_mute_ctl_get, ++ .put = scarlett2_dim_mute_ctl_put ++}; ++ ++/*** Create the analogue output controls ***/ ++ ++static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ int num_line_out = ++ port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; ++ int err, i; ++ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; ++ ++ /* Add R/O HW volume control */ ++ if (info->line_out_hw_vol) { ++ snprintf(s, sizeof(s), "Master HW Playback Volume"); ++ err = scarlett2_add_new_ctl(mixer, ++ &scarlett2_master_volume_ctl, ++ 0, 1, s, &private->master_vol_ctl); ++ if (err < 0) ++ return err; ++ } ++ ++ /* Add volume controls */ ++ for (i = 0; i < num_line_out; i++) { ++ int index = line_out_remap(private, i); ++ ++ /* Fader */ ++ if (info->line_out_descrs[i]) ++ snprintf(s, sizeof(s), ++ "Line %02d (%s) Playback Volume", ++ i + 1, info->line_out_descrs[i]); ++ else ++ snprintf(s, sizeof(s), ++ "Line %02d Playback Volume", ++ i + 1); ++ err = scarlett2_add_new_ctl(mixer, ++ &scarlett2_line_out_volume_ctl, ++ i, 1, s, &private->vol_ctls[i]); ++ if (err < 0) ++ return err; ++ ++ /* Mute Switch */ ++ snprintf(s, sizeof(s), ++ "Line %02d Mute Playback Switch", ++ i + 1); ++ err = scarlett2_add_new_ctl(mixer, ++ &scarlett2_mute_ctl, ++ i, 1, s, ++ &private->mute_ctls[i]); ++ if (err < 0) ++ return err; ++ ++ /* Make the fader and mute controls read-only if the ++ * SW/HW switch is set to HW ++ */ ++ if (private->vol_sw_hw_switch[index]) ++ scarlett2_vol_ctl_set_writable(mixer, i, 0); ++ ++ /* SW/HW Switch */ ++ if (info->line_out_hw_vol) { ++ snprintf(s, sizeof(s), ++ "Line Out %02d Volume Control Playback Enum", ++ i + 1); ++ err = scarlett2_add_new_ctl(mixer, ++ &scarlett2_sw_hw_enum_ctl, ++ i, 1, s, ++ &private->sw_hw_ctls[i]); ++ if (err < 0) ++ return err; ++ ++ /* Make the switch read-only if the line is ++ * involved in speaker switching ++ */ ++ if (private->speaker_switching_switch && i < 4) ++ scarlett2_sw_hw_ctl_ro(private, i); ++ } ++ } ++ ++ /* Add dim/mute controls */ ++ if (info->line_out_hw_vol) ++ for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) { ++ err = scarlett2_add_new_ctl( ++ mixer, &scarlett2_dim_mute_ctl, ++ i, 1, scarlett2_dim_mute_names[i], ++ &private->dim_mute_ctls[i]); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/*** Create the analogue input controls ***/ ++ ++static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ int err, i; ++ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; ++ const char *fmt = "Line In %d %s Capture %s"; ++ const char *fmt2 = "Line In %d-%d %s Capture %s"; ++ ++ /* Add input level (line/inst) controls */ ++ for (i = 0; i < info->level_input_count; i++) { ++ snprintf(s, sizeof(s), fmt, i + 1 + info->level_input_first, ++ "Level", "Enum"); ++ err = scarlett2_add_new_ctl(mixer, &scarlett2_level_enum_ctl, ++ i, 1, s, &private->level_ctls[i]); ++ if (err < 0) ++ return err; ++ } ++ ++ /* Add input pad controls */ ++ for (i = 0; i < info->pad_input_count; i++) { ++ snprintf(s, sizeof(s), fmt, i + 1, "Pad", "Switch"); ++ err = scarlett2_add_new_ctl(mixer, &scarlett2_pad_ctl, ++ i, 1, s, &private->pad_ctls[i]); ++ if (err < 0) ++ return err; ++ } ++ ++ /* Add input air controls */ ++ for (i = 0; i < info->air_input_count; i++) { ++ snprintf(s, sizeof(s), fmt, i + 1, "Air", "Switch"); ++ err = scarlett2_add_new_ctl(mixer, &scarlett2_air_ctl, ++ i, 1, s, &private->air_ctls[i]); ++ if (err < 0) ++ return err; ++ } ++ ++ /* Add input phantom controls */ ++ if (info->inputs_per_phantom == 1) { ++ for (i = 0; i < info->phantom_count; i++) { ++ scnprintf(s, sizeof(s), fmt, i + 1, ++ "Phantom Power", "Switch"); ++ err = scarlett2_add_new_ctl( ++ mixer, &scarlett2_phantom_ctl, ++ i, 1, s, &private->phantom_ctls[i]); ++ if (err < 0) ++ return err; ++ } ++ } else if (info->inputs_per_phantom > 1) { ++ for (i = 0; i < info->phantom_count; i++) { ++ int from = i * info->inputs_per_phantom + 1; ++ int to = (i + 1) * info->inputs_per_phantom; ++ ++ scnprintf(s, sizeof(s), fmt2, from, to, ++ "Phantom Power", "Switch"); ++ err = scarlett2_add_new_ctl( ++ mixer, &scarlett2_phantom_ctl, ++ i, 1, s, &private->phantom_ctls[i]); ++ if (err < 0) ++ return err; ++ } ++ } ++ if (info->phantom_count) { ++ err = scarlett2_add_new_ctl( ++ mixer, &scarlett2_phantom_persistence_ctl, 0, 1, ++ "Phantom Power Persistence Capture Switch", NULL); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/*** Mixer Volume Controls ***/ ++ ++static int scarlett2_mixer_ctl_info(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = elem->channels; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = SCARLETT2_MIXER_MAX_VALUE; ++ uinfo->value.integer.step = 1; ++ return 0; ++} ++ ++static int scarlett2_mixer_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct scarlett2_data *private = elem->head.mixer->private_data; ++ ++ ucontrol->value.integer.value[0] = private->mix[elem->control]; ++ return 0; ++} ++ ++static int scarlett2_mixer_ctl_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ int oval, val, num_mixer_in, mix_num, err = 0; ++ int index = elem->control; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->mix[index]; ++ val = clamp(ucontrol->value.integer.value[0], ++ 0L, (long)SCARLETT2_MIXER_MAX_VALUE); ++ num_mixer_in = port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT]; ++ mix_num = index / num_mixer_in; ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->mix[index] = val; ++ err = scarlett2_usb_set_mix(mixer, mix_num); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const DECLARE_TLV_DB_MINMAX( ++ db_scale_scarlett2_mixer, ++ SCARLETT2_MIXER_MIN_DB * 100, ++ SCARLETT2_MIXER_MAX_DB * 100 ++); ++ ++static const struct snd_kcontrol_new scarlett2_mixer_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "", ++ .info = scarlett2_mixer_ctl_info, ++ .get = scarlett2_mixer_ctl_get, ++ .put = scarlett2_mixer_ctl_put, ++ .private_value = SCARLETT2_MIXER_MAX_DB, /* max value */ ++ .tlv = { .p = db_scale_scarlett2_mixer } ++}; ++ ++static int scarlett2_add_mixer_ctls(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ int err, i, j; ++ int index; ++ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; ++ ++ int num_inputs = ++ port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT]; ++ int num_outputs = ++ port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; ++ ++ for (i = 0, index = 0; i < num_outputs; i++) ++ for (j = 0; j < num_inputs; j++, index++) { ++ snprintf(s, sizeof(s), ++ "Mix %c Input %02d Playback Volume", ++ 'A' + i, j + 1); ++ err = scarlett2_add_new_ctl(mixer, &scarlett2_mixer_ctl, ++ index, 1, s, NULL); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/*** Mux Source Selection Controls ***/ ++ ++static int scarlett2_mux_src_enum_ctl_info(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct scarlett2_data *private = elem->head.mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ unsigned int item = uinfo->value.enumerated.item; ++ int items = private->num_mux_srcs; ++ int port_type; ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; ++ uinfo->count = elem->channels; ++ uinfo->value.enumerated.items = items; ++ ++ if (item >= items) ++ item = uinfo->value.enumerated.item = items - 1; ++ ++ for (port_type = 0; ++ port_type < SCARLETT2_PORT_TYPE_COUNT; ++ port_type++) { ++ if (item < port_count[port_type][SCARLETT2_PORT_IN]) { ++ const struct scarlett2_port *port = ++ &scarlett2_ports[port_type]; ++ ++ sprintf(uinfo->value.enumerated.name, ++ port->src_descr, item + port->src_num_offset); ++ return 0; ++ } ++ item -= port_count[port_type][SCARLETT2_PORT_IN]; ++ } ++ ++ return -EINVAL; ++} ++ ++static int scarlett2_mux_src_enum_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int index = line_out_remap(private, elem->control); ++ int err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ if (private->mux_updated) { ++ err = scarlett2_usb_get_mux(mixer); ++ if (err < 0) ++ goto unlock; ++ } ++ ucontrol->value.enumerated.item[0] = private->mux[index]; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static int scarlett2_mux_src_enum_ctl_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ int index = line_out_remap(private, elem->control); ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->mux[index]; ++ val = min(ucontrol->value.enumerated.item[0], ++ private->num_mux_srcs - 1U); ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->mux[index] = val; ++ err = scarlett2_usb_set_mux(mixer); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_mux_src_enum_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = scarlett2_mux_src_enum_ctl_info, ++ .get = scarlett2_mux_src_enum_ctl_get, ++ .put = scarlett2_mux_src_enum_ctl_put, ++}; ++ ++static int scarlett2_add_mux_enums(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ int port_type, channel, i; ++ ++ for (i = 0, port_type = 0; ++ port_type < SCARLETT2_PORT_TYPE_COUNT; ++ port_type++) { ++ for (channel = 0; ++ channel < port_count[port_type][SCARLETT2_PORT_OUT]; ++ channel++, i++) { ++ int err; ++ char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; ++ const char *const descr = ++ scarlett2_ports[port_type].dst_descr; ++ ++ snprintf(s, sizeof(s) - 5, descr, channel + 1); ++ strcat(s, " Enum"); ++ ++ err = scarlett2_add_new_ctl(mixer, ++ &scarlett2_mux_src_enum_ctl, ++ i, 1, s, ++ &private->mux_ctls[i]); ++ if (err < 0) ++ return err; ++ } ++ } ++ ++ return 0; ++} ++ ++/*** Meter Controls ***/ ++ ++static int scarlett2_meter_ctl_info(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = elem->channels; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = 4095; ++ uinfo->value.integer.step = 1; ++ return 0; ++} ++ ++static int scarlett2_meter_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ u16 meter_levels[SCARLETT2_MAX_METERS]; ++ int i, err; ++ ++ err = scarlett2_usb_get_meter_levels(elem->head.mixer, elem->channels, ++ meter_levels); ++ if (err < 0) ++ return err; ++ ++ for (i = 0; i < elem->channels; i++) ++ ucontrol->value.integer.value[i] = meter_levels[i]; ++ ++ return 0; ++} ++ ++static const struct snd_kcontrol_new scarlett2_meter_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_PCM, ++ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, ++ .name = "", ++ .info = scarlett2_meter_ctl_info, ++ .get = scarlett2_meter_ctl_get ++}; ++ ++static int scarlett2_add_meter_ctl(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ ++ /* devices without a mixer also don't support reporting levels */ ++ if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) ++ return 0; ++ ++ return scarlett2_add_new_ctl(mixer, &scarlett2_meter_ctl, ++ 0, private->num_mux_dsts, ++ "Level Meter", NULL); ++} ++ ++/*** MSD Controls ***/ ++ ++static int scarlett2_msd_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct scarlett2_data *private = elem->head.mixer->private_data; ++ ++ ucontrol->value.integer.value[0] = private->msd_switch; ++ return 0; ++} ++ ++static int scarlett2_msd_ctl_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->msd_switch; ++ val = !!ucontrol->value.integer.value[0]; ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->msd_switch = val; ++ ++ /* Send switch change to the device */ ++ err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_MSD_SWITCH, ++ 0, val); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_msd_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = snd_ctl_boolean_mono_info, ++ .get = scarlett2_msd_ctl_get, ++ .put = scarlett2_msd_ctl_put, ++}; ++ ++static int scarlett2_add_msd_ctl(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ ++ if (!info->has_msd_mode) ++ return 0; ++ ++ /* If MSD mode is off, hide the switch by default */ ++ if (!private->msd_switch && !(mixer->chip->setup & SCARLETT2_MSD_ENABLE)) ++ return 0; ++ ++ /* Add MSD control */ ++ return scarlett2_add_new_ctl(mixer, &scarlett2_msd_ctl, ++ 0, 1, "MSD Mode Switch", NULL); ++} ++ ++/*** Standalone Control ***/ ++ ++static int scarlett2_standalone_ctl_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct scarlett2_data *private = elem->head.mixer->private_data; ++ ++ ucontrol->value.integer.value[0] = private->standalone_switch; ++ return 0; ++} ++ ++static int scarlett2_standalone_ctl_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct usb_mixer_elem_info *elem = kctl->private_data; ++ struct usb_mixer_interface *mixer = elem->head.mixer; ++ struct scarlett2_data *private = mixer->private_data; ++ ++ int oval, val, err = 0; ++ ++ mutex_lock(&private->data_mutex); ++ ++ oval = private->standalone_switch; ++ val = !!ucontrol->value.integer.value[0]; ++ ++ if (oval == val) ++ goto unlock; ++ ++ private->standalone_switch = val; ++ ++ /* Send switch change to the device */ ++ err = scarlett2_usb_set_config(mixer, ++ SCARLETT2_CONFIG_STANDALONE_SWITCH, ++ 0, val); ++ if (err == 0) ++ err = 1; ++ ++unlock: ++ mutex_unlock(&private->data_mutex); ++ return err; ++} ++ ++static const struct snd_kcontrol_new scarlett2_standalone_ctl = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "", ++ .info = snd_ctl_boolean_mono_info, ++ .get = scarlett2_standalone_ctl_get, ++ .put = scarlett2_standalone_ctl_put, ++}; ++ ++static int scarlett2_add_standalone_ctl(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ ++ if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) ++ return 0; ++ ++ /* Add standalone control */ ++ return scarlett2_add_new_ctl(mixer, &scarlett2_standalone_ctl, ++ 0, 1, "Standalone Switch", NULL); ++} ++ ++/*** Cleanup/Suspend Callbacks ***/ ++ ++static void scarlett2_private_free(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ ++ cancel_delayed_work_sync(&private->work); ++ kfree(private); ++ mixer->private_data = NULL; ++} ++ ++static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ ++ if (cancel_delayed_work_sync(&private->work)) ++ scarlett2_config_save(private->mixer); ++} ++ ++/*** Initialisation ***/ ++ ++static void scarlett2_count_mux_io(struct scarlett2_data *private) ++{ ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ int port_type, srcs = 0, dsts = 0; ++ ++ for (port_type = 0; ++ port_type < SCARLETT2_PORT_TYPE_COUNT; ++ port_type++) { ++ srcs += port_count[port_type][SCARLETT2_PORT_IN]; ++ dsts += port_count[port_type][SCARLETT2_PORT_OUT]; ++ } ++ ++ private->num_mux_srcs = srcs; ++ private->num_mux_dsts = dsts; ++} ++ ++/* Look through the interface descriptors for the Focusrite Control ++ * interface (bInterfaceClass = 255 Vendor Specific Class) and set ++ * bInterfaceNumber, bEndpointAddress, wMaxPacketSize, and bInterval ++ * in private ++ */ ++static int scarlett2_find_fc_interface(struct usb_device *dev, ++ struct scarlett2_data *private) ++{ ++ struct usb_host_config *config = dev->actconfig; ++ int i; ++ ++ for (i = 0; i < config->desc.bNumInterfaces; i++) { ++ struct usb_interface *intf = config->interface[i]; ++ struct usb_interface_descriptor *desc = ++ &intf->altsetting[0].desc; ++ struct usb_endpoint_descriptor *epd; ++ ++ if (desc->bInterfaceClass != 255) ++ continue; ++ ++ epd = get_endpoint(intf->altsetting, 0); ++ private->bInterfaceNumber = desc->bInterfaceNumber; ++ private->bEndpointAddress = epd->bEndpointAddress & ++ USB_ENDPOINT_NUMBER_MASK; ++ private->wMaxPacketSize = le16_to_cpu(epd->wMaxPacketSize); ++ private->bInterval = epd->bInterval; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++/* Initialise private data */ ++static int scarlett2_init_private(struct usb_mixer_interface *mixer, ++ const struct scarlett2_device_entry *entry) ++{ ++ struct scarlett2_data *private = ++ kzalloc(sizeof(struct scarlett2_data), GFP_KERNEL); ++ ++ if (!private) ++ return -ENOMEM; ++ ++ mutex_init(&private->usb_mutex); ++ mutex_init(&private->data_mutex); ++ INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work); ++ ++ mixer->private_data = private; ++ mixer->private_free = scarlett2_private_free; ++ mixer->private_suspend = scarlett2_private_suspend; ++ ++ private->info = entry->info; ++ private->series_name = entry->series_name; ++ scarlett2_count_mux_io(private); ++ private->scarlett2_seq = 0; ++ private->mixer = mixer; ++ ++ return scarlett2_find_fc_interface(mixer->chip->dev, private); ++} ++ ++/* Cargo cult proprietary initialisation sequence */ ++static int scarlett2_usb_init(struct usb_mixer_interface *mixer) ++{ ++ struct usb_device *dev = mixer->chip->dev; ++ struct scarlett2_data *private = mixer->private_data; ++ u8 buf[24]; ++ int err; ++ ++ if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0))) ++ return -EINVAL; ++ ++ /* step 0 */ ++ err = scarlett2_usb_rx(dev, private->bInterfaceNumber, ++ SCARLETT2_USB_CMD_INIT, buf, sizeof(buf)); ++ if (err < 0) ++ return err; ++ ++ /* step 1 */ ++ private->scarlett2_seq = 1; ++ err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_1, NULL, 0, NULL, 0); ++ if (err < 0) ++ return err; ++ ++ /* step 2 */ ++ private->scarlett2_seq = 1; ++ return scarlett2_usb(mixer, SCARLETT2_USB_INIT_2, NULL, 0, NULL, 84); ++} ++ ++/* Read configuration from the interface on start */ ++static int scarlett2_read_configs(struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ int num_line_out = ++ port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; ++ int num_mixer_out = ++ port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; ++ struct scarlett2_usb_volume_status volume_status; ++ int err, i; ++ ++ if (info->has_msd_mode) { ++ err = scarlett2_usb_get_config( ++ mixer, SCARLETT2_CONFIG_MSD_SWITCH, ++ 1, &private->msd_switch); ++ if (err < 0) ++ return err; ++ ++ /* no other controls are created if MSD mode is on */ ++ if (private->msd_switch) ++ return 0; ++ } ++ ++ err = scarlett2_update_input_other(mixer); ++ if (err < 0) ++ return err; ++ ++ err = scarlett2_update_monitor_other(mixer); ++ if (err < 0) ++ return err; ++ ++ /* the rest of the configuration is for devices with a mixer */ ++ if (info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) ++ return 0; ++ ++ err = scarlett2_usb_get_config( ++ mixer, SCARLETT2_CONFIG_STANDALONE_SWITCH, ++ 1, &private->standalone_switch); ++ if (err < 0) ++ return err; ++ ++ err = scarlett2_update_sync(mixer); ++ if (err < 0) ++ return err; ++ ++ err = scarlett2_usb_get_volume_status(mixer, &volume_status); ++ if (err < 0) ++ return err; ++ ++ if (info->line_out_hw_vol) ++ for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) ++ private->dim_mute[i] = !!volume_status.dim_mute[i]; ++ ++ private->master_vol = clamp( ++ volume_status.master_vol + SCARLETT2_VOLUME_BIAS, ++ 0, SCARLETT2_VOLUME_BIAS); ++ ++ for (i = 0; i < num_line_out; i++) { ++ int volume, mute; ++ ++ private->vol_sw_hw_switch[i] = ++ info->line_out_hw_vol ++ && volume_status.sw_hw_switch[i]; ++ ++ volume = private->vol_sw_hw_switch[i] ++ ? volume_status.master_vol ++ : volume_status.sw_vol[i]; ++ volume = clamp(volume + SCARLETT2_VOLUME_BIAS, ++ 0, SCARLETT2_VOLUME_BIAS); ++ private->vol[i] = volume; ++ ++ mute = private->vol_sw_hw_switch[i] ++ ? private->dim_mute[SCARLETT2_BUTTON_MUTE] ++ : volume_status.mute_switch[i]; ++ private->mute_switch[i] = mute; ++ } ++ ++ for (i = 0; i < num_mixer_out; i++) { ++ err = scarlett2_usb_get_mix(mixer, i); ++ if (err < 0) ++ return err; ++ } ++ ++ return scarlett2_usb_get_mux(mixer); ++} ++ ++/* Notify on sync change */ ++static void scarlett2_notify_sync( ++ struct usb_mixer_interface *mixer) ++{ ++ struct scarlett2_data *private = mixer->private_data; ++ ++ private->sync_updated = 1; ++ ++ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->sync_ctl->id); ++} ++ ++/* Notify on monitor change */ ++static void scarlett2_notify_monitor( ++ struct usb_mixer_interface *mixer) ++{ ++ struct snd_card *card = mixer->chip->card; ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ int num_line_out = ++ port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; ++ int i; ++ ++ /* if line_out_hw_vol is 0, there are no controls to update */ ++ if (!info->line_out_hw_vol) ++ return; ++ ++ private->vol_updated = 1; ++ ++ snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->master_vol_ctl->id); ++ ++ for (i = 0; i < num_line_out; i++) ++ if (private->vol_sw_hw_switch[line_out_remap(private, i)]) ++ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->vol_ctls[i]->id); ++} ++ ++/* Notify on dim/mute change */ ++static void scarlett2_notify_dim_mute( ++ struct usb_mixer_interface *mixer) ++{ ++ struct snd_card *card = mixer->chip->card; ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; ++ int num_line_out = ++ port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; ++ int i; ++ ++ private->vol_updated = 1; ++ ++ if (!info->line_out_hw_vol) ++ return; ++ ++ for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) ++ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->dim_mute_ctls[i]->id); ++ ++ for (i = 0; i < num_line_out; i++) ++ if (private->vol_sw_hw_switch[line_out_remap(private, i)]) ++ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->mute_ctls[i]->id); ++} ++ ++/* Notify on "input other" change (level/pad/air) */ ++static void scarlett2_notify_input_other( ++ struct usb_mixer_interface *mixer) ++{ ++ struct snd_card *card = mixer->chip->card; ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ int i; ++ ++ private->input_other_updated = 1; ++ ++ for (i = 0; i < info->level_input_count; i++) ++ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->level_ctls[i]->id); ++ for (i = 0; i < info->pad_input_count; i++) ++ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->pad_ctls[i]->id); ++ for (i = 0; i < info->air_input_count; i++) ++ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->air_ctls[i]->id); ++ for (i = 0; i < info->phantom_count; i++) ++ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->phantom_ctls[i]->id); ++} ++ ++/* Notify on "monitor other" change (direct monitor, speaker ++ * switching, talkback) ++ */ ++static void scarlett2_notify_monitor_other( ++ struct usb_mixer_interface *mixer) ++{ ++ struct snd_card *card = mixer->chip->card; ++ struct scarlett2_data *private = mixer->private_data; ++ const struct scarlett2_device_info *info = private->info; ++ ++ private->monitor_other_updated = 1; ++ ++ if (info->direct_monitor) { ++ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->direct_monitor_ctl->id); ++ return; ++ } ++ ++ if (info->has_speaker_switching) ++ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->speaker_switching_ctl->id); ++ ++ if (info->has_talkback) ++ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->talkback_ctl->id); ++ ++ /* if speaker switching was recently enabled or disabled, ++ * invalidate the dim/mute and mux enum controls ++ */ ++ if (private->speaker_switching_switched) { ++ int i; ++ ++ scarlett2_notify_dim_mute(mixer); ++ ++ private->speaker_switching_switched = 0; ++ private->mux_updated = 1; ++ ++ for (i = 0; i < private->num_mux_dsts; i++) ++ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, ++ &private->mux_ctls[i]->id); ++ } ++} ++ ++/* Interrupt callback */ ++static void scarlett2_notify(struct urb *urb) ++{ ++ struct usb_mixer_interface *mixer = urb->context; ++ int len = urb->actual_length; ++ int ustatus = urb->status; ++ u32 data; ++ ++ if (ustatus != 0 || len != 8) ++ goto requeue; ++ ++ data = le32_to_cpu(*(__le32 *)urb->transfer_buffer); ++ if (data & SCARLETT2_USB_NOTIFY_SYNC) ++ scarlett2_notify_sync(mixer); ++ if (data & SCARLETT2_USB_NOTIFY_MONITOR) ++ scarlett2_notify_monitor(mixer); ++ if (data & SCARLETT2_USB_NOTIFY_DIM_MUTE) ++ scarlett2_notify_dim_mute(mixer); ++ if (data & SCARLETT2_USB_NOTIFY_INPUT_OTHER) ++ scarlett2_notify_input_other(mixer); ++ if (data & SCARLETT2_USB_NOTIFY_MONITOR_OTHER) ++ scarlett2_notify_monitor_other(mixer); ++ ++requeue: ++ if (ustatus != -ENOENT && ++ ustatus != -ECONNRESET && ++ ustatus != -ESHUTDOWN) { ++ urb->dev = mixer->chip->dev; ++ usb_submit_urb(urb, GFP_ATOMIC); ++ } ++} ++ ++static int scarlett2_init_notify(struct usb_mixer_interface *mixer) ++{ ++ struct usb_device *dev = mixer->chip->dev; ++ struct scarlett2_data *private = mixer->private_data; ++ unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress); ++ void *transfer_buffer; ++ ++ if (mixer->urb) { ++ usb_audio_err(mixer->chip, ++ "%s: mixer urb already in use!\n", __func__); ++ return 0; ++ } ++ ++ if (usb_pipe_type_check(dev, pipe)) ++ return -EINVAL; ++ ++ mixer->urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!mixer->urb) ++ return -ENOMEM; ++ ++ transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL); ++ if (!transfer_buffer) ++ return -ENOMEM; ++ ++ usb_fill_int_urb(mixer->urb, dev, pipe, ++ transfer_buffer, private->wMaxPacketSize, ++ scarlett2_notify, mixer, private->bInterval); ++ ++ return usb_submit_urb(mixer->urb, GFP_KERNEL); ++} ++ ++static const struct scarlett2_device_entry *get_scarlett2_device_entry( ++ struct usb_mixer_interface *mixer) ++{ ++ const struct scarlett2_device_entry *entry = scarlett2_devices; ++ ++ /* Find entry in scarlett2_devices */ ++ while (entry->usb_id && entry->usb_id != mixer->chip->usb_id) ++ entry++; ++ if (!entry->usb_id) ++ return NULL; ++ ++ return entry; ++} ++ ++static int snd_scarlett2_controls_create( ++ struct usb_mixer_interface *mixer, ++ const struct scarlett2_device_entry *entry) ++{ ++ int err; ++ ++ /* Initialise private data */ ++ err = scarlett2_init_private(mixer, entry); ++ if (err < 0) ++ return err; ++ ++ /* Send proprietary USB initialisation sequence */ ++ err = scarlett2_usb_init(mixer); ++ if (err < 0) ++ return err; ++ ++ /* Read volume levels and controls from the interface */ ++ err = scarlett2_read_configs(mixer); ++ if (err < 0) ++ return err; ++ ++ /* Create the MSD control */ ++ err = scarlett2_add_msd_ctl(mixer); ++ if (err < 0) ++ return err; ++ ++ /* If MSD mode is enabled, don't create any other controls */ ++ if (((struct scarlett2_data *)mixer->private_data)->msd_switch) ++ return 0; ++ ++ /* Create the analogue output controls */ ++ err = scarlett2_add_line_out_ctls(mixer); ++ if (err < 0) ++ return err; ++ ++ /* Create the analogue input controls */ ++ err = scarlett2_add_line_in_ctls(mixer); ++ if (err < 0) ++ return err; ++ ++ /* Create the input, output, and mixer mux input selections */ ++ err = scarlett2_add_mux_enums(mixer); ++ if (err < 0) ++ return err; ++ ++ /* Create the matrix mixer controls */ ++ err = scarlett2_add_mixer_ctls(mixer); ++ if (err < 0) ++ return err; ++ ++ /* Create the level meter controls */ ++ err = scarlett2_add_meter_ctl(mixer); ++ if (err < 0) ++ return err; ++ ++ /* Create the sync control */ ++ err = scarlett2_add_sync_ctl(mixer); ++ if (err < 0) ++ return err; ++ ++ /* Create the direct monitor control */ ++ err = scarlett2_add_direct_monitor_ctl(mixer); ++ if (err < 0) ++ return err; ++ ++ /* Create the speaker switching control */ ++ err = scarlett2_add_speaker_switch_ctl(mixer); ++ if (err < 0) ++ return err; ++ ++ /* Create the talkback controls */ ++ err = scarlett2_add_talkback_ctls(mixer); ++ if (err < 0) ++ return err; ++ ++ /* Create the standalone control */ ++ err = scarlett2_add_standalone_ctl(mixer); ++ if (err < 0) ++ return err; ++ ++ /* Set up the interrupt polling */ ++ err = scarlett2_init_notify(mixer); ++ if (err < 0) ++ return err; ++ ++ return 0; ++} ++ ++int snd_scarlett2_init(struct usb_mixer_interface *mixer) ++{ ++ struct snd_usb_audio *chip = mixer->chip; ++ const struct scarlett2_device_entry *entry; ++ int err; ++ ++ /* only use UAC_VERSION_2 */ ++ if (!mixer->protocol) ++ return 0; ++ ++ /* find entry in scarlett2_devices */ ++ entry = get_scarlett2_device_entry(mixer); ++ if (!entry) { ++ usb_audio_err(mixer->chip, ++ "%s: missing device entry for %04x:%04x\n", ++ __func__, ++ USB_ID_VENDOR(chip->usb_id), ++ USB_ID_PRODUCT(chip->usb_id)); ++ return 0; ++ } ++ ++ if (chip->setup & SCARLETT2_DISABLE) { ++ usb_audio_info(chip, ++ "Focusrite %s Mixer Driver disabled " ++ "by modprobe options (snd_usb_audio " ++ "vid=0x%04x pid=0x%04x device_setup=%d)\n", ++ entry->series_name, ++ USB_ID_VENDOR(chip->usb_id), ++ USB_ID_PRODUCT(chip->usb_id), ++ SCARLETT2_DISABLE); ++ return 0; ++ } ++ ++ usb_audio_info(chip, ++ "Focusrite %s Mixer Driver enabled (pid=0x%04x); " ++ "report any issues to g@b4.vu", ++ entry->series_name, ++ USB_ID_PRODUCT(chip->usb_id)); ++ ++ err = snd_scarlett2_controls_create(mixer, entry); ++ if (err < 0) ++ usb_audio_err(mixer->chip, ++ "Error initialising %s Mixer Driver: %d", ++ entry->series_name, ++ err); ++ ++ return err; ++} +diff --git a/sound/usb/mixer_scarlett2.h b/sound/usb/mixer_scarlett2.h +new file mode 100644 +index 0000000000000..d209362cf41a6 +--- /dev/null ++++ b/sound/usb/mixer_scarlett2.h +@@ -0,0 +1,7 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __USB_MIXER_SCARLETT2_H ++#define __USB_MIXER_SCARLETT2_H ++ ++int snd_scarlett2_init(struct usb_mixer_interface *mixer); ++ ++#endif /* __USB_MIXER_SCARLETT2_H */ +diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c +deleted file mode 100644 +index c04cff7225411..0000000000000 +--- a/sound/usb/mixer_scarlett_gen2.c ++++ /dev/null +@@ -1,4274 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Focusrite Scarlett Gen 2/3 and Clarett+ Driver for ALSA +- * +- * Supported models: +- * - 6i6/18i8/18i20 Gen 2 +- * - Solo/2i2/4i4/8i6/18i8/18i20 Gen 3 +- * - Clarett+ 8Pre +- * +- * Copyright (c) 2018-2022 by Geoffrey D. Bennett +- * Copyright (c) 2020-2021 by Vladimir Sadovnikov +- * Copyright (c) 2022 by Christian Colglazier +- * +- * Based on the Scarlett (Gen 1) Driver for ALSA: +- * +- * Copyright (c) 2013 by Tobias Hoffmann +- * Copyright (c) 2013 by Robin Gareus +- * Copyright (c) 2002 by Takashi Iwai +- * Copyright (c) 2014 by Chris J Arges +- * +- * Many codes borrowed from audio.c by +- * Alan Cox (alan at lxorguk.ukuu.org.uk) +- * Thomas Sailer (sailer at ife.ee.ethz.ch) +- * +- * Code cleanup: +- * David Henningsson +- */ +- +-/* The protocol was reverse engineered by looking at the communication +- * between Focusrite Control 2.3.4 and the Focusrite(R) Scarlett 18i20 +- * (firmware 1083) using usbmon in July-August 2018. +- * +- * Scarlett 18i8 support added in April 2019. +- * +- * Scarlett 6i6 support added in June 2019 (thanks to Martin Wittmann +- * for providing usbmon output and testing). +- * +- * Scarlett 4i4/8i6 Gen 3 support added in May 2020 (thanks to Laurent +- * Debricon for donating a 4i4 and to Fredrik Unger for providing 8i6 +- * usbmon output and testing). +- * +- * Scarlett 18i8/18i20 Gen 3 support added in June 2020 (thanks to +- * Darren Jaeckel, Alex Sedlack, and Clovis Lunel for providing usbmon +- * output, protocol traces and testing). +- * +- * Support for loading mixer volume and mux configuration from the +- * interface during driver initialisation added in May 2021 (thanks to +- * Vladimir Sadovnikov for figuring out how). +- * +- * Support for Solo/2i2 Gen 3 added in May 2021 (thanks to Alexander +- * Vorona for 2i2 protocol traces). +- * +- * Support for phantom power, direct monitoring, speaker switching, +- * and talkback added in May-June 2021. +- * +- * Support for Clarett+ 8Pre added in Aug 2022 by Christian +- * Colglazier. +- * +- * This ALSA mixer gives access to (model-dependent): +- * - input, output, mixer-matrix muxes +- * - mixer-matrix gain stages +- * - gain/volume/mute controls +- * - level meters +- * - line/inst level, pad, and air controls +- * - phantom power, direct monitor, speaker switching, and talkback +- * controls +- * - disable/enable MSD mode +- * - disable/enable standalone mode +- * +- * +- * /--------------\ 18chn 20chn /--------------\ +- * | Hardware in +--+------\ /-------------+--+ ALSA PCM out | +- * \--------------/ | | | | \--------------/ +- * | | | /-----\ | +- * | | | | | | +- * | v v v | | +- * | +---------------+ | | +- * | \ Matrix Mux / | | +- * | +-----+-----+ | | +- * | | | | +- * | |18chn | | +- * | | | | +- * | | 10chn| | +- * | v | | +- * | +------------+ | | +- * | | Mixer | | | +- * | | Matrix | | | +- * | | | | | +- * | | 18x10 Gain | | | +- * | | stages | | | +- * | +-----+------+ | | +- * | | | | +- * |18chn |10chn | |20chn +- * | | | | +- * | +----------/ | +- * | | | +- * v v v +- * =========================== +- * +---------------+ +--—------------+ +- * \ Output Mux / \ Capture Mux / +- * +---+---+---+ +-----+-----+ +- * | | | +- * 10chn| | |18chn +- * | | | +- * /--------------\ | | | /--------------\ +- * | S/PDIF, ADAT |<--/ |10chn \-->| ALSA PCM in | +- * | Hardware out | | \--------------/ +- * \--------------/ | +- * v +- * +-------------+ Software gain per channel. +- * | Master Gain |<-- 18i20 only: Switch per channel +- * +------+------+ to select HW or SW gain control. +- * | +- * |10chn +- * /--------------\ | +- * | Analogue |<------/ +- * | Hardware out | +- * \--------------/ +- * +- * +- * Gen 3 devices have a Mass Storage Device (MSD) mode where a small +- * disk with registration and driver download information is presented +- * to the host. To access the full functionality of the device without +- * proprietary software, MSD mode can be disabled by: +- * - holding down the 48V button for five seconds while powering on +- * the device, or +- * - using this driver and alsamixer to change the "MSD Mode" setting +- * to Off and power-cycling the device +- */ +- +-#include +-#include +-#include +- +-#include +-#include +- +-#include "usbaudio.h" +-#include "mixer.h" +-#include "helper.h" +- +-#include "mixer_scarlett_gen2.h" +- +-/* device_setup value to enable */ +-#define SCARLETT2_ENABLE 0x01 +- +-/* device_setup value to allow turning MSD mode back on */ +-#define SCARLETT2_MSD_ENABLE 0x02 +- +-/* some gui mixers can't handle negative ctl values */ +-#define SCARLETT2_VOLUME_BIAS 127 +- +-/* mixer range from -80dB to +6dB in 0.5dB steps */ +-#define SCARLETT2_MIXER_MIN_DB -80 +-#define SCARLETT2_MIXER_BIAS (-SCARLETT2_MIXER_MIN_DB * 2) +-#define SCARLETT2_MIXER_MAX_DB 6 +-#define SCARLETT2_MIXER_MAX_VALUE \ +- ((SCARLETT2_MIXER_MAX_DB - SCARLETT2_MIXER_MIN_DB) * 2) +-#define SCARLETT2_MIXER_VALUE_COUNT (SCARLETT2_MIXER_MAX_VALUE + 1) +- +-/* map from (dB + 80) * 2 to mixer value +- * for dB in 0 .. 172: int(8192 * pow(10, ((dB - 160) / 2 / 20))) +- */ +-static const u16 scarlett2_mixer_values[SCARLETT2_MIXER_VALUE_COUNT] = { +- 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, +- 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, +- 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, +- 23, 24, 25, 27, 29, 30, 32, 34, 36, 38, 41, 43, 46, 48, 51, +- 54, 57, 61, 65, 68, 73, 77, 81, 86, 91, 97, 103, 109, 115, +- 122, 129, 137, 145, 154, 163, 173, 183, 194, 205, 217, 230, +- 244, 259, 274, 290, 307, 326, 345, 365, 387, 410, 434, 460, +- 487, 516, 547, 579, 614, 650, 689, 730, 773, 819, 867, 919, +- 973, 1031, 1092, 1157, 1225, 1298, 1375, 1456, 1543, 1634, +- 1731, 1833, 1942, 2057, 2179, 2308, 2445, 2590, 2744, 2906, +- 3078, 3261, 3454, 3659, 3876, 4105, 4349, 4606, 4879, 5168, +- 5475, 5799, 6143, 6507, 6892, 7301, 7733, 8192, 8677, 9191, +- 9736, 10313, 10924, 11571, 12257, 12983, 13752, 14567, 15430, +- 16345 +-}; +- +-/* Maximum number of analogue outputs */ +-#define SCARLETT2_ANALOGUE_MAX 10 +- +-/* Maximum number of level and pad switches */ +-#define SCARLETT2_LEVEL_SWITCH_MAX 2 +-#define SCARLETT2_PAD_SWITCH_MAX 8 +-#define SCARLETT2_AIR_SWITCH_MAX 8 +-#define SCARLETT2_PHANTOM_SWITCH_MAX 2 +- +-/* Maximum number of inputs to the mixer */ +-#define SCARLETT2_INPUT_MIX_MAX 25 +- +-/* Maximum number of outputs from the mixer */ +-#define SCARLETT2_OUTPUT_MIX_MAX 12 +- +-/* Maximum size of the data in the USB mux assignment message: +- * 20 inputs, 20 outputs, 25 matrix inputs, 12 spare +- */ +-#define SCARLETT2_MUX_MAX 77 +- +-/* Maximum number of meters (sum of output port counts) */ +-#define SCARLETT2_MAX_METERS 65 +- +-/* There are three different sets of configuration parameters across +- * the devices +- */ +-enum { +- SCARLETT2_CONFIG_SET_NO_MIXER = 0, +- SCARLETT2_CONFIG_SET_GEN_2 = 1, +- SCARLETT2_CONFIG_SET_GEN_3 = 2, +- SCARLETT2_CONFIG_SET_CLARETT = 3, +- SCARLETT2_CONFIG_SET_COUNT = 4 +-}; +- +-/* Hardware port types: +- * - None (no input to mux) +- * - Analogue I/O +- * - S/PDIF I/O +- * - ADAT I/O +- * - Mixer I/O +- * - PCM I/O +- */ +-enum { +- SCARLETT2_PORT_TYPE_NONE = 0, +- SCARLETT2_PORT_TYPE_ANALOGUE = 1, +- SCARLETT2_PORT_TYPE_SPDIF = 2, +- SCARLETT2_PORT_TYPE_ADAT = 3, +- SCARLETT2_PORT_TYPE_MIX = 4, +- SCARLETT2_PORT_TYPE_PCM = 5, +- SCARLETT2_PORT_TYPE_COUNT = 6, +-}; +- +-/* I/O count of each port type kept in struct scarlett2_ports */ +-enum { +- SCARLETT2_PORT_IN = 0, +- SCARLETT2_PORT_OUT = 1, +- SCARLETT2_PORT_DIRNS = 2, +-}; +- +-/* Dim/Mute buttons on the 18i20 */ +-enum { +- SCARLETT2_BUTTON_MUTE = 0, +- SCARLETT2_BUTTON_DIM = 1, +- SCARLETT2_DIM_MUTE_COUNT = 2, +-}; +- +-static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = { +- "Mute Playback Switch", "Dim Playback Switch" +-}; +- +-/* Description of each hardware port type: +- * - id: hardware ID of this port type +- * - src_descr: printf format string for mux input selections +- * - src_num_offset: added to channel number for the fprintf +- * - dst_descr: printf format string for mixer controls +- */ +-struct scarlett2_port { +- u16 id; +- const char * const src_descr; +- int src_num_offset; +- const char * const dst_descr; +-}; +- +-static const struct scarlett2_port scarlett2_ports[SCARLETT2_PORT_TYPE_COUNT] = { +- [SCARLETT2_PORT_TYPE_NONE] = { +- .id = 0x000, +- .src_descr = "Off" +- }, +- [SCARLETT2_PORT_TYPE_ANALOGUE] = { +- .id = 0x080, +- .src_descr = "Analogue %d", +- .src_num_offset = 1, +- .dst_descr = "Analogue Output %02d Playback" +- }, +- [SCARLETT2_PORT_TYPE_SPDIF] = { +- .id = 0x180, +- .src_descr = "S/PDIF %d", +- .src_num_offset = 1, +- .dst_descr = "S/PDIF Output %d Playback" +- }, +- [SCARLETT2_PORT_TYPE_ADAT] = { +- .id = 0x200, +- .src_descr = "ADAT %d", +- .src_num_offset = 1, +- .dst_descr = "ADAT Output %d Playback" +- }, +- [SCARLETT2_PORT_TYPE_MIX] = { +- .id = 0x300, +- .src_descr = "Mix %c", +- .src_num_offset = 'A', +- .dst_descr = "Mixer Input %02d Capture" +- }, +- [SCARLETT2_PORT_TYPE_PCM] = { +- .id = 0x600, +- .src_descr = "PCM %d", +- .src_num_offset = 1, +- .dst_descr = "PCM %02d Capture" +- }, +-}; +- +-/* Number of mux tables: one for each band of sample rates +- * (44.1/48kHz, 88.2/96kHz, and 176.4/176kHz) +- */ +-#define SCARLETT2_MUX_TABLES 3 +- +-/* Maximum number of entries in a mux table */ +-#define SCARLETT2_MAX_MUX_ENTRIES 10 +- +-/* One entry within mux_assignment defines the port type and range of +- * ports to add to the set_mux message. The end of the list is marked +- * with count == 0. +- */ +-struct scarlett2_mux_entry { +- u8 port_type; +- u8 start; +- u8 count; +-}; +- +-struct scarlett2_device_info { +- u32 usb_id; /* USB device identifier */ +- +- /* Gen 3 devices have an internal MSD mode switch that needs +- * to be disabled in order to access the full functionality of +- * the device. +- */ +- u8 has_msd_mode; +- +- /* which set of configuration parameters the device uses */ +- u8 config_set; +- +- /* line out hw volume is sw controlled */ +- u8 line_out_hw_vol; +- +- /* support for main/alt speaker switching */ +- u8 has_speaker_switching; +- +- /* support for talkback microphone */ +- u8 has_talkback; +- +- /* the number of analogue inputs with a software switchable +- * level control that can be set to line or instrument +- */ +- u8 level_input_count; +- +- /* the first input with a level control (0-based) */ +- u8 level_input_first; +- +- /* the number of analogue inputs with a software switchable +- * 10dB pad control +- */ +- u8 pad_input_count; +- +- /* the number of analogue inputs with a software switchable +- * "air" control +- */ +- u8 air_input_count; +- +- /* the number of phantom (48V) software switchable controls */ +- u8 phantom_count; +- +- /* the number of inputs each phantom switch controls */ +- u8 inputs_per_phantom; +- +- /* the number of direct monitor options +- * (0 = none, 1 = mono only, 2 = mono/stereo) +- */ +- u8 direct_monitor; +- +- /* remap analogue outputs; 18i8 Gen 3 has "line 3/4" connected +- * internally to the analogue 7/8 outputs +- */ +- u8 line_out_remap_enable; +- u8 line_out_remap[SCARLETT2_ANALOGUE_MAX]; +- +- /* additional description for the line out volume controls */ +- const char * const line_out_descrs[SCARLETT2_ANALOGUE_MAX]; +- +- /* number of sources/destinations of each port type */ +- const int port_count[SCARLETT2_PORT_TYPE_COUNT][SCARLETT2_PORT_DIRNS]; +- +- /* layout/order of the entries in the set_mux message */ +- struct scarlett2_mux_entry mux_assignment[SCARLETT2_MUX_TABLES] +- [SCARLETT2_MAX_MUX_ENTRIES]; +-}; +- +-struct scarlett2_data { +- struct usb_mixer_interface *mixer; +- struct mutex usb_mutex; /* prevent sending concurrent USB requests */ +- struct mutex data_mutex; /* lock access to this data */ +- struct delayed_work work; +- const struct scarlett2_device_info *info; +- __u8 bInterfaceNumber; +- __u8 bEndpointAddress; +- __u16 wMaxPacketSize; +- __u8 bInterval; +- int num_mux_srcs; +- int num_mux_dsts; +- u16 scarlett2_seq; +- u8 sync_updated; +- u8 vol_updated; +- u8 input_other_updated; +- u8 monitor_other_updated; +- u8 mux_updated; +- u8 speaker_switching_switched; +- u8 sync; +- u8 master_vol; +- u8 vol[SCARLETT2_ANALOGUE_MAX]; +- u8 vol_sw_hw_switch[SCARLETT2_ANALOGUE_MAX]; +- u8 mute_switch[SCARLETT2_ANALOGUE_MAX]; +- u8 level_switch[SCARLETT2_LEVEL_SWITCH_MAX]; +- u8 pad_switch[SCARLETT2_PAD_SWITCH_MAX]; +- u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT]; +- u8 air_switch[SCARLETT2_AIR_SWITCH_MAX]; +- u8 phantom_switch[SCARLETT2_PHANTOM_SWITCH_MAX]; +- u8 phantom_persistence; +- u8 direct_monitor_switch; +- u8 speaker_switching_switch; +- u8 talkback_switch; +- u8 talkback_map[SCARLETT2_OUTPUT_MIX_MAX]; +- u8 msd_switch; +- u8 standalone_switch; +- struct snd_kcontrol *sync_ctl; +- struct snd_kcontrol *master_vol_ctl; +- struct snd_kcontrol *vol_ctls[SCARLETT2_ANALOGUE_MAX]; +- struct snd_kcontrol *sw_hw_ctls[SCARLETT2_ANALOGUE_MAX]; +- struct snd_kcontrol *mute_ctls[SCARLETT2_ANALOGUE_MAX]; +- struct snd_kcontrol *dim_mute_ctls[SCARLETT2_DIM_MUTE_COUNT]; +- struct snd_kcontrol *level_ctls[SCARLETT2_LEVEL_SWITCH_MAX]; +- struct snd_kcontrol *pad_ctls[SCARLETT2_PAD_SWITCH_MAX]; +- struct snd_kcontrol *air_ctls[SCARLETT2_AIR_SWITCH_MAX]; +- struct snd_kcontrol *phantom_ctls[SCARLETT2_PHANTOM_SWITCH_MAX]; +- struct snd_kcontrol *mux_ctls[SCARLETT2_MUX_MAX]; +- struct snd_kcontrol *direct_monitor_ctl; +- struct snd_kcontrol *speaker_switching_ctl; +- struct snd_kcontrol *talkback_ctl; +- u8 mux[SCARLETT2_MUX_MAX]; +- u8 mix[SCARLETT2_INPUT_MIX_MAX * SCARLETT2_OUTPUT_MIX_MAX]; +-}; +- +-/*** Model-specific data ***/ +- +-static const struct scarlett2_device_info s6i6_gen2_info = { +- .usb_id = USB_ID(0x1235, 0x8203), +- +- .config_set = SCARLETT2_CONFIG_SET_GEN_2, +- .level_input_count = 2, +- .pad_input_count = 2, +- +- .line_out_descrs = { +- "Headphones 1 L", +- "Headphones 1 R", +- "Headphones 2 L", +- "Headphones 2 R", +- }, +- +- .port_count = { +- [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, +- [SCARLETT2_PORT_TYPE_ANALOGUE] = { 4, 4 }, +- [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, +- [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, +- [SCARLETT2_PORT_TYPE_PCM] = { 6, 6 }, +- }, +- +- .mux_assignment = { { +- { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, +- { 0, 0, 0 }, +- } }, +-}; +- +-static const struct scarlett2_device_info s18i8_gen2_info = { +- .usb_id = USB_ID(0x1235, 0x8204), +- +- .config_set = SCARLETT2_CONFIG_SET_GEN_2, +- .level_input_count = 2, +- .pad_input_count = 4, +- +- .line_out_descrs = { +- "Monitor L", +- "Monitor R", +- "Headphones 1 L", +- "Headphones 1 R", +- "Headphones 2 L", +- "Headphones 2 R", +- }, +- +- .port_count = { +- [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, +- [SCARLETT2_PORT_TYPE_ANALOGUE] = { 8, 6 }, +- [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, +- [SCARLETT2_PORT_TYPE_ADAT] = { 8, 0 }, +- [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, +- [SCARLETT2_PORT_TYPE_PCM] = { 8, 18 }, +- }, +- +- .mux_assignment = { { +- { SCARLETT2_PORT_TYPE_PCM, 0, 18 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 14 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 6 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 4 }, +- { 0, 0, 0 }, +- } }, +-}; +- +-static const struct scarlett2_device_info s18i20_gen2_info = { +- .usb_id = USB_ID(0x1235, 0x8201), +- +- .config_set = SCARLETT2_CONFIG_SET_GEN_2, +- .line_out_hw_vol = 1, +- +- .line_out_descrs = { +- "Monitor L", +- "Monitor R", +- NULL, +- NULL, +- NULL, +- NULL, +- "Headphones 1 L", +- "Headphones 1 R", +- "Headphones 2 L", +- "Headphones 2 R", +- }, +- +- .port_count = { +- [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, +- [SCARLETT2_PORT_TYPE_ANALOGUE] = { 8, 10 }, +- [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, +- [SCARLETT2_PORT_TYPE_ADAT] = { 8, 8 }, +- [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, +- [SCARLETT2_PORT_TYPE_PCM] = { 20, 18 }, +- }, +- +- .mux_assignment = { { +- { SCARLETT2_PORT_TYPE_PCM, 0, 18 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_ADAT, 0, 8 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 14 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_ADAT, 0, 4 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 6 }, +- { 0, 0, 0 }, +- } }, +-}; +- +-static const struct scarlett2_device_info solo_gen3_info = { +- .usb_id = USB_ID(0x1235, 0x8211), +- +- .has_msd_mode = 1, +- .config_set = SCARLETT2_CONFIG_SET_NO_MIXER, +- .level_input_count = 1, +- .level_input_first = 1, +- .air_input_count = 1, +- .phantom_count = 1, +- .inputs_per_phantom = 1, +- .direct_monitor = 1, +-}; +- +-static const struct scarlett2_device_info s2i2_gen3_info = { +- .usb_id = USB_ID(0x1235, 0x8210), +- +- .has_msd_mode = 1, +- .config_set = SCARLETT2_CONFIG_SET_NO_MIXER, +- .level_input_count = 2, +- .air_input_count = 2, +- .phantom_count = 1, +- .inputs_per_phantom = 2, +- .direct_monitor = 2, +-}; +- +-static const struct scarlett2_device_info s4i4_gen3_info = { +- .usb_id = USB_ID(0x1235, 0x8212), +- +- .has_msd_mode = 1, +- .config_set = SCARLETT2_CONFIG_SET_GEN_3, +- .level_input_count = 2, +- .pad_input_count = 2, +- .air_input_count = 2, +- .phantom_count = 1, +- .inputs_per_phantom = 2, +- +- .line_out_descrs = { +- "Monitor L", +- "Monitor R", +- "Headphones L", +- "Headphones R", +- }, +- +- .port_count = { +- [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, +- [SCARLETT2_PORT_TYPE_ANALOGUE] = { 4, 4 }, +- [SCARLETT2_PORT_TYPE_MIX] = { 6, 8 }, +- [SCARLETT2_PORT_TYPE_PCM] = { 4, 6 }, +- }, +- +- .mux_assignment = { { +- { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 16 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 16 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 6 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 16 }, +- { 0, 0, 0 }, +- } }, +-}; +- +-static const struct scarlett2_device_info s8i6_gen3_info = { +- .usb_id = USB_ID(0x1235, 0x8213), +- +- .has_msd_mode = 1, +- .config_set = SCARLETT2_CONFIG_SET_GEN_3, +- .level_input_count = 2, +- .pad_input_count = 2, +- .air_input_count = 2, +- .phantom_count = 1, +- .inputs_per_phantom = 2, +- +- .line_out_descrs = { +- "Headphones 1 L", +- "Headphones 1 R", +- "Headphones 2 L", +- "Headphones 2 R", +- }, +- +- .port_count = { +- [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, +- [SCARLETT2_PORT_TYPE_ANALOGUE] = { 6, 4 }, +- [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, +- [SCARLETT2_PORT_TYPE_MIX] = { 8, 8 }, +- [SCARLETT2_PORT_TYPE_PCM] = { 6, 10 }, +- }, +- +- .mux_assignment = { { +- { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 18 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 18 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 4 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 8 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 18 }, +- { 0, 0, 0 }, +- } }, +-}; +- +-static const struct scarlett2_device_info s18i8_gen3_info = { +- .usb_id = USB_ID(0x1235, 0x8214), +- +- .has_msd_mode = 1, +- .config_set = SCARLETT2_CONFIG_SET_GEN_3, +- .line_out_hw_vol = 1, +- .has_speaker_switching = 1, +- .level_input_count = 2, +- .pad_input_count = 4, +- .air_input_count = 4, +- .phantom_count = 2, +- .inputs_per_phantom = 2, +- +- .line_out_remap_enable = 1, +- .line_out_remap = { 0, 1, 6, 7, 2, 3, 4, 5 }, +- +- .line_out_descrs = { +- "Monitor L", +- "Monitor R", +- "Alt Monitor L", +- "Alt Monitor R", +- "Headphones 1 L", +- "Headphones 1 R", +- "Headphones 2 L", +- "Headphones 2 R", +- }, +- +- .port_count = { +- [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, +- [SCARLETT2_PORT_TYPE_ANALOGUE] = { 8, 8 }, +- [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, +- [SCARLETT2_PORT_TYPE_ADAT] = { 8, 0 }, +- [SCARLETT2_PORT_TYPE_MIX] = { 10, 20 }, +- [SCARLETT2_PORT_TYPE_PCM] = { 8, 20 }, +- }, +- +- .mux_assignment = { { +- { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, +- { SCARLETT2_PORT_TYPE_PCM, 12, 8 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 6, 2 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 2, 4 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_PCM, 10, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 20 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 10 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, +- { SCARLETT2_PORT_TYPE_PCM, 12, 4 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 6, 2 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 2, 4 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_PCM, 10, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 20 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 10 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 2 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 6, 2 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 2, 4 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 20 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 10 }, +- { 0, 0, 0 }, +- } }, +-}; +- +-static const struct scarlett2_device_info s18i20_gen3_info = { +- .usb_id = USB_ID(0x1235, 0x8215), +- +- .has_msd_mode = 1, +- .config_set = SCARLETT2_CONFIG_SET_GEN_3, +- .line_out_hw_vol = 1, +- .has_speaker_switching = 1, +- .has_talkback = 1, +- .level_input_count = 2, +- .pad_input_count = 8, +- .air_input_count = 8, +- .phantom_count = 2, +- .inputs_per_phantom = 4, +- +- .line_out_descrs = { +- "Monitor 1 L", +- "Monitor 1 R", +- "Monitor 2 L", +- "Monitor 2 R", +- NULL, +- NULL, +- "Headphones 1 L", +- "Headphones 1 R", +- "Headphones 2 L", +- "Headphones 2 R", +- }, +- +- .port_count = { +- [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, +- [SCARLETT2_PORT_TYPE_ANALOGUE] = { 9, 10 }, +- [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, +- [SCARLETT2_PORT_TYPE_ADAT] = { 8, 8 }, +- [SCARLETT2_PORT_TYPE_MIX] = { 12, 25 }, +- [SCARLETT2_PORT_TYPE_PCM] = { 20, 20 }, +- }, +- +- .mux_assignment = { { +- { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, +- { SCARLETT2_PORT_TYPE_PCM, 10, 10 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_ADAT, 0, 8 }, +- { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 25 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 12 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 8 }, +- { SCARLETT2_PORT_TYPE_PCM, 10, 8 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_ADAT, 0, 8 }, +- { SCARLETT2_PORT_TYPE_PCM, 8, 2 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 25 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 10 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 10 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 24 }, +- { 0, 0, 0 }, +- } }, +-}; +- +-static const struct scarlett2_device_info clarett_8pre_info = { +- .usb_id = USB_ID(0x1235, 0x820c), +- +- .config_set = SCARLETT2_CONFIG_SET_CLARETT, +- .line_out_hw_vol = 1, +- .level_input_count = 2, +- .air_input_count = 8, +- +- .line_out_descrs = { +- "Monitor L", +- "Monitor R", +- NULL, +- NULL, +- NULL, +- NULL, +- "Headphones 1 L", +- "Headphones 1 R", +- "Headphones 2 L", +- "Headphones 2 R", +- }, +- +- .port_count = { +- [SCARLETT2_PORT_TYPE_NONE] = { 1, 0 }, +- [SCARLETT2_PORT_TYPE_ANALOGUE] = { 8, 10 }, +- [SCARLETT2_PORT_TYPE_SPDIF] = { 2, 2 }, +- [SCARLETT2_PORT_TYPE_ADAT] = { 8, 8 }, +- [SCARLETT2_PORT_TYPE_MIX] = { 10, 18 }, +- [SCARLETT2_PORT_TYPE_PCM] = { 20, 18 }, +- }, +- +- .mux_assignment = { { +- { SCARLETT2_PORT_TYPE_PCM, 0, 18 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_ADAT, 0, 8 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 14 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_ADAT, 0, 4 }, +- { SCARLETT2_PORT_TYPE_MIX, 0, 18 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 8 }, +- { 0, 0, 0 }, +- }, { +- { SCARLETT2_PORT_TYPE_PCM, 0, 12 }, +- { SCARLETT2_PORT_TYPE_ANALOGUE, 0, 10 }, +- { SCARLETT2_PORT_TYPE_SPDIF, 0, 2 }, +- { SCARLETT2_PORT_TYPE_NONE, 0, 22 }, +- { 0, 0, 0 }, +- } }, +-}; +- +-static const struct scarlett2_device_info *scarlett2_devices[] = { +- /* Supported Gen 2 devices */ +- &s6i6_gen2_info, +- &s18i8_gen2_info, +- &s18i20_gen2_info, +- +- /* Supported Gen 3 devices */ +- &solo_gen3_info, +- &s2i2_gen3_info, +- &s4i4_gen3_info, +- &s8i6_gen3_info, +- &s18i8_gen3_info, +- &s18i20_gen3_info, +- +- /* Supported Clarett+ devices */ +- &clarett_8pre_info, +- +- /* End of list */ +- NULL +-}; +- +-/* get the starting port index number for a given port type/direction */ +-static int scarlett2_get_port_start_num( +- const int port_count[][SCARLETT2_PORT_DIRNS], +- int direction, int port_type) +-{ +- int i, num = 0; +- +- for (i = 0; i < port_type; i++) +- num += port_count[i][direction]; +- +- return num; +-} +- +-/*** USB Interactions ***/ +- +-/* Notifications from the interface */ +-#define SCARLETT2_USB_NOTIFY_SYNC 0x00000008 +-#define SCARLETT2_USB_NOTIFY_DIM_MUTE 0x00200000 +-#define SCARLETT2_USB_NOTIFY_MONITOR 0x00400000 +-#define SCARLETT2_USB_NOTIFY_INPUT_OTHER 0x00800000 +-#define SCARLETT2_USB_NOTIFY_MONITOR_OTHER 0x01000000 +- +-/* Commands for sending/receiving requests/responses */ +-#define SCARLETT2_USB_CMD_INIT 0 +-#define SCARLETT2_USB_CMD_REQ 2 +-#define SCARLETT2_USB_CMD_RESP 3 +- +-#define SCARLETT2_USB_INIT_1 0x00000000 +-#define SCARLETT2_USB_INIT_2 0x00000002 +-#define SCARLETT2_USB_GET_METER 0x00001001 +-#define SCARLETT2_USB_GET_MIX 0x00002001 +-#define SCARLETT2_USB_SET_MIX 0x00002002 +-#define SCARLETT2_USB_GET_MUX 0x00003001 +-#define SCARLETT2_USB_SET_MUX 0x00003002 +-#define SCARLETT2_USB_GET_SYNC 0x00006004 +-#define SCARLETT2_USB_GET_DATA 0x00800000 +-#define SCARLETT2_USB_SET_DATA 0x00800001 +-#define SCARLETT2_USB_DATA_CMD 0x00800002 +- +-#define SCARLETT2_USB_CONFIG_SAVE 6 +- +-#define SCARLETT2_USB_VOLUME_STATUS_OFFSET 0x31 +-#define SCARLETT2_USB_METER_LEVELS_GET_MAGIC 1 +- +-/* volume status is read together (matches scarlett2_config_items[1]) */ +-struct scarlett2_usb_volume_status { +- /* dim/mute buttons */ +- u8 dim_mute[SCARLETT2_DIM_MUTE_COUNT]; +- +- u8 pad1; +- +- /* software volume setting */ +- s16 sw_vol[SCARLETT2_ANALOGUE_MAX]; +- +- /* actual volume of output inc. dim (-18dB) */ +- s16 hw_vol[SCARLETT2_ANALOGUE_MAX]; +- +- /* internal mute buttons */ +- u8 mute_switch[SCARLETT2_ANALOGUE_MAX]; +- +- /* sw (0) or hw (1) controlled */ +- u8 sw_hw_switch[SCARLETT2_ANALOGUE_MAX]; +- +- u8 pad3[6]; +- +- /* front panel volume knob */ +- s16 master_vol; +-} __packed; +- +-/* Configuration parameters that can be read and written */ +-enum { +- SCARLETT2_CONFIG_DIM_MUTE = 0, +- SCARLETT2_CONFIG_LINE_OUT_VOLUME = 1, +- SCARLETT2_CONFIG_MUTE_SWITCH = 2, +- SCARLETT2_CONFIG_SW_HW_SWITCH = 3, +- SCARLETT2_CONFIG_LEVEL_SWITCH = 4, +- SCARLETT2_CONFIG_PAD_SWITCH = 5, +- SCARLETT2_CONFIG_MSD_SWITCH = 6, +- SCARLETT2_CONFIG_AIR_SWITCH = 7, +- SCARLETT2_CONFIG_STANDALONE_SWITCH = 8, +- SCARLETT2_CONFIG_PHANTOM_SWITCH = 9, +- SCARLETT2_CONFIG_PHANTOM_PERSISTENCE = 10, +- SCARLETT2_CONFIG_DIRECT_MONITOR = 11, +- SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH = 12, +- SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE = 13, +- SCARLETT2_CONFIG_TALKBACK_MAP = 14, +- SCARLETT2_CONFIG_COUNT = 15 +-}; +- +-/* Location, size, and activation command number for the configuration +- * parameters. Size is in bits and may be 1, 8, or 16. +- */ +-struct scarlett2_config { +- u8 offset; +- u8 size; +- u8 activate; +-}; +- +-static const struct scarlett2_config +- scarlett2_config_items[SCARLETT2_CONFIG_SET_COUNT] +- [SCARLETT2_CONFIG_COUNT] = +- +-/* Devices without a mixer (Gen 3 Solo and 2i2) */ +-{ { +- [SCARLETT2_CONFIG_MSD_SWITCH] = { +- .offset = 0x04, .size = 8, .activate = 6 }, +- +- [SCARLETT2_CONFIG_PHANTOM_PERSISTENCE] = { +- .offset = 0x05, .size = 8, .activate = 6 }, +- +- [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { +- .offset = 0x06, .size = 8, .activate = 3 }, +- +- [SCARLETT2_CONFIG_DIRECT_MONITOR] = { +- .offset = 0x07, .size = 8, .activate = 4 }, +- +- [SCARLETT2_CONFIG_LEVEL_SWITCH] = { +- .offset = 0x08, .size = 1, .activate = 7 }, +- +- [SCARLETT2_CONFIG_AIR_SWITCH] = { +- .offset = 0x09, .size = 1, .activate = 8 }, +- +-/* Gen 2 devices: 6i6, 18i8, 18i20 */ +-}, { +- [SCARLETT2_CONFIG_DIM_MUTE] = { +- .offset = 0x31, .size = 8, .activate = 2 }, +- +- [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = { +- .offset = 0x34, .size = 16, .activate = 1 }, +- +- [SCARLETT2_CONFIG_MUTE_SWITCH] = { +- .offset = 0x5c, .size = 8, .activate = 1 }, +- +- [SCARLETT2_CONFIG_SW_HW_SWITCH] = { +- .offset = 0x66, .size = 8, .activate = 3 }, +- +- [SCARLETT2_CONFIG_LEVEL_SWITCH] = { +- .offset = 0x7c, .size = 8, .activate = 7 }, +- +- [SCARLETT2_CONFIG_PAD_SWITCH] = { +- .offset = 0x84, .size = 8, .activate = 8 }, +- +- [SCARLETT2_CONFIG_STANDALONE_SWITCH] = { +- .offset = 0x8d, .size = 8, .activate = 6 }, +- +-/* Gen 3 devices: 4i4, 8i6, 18i8, 18i20 */ +-}, { +- [SCARLETT2_CONFIG_DIM_MUTE] = { +- .offset = 0x31, .size = 8, .activate = 2 }, +- +- [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = { +- .offset = 0x34, .size = 16, .activate = 1 }, +- +- [SCARLETT2_CONFIG_MUTE_SWITCH] = { +- .offset = 0x5c, .size = 8, .activate = 1 }, +- +- [SCARLETT2_CONFIG_SW_HW_SWITCH] = { +- .offset = 0x66, .size = 8, .activate = 3 }, +- +- [SCARLETT2_CONFIG_LEVEL_SWITCH] = { +- .offset = 0x7c, .size = 8, .activate = 7 }, +- +- [SCARLETT2_CONFIG_PAD_SWITCH] = { +- .offset = 0x84, .size = 8, .activate = 8 }, +- +- [SCARLETT2_CONFIG_AIR_SWITCH] = { +- .offset = 0x8c, .size = 8, .activate = 8 }, +- +- [SCARLETT2_CONFIG_STANDALONE_SWITCH] = { +- .offset = 0x95, .size = 8, .activate = 6 }, +- +- [SCARLETT2_CONFIG_PHANTOM_SWITCH] = { +- .offset = 0x9c, .size = 1, .activate = 8 }, +- +- [SCARLETT2_CONFIG_MSD_SWITCH] = { +- .offset = 0x9d, .size = 8, .activate = 6 }, +- +- [SCARLETT2_CONFIG_PHANTOM_PERSISTENCE] = { +- .offset = 0x9e, .size = 8, .activate = 6 }, +- +- [SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH] = { +- .offset = 0x9f, .size = 1, .activate = 10 }, +- +- [SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE] = { +- .offset = 0xa0, .size = 1, .activate = 10 }, +- +- [SCARLETT2_CONFIG_TALKBACK_MAP] = { +- .offset = 0xb0, .size = 16, .activate = 10 }, +- +-/* Clarett+ 8Pre */ +-}, { +- [SCARLETT2_CONFIG_DIM_MUTE] = { +- .offset = 0x31, .size = 8, .activate = 2 }, +- +- [SCARLETT2_CONFIG_LINE_OUT_VOLUME] = { +- .offset = 0x34, .size = 16, .activate = 1 }, +- +- [SCARLETT2_CONFIG_MUTE_SWITCH] = { +- .offset = 0x5c, .size = 8, .activate = 1 }, +- +- [SCARLETT2_CONFIG_SW_HW_SWITCH] = { +- .offset = 0x66, .size = 8, .activate = 3 }, +- +- [SCARLETT2_CONFIG_LEVEL_SWITCH] = { +- .offset = 0x7c, .size = 8, .activate = 7 }, +- +- [SCARLETT2_CONFIG_AIR_SWITCH] = { +- .offset = 0x95, .size = 8, .activate = 8 }, +- +- [SCARLETT2_CONFIG_STANDALONE_SWITCH] = { +- .offset = 0x8d, .size = 8, .activate = 6 }, +-} }; +- +-/* proprietary request/response format */ +-struct scarlett2_usb_packet { +- __le32 cmd; +- __le16 size; +- __le16 seq; +- __le32 error; +- __le32 pad; +- u8 data[]; +-}; +- +-static void scarlett2_fill_request_header(struct scarlett2_data *private, +- struct scarlett2_usb_packet *req, +- u32 cmd, u16 req_size) +-{ +- /* sequence must go up by 1 for each request */ +- u16 seq = private->scarlett2_seq++; +- +- req->cmd = cpu_to_le32(cmd); +- req->size = cpu_to_le16(req_size); +- req->seq = cpu_to_le16(seq); +- req->error = 0; +- req->pad = 0; +-} +- +-static int scarlett2_usb_tx(struct usb_device *dev, int interface, +- void *buf, u16 size) +-{ +- return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), +- SCARLETT2_USB_CMD_REQ, +- USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, +- 0, interface, buf, size); +-} +- +-static int scarlett2_usb_rx(struct usb_device *dev, int interface, +- u32 usb_req, void *buf, u16 size) +-{ +- return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), +- usb_req, +- USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, +- 0, interface, buf, size); +-} +- +-/* Send a proprietary format request to the Scarlett interface */ +-static int scarlett2_usb( +- struct usb_mixer_interface *mixer, u32 cmd, +- void *req_data, u16 req_size, void *resp_data, u16 resp_size) +-{ +- struct scarlett2_data *private = mixer->private_data; +- struct usb_device *dev = mixer->chip->dev; +- struct scarlett2_usb_packet *req, *resp = NULL; +- size_t req_buf_size = struct_size(req, data, req_size); +- size_t resp_buf_size = struct_size(resp, data, resp_size); +- int err; +- +- req = kmalloc(req_buf_size, GFP_KERNEL); +- if (!req) { +- err = -ENOMEM; +- goto error; +- } +- +- resp = kmalloc(resp_buf_size, GFP_KERNEL); +- if (!resp) { +- err = -ENOMEM; +- goto error; +- } +- +- mutex_lock(&private->usb_mutex); +- +- /* build request message and send it */ +- +- scarlett2_fill_request_header(private, req, cmd, req_size); +- +- if (req_size) +- memcpy(req->data, req_data, req_size); +- +- err = scarlett2_usb_tx(dev, private->bInterfaceNumber, +- req, req_buf_size); +- +- if (err != req_buf_size) { +- usb_audio_err( +- mixer->chip, +- "Scarlett Gen 2/3 USB request result cmd %x was %d\n", +- cmd, err); +- err = -EINVAL; +- goto unlock; +- } +- +- /* send a second message to get the response */ +- +- err = scarlett2_usb_rx(dev, private->bInterfaceNumber, +- SCARLETT2_USB_CMD_RESP, +- resp, resp_buf_size); +- +- /* validate the response */ +- +- if (err != resp_buf_size) { +- usb_audio_err( +- mixer->chip, +- "Scarlett Gen 2/3 USB response result cmd %x was %d " +- "expected %zu\n", +- cmd, err, resp_buf_size); +- err = -EINVAL; +- goto unlock; +- } +- +- /* cmd/seq/size should match except when initialising +- * seq sent = 1, response = 0 +- */ +- if (resp->cmd != req->cmd || +- (resp->seq != req->seq && +- (le16_to_cpu(req->seq) != 1 || resp->seq != 0)) || +- resp_size != le16_to_cpu(resp->size) || +- resp->error || +- resp->pad) { +- usb_audio_err( +- mixer->chip, +- "Scarlett Gen 2/3 USB invalid response; " +- "cmd tx/rx %d/%d seq %d/%d size %d/%d " +- "error %d pad %d\n", +- le32_to_cpu(req->cmd), le32_to_cpu(resp->cmd), +- le16_to_cpu(req->seq), le16_to_cpu(resp->seq), +- resp_size, le16_to_cpu(resp->size), +- le32_to_cpu(resp->error), +- le32_to_cpu(resp->pad)); +- err = -EINVAL; +- goto unlock; +- } +- +- if (resp_data && resp_size > 0) +- memcpy(resp_data, resp->data, resp_size); +- +-unlock: +- mutex_unlock(&private->usb_mutex); +-error: +- kfree(req); +- kfree(resp); +- return err; +-} +- +-/* Send a USB message to get data; result placed in *buf */ +-static int scarlett2_usb_get( +- struct usb_mixer_interface *mixer, +- int offset, void *buf, int size) +-{ +- struct { +- __le32 offset; +- __le32 size; +- } __packed req; +- +- req.offset = cpu_to_le32(offset); +- req.size = cpu_to_le32(size); +- return scarlett2_usb(mixer, SCARLETT2_USB_GET_DATA, +- &req, sizeof(req), buf, size); +-} +- +-/* Send a USB message to get configuration parameters; result placed in *buf */ +-static int scarlett2_usb_get_config( +- struct usb_mixer_interface *mixer, +- int config_item_num, int count, void *buf) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const struct scarlett2_config *config_item = +- &scarlett2_config_items[info->config_set][config_item_num]; +- int size, err, i; +- u8 *buf_8; +- u8 value; +- +- /* For byte-sized parameters, retrieve directly into buf */ +- if (config_item->size >= 8) { +- size = config_item->size / 8 * count; +- err = scarlett2_usb_get(mixer, config_item->offset, buf, size); +- if (err < 0) +- return err; +- if (size == 2) { +- u16 *buf_16 = buf; +- +- for (i = 0; i < count; i++, buf_16++) +- *buf_16 = le16_to_cpu(*(__le16 *)buf_16); +- } +- return 0; +- } +- +- /* For bit-sized parameters, retrieve into value */ +- err = scarlett2_usb_get(mixer, config_item->offset, &value, 1); +- if (err < 0) +- return err; +- +- /* then unpack from value into buf[] */ +- buf_8 = buf; +- for (i = 0; i < 8 && i < count; i++, value >>= 1) +- *buf_8++ = value & 1; +- +- return 0; +-} +- +-/* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */ +-static void scarlett2_config_save(struct usb_mixer_interface *mixer) +-{ +- __le32 req = cpu_to_le32(SCARLETT2_USB_CONFIG_SAVE); +- +- int err = scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD, +- &req, sizeof(u32), +- NULL, 0); +- if (err < 0) +- usb_audio_err(mixer->chip, "config save failed: %d\n", err); +-} +- +-/* Delayed work to save config */ +-static void scarlett2_config_save_work(struct work_struct *work) +-{ +- struct scarlett2_data *private = +- container_of(work, struct scarlett2_data, work.work); +- +- scarlett2_config_save(private->mixer); +-} +- +-/* Send a USB message to set a SCARLETT2_CONFIG_* parameter */ +-static int scarlett2_usb_set_config( +- struct usb_mixer_interface *mixer, +- int config_item_num, int index, int value) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const struct scarlett2_config *config_item = +- &scarlett2_config_items[info->config_set][config_item_num]; +- struct { +- __le32 offset; +- __le32 bytes; +- __le32 value; +- } __packed req; +- __le32 req2; +- int offset, size; +- int err; +- +- /* Cancel any pending NVRAM save */ +- cancel_delayed_work_sync(&private->work); +- +- /* Convert config_item->size in bits to size in bytes and +- * calculate offset +- */ +- if (config_item->size >= 8) { +- size = config_item->size / 8; +- offset = config_item->offset + index * size; +- +- /* If updating a bit, retrieve the old value, set/clear the +- * bit as needed, and update value +- */ +- } else { +- u8 tmp; +- +- size = 1; +- offset = config_item->offset; +- +- err = scarlett2_usb_get(mixer, offset, &tmp, 1); +- if (err < 0) +- return err; +- +- if (value) +- tmp |= (1 << index); +- else +- tmp &= ~(1 << index); +- +- value = tmp; +- } +- +- /* Send the configuration parameter data */ +- req.offset = cpu_to_le32(offset); +- req.bytes = cpu_to_le32(size); +- req.value = cpu_to_le32(value); +- err = scarlett2_usb(mixer, SCARLETT2_USB_SET_DATA, +- &req, sizeof(u32) * 2 + size, +- NULL, 0); +- if (err < 0) +- return err; +- +- /* Activate the change */ +- req2 = cpu_to_le32(config_item->activate); +- err = scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD, +- &req2, sizeof(req2), NULL, 0); +- if (err < 0) +- return err; +- +- /* Schedule the change to be written to NVRAM */ +- if (config_item->activate != SCARLETT2_USB_CONFIG_SAVE) +- schedule_delayed_work(&private->work, msecs_to_jiffies(2000)); +- +- return 0; +-} +- +-/* Send a USB message to get sync status; result placed in *sync */ +-static int scarlett2_usb_get_sync_status( +- struct usb_mixer_interface *mixer, +- u8 *sync) +-{ +- __le32 data; +- int err; +- +- err = scarlett2_usb(mixer, SCARLETT2_USB_GET_SYNC, +- NULL, 0, &data, sizeof(data)); +- if (err < 0) +- return err; +- +- *sync = !!data; +- return 0; +-} +- +-/* Send a USB message to get volume status; result placed in *buf */ +-static int scarlett2_usb_get_volume_status( +- struct usb_mixer_interface *mixer, +- struct scarlett2_usb_volume_status *buf) +-{ +- return scarlett2_usb_get(mixer, SCARLETT2_USB_VOLUME_STATUS_OFFSET, +- buf, sizeof(*buf)); +-} +- +-/* Send a USB message to get the volumes for all inputs of one mix +- * and put the values into private->mix[] +- */ +-static int scarlett2_usb_get_mix(struct usb_mixer_interface *mixer, +- int mix_num) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- +- int num_mixer_in = +- info->port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT]; +- int err, i, j, k; +- +- struct { +- __le16 mix_num; +- __le16 count; +- } __packed req; +- +- __le16 data[SCARLETT2_INPUT_MIX_MAX]; +- +- req.mix_num = cpu_to_le16(mix_num); +- req.count = cpu_to_le16(num_mixer_in); +- +- err = scarlett2_usb(mixer, SCARLETT2_USB_GET_MIX, +- &req, sizeof(req), +- data, num_mixer_in * sizeof(u16)); +- if (err < 0) +- return err; +- +- for (i = 0, j = mix_num * num_mixer_in; i < num_mixer_in; i++, j++) { +- u16 mixer_value = le16_to_cpu(data[i]); +- +- for (k = 0; k < SCARLETT2_MIXER_VALUE_COUNT; k++) +- if (scarlett2_mixer_values[k] >= mixer_value) +- break; +- if (k == SCARLETT2_MIXER_VALUE_COUNT) +- k = SCARLETT2_MIXER_MAX_VALUE; +- private->mix[j] = k; +- } +- +- return 0; +-} +- +-/* Send a USB message to set the volumes for all inputs of one mix +- * (values obtained from private->mix[]) +- */ +-static int scarlett2_usb_set_mix(struct usb_mixer_interface *mixer, +- int mix_num) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- +- struct { +- __le16 mix_num; +- __le16 data[SCARLETT2_INPUT_MIX_MAX]; +- } __packed req; +- +- int i, j; +- int num_mixer_in = +- info->port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT]; +- +- req.mix_num = cpu_to_le16(mix_num); +- +- for (i = 0, j = mix_num * num_mixer_in; i < num_mixer_in; i++, j++) +- req.data[i] = cpu_to_le16( +- scarlett2_mixer_values[private->mix[j]] +- ); +- +- return scarlett2_usb(mixer, SCARLETT2_USB_SET_MIX, +- &req, (num_mixer_in + 1) * sizeof(u16), +- NULL, 0); +-} +- +-/* Convert a port number index (per info->port_count) to a hardware ID */ +-static u32 scarlett2_mux_src_num_to_id( +- const int port_count[][SCARLETT2_PORT_DIRNS], int num) +-{ +- int port_type; +- +- for (port_type = 0; +- port_type < SCARLETT2_PORT_TYPE_COUNT; +- port_type++) { +- if (num < port_count[port_type][SCARLETT2_PORT_IN]) +- return scarlett2_ports[port_type].id | num; +- num -= port_count[port_type][SCARLETT2_PORT_IN]; +- } +- +- /* Oops */ +- return 0; +-} +- +-/* Convert a hardware ID to a port number index */ +-static u32 scarlett2_mux_id_to_num( +- const int port_count[][SCARLETT2_PORT_DIRNS], int direction, u32 id) +-{ +- int port_type; +- int port_num = 0; +- +- for (port_type = 0; +- port_type < SCARLETT2_PORT_TYPE_COUNT; +- port_type++) { +- int base = scarlett2_ports[port_type].id; +- int count = port_count[port_type][direction]; +- +- if (id >= base && id < base + count) +- return port_num + id - base; +- port_num += count; +- } +- +- /* Oops */ +- return -1; +-} +- +-/* Convert one mux entry from the interface and load into private->mux[] */ +-static void scarlett2_usb_populate_mux(struct scarlett2_data *private, +- u32 mux_entry) +-{ +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- +- int dst_idx, src_idx; +- +- dst_idx = scarlett2_mux_id_to_num(port_count, SCARLETT2_PORT_OUT, +- mux_entry & 0xFFF); +- if (dst_idx < 0) +- return; +- +- if (dst_idx >= private->num_mux_dsts) { +- usb_audio_err(private->mixer->chip, +- "BUG: scarlett2_mux_id_to_num(%06x, OUT): %d >= %d", +- mux_entry, dst_idx, private->num_mux_dsts); +- return; +- } +- +- src_idx = scarlett2_mux_id_to_num(port_count, SCARLETT2_PORT_IN, +- mux_entry >> 12); +- if (src_idx < 0) +- return; +- +- if (src_idx >= private->num_mux_srcs) { +- usb_audio_err(private->mixer->chip, +- "BUG: scarlett2_mux_id_to_num(%06x, IN): %d >= %d", +- mux_entry, src_idx, private->num_mux_srcs); +- return; +- } +- +- private->mux[dst_idx] = src_idx; +-} +- +-/* Send USB message to get mux inputs and then populate private->mux[] */ +-static int scarlett2_usb_get_mux(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- int count = private->num_mux_dsts; +- int err, i; +- +- struct { +- __le16 num; +- __le16 count; +- } __packed req; +- +- __le32 data[SCARLETT2_MUX_MAX]; +- +- private->mux_updated = 0; +- +- req.num = 0; +- req.count = cpu_to_le16(count); +- +- err = scarlett2_usb(mixer, SCARLETT2_USB_GET_MUX, +- &req, sizeof(req), +- data, count * sizeof(u32)); +- if (err < 0) +- return err; +- +- for (i = 0; i < count; i++) +- scarlett2_usb_populate_mux(private, le32_to_cpu(data[i])); +- +- return 0; +-} +- +-/* Send USB messages to set mux inputs */ +-static int scarlett2_usb_set_mux(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- int table; +- +- struct { +- __le16 pad; +- __le16 num; +- __le32 data[SCARLETT2_MUX_MAX]; +- } __packed req; +- +- req.pad = 0; +- +- /* set mux settings for each rate */ +- for (table = 0; table < SCARLETT2_MUX_TABLES; table++) { +- const struct scarlett2_mux_entry *entry; +- +- /* i counts over the output array */ +- int i = 0, err; +- +- req.num = cpu_to_le16(table); +- +- /* loop through each entry */ +- for (entry = info->mux_assignment[table]; +- entry->count; +- entry++) { +- int j; +- int port_type = entry->port_type; +- int port_idx = entry->start; +- int mux_idx = scarlett2_get_port_start_num(port_count, +- SCARLETT2_PORT_OUT, port_type) + port_idx; +- int dst_id = scarlett2_ports[port_type].id + port_idx; +- +- /* Empty slots */ +- if (!dst_id) { +- for (j = 0; j < entry->count; j++) +- req.data[i++] = 0; +- continue; +- } +- +- /* Non-empty mux slots use the lower 12 bits +- * for the destination and next 12 bits for +- * the source +- */ +- for (j = 0; j < entry->count; j++) { +- int src_id = scarlett2_mux_src_num_to_id( +- port_count, private->mux[mux_idx++]); +- req.data[i++] = cpu_to_le32(dst_id | +- src_id << 12); +- dst_id++; +- } +- } +- +- err = scarlett2_usb(mixer, SCARLETT2_USB_SET_MUX, +- &req, (i + 1) * sizeof(u32), +- NULL, 0); +- if (err < 0) +- return err; +- } +- +- return 0; +-} +- +-/* Send USB message to get meter levels */ +-static int scarlett2_usb_get_meter_levels(struct usb_mixer_interface *mixer, +- u16 num_meters, u16 *levels) +-{ +- struct { +- __le16 pad; +- __le16 num_meters; +- __le32 magic; +- } __packed req; +- u32 resp[SCARLETT2_MAX_METERS]; +- int i, err; +- +- req.pad = 0; +- req.num_meters = cpu_to_le16(num_meters); +- req.magic = cpu_to_le32(SCARLETT2_USB_METER_LEVELS_GET_MAGIC); +- err = scarlett2_usb(mixer, SCARLETT2_USB_GET_METER, +- &req, sizeof(req), resp, num_meters * sizeof(u32)); +- if (err < 0) +- return err; +- +- /* copy, convert to u16 */ +- for (i = 0; i < num_meters; i++) +- levels[i] = resp[i]; +- +- return 0; +-} +- +-/*** Control Functions ***/ +- +-/* helper function to create a new control */ +-static int scarlett2_add_new_ctl(struct usb_mixer_interface *mixer, +- const struct snd_kcontrol_new *ncontrol, +- int index, int channels, const char *name, +- struct snd_kcontrol **kctl_return) +-{ +- struct snd_kcontrol *kctl; +- struct usb_mixer_elem_info *elem; +- int err; +- +- elem = kzalloc(sizeof(*elem), GFP_KERNEL); +- if (!elem) +- return -ENOMEM; +- +- /* We set USB_MIXER_BESPOKEN type, so that the core USB mixer code +- * ignores them for resume and other operations. +- * Also, the head.id field is set to 0, as we don't use this field. +- */ +- elem->head.mixer = mixer; +- elem->control = index; +- elem->head.id = 0; +- elem->channels = channels; +- elem->val_type = USB_MIXER_BESPOKEN; +- +- kctl = snd_ctl_new1(ncontrol, elem); +- if (!kctl) { +- kfree(elem); +- return -ENOMEM; +- } +- kctl->private_free = snd_usb_mixer_elem_free; +- +- strscpy(kctl->id.name, name, sizeof(kctl->id.name)); +- +- err = snd_usb_mixer_add_control(&elem->head, kctl); +- if (err < 0) +- return err; +- +- if (kctl_return) +- *kctl_return = kctl; +- +- return 0; +-} +- +-/*** Sync Control ***/ +- +-/* Update sync control after receiving notification that the status +- * has changed +- */ +-static int scarlett2_update_sync(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- +- private->sync_updated = 0; +- return scarlett2_usb_get_sync_status(mixer, &private->sync); +-} +- +-static int scarlett2_sync_ctl_info(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_info *uinfo) +-{ +- static const char *texts[2] = { +- "Unlocked", "Locked" +- }; +- return snd_ctl_enum_info(uinfo, 1, 2, texts); +-} +- +-static int scarlett2_sync_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int err = 0; +- +- mutex_lock(&private->data_mutex); +- +- if (private->sync_updated) { +- err = scarlett2_update_sync(mixer); +- if (err < 0) +- goto unlock; +- } +- ucontrol->value.enumerated.item[0] = private->sync; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_sync_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .access = SNDRV_CTL_ELEM_ACCESS_READ, +- .name = "", +- .info = scarlett2_sync_ctl_info, +- .get = scarlett2_sync_ctl_get +-}; +- +-static int scarlett2_add_sync_ctl(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- +- /* devices without a mixer also don't support reporting sync status */ +- if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) +- return 0; +- +- return scarlett2_add_new_ctl(mixer, &scarlett2_sync_ctl, +- 0, 1, "Sync Status", &private->sync_ctl); +-} +- +-/*** Analogue Line Out Volume Controls ***/ +- +-/* Update hardware volume controls after receiving notification that +- * they have changed +- */ +-static int scarlett2_update_volumes(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- struct scarlett2_usb_volume_status volume_status; +- int num_line_out = +- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; +- int err, i; +- int mute; +- +- private->vol_updated = 0; +- +- err = scarlett2_usb_get_volume_status(mixer, &volume_status); +- if (err < 0) +- return err; +- +- private->master_vol = clamp( +- volume_status.master_vol + SCARLETT2_VOLUME_BIAS, +- 0, SCARLETT2_VOLUME_BIAS); +- +- if (info->line_out_hw_vol) +- for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) +- private->dim_mute[i] = !!volume_status.dim_mute[i]; +- +- mute = private->dim_mute[SCARLETT2_BUTTON_MUTE]; +- +- for (i = 0; i < num_line_out; i++) +- if (private->vol_sw_hw_switch[i]) { +- private->vol[i] = private->master_vol; +- private->mute_switch[i] = mute; +- } +- +- return 0; +-} +- +-static int scarlett2_volume_ctl_info(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_info *uinfo) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- +- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; +- uinfo->count = elem->channels; +- uinfo->value.integer.min = 0; +- uinfo->value.integer.max = SCARLETT2_VOLUME_BIAS; +- uinfo->value.integer.step = 1; +- return 0; +-} +- +-static int scarlett2_master_volume_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int err = 0; +- +- mutex_lock(&private->data_mutex); +- +- if (private->vol_updated) { +- err = scarlett2_update_volumes(mixer); +- if (err < 0) +- goto unlock; +- } +- ucontrol->value.integer.value[0] = private->master_vol; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static int line_out_remap(struct scarlett2_data *private, int index) +-{ +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- int line_out_count = +- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; +- +- if (!info->line_out_remap_enable) +- return index; +- +- if (index >= line_out_count) +- return index; +- +- return info->line_out_remap[index]; +-} +- +-static int scarlett2_volume_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int index = line_out_remap(private, elem->control); +- int err = 0; +- +- mutex_lock(&private->data_mutex); +- +- if (private->vol_updated) { +- err = scarlett2_update_volumes(mixer); +- if (err < 0) +- goto unlock; +- } +- ucontrol->value.integer.value[0] = private->vol[index]; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static int scarlett2_volume_ctl_put(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int index = line_out_remap(private, elem->control); +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->vol[index]; +- val = ucontrol->value.integer.value[0]; +- +- if (oval == val) +- goto unlock; +- +- private->vol[index] = val; +- err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME, +- index, val - SCARLETT2_VOLUME_BIAS); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const DECLARE_TLV_DB_MINMAX( +- db_scale_scarlett2_gain, -SCARLETT2_VOLUME_BIAS * 100, 0 +-); +- +-static const struct snd_kcontrol_new scarlett2_master_volume_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .access = SNDRV_CTL_ELEM_ACCESS_READ | +- SNDRV_CTL_ELEM_ACCESS_TLV_READ, +- .name = "", +- .info = scarlett2_volume_ctl_info, +- .get = scarlett2_master_volume_ctl_get, +- .private_value = 0, /* max value */ +- .tlv = { .p = db_scale_scarlett2_gain } +-}; +- +-static const struct snd_kcontrol_new scarlett2_line_out_volume_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | +- SNDRV_CTL_ELEM_ACCESS_TLV_READ, +- .name = "", +- .info = scarlett2_volume_ctl_info, +- .get = scarlett2_volume_ctl_get, +- .put = scarlett2_volume_ctl_put, +- .private_value = 0, /* max value */ +- .tlv = { .p = db_scale_scarlett2_gain } +-}; +- +-/*** Mute Switch Controls ***/ +- +-static int scarlett2_mute_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int index = line_out_remap(private, elem->control); +- int err = 0; +- +- mutex_lock(&private->data_mutex); +- +- if (private->vol_updated) { +- err = scarlett2_update_volumes(mixer); +- if (err < 0) +- goto unlock; +- } +- ucontrol->value.integer.value[0] = private->mute_switch[index]; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static int scarlett2_mute_ctl_put(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int index = line_out_remap(private, elem->control); +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->mute_switch[index]; +- val = !!ucontrol->value.integer.value[0]; +- +- if (oval == val) +- goto unlock; +- +- private->mute_switch[index] = val; +- +- /* Send mute change to the device */ +- err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_MUTE_SWITCH, +- index, val); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_mute_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = snd_ctl_boolean_mono_info, +- .get = scarlett2_mute_ctl_get, +- .put = scarlett2_mute_ctl_put, +-}; +- +-/*** HW/SW Volume Switch Controls ***/ +- +-static void scarlett2_sw_hw_ctl_ro(struct scarlett2_data *private, int index) +-{ +- private->sw_hw_ctls[index]->vd[0].access &= +- ~SNDRV_CTL_ELEM_ACCESS_WRITE; +-} +- +-static void scarlett2_sw_hw_ctl_rw(struct scarlett2_data *private, int index) +-{ +- private->sw_hw_ctls[index]->vd[0].access |= +- SNDRV_CTL_ELEM_ACCESS_WRITE; +-} +- +-static int scarlett2_sw_hw_enum_ctl_info(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_info *uinfo) +-{ +- static const char *const values[2] = { +- "SW", "HW" +- }; +- +- return snd_ctl_enum_info(uinfo, 1, 2, values); +-} +- +-static int scarlett2_sw_hw_enum_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct scarlett2_data *private = elem->head.mixer->private_data; +- int index = line_out_remap(private, elem->control); +- +- ucontrol->value.enumerated.item[0] = private->vol_sw_hw_switch[index]; +- return 0; +-} +- +-static void scarlett2_vol_ctl_set_writable(struct usb_mixer_interface *mixer, +- int index, int value) +-{ +- struct scarlett2_data *private = mixer->private_data; +- struct snd_card *card = mixer->chip->card; +- +- /* Set/Clear write bits */ +- if (value) { +- private->vol_ctls[index]->vd[0].access |= +- SNDRV_CTL_ELEM_ACCESS_WRITE; +- private->mute_ctls[index]->vd[0].access |= +- SNDRV_CTL_ELEM_ACCESS_WRITE; +- } else { +- private->vol_ctls[index]->vd[0].access &= +- ~SNDRV_CTL_ELEM_ACCESS_WRITE; +- private->mute_ctls[index]->vd[0].access &= +- ~SNDRV_CTL_ELEM_ACCESS_WRITE; +- } +- +- /* Notify of write bit and possible value change */ +- snd_ctl_notify(card, +- SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, +- &private->vol_ctls[index]->id); +- snd_ctl_notify(card, +- SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, +- &private->mute_ctls[index]->id); +-} +- +-static int scarlett2_sw_hw_change(struct usb_mixer_interface *mixer, +- int ctl_index, int val) +-{ +- struct scarlett2_data *private = mixer->private_data; +- int index = line_out_remap(private, ctl_index); +- int err; +- +- private->vol_sw_hw_switch[index] = val; +- +- /* Change access mode to RO (hardware controlled volume) +- * or RW (software controlled volume) +- */ +- scarlett2_vol_ctl_set_writable(mixer, ctl_index, !val); +- +- /* Reset volume/mute to master volume/mute */ +- private->vol[index] = private->master_vol; +- private->mute_switch[index] = private->dim_mute[SCARLETT2_BUTTON_MUTE]; +- +- /* Set SW volume to current HW volume */ +- err = scarlett2_usb_set_config( +- mixer, SCARLETT2_CONFIG_LINE_OUT_VOLUME, +- index, private->master_vol - SCARLETT2_VOLUME_BIAS); +- if (err < 0) +- return err; +- +- /* Set SW mute to current HW mute */ +- err = scarlett2_usb_set_config( +- mixer, SCARLETT2_CONFIG_MUTE_SWITCH, +- index, private->dim_mute[SCARLETT2_BUTTON_MUTE]); +- if (err < 0) +- return err; +- +- /* Send SW/HW switch change to the device */ +- return scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_SW_HW_SWITCH, +- index, val); +-} +- +-static int scarlett2_sw_hw_enum_ctl_put(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int ctl_index = elem->control; +- int index = line_out_remap(private, ctl_index); +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->vol_sw_hw_switch[index]; +- val = !!ucontrol->value.enumerated.item[0]; +- +- if (oval == val) +- goto unlock; +- +- err = scarlett2_sw_hw_change(mixer, ctl_index, val); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_sw_hw_enum_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = scarlett2_sw_hw_enum_ctl_info, +- .get = scarlett2_sw_hw_enum_ctl_get, +- .put = scarlett2_sw_hw_enum_ctl_put, +-}; +- +-/*** Line Level/Instrument Level Switch Controls ***/ +- +-static int scarlett2_update_input_other(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- +- private->input_other_updated = 0; +- +- if (info->level_input_count) { +- int err = scarlett2_usb_get_config( +- mixer, SCARLETT2_CONFIG_LEVEL_SWITCH, +- info->level_input_count + info->level_input_first, +- private->level_switch); +- if (err < 0) +- return err; +- } +- +- if (info->pad_input_count) { +- int err = scarlett2_usb_get_config( +- mixer, SCARLETT2_CONFIG_PAD_SWITCH, +- info->pad_input_count, private->pad_switch); +- if (err < 0) +- return err; +- } +- +- if (info->air_input_count) { +- int err = scarlett2_usb_get_config( +- mixer, SCARLETT2_CONFIG_AIR_SWITCH, +- info->air_input_count, private->air_switch); +- if (err < 0) +- return err; +- } +- +- if (info->phantom_count) { +- int err = scarlett2_usb_get_config( +- mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH, +- info->phantom_count, private->phantom_switch); +- if (err < 0) +- return err; +- +- err = scarlett2_usb_get_config( +- mixer, SCARLETT2_CONFIG_PHANTOM_PERSISTENCE, +- 1, &private->phantom_persistence); +- if (err < 0) +- return err; +- } +- +- return 0; +-} +- +-static int scarlett2_level_enum_ctl_info(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_info *uinfo) +-{ +- static const char *const values[2] = { +- "Line", "Inst" +- }; +- +- return snd_ctl_enum_info(uinfo, 1, 2, values); +-} +- +-static int scarlett2_level_enum_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- +- int index = elem->control + info->level_input_first; +- int err = 0; +- +- mutex_lock(&private->data_mutex); +- +- if (private->input_other_updated) { +- err = scarlett2_update_input_other(mixer); +- if (err < 0) +- goto unlock; +- } +- ucontrol->value.enumerated.item[0] = private->level_switch[index]; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static int scarlett2_level_enum_ctl_put(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- +- int index = elem->control + info->level_input_first; +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->level_switch[index]; +- val = !!ucontrol->value.enumerated.item[0]; +- +- if (oval == val) +- goto unlock; +- +- private->level_switch[index] = val; +- +- /* Send switch change to the device */ +- err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_LEVEL_SWITCH, +- index, val); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_level_enum_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = scarlett2_level_enum_ctl_info, +- .get = scarlett2_level_enum_ctl_get, +- .put = scarlett2_level_enum_ctl_put, +-}; +- +-/*** Pad Switch Controls ***/ +- +-static int scarlett2_pad_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int err = 0; +- +- mutex_lock(&private->data_mutex); +- +- if (private->input_other_updated) { +- err = scarlett2_update_input_other(mixer); +- if (err < 0) +- goto unlock; +- } +- ucontrol->value.integer.value[0] = +- private->pad_switch[elem->control]; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static int scarlett2_pad_ctl_put(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- +- int index = elem->control; +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->pad_switch[index]; +- val = !!ucontrol->value.integer.value[0]; +- +- if (oval == val) +- goto unlock; +- +- private->pad_switch[index] = val; +- +- /* Send switch change to the device */ +- err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PAD_SWITCH, +- index, val); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_pad_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = snd_ctl_boolean_mono_info, +- .get = scarlett2_pad_ctl_get, +- .put = scarlett2_pad_ctl_put, +-}; +- +-/*** Air Switch Controls ***/ +- +-static int scarlett2_air_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int err = 0; +- +- mutex_lock(&private->data_mutex); +- +- if (private->input_other_updated) { +- err = scarlett2_update_input_other(mixer); +- if (err < 0) +- goto unlock; +- } +- ucontrol->value.integer.value[0] = private->air_switch[elem->control]; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static int scarlett2_air_ctl_put(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- +- int index = elem->control; +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->air_switch[index]; +- val = !!ucontrol->value.integer.value[0]; +- +- if (oval == val) +- goto unlock; +- +- private->air_switch[index] = val; +- +- /* Send switch change to the device */ +- err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_AIR_SWITCH, +- index, val); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_air_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = snd_ctl_boolean_mono_info, +- .get = scarlett2_air_ctl_get, +- .put = scarlett2_air_ctl_put, +-}; +- +-/*** Phantom Switch Controls ***/ +- +-static int scarlett2_phantom_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int err = 0; +- +- mutex_lock(&private->data_mutex); +- +- if (private->input_other_updated) { +- err = scarlett2_update_input_other(mixer); +- if (err < 0) +- goto unlock; +- } +- ucontrol->value.integer.value[0] = +- private->phantom_switch[elem->control]; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static int scarlett2_phantom_ctl_put(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- +- int index = elem->control; +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->phantom_switch[index]; +- val = !!ucontrol->value.integer.value[0]; +- +- if (oval == val) +- goto unlock; +- +- private->phantom_switch[index] = val; +- +- /* Send switch change to the device */ +- err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_PHANTOM_SWITCH, +- index, val); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_phantom_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = snd_ctl_boolean_mono_info, +- .get = scarlett2_phantom_ctl_get, +- .put = scarlett2_phantom_ctl_put, +-}; +- +-/*** Phantom Persistence Control ***/ +- +-static int scarlett2_phantom_persistence_ctl_get( +- struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct scarlett2_data *private = elem->head.mixer->private_data; +- +- ucontrol->value.integer.value[0] = private->phantom_persistence; +- return 0; +-} +- +-static int scarlett2_phantom_persistence_ctl_put( +- struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- +- int index = elem->control; +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->phantom_persistence; +- val = !!ucontrol->value.integer.value[0]; +- +- if (oval == val) +- goto unlock; +- +- private->phantom_persistence = val; +- +- /* Send switch change to the device */ +- err = scarlett2_usb_set_config( +- mixer, SCARLETT2_CONFIG_PHANTOM_PERSISTENCE, index, val); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_phantom_persistence_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = snd_ctl_boolean_mono_info, +- .get = scarlett2_phantom_persistence_ctl_get, +- .put = scarlett2_phantom_persistence_ctl_put, +-}; +- +-/*** Direct Monitor Control ***/ +- +-static int scarlett2_update_monitor_other(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- int err; +- +- /* monitor_other_enable[0] enables speaker switching +- * monitor_other_enable[1] enables talkback +- */ +- u8 monitor_other_enable[2]; +- +- /* monitor_other_switch[0] activates the alternate speakers +- * monitor_other_switch[1] activates talkback +- */ +- u8 monitor_other_switch[2]; +- +- private->monitor_other_updated = 0; +- +- if (info->direct_monitor) +- return scarlett2_usb_get_config( +- mixer, SCARLETT2_CONFIG_DIRECT_MONITOR, +- 1, &private->direct_monitor_switch); +- +- /* if it doesn't do speaker switching then it also doesn't do +- * talkback +- */ +- if (!info->has_speaker_switching) +- return 0; +- +- err = scarlett2_usb_get_config( +- mixer, SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE, +- 2, monitor_other_enable); +- if (err < 0) +- return err; +- +- err = scarlett2_usb_get_config( +- mixer, SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH, +- 2, monitor_other_switch); +- if (err < 0) +- return err; +- +- if (!monitor_other_enable[0]) +- private->speaker_switching_switch = 0; +- else +- private->speaker_switching_switch = monitor_other_switch[0] + 1; +- +- if (info->has_talkback) { +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = +- info->port_count; +- int num_mixes = +- port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; +- u16 bitmap; +- int i; +- +- if (!monitor_other_enable[1]) +- private->talkback_switch = 0; +- else +- private->talkback_switch = monitor_other_switch[1] + 1; +- +- err = scarlett2_usb_get_config(mixer, +- SCARLETT2_CONFIG_TALKBACK_MAP, +- 1, &bitmap); +- if (err < 0) +- return err; +- for (i = 0; i < num_mixes; i++, bitmap >>= 1) +- private->talkback_map[i] = bitmap & 1; +- } +- +- return 0; +-} +- +-static int scarlett2_direct_monitor_ctl_get( +- struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = elem->head.mixer->private_data; +- int err = 0; +- +- mutex_lock(&private->data_mutex); +- +- if (private->monitor_other_updated) { +- err = scarlett2_update_monitor_other(mixer); +- if (err < 0) +- goto unlock; +- } +- ucontrol->value.enumerated.item[0] = private->direct_monitor_switch; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static int scarlett2_direct_monitor_ctl_put( +- struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- +- int index = elem->control; +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->direct_monitor_switch; +- val = min(ucontrol->value.enumerated.item[0], 2U); +- +- if (oval == val) +- goto unlock; +- +- private->direct_monitor_switch = val; +- +- /* Send switch change to the device */ +- err = scarlett2_usb_set_config( +- mixer, SCARLETT2_CONFIG_DIRECT_MONITOR, index, val); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static int scarlett2_direct_monitor_stereo_enum_ctl_info( +- struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +-{ +- static const char *const values[3] = { +- "Off", "Mono", "Stereo" +- }; +- +- return snd_ctl_enum_info(uinfo, 1, 3, values); +-} +- +-/* Direct Monitor for Solo is mono-only and only needs a boolean control +- * Direct Monitor for 2i2 is selectable between Off/Mono/Stereo +- */ +-static const struct snd_kcontrol_new scarlett2_direct_monitor_ctl[2] = { +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = snd_ctl_boolean_mono_info, +- .get = scarlett2_direct_monitor_ctl_get, +- .put = scarlett2_direct_monitor_ctl_put, +- }, +- { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = scarlett2_direct_monitor_stereo_enum_ctl_info, +- .get = scarlett2_direct_monitor_ctl_get, +- .put = scarlett2_direct_monitor_ctl_put, +- } +-}; +- +-static int scarlett2_add_direct_monitor_ctl(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const char *s; +- +- if (!info->direct_monitor) +- return 0; +- +- s = info->direct_monitor == 1 +- ? "Direct Monitor Playback Switch" +- : "Direct Monitor Playback Enum"; +- +- return scarlett2_add_new_ctl( +- mixer, &scarlett2_direct_monitor_ctl[info->direct_monitor - 1], +- 0, 1, s, &private->direct_monitor_ctl); +-} +- +-/*** Speaker Switching Control ***/ +- +-static int scarlett2_speaker_switch_enum_ctl_info( +- struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +-{ +- static const char *const values[3] = { +- "Off", "Main", "Alt" +- }; +- +- return snd_ctl_enum_info(uinfo, 1, 3, values); +-} +- +-static int scarlett2_speaker_switch_enum_ctl_get( +- struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int err = 0; +- +- mutex_lock(&private->data_mutex); +- +- if (private->monitor_other_updated) { +- err = scarlett2_update_monitor_other(mixer); +- if (err < 0) +- goto unlock; +- } +- ucontrol->value.enumerated.item[0] = private->speaker_switching_switch; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-/* when speaker switching gets enabled, switch the main/alt speakers +- * to HW volume and disable those controls +- */ +-static int scarlett2_speaker_switch_enable(struct usb_mixer_interface *mixer) +-{ +- struct snd_card *card = mixer->chip->card; +- struct scarlett2_data *private = mixer->private_data; +- int i, err; +- +- for (i = 0; i < 4; i++) { +- int index = line_out_remap(private, i); +- +- /* switch the main/alt speakers to HW volume */ +- if (!private->vol_sw_hw_switch[index]) { +- err = scarlett2_sw_hw_change(private->mixer, i, 1); +- if (err < 0) +- return err; +- } +- +- /* disable the line out SW/HW switch */ +- scarlett2_sw_hw_ctl_ro(private, i); +- snd_ctl_notify(card, +- SNDRV_CTL_EVENT_MASK_VALUE | +- SNDRV_CTL_EVENT_MASK_INFO, +- &private->sw_hw_ctls[i]->id); +- } +- +- /* when the next monitor-other notify comes in, update the mux +- * configuration +- */ +- private->speaker_switching_switched = 1; +- +- return 0; +-} +- +-/* when speaker switching gets disabled, reenable the hw/sw controls +- * and invalidate the routing +- */ +-static void scarlett2_speaker_switch_disable(struct usb_mixer_interface *mixer) +-{ +- struct snd_card *card = mixer->chip->card; +- struct scarlett2_data *private = mixer->private_data; +- int i; +- +- /* enable the line out SW/HW switch */ +- for (i = 0; i < 4; i++) { +- scarlett2_sw_hw_ctl_rw(private, i); +- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, +- &private->sw_hw_ctls[i]->id); +- } +- +- /* when the next monitor-other notify comes in, update the mux +- * configuration +- */ +- private->speaker_switching_switched = 1; +-} +- +-static int scarlett2_speaker_switch_enum_ctl_put( +- struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->speaker_switching_switch; +- val = min(ucontrol->value.enumerated.item[0], 2U); +- +- if (oval == val) +- goto unlock; +- +- private->speaker_switching_switch = val; +- +- /* enable/disable speaker switching */ +- err = scarlett2_usb_set_config( +- mixer, SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE, +- 0, !!val); +- if (err < 0) +- goto unlock; +- +- /* if speaker switching is enabled, select main or alt */ +- err = scarlett2_usb_set_config( +- mixer, SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH, +- 0, val == 2); +- if (err < 0) +- goto unlock; +- +- /* update controls if speaker switching gets enabled or disabled */ +- if (!oval && val) +- err = scarlett2_speaker_switch_enable(mixer); +- else if (oval && !val) +- scarlett2_speaker_switch_disable(mixer); +- +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_speaker_switch_enum_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = scarlett2_speaker_switch_enum_ctl_info, +- .get = scarlett2_speaker_switch_enum_ctl_get, +- .put = scarlett2_speaker_switch_enum_ctl_put, +-}; +- +-static int scarlett2_add_speaker_switch_ctl( +- struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- +- if (!info->has_speaker_switching) +- return 0; +- +- return scarlett2_add_new_ctl( +- mixer, &scarlett2_speaker_switch_enum_ctl, +- 0, 1, "Speaker Switching Playback Enum", +- &private->speaker_switching_ctl); +-} +- +-/*** Talkback and Talkback Map Controls ***/ +- +-static int scarlett2_talkback_enum_ctl_info( +- struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) +-{ +- static const char *const values[3] = { +- "Disabled", "Off", "On" +- }; +- +- return snd_ctl_enum_info(uinfo, 1, 3, values); +-} +- +-static int scarlett2_talkback_enum_ctl_get( +- struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int err = 0; +- +- mutex_lock(&private->data_mutex); +- +- if (private->monitor_other_updated) { +- err = scarlett2_update_monitor_other(mixer); +- if (err < 0) +- goto unlock; +- } +- ucontrol->value.enumerated.item[0] = private->talkback_switch; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static int scarlett2_talkback_enum_ctl_put( +- struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->talkback_switch; +- val = min(ucontrol->value.enumerated.item[0], 2U); +- +- if (oval == val) +- goto unlock; +- +- private->talkback_switch = val; +- +- /* enable/disable talkback */ +- err = scarlett2_usb_set_config( +- mixer, SCARLETT2_CONFIG_MONITOR_OTHER_ENABLE, +- 1, !!val); +- if (err < 0) +- goto unlock; +- +- /* if talkback is enabled, select main or alt */ +- err = scarlett2_usb_set_config( +- mixer, SCARLETT2_CONFIG_MONITOR_OTHER_SWITCH, +- 1, val == 2); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_talkback_enum_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = scarlett2_talkback_enum_ctl_info, +- .get = scarlett2_talkback_enum_ctl_get, +- .put = scarlett2_talkback_enum_ctl_put, +-}; +- +-static int scarlett2_talkback_map_ctl_get( +- struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int index = elem->control; +- +- ucontrol->value.integer.value[0] = private->talkback_map[index]; +- +- return 0; +-} +- +-static int scarlett2_talkback_map_ctl_put( +- struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = +- private->info->port_count; +- int num_mixes = port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; +- +- int index = elem->control; +- int oval, val, err = 0, i; +- u16 bitmap = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->talkback_map[index]; +- val = !!ucontrol->value.integer.value[0]; +- +- if (oval == val) +- goto unlock; +- +- private->talkback_map[index] = val; +- +- for (i = 0; i < num_mixes; i++) +- bitmap |= private->talkback_map[i] << i; +- +- /* Send updated bitmap to the device */ +- err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_TALKBACK_MAP, +- 0, bitmap); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_talkback_map_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = snd_ctl_boolean_mono_info, +- .get = scarlett2_talkback_map_ctl_get, +- .put = scarlett2_talkback_map_ctl_put, +-}; +- +-static int scarlett2_add_talkback_ctls( +- struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- int num_mixes = port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; +- int err, i; +- char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; +- +- if (!info->has_talkback) +- return 0; +- +- err = scarlett2_add_new_ctl( +- mixer, &scarlett2_talkback_enum_ctl, +- 0, 1, "Talkback Playback Enum", +- &private->talkback_ctl); +- if (err < 0) +- return err; +- +- for (i = 0; i < num_mixes; i++) { +- snprintf(s, sizeof(s), +- "Talkback Mix %c Playback Switch", i + 'A'); +- err = scarlett2_add_new_ctl(mixer, &scarlett2_talkback_map_ctl, +- i, 1, s, NULL); +- if (err < 0) +- return err; +- } +- +- return 0; +-} +- +-/*** Dim/Mute Controls ***/ +- +-static int scarlett2_dim_mute_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int err = 0; +- +- mutex_lock(&private->data_mutex); +- +- if (private->vol_updated) { +- err = scarlett2_update_volumes(mixer); +- if (err < 0) +- goto unlock; +- } +- ucontrol->value.integer.value[0] = private->dim_mute[elem->control]; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static int scarlett2_dim_mute_ctl_put(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- int num_line_out = +- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; +- +- int index = elem->control; +- int oval, val, err = 0, i; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->dim_mute[index]; +- val = !!ucontrol->value.integer.value[0]; +- +- if (oval == val) +- goto unlock; +- +- private->dim_mute[index] = val; +- +- /* Send switch change to the device */ +- err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_DIM_MUTE, +- index, val); +- if (err == 0) +- err = 1; +- +- if (index == SCARLETT2_BUTTON_MUTE) +- for (i = 0; i < num_line_out; i++) { +- int line_index = line_out_remap(private, i); +- +- if (private->vol_sw_hw_switch[line_index]) { +- private->mute_switch[line_index] = val; +- snd_ctl_notify(mixer->chip->card, +- SNDRV_CTL_EVENT_MASK_VALUE, +- &private->mute_ctls[i]->id); +- } +- } +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_dim_mute_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = snd_ctl_boolean_mono_info, +- .get = scarlett2_dim_mute_ctl_get, +- .put = scarlett2_dim_mute_ctl_put +-}; +- +-/*** Create the analogue output controls ***/ +- +-static int scarlett2_add_line_out_ctls(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- int num_line_out = +- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; +- int err, i; +- char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; +- +- /* Add R/O HW volume control */ +- if (info->line_out_hw_vol) { +- snprintf(s, sizeof(s), "Master HW Playback Volume"); +- err = scarlett2_add_new_ctl(mixer, +- &scarlett2_master_volume_ctl, +- 0, 1, s, &private->master_vol_ctl); +- if (err < 0) +- return err; +- } +- +- /* Add volume controls */ +- for (i = 0; i < num_line_out; i++) { +- int index = line_out_remap(private, i); +- +- /* Fader */ +- if (info->line_out_descrs[i]) +- snprintf(s, sizeof(s), +- "Line %02d (%s) Playback Volume", +- i + 1, info->line_out_descrs[i]); +- else +- snprintf(s, sizeof(s), +- "Line %02d Playback Volume", +- i + 1); +- err = scarlett2_add_new_ctl(mixer, +- &scarlett2_line_out_volume_ctl, +- i, 1, s, &private->vol_ctls[i]); +- if (err < 0) +- return err; +- +- /* Mute Switch */ +- snprintf(s, sizeof(s), +- "Line %02d Mute Playback Switch", +- i + 1); +- err = scarlett2_add_new_ctl(mixer, +- &scarlett2_mute_ctl, +- i, 1, s, +- &private->mute_ctls[i]); +- if (err < 0) +- return err; +- +- /* Make the fader and mute controls read-only if the +- * SW/HW switch is set to HW +- */ +- if (private->vol_sw_hw_switch[index]) +- scarlett2_vol_ctl_set_writable(mixer, i, 0); +- +- /* SW/HW Switch */ +- if (info->line_out_hw_vol) { +- snprintf(s, sizeof(s), +- "Line Out %02d Volume Control Playback Enum", +- i + 1); +- err = scarlett2_add_new_ctl(mixer, +- &scarlett2_sw_hw_enum_ctl, +- i, 1, s, +- &private->sw_hw_ctls[i]); +- if (err < 0) +- return err; +- +- /* Make the switch read-only if the line is +- * involved in speaker switching +- */ +- if (private->speaker_switching_switch && i < 4) +- scarlett2_sw_hw_ctl_ro(private, i); +- } +- } +- +- /* Add dim/mute controls */ +- if (info->line_out_hw_vol) +- for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) { +- err = scarlett2_add_new_ctl( +- mixer, &scarlett2_dim_mute_ctl, +- i, 1, scarlett2_dim_mute_names[i], +- &private->dim_mute_ctls[i]); +- if (err < 0) +- return err; +- } +- +- return 0; +-} +- +-/*** Create the analogue input controls ***/ +- +-static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- int err, i; +- char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; +- const char *fmt = "Line In %d %s Capture %s"; +- const char *fmt2 = "Line In %d-%d %s Capture %s"; +- +- /* Add input level (line/inst) controls */ +- for (i = 0; i < info->level_input_count; i++) { +- snprintf(s, sizeof(s), fmt, i + 1 + info->level_input_first, +- "Level", "Enum"); +- err = scarlett2_add_new_ctl(mixer, &scarlett2_level_enum_ctl, +- i, 1, s, &private->level_ctls[i]); +- if (err < 0) +- return err; +- } +- +- /* Add input pad controls */ +- for (i = 0; i < info->pad_input_count; i++) { +- snprintf(s, sizeof(s), fmt, i + 1, "Pad", "Switch"); +- err = scarlett2_add_new_ctl(mixer, &scarlett2_pad_ctl, +- i, 1, s, &private->pad_ctls[i]); +- if (err < 0) +- return err; +- } +- +- /* Add input air controls */ +- for (i = 0; i < info->air_input_count; i++) { +- snprintf(s, sizeof(s), fmt, i + 1, "Air", "Switch"); +- err = scarlett2_add_new_ctl(mixer, &scarlett2_air_ctl, +- i, 1, s, &private->air_ctls[i]); +- if (err < 0) +- return err; +- } +- +- /* Add input phantom controls */ +- if (info->inputs_per_phantom == 1) { +- for (i = 0; i < info->phantom_count; i++) { +- scnprintf(s, sizeof(s), fmt, i + 1, +- "Phantom Power", "Switch"); +- err = scarlett2_add_new_ctl( +- mixer, &scarlett2_phantom_ctl, +- i, 1, s, &private->phantom_ctls[i]); +- if (err < 0) +- return err; +- } +- } else if (info->inputs_per_phantom > 1) { +- for (i = 0; i < info->phantom_count; i++) { +- int from = i * info->inputs_per_phantom + 1; +- int to = (i + 1) * info->inputs_per_phantom; +- +- scnprintf(s, sizeof(s), fmt2, from, to, +- "Phantom Power", "Switch"); +- err = scarlett2_add_new_ctl( +- mixer, &scarlett2_phantom_ctl, +- i, 1, s, &private->phantom_ctls[i]); +- if (err < 0) +- return err; +- } +- } +- if (info->phantom_count) { +- err = scarlett2_add_new_ctl( +- mixer, &scarlett2_phantom_persistence_ctl, 0, 1, +- "Phantom Power Persistence Capture Switch", NULL); +- if (err < 0) +- return err; +- } +- +- return 0; +-} +- +-/*** Mixer Volume Controls ***/ +- +-static int scarlett2_mixer_ctl_info(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_info *uinfo) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- +- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; +- uinfo->count = elem->channels; +- uinfo->value.integer.min = 0; +- uinfo->value.integer.max = SCARLETT2_MIXER_MAX_VALUE; +- uinfo->value.integer.step = 1; +- return 0; +-} +- +-static int scarlett2_mixer_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct scarlett2_data *private = elem->head.mixer->private_data; +- +- ucontrol->value.integer.value[0] = private->mix[elem->control]; +- return 0; +-} +- +-static int scarlett2_mixer_ctl_put(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- int oval, val, num_mixer_in, mix_num, err = 0; +- int index = elem->control; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->mix[index]; +- val = clamp(ucontrol->value.integer.value[0], +- 0L, (long)SCARLETT2_MIXER_MAX_VALUE); +- num_mixer_in = port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT]; +- mix_num = index / num_mixer_in; +- +- if (oval == val) +- goto unlock; +- +- private->mix[index] = val; +- err = scarlett2_usb_set_mix(mixer, mix_num); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const DECLARE_TLV_DB_MINMAX( +- db_scale_scarlett2_mixer, +- SCARLETT2_MIXER_MIN_DB * 100, +- SCARLETT2_MIXER_MAX_DB * 100 +-); +- +-static const struct snd_kcontrol_new scarlett2_mixer_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | +- SNDRV_CTL_ELEM_ACCESS_TLV_READ, +- .name = "", +- .info = scarlett2_mixer_ctl_info, +- .get = scarlett2_mixer_ctl_get, +- .put = scarlett2_mixer_ctl_put, +- .private_value = SCARLETT2_MIXER_MAX_DB, /* max value */ +- .tlv = { .p = db_scale_scarlett2_mixer } +-}; +- +-static int scarlett2_add_mixer_ctls(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- int err, i, j; +- int index; +- char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; +- +- int num_inputs = +- port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_OUT]; +- int num_outputs = +- port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; +- +- for (i = 0, index = 0; i < num_outputs; i++) +- for (j = 0; j < num_inputs; j++, index++) { +- snprintf(s, sizeof(s), +- "Mix %c Input %02d Playback Volume", +- 'A' + i, j + 1); +- err = scarlett2_add_new_ctl(mixer, &scarlett2_mixer_ctl, +- index, 1, s, NULL); +- if (err < 0) +- return err; +- } +- +- return 0; +-} +- +-/*** Mux Source Selection Controls ***/ +- +-static int scarlett2_mux_src_enum_ctl_info(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_info *uinfo) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct scarlett2_data *private = elem->head.mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- unsigned int item = uinfo->value.enumerated.item; +- int items = private->num_mux_srcs; +- int port_type; +- +- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; +- uinfo->count = elem->channels; +- uinfo->value.enumerated.items = items; +- +- if (item >= items) +- item = uinfo->value.enumerated.item = items - 1; +- +- for (port_type = 0; +- port_type < SCARLETT2_PORT_TYPE_COUNT; +- port_type++) { +- if (item < port_count[port_type][SCARLETT2_PORT_IN]) { +- const struct scarlett2_port *port = +- &scarlett2_ports[port_type]; +- +- sprintf(uinfo->value.enumerated.name, +- port->src_descr, item + port->src_num_offset); +- return 0; +- } +- item -= port_count[port_type][SCARLETT2_PORT_IN]; +- } +- +- return -EINVAL; +-} +- +-static int scarlett2_mux_src_enum_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int index = line_out_remap(private, elem->control); +- int err = 0; +- +- mutex_lock(&private->data_mutex); +- +- if (private->mux_updated) { +- err = scarlett2_usb_get_mux(mixer); +- if (err < 0) +- goto unlock; +- } +- ucontrol->value.enumerated.item[0] = private->mux[index]; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static int scarlett2_mux_src_enum_ctl_put(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- int index = line_out_remap(private, elem->control); +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->mux[index]; +- val = min(ucontrol->value.enumerated.item[0], +- private->num_mux_srcs - 1U); +- +- if (oval == val) +- goto unlock; +- +- private->mux[index] = val; +- err = scarlett2_usb_set_mux(mixer); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_mux_src_enum_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = scarlett2_mux_src_enum_ctl_info, +- .get = scarlett2_mux_src_enum_ctl_get, +- .put = scarlett2_mux_src_enum_ctl_put, +-}; +- +-static int scarlett2_add_mux_enums(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- int port_type, channel, i; +- +- for (i = 0, port_type = 0; +- port_type < SCARLETT2_PORT_TYPE_COUNT; +- port_type++) { +- for (channel = 0; +- channel < port_count[port_type][SCARLETT2_PORT_OUT]; +- channel++, i++) { +- int err; +- char s[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; +- const char *const descr = +- scarlett2_ports[port_type].dst_descr; +- +- snprintf(s, sizeof(s) - 5, descr, channel + 1); +- strcat(s, " Enum"); +- +- err = scarlett2_add_new_ctl(mixer, +- &scarlett2_mux_src_enum_ctl, +- i, 1, s, +- &private->mux_ctls[i]); +- if (err < 0) +- return err; +- } +- } +- +- return 0; +-} +- +-/*** Meter Controls ***/ +- +-static int scarlett2_meter_ctl_info(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_info *uinfo) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- +- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; +- uinfo->count = elem->channels; +- uinfo->value.integer.min = 0; +- uinfo->value.integer.max = 4095; +- uinfo->value.integer.step = 1; +- return 0; +-} +- +-static int scarlett2_meter_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- u16 meter_levels[SCARLETT2_MAX_METERS]; +- int i, err; +- +- err = scarlett2_usb_get_meter_levels(elem->head.mixer, elem->channels, +- meter_levels); +- if (err < 0) +- return err; +- +- for (i = 0; i < elem->channels; i++) +- ucontrol->value.integer.value[i] = meter_levels[i]; +- +- return 0; +-} +- +-static const struct snd_kcontrol_new scarlett2_meter_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_PCM, +- .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, +- .name = "", +- .info = scarlett2_meter_ctl_info, +- .get = scarlett2_meter_ctl_get +-}; +- +-static int scarlett2_add_meter_ctl(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- +- /* devices without a mixer also don't support reporting levels */ +- if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) +- return 0; +- +- return scarlett2_add_new_ctl(mixer, &scarlett2_meter_ctl, +- 0, private->num_mux_dsts, +- "Level Meter", NULL); +-} +- +-/*** MSD Controls ***/ +- +-static int scarlett2_msd_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct scarlett2_data *private = elem->head.mixer->private_data; +- +- ucontrol->value.integer.value[0] = private->msd_switch; +- return 0; +-} +- +-static int scarlett2_msd_ctl_put(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->msd_switch; +- val = !!ucontrol->value.integer.value[0]; +- +- if (oval == val) +- goto unlock; +- +- private->msd_switch = val; +- +- /* Send switch change to the device */ +- err = scarlett2_usb_set_config(mixer, SCARLETT2_CONFIG_MSD_SWITCH, +- 0, val); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_msd_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = snd_ctl_boolean_mono_info, +- .get = scarlett2_msd_ctl_get, +- .put = scarlett2_msd_ctl_put, +-}; +- +-static int scarlett2_add_msd_ctl(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- +- if (!info->has_msd_mode) +- return 0; +- +- /* If MSD mode is off, hide the switch by default */ +- if (!private->msd_switch && !(mixer->chip->setup & SCARLETT2_MSD_ENABLE)) +- return 0; +- +- /* Add MSD control */ +- return scarlett2_add_new_ctl(mixer, &scarlett2_msd_ctl, +- 0, 1, "MSD Mode Switch", NULL); +-} +- +-/*** Standalone Control ***/ +- +-static int scarlett2_standalone_ctl_get(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct scarlett2_data *private = elem->head.mixer->private_data; +- +- ucontrol->value.integer.value[0] = private->standalone_switch; +- return 0; +-} +- +-static int scarlett2_standalone_ctl_put(struct snd_kcontrol *kctl, +- struct snd_ctl_elem_value *ucontrol) +-{ +- struct usb_mixer_elem_info *elem = kctl->private_data; +- struct usb_mixer_interface *mixer = elem->head.mixer; +- struct scarlett2_data *private = mixer->private_data; +- +- int oval, val, err = 0; +- +- mutex_lock(&private->data_mutex); +- +- oval = private->standalone_switch; +- val = !!ucontrol->value.integer.value[0]; +- +- if (oval == val) +- goto unlock; +- +- private->standalone_switch = val; +- +- /* Send switch change to the device */ +- err = scarlett2_usb_set_config(mixer, +- SCARLETT2_CONFIG_STANDALONE_SWITCH, +- 0, val); +- if (err == 0) +- err = 1; +- +-unlock: +- mutex_unlock(&private->data_mutex); +- return err; +-} +- +-static const struct snd_kcontrol_new scarlett2_standalone_ctl = { +- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, +- .name = "", +- .info = snd_ctl_boolean_mono_info, +- .get = scarlett2_standalone_ctl_get, +- .put = scarlett2_standalone_ctl_put, +-}; +- +-static int scarlett2_add_standalone_ctl(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- +- if (private->info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) +- return 0; +- +- /* Add standalone control */ +- return scarlett2_add_new_ctl(mixer, &scarlett2_standalone_ctl, +- 0, 1, "Standalone Switch", NULL); +-} +- +-/*** Cleanup/Suspend Callbacks ***/ +- +-static void scarlett2_private_free(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- +- cancel_delayed_work_sync(&private->work); +- kfree(private); +- mixer->private_data = NULL; +-} +- +-static void scarlett2_private_suspend(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- +- if (cancel_delayed_work_sync(&private->work)) +- scarlett2_config_save(private->mixer); +-} +- +-/*** Initialisation ***/ +- +-static void scarlett2_count_mux_io(struct scarlett2_data *private) +-{ +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- int port_type, srcs = 0, dsts = 0; +- +- for (port_type = 0; +- port_type < SCARLETT2_PORT_TYPE_COUNT; +- port_type++) { +- srcs += port_count[port_type][SCARLETT2_PORT_IN]; +- dsts += port_count[port_type][SCARLETT2_PORT_OUT]; +- } +- +- private->num_mux_srcs = srcs; +- private->num_mux_dsts = dsts; +-} +- +-/* Look through the interface descriptors for the Focusrite Control +- * interface (bInterfaceClass = 255 Vendor Specific Class) and set +- * bInterfaceNumber, bEndpointAddress, wMaxPacketSize, and bInterval +- * in private +- */ +-static int scarlett2_find_fc_interface(struct usb_device *dev, +- struct scarlett2_data *private) +-{ +- struct usb_host_config *config = dev->actconfig; +- int i; +- +- for (i = 0; i < config->desc.bNumInterfaces; i++) { +- struct usb_interface *intf = config->interface[i]; +- struct usb_interface_descriptor *desc = +- &intf->altsetting[0].desc; +- struct usb_endpoint_descriptor *epd; +- +- if (desc->bInterfaceClass != 255) +- continue; +- +- epd = get_endpoint(intf->altsetting, 0); +- private->bInterfaceNumber = desc->bInterfaceNumber; +- private->bEndpointAddress = epd->bEndpointAddress & +- USB_ENDPOINT_NUMBER_MASK; +- private->wMaxPacketSize = le16_to_cpu(epd->wMaxPacketSize); +- private->bInterval = epd->bInterval; +- return 0; +- } +- +- return -EINVAL; +-} +- +-/* Initialise private data */ +-static int scarlett2_init_private(struct usb_mixer_interface *mixer, +- const struct scarlett2_device_info *info) +-{ +- struct scarlett2_data *private = +- kzalloc(sizeof(struct scarlett2_data), GFP_KERNEL); +- +- if (!private) +- return -ENOMEM; +- +- mutex_init(&private->usb_mutex); +- mutex_init(&private->data_mutex); +- INIT_DELAYED_WORK(&private->work, scarlett2_config_save_work); +- +- mixer->private_data = private; +- mixer->private_free = scarlett2_private_free; +- mixer->private_suspend = scarlett2_private_suspend; +- +- private->info = info; +- scarlett2_count_mux_io(private); +- private->scarlett2_seq = 0; +- private->mixer = mixer; +- +- return scarlett2_find_fc_interface(mixer->chip->dev, private); +-} +- +-/* Cargo cult proprietary initialisation sequence */ +-static int scarlett2_usb_init(struct usb_mixer_interface *mixer) +-{ +- struct usb_device *dev = mixer->chip->dev; +- struct scarlett2_data *private = mixer->private_data; +- u8 buf[24]; +- int err; +- +- if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0))) +- return -EINVAL; +- +- /* step 0 */ +- err = scarlett2_usb_rx(dev, private->bInterfaceNumber, +- SCARLETT2_USB_CMD_INIT, buf, sizeof(buf)); +- if (err < 0) +- return err; +- +- /* step 1 */ +- private->scarlett2_seq = 1; +- err = scarlett2_usb(mixer, SCARLETT2_USB_INIT_1, NULL, 0, NULL, 0); +- if (err < 0) +- return err; +- +- /* step 2 */ +- private->scarlett2_seq = 1; +- return scarlett2_usb(mixer, SCARLETT2_USB_INIT_2, NULL, 0, NULL, 84); +-} +- +-/* Read configuration from the interface on start */ +-static int scarlett2_read_configs(struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- int num_line_out = +- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; +- int num_mixer_out = +- port_count[SCARLETT2_PORT_TYPE_MIX][SCARLETT2_PORT_IN]; +- struct scarlett2_usb_volume_status volume_status; +- int err, i; +- +- if (info->has_msd_mode) { +- err = scarlett2_usb_get_config( +- mixer, SCARLETT2_CONFIG_MSD_SWITCH, +- 1, &private->msd_switch); +- if (err < 0) +- return err; +- +- /* no other controls are created if MSD mode is on */ +- if (private->msd_switch) +- return 0; +- } +- +- err = scarlett2_update_input_other(mixer); +- if (err < 0) +- return err; +- +- err = scarlett2_update_monitor_other(mixer); +- if (err < 0) +- return err; +- +- /* the rest of the configuration is for devices with a mixer */ +- if (info->config_set == SCARLETT2_CONFIG_SET_NO_MIXER) +- return 0; +- +- err = scarlett2_usb_get_config( +- mixer, SCARLETT2_CONFIG_STANDALONE_SWITCH, +- 1, &private->standalone_switch); +- if (err < 0) +- return err; +- +- err = scarlett2_update_sync(mixer); +- if (err < 0) +- return err; +- +- err = scarlett2_usb_get_volume_status(mixer, &volume_status); +- if (err < 0) +- return err; +- +- if (info->line_out_hw_vol) +- for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) +- private->dim_mute[i] = !!volume_status.dim_mute[i]; +- +- private->master_vol = clamp( +- volume_status.master_vol + SCARLETT2_VOLUME_BIAS, +- 0, SCARLETT2_VOLUME_BIAS); +- +- for (i = 0; i < num_line_out; i++) { +- int volume, mute; +- +- private->vol_sw_hw_switch[i] = +- info->line_out_hw_vol +- && volume_status.sw_hw_switch[i]; +- +- volume = private->vol_sw_hw_switch[i] +- ? volume_status.master_vol +- : volume_status.sw_vol[i]; +- volume = clamp(volume + SCARLETT2_VOLUME_BIAS, +- 0, SCARLETT2_VOLUME_BIAS); +- private->vol[i] = volume; +- +- mute = private->vol_sw_hw_switch[i] +- ? private->dim_mute[SCARLETT2_BUTTON_MUTE] +- : volume_status.mute_switch[i]; +- private->mute_switch[i] = mute; +- } +- +- for (i = 0; i < num_mixer_out; i++) { +- err = scarlett2_usb_get_mix(mixer, i); +- if (err < 0) +- return err; +- } +- +- return scarlett2_usb_get_mux(mixer); +-} +- +-/* Notify on sync change */ +-static void scarlett2_notify_sync( +- struct usb_mixer_interface *mixer) +-{ +- struct scarlett2_data *private = mixer->private_data; +- +- private->sync_updated = 1; +- +- snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, +- &private->sync_ctl->id); +-} +- +-/* Notify on monitor change */ +-static void scarlett2_notify_monitor( +- struct usb_mixer_interface *mixer) +-{ +- struct snd_card *card = mixer->chip->card; +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- int num_line_out = +- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; +- int i; +- +- /* if line_out_hw_vol is 0, there are no controls to update */ +- if (!info->line_out_hw_vol) +- return; +- +- private->vol_updated = 1; +- +- snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, +- &private->master_vol_ctl->id); +- +- for (i = 0; i < num_line_out; i++) +- if (private->vol_sw_hw_switch[line_out_remap(private, i)]) +- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, +- &private->vol_ctls[i]->id); +-} +- +-/* Notify on dim/mute change */ +-static void scarlett2_notify_dim_mute( +- struct usb_mixer_interface *mixer) +-{ +- struct snd_card *card = mixer->chip->card; +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count; +- int num_line_out = +- port_count[SCARLETT2_PORT_TYPE_ANALOGUE][SCARLETT2_PORT_OUT]; +- int i; +- +- private->vol_updated = 1; +- +- if (!info->line_out_hw_vol) +- return; +- +- for (i = 0; i < SCARLETT2_DIM_MUTE_COUNT; i++) +- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, +- &private->dim_mute_ctls[i]->id); +- +- for (i = 0; i < num_line_out; i++) +- if (private->vol_sw_hw_switch[line_out_remap(private, i)]) +- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, +- &private->mute_ctls[i]->id); +-} +- +-/* Notify on "input other" change (level/pad/air) */ +-static void scarlett2_notify_input_other( +- struct usb_mixer_interface *mixer) +-{ +- struct snd_card *card = mixer->chip->card; +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- int i; +- +- private->input_other_updated = 1; +- +- for (i = 0; i < info->level_input_count; i++) +- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, +- &private->level_ctls[i]->id); +- for (i = 0; i < info->pad_input_count; i++) +- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, +- &private->pad_ctls[i]->id); +- for (i = 0; i < info->air_input_count; i++) +- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, +- &private->air_ctls[i]->id); +- for (i = 0; i < info->phantom_count; i++) +- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, +- &private->phantom_ctls[i]->id); +-} +- +-/* Notify on "monitor other" change (direct monitor, speaker +- * switching, talkback) +- */ +-static void scarlett2_notify_monitor_other( +- struct usb_mixer_interface *mixer) +-{ +- struct snd_card *card = mixer->chip->card; +- struct scarlett2_data *private = mixer->private_data; +- const struct scarlett2_device_info *info = private->info; +- +- private->monitor_other_updated = 1; +- +- if (info->direct_monitor) { +- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, +- &private->direct_monitor_ctl->id); +- return; +- } +- +- if (info->has_speaker_switching) +- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, +- &private->speaker_switching_ctl->id); +- +- if (info->has_talkback) +- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, +- &private->talkback_ctl->id); +- +- /* if speaker switching was recently enabled or disabled, +- * invalidate the dim/mute and mux enum controls +- */ +- if (private->speaker_switching_switched) { +- int i; +- +- scarlett2_notify_dim_mute(mixer); +- +- private->speaker_switching_switched = 0; +- private->mux_updated = 1; +- +- for (i = 0; i < private->num_mux_dsts; i++) +- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, +- &private->mux_ctls[i]->id); +- } +-} +- +-/* Interrupt callback */ +-static void scarlett2_notify(struct urb *urb) +-{ +- struct usb_mixer_interface *mixer = urb->context; +- int len = urb->actual_length; +- int ustatus = urb->status; +- u32 data; +- +- if (ustatus != 0 || len != 8) +- goto requeue; +- +- data = le32_to_cpu(*(__le32 *)urb->transfer_buffer); +- if (data & SCARLETT2_USB_NOTIFY_SYNC) +- scarlett2_notify_sync(mixer); +- if (data & SCARLETT2_USB_NOTIFY_MONITOR) +- scarlett2_notify_monitor(mixer); +- if (data & SCARLETT2_USB_NOTIFY_DIM_MUTE) +- scarlett2_notify_dim_mute(mixer); +- if (data & SCARLETT2_USB_NOTIFY_INPUT_OTHER) +- scarlett2_notify_input_other(mixer); +- if (data & SCARLETT2_USB_NOTIFY_MONITOR_OTHER) +- scarlett2_notify_monitor_other(mixer); +- +-requeue: +- if (ustatus != -ENOENT && +- ustatus != -ECONNRESET && +- ustatus != -ESHUTDOWN) { +- urb->dev = mixer->chip->dev; +- usb_submit_urb(urb, GFP_ATOMIC); +- } +-} +- +-static int scarlett2_init_notify(struct usb_mixer_interface *mixer) +-{ +- struct usb_device *dev = mixer->chip->dev; +- struct scarlett2_data *private = mixer->private_data; +- unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress); +- void *transfer_buffer; +- +- if (mixer->urb) { +- usb_audio_err(mixer->chip, +- "%s: mixer urb already in use!\n", __func__); +- return 0; +- } +- +- if (usb_pipe_type_check(dev, pipe)) +- return -EINVAL; +- +- mixer->urb = usb_alloc_urb(0, GFP_KERNEL); +- if (!mixer->urb) +- return -ENOMEM; +- +- transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL); +- if (!transfer_buffer) +- return -ENOMEM; +- +- usb_fill_int_urb(mixer->urb, dev, pipe, +- transfer_buffer, private->wMaxPacketSize, +- scarlett2_notify, mixer, private->bInterval); +- +- return usb_submit_urb(mixer->urb, GFP_KERNEL); +-} +- +-static int snd_scarlett_gen2_controls_create(struct usb_mixer_interface *mixer) +-{ +- const struct scarlett2_device_info **info = scarlett2_devices; +- int err; +- +- /* Find device in scarlett2_devices */ +- while (*info && (*info)->usb_id != mixer->chip->usb_id) +- info++; +- if (!*info) +- return -EINVAL; +- +- /* Initialise private data */ +- err = scarlett2_init_private(mixer, *info); +- if (err < 0) +- return err; +- +- /* Send proprietary USB initialisation sequence */ +- err = scarlett2_usb_init(mixer); +- if (err < 0) +- return err; +- +- /* Read volume levels and controls from the interface */ +- err = scarlett2_read_configs(mixer); +- if (err < 0) +- return err; +- +- /* Create the MSD control */ +- err = scarlett2_add_msd_ctl(mixer); +- if (err < 0) +- return err; +- +- /* If MSD mode is enabled, don't create any other controls */ +- if (((struct scarlett2_data *)mixer->private_data)->msd_switch) +- return 0; +- +- /* Create the analogue output controls */ +- err = scarlett2_add_line_out_ctls(mixer); +- if (err < 0) +- return err; +- +- /* Create the analogue input controls */ +- err = scarlett2_add_line_in_ctls(mixer); +- if (err < 0) +- return err; +- +- /* Create the input, output, and mixer mux input selections */ +- err = scarlett2_add_mux_enums(mixer); +- if (err < 0) +- return err; +- +- /* Create the matrix mixer controls */ +- err = scarlett2_add_mixer_ctls(mixer); +- if (err < 0) +- return err; +- +- /* Create the level meter controls */ +- err = scarlett2_add_meter_ctl(mixer); +- if (err < 0) +- return err; +- +- /* Create the sync control */ +- err = scarlett2_add_sync_ctl(mixer); +- if (err < 0) +- return err; +- +- /* Create the direct monitor control */ +- err = scarlett2_add_direct_monitor_ctl(mixer); +- if (err < 0) +- return err; +- +- /* Create the speaker switching control */ +- err = scarlett2_add_speaker_switch_ctl(mixer); +- if (err < 0) +- return err; +- +- /* Create the talkback controls */ +- err = scarlett2_add_talkback_ctls(mixer); +- if (err < 0) +- return err; +- +- /* Create the standalone control */ +- err = scarlett2_add_standalone_ctl(mixer); +- if (err < 0) +- return err; +- +- /* Set up the interrupt polling */ +- err = scarlett2_init_notify(mixer); +- if (err < 0) +- return err; +- +- return 0; +-} +- +-int snd_scarlett_gen2_init(struct usb_mixer_interface *mixer) +-{ +- struct snd_usb_audio *chip = mixer->chip; +- int err; +- +- /* only use UAC_VERSION_2 */ +- if (!mixer->protocol) +- return 0; +- +- if (!(chip->setup & SCARLETT2_ENABLE)) { +- usb_audio_info(chip, +- "Focusrite Scarlett Gen 2/3 Mixer Driver disabled; " +- "use options snd_usb_audio vid=0x%04x pid=0x%04x " +- "device_setup=1 to enable and report any issues " +- "to g@b4.vu", +- USB_ID_VENDOR(chip->usb_id), +- USB_ID_PRODUCT(chip->usb_id)); +- return 0; +- } +- +- usb_audio_info(chip, +- "Focusrite Scarlett Gen 2/3 Mixer Driver enabled pid=0x%04x", +- USB_ID_PRODUCT(chip->usb_id)); +- +- err = snd_scarlett_gen2_controls_create(mixer); +- if (err < 0) +- usb_audio_err(mixer->chip, +- "Error initialising Scarlett Mixer Driver: %d", +- err); +- +- return err; +-} +diff --git a/sound/usb/mixer_scarlett_gen2.h b/sound/usb/mixer_scarlett_gen2.h +deleted file mode 100644 +index 668c6b0cb50a6..0000000000000 +--- a/sound/usb/mixer_scarlett_gen2.h ++++ /dev/null +@@ -1,7 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0 */ +-#ifndef __USB_MIXER_SCARLETT_GEN2_H +-#define __USB_MIXER_SCARLETT_GEN2_H +- +-int snd_scarlett_gen2_init(struct usb_mixer_interface *mixer); +- +-#endif /* __USB_MIXER_SCARLETT_GEN2_H */ +diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/util/bpf_skel/lock_contention.bpf.c +index 8d3cfbb3cc65b..473106c72c695 100644 +--- a/tools/perf/util/bpf_skel/lock_contention.bpf.c ++++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c +@@ -238,6 +238,7 @@ static inline __u32 check_lock_type(__u64 lock, __u32 flags) + struct task_struct *curr; + struct mm_struct___old *mm_old; + struct mm_struct___new *mm_new; ++ struct sighand_struct *sighand; + + switch (flags) { + case LCB_F_READ: /* rwsem */ +@@ -259,7 +260,9 @@ static inline __u32 check_lock_type(__u64 lock, __u32 flags) + break; + case LCB_F_SPIN: /* spinlock */ + curr = bpf_get_current_task_btf(); +- if (&curr->sighand->siglock == (void *)lock) ++ sighand = curr->sighand; ++ ++ if (sighand && &sighand->siglock == (void *)lock) + return LCD_F_SIGHAND_LOCK; + break; + default: +diff --git a/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc b/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc +index b1ede62498667..b7c8f29c09a97 100644 +--- a/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc ++++ b/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc +@@ -18,7 +18,7 @@ echo 'sched:*' > set_event + + yield + +-count=`cat trace | grep -v ^# | awk '{ print $5 }' | sort -u | wc -l` ++count=`head -n 100 trace | grep -v ^# | awk '{ print $5 }' | sort -u | wc -l` + if [ $count -lt 3 ]; then + fail "at least fork, exec and exit events should be recorded" + fi +@@ -29,7 +29,7 @@ echo 1 > events/sched/enable + + yield + +-count=`cat trace | grep -v ^# | awk '{ print $5 }' | sort -u | wc -l` ++count=`head -n 100 trace | grep -v ^# | awk '{ print $5 }' | sort -u | wc -l` + if [ $count -lt 3 ]; then + fail "at least fork, exec and exit events should be recorded" + fi +@@ -40,7 +40,7 @@ echo 0 > events/sched/enable + + yield + +-count=`cat trace | grep -v ^# | awk '{ print $5 }' | sort -u | wc -l` ++count=`head -n 100 trace | grep -v ^# | awk '{ print $5 }' | sort -u | wc -l` + if [ $count -ne 0 ]; then + fail "any of scheduler events should not be recorded" + fi +diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h +index 529d29a359002..e8eecbc83a60e 100644 +--- a/tools/testing/selftests/kselftest.h ++++ b/tools/testing/selftests/kselftest.h +@@ -49,6 +49,7 @@ + #include + #include + #include ++#include + #endif + + #ifndef ARRAY_SIZE +@@ -327,4 +328,21 @@ static inline int ksft_exit_skip(const char *msg, ...) + exit(KSFT_SKIP); + } + ++static inline int ksft_min_kernel_version(unsigned int min_major, ++ unsigned int min_minor) ++{ ++#ifdef NOLIBC ++ ksft_print_msg("NOLIBC: Can't check kernel version: Function not implemented\n"); ++ return 0; ++#else ++ unsigned int major, minor; ++ struct utsname info; ++ ++ if (uname(&info) || sscanf(info.release, "%u.%u.", &major, &minor) != 2) ++ ksft_exit_fail_msg("Can't parse kernel version\n"); ++ ++ return major > min_major || (major == min_major && minor >= min_minor); ++#endif ++} ++ + #endif /* __KSELFTEST_H */ +diff --git a/tools/testing/selftests/net/udpgso.c b/tools/testing/selftests/net/udpgso.c +index 7badaf215de28..b02080d09fbc0 100644 +--- a/tools/testing/selftests/net/udpgso.c ++++ b/tools/testing/selftests/net/udpgso.c +@@ -34,7 +34,7 @@ + #endif + + #ifndef UDP_MAX_SEGMENTS +-#define UDP_MAX_SEGMENTS (1 << 6UL) ++#define UDP_MAX_SEGMENTS (1 << 7UL) + #endif + + #define CONST_MTU_TEST 1500 +diff --git a/tools/testing/selftests/timers/posix_timers.c b/tools/testing/selftests/timers/posix_timers.c +index 9a42403eaff70..c001dd79179d5 100644 +--- a/tools/testing/selftests/timers/posix_timers.c ++++ b/tools/testing/selftests/timers/posix_timers.c +@@ -76,22 +76,21 @@ static int check_diff(struct timeval start, struct timeval end) + + static int check_itimer(int which) + { ++ const char *name; + int err; + struct timeval start, end; + struct itimerval val = { + .it_value.tv_sec = DELAY, + }; + +- printf("Check itimer "); +- + if (which == ITIMER_VIRTUAL) +- printf("virtual... "); ++ name = "ITIMER_VIRTUAL"; + else if (which == ITIMER_PROF) +- printf("prof... "); ++ name = "ITIMER_PROF"; + else if (which == ITIMER_REAL) +- printf("real... "); +- +- fflush(stdout); ++ name = "ITIMER_REAL"; ++ else ++ return -1; + + done = 0; + +@@ -104,13 +103,13 @@ static int check_itimer(int which) + + err = gettimeofday(&start, NULL); + if (err < 0) { +- perror("Can't call gettimeofday()\n"); ++ ksft_perror("Can't call gettimeofday()"); + return -1; + } + + err = setitimer(which, &val, NULL); + if (err < 0) { +- perror("Can't set timer\n"); ++ ksft_perror("Can't set timer"); + return -1; + } + +@@ -123,20 +122,18 @@ static int check_itimer(int which) + + err = gettimeofday(&end, NULL); + if (err < 0) { +- perror("Can't call gettimeofday()\n"); ++ ksft_perror("Can't call gettimeofday()"); + return -1; + } + +- if (!check_diff(start, end)) +- printf("[OK]\n"); +- else +- printf("[FAIL]\n"); ++ ksft_test_result(check_diff(start, end) == 0, "%s\n", name); + + return 0; + } + + static int check_timer_create(int which) + { ++ const char *type; + int err; + timer_t id; + struct timeval start, end; +@@ -144,31 +141,32 @@ static int check_timer_create(int which) + .it_value.tv_sec = DELAY, + }; + +- printf("Check timer_create() "); + if (which == CLOCK_THREAD_CPUTIME_ID) { +- printf("per thread... "); ++ type = "thread"; + } else if (which == CLOCK_PROCESS_CPUTIME_ID) { +- printf("per process... "); ++ type = "process"; ++ } else { ++ ksft_print_msg("Unknown timer_create() type %d\n", which); ++ return -1; + } +- fflush(stdout); + + done = 0; + err = timer_create(which, NULL, &id); + if (err < 0) { +- perror("Can't create timer\n"); ++ ksft_perror("Can't create timer"); + return -1; + } + signal(SIGALRM, sig_handler); + + err = gettimeofday(&start, NULL); + if (err < 0) { +- perror("Can't call gettimeofday()\n"); ++ ksft_perror("Can't call gettimeofday()"); + return -1; + } + + err = timer_settime(id, 0, &val, NULL); + if (err < 0) { +- perror("Can't set timer\n"); ++ ksft_perror("Can't set timer"); + return -1; + } + +@@ -176,96 +174,90 @@ static int check_timer_create(int which) + + err = gettimeofday(&end, NULL); + if (err < 0) { +- perror("Can't call gettimeofday()\n"); ++ ksft_perror("Can't call gettimeofday()"); + return -1; + } + +- if (!check_diff(start, end)) +- printf("[OK]\n"); +- else +- printf("[FAIL]\n"); ++ ksft_test_result(check_diff(start, end) == 0, ++ "timer_create() per %s\n", type); + + return 0; + } + +-int remain; +-__thread int got_signal; ++static pthread_t ctd_thread; ++static volatile int ctd_count, ctd_failed; + +-static void *distribution_thread(void *arg) ++static void ctd_sighandler(int sig) + { +- while (__atomic_load_n(&remain, __ATOMIC_RELAXED)); +- return NULL; ++ if (pthread_self() != ctd_thread) ++ ctd_failed = 1; ++ ctd_count--; + } + +-static void distribution_handler(int nr) ++static void *ctd_thread_func(void *arg) + { +- if (!__atomic_exchange_n(&got_signal, 1, __ATOMIC_RELAXED)) +- __atomic_fetch_sub(&remain, 1, __ATOMIC_RELAXED); +-} +- +-/* +- * Test that all running threads _eventually_ receive CLOCK_PROCESS_CPUTIME_ID +- * timer signals. This primarily tests that the kernel does not favour any one. +- */ +-static int check_timer_distribution(void) +-{ +- int err, i; +- timer_t id; +- const int nthreads = 10; +- pthread_t threads[nthreads]; + struct itimerspec val = { + .it_value.tv_sec = 0, + .it_value.tv_nsec = 1000 * 1000, + .it_interval.tv_sec = 0, + .it_interval.tv_nsec = 1000 * 1000, + }; ++ timer_t id; + +- printf("Check timer_create() per process signal distribution... "); +- fflush(stdout); ++ /* 1/10 seconds to ensure the leader sleeps */ ++ usleep(10000); + +- remain = nthreads + 1; /* worker threads + this thread */ +- signal(SIGALRM, distribution_handler); +- err = timer_create(CLOCK_PROCESS_CPUTIME_ID, NULL, &id); +- if (err < 0) { +- perror("Can't create timer\n"); +- return -1; +- } +- err = timer_settime(id, 0, &val, NULL); +- if (err < 0) { +- perror("Can't set timer\n"); +- return -1; +- } ++ ctd_count = 100; ++ if (timer_create(CLOCK_PROCESS_CPUTIME_ID, NULL, &id)) ++ return "Can't create timer\n"; ++ if (timer_settime(id, 0, &val, NULL)) ++ return "Can't set timer\n"; + +- for (i = 0; i < nthreads; i++) { +- if (pthread_create(&threads[i], NULL, distribution_thread, NULL)) { +- perror("Can't create thread\n"); +- return -1; +- } +- } ++ while (ctd_count > 0 && !ctd_failed) ++ ; + +- /* Wait for all threads to receive the signal. */ +- while (__atomic_load_n(&remain, __ATOMIC_RELAXED)); ++ if (timer_delete(id)) ++ return "Can't delete timer\n"; + +- for (i = 0; i < nthreads; i++) { +- if (pthread_join(threads[i], NULL)) { +- perror("Can't join thread\n"); +- return -1; +- } +- } ++ return NULL; ++} + +- if (timer_delete(id)) { +- perror("Can't delete timer\n"); +- return -1; +- } ++/* ++ * Test that only the running thread receives the timer signal. ++ */ ++static int check_timer_distribution(void) ++{ ++ const char *errmsg; ++ ++ signal(SIGALRM, ctd_sighandler); + +- printf("[OK]\n"); ++ errmsg = "Can't create thread\n"; ++ if (pthread_create(&ctd_thread, NULL, ctd_thread_func, NULL)) ++ goto err; ++ ++ errmsg = "Can't join thread\n"; ++ if (pthread_join(ctd_thread, (void **)&errmsg) || errmsg) ++ goto err; ++ ++ if (!ctd_failed) ++ ksft_test_result_pass("check signal distribution\n"); ++ else if (ksft_min_kernel_version(6, 3)) ++ ksft_test_result_fail("check signal distribution\n"); ++ else ++ ksft_test_result_skip("check signal distribution (old kernel)\n"); + return 0; ++err: ++ ksft_print_msg("%s", errmsg); ++ return -1; + } + + int main(int argc, char **argv) + { +- printf("Testing posix timers. False negative may happen on CPU execution \n"); +- printf("based timers if other threads run on the CPU...\n"); ++ ksft_print_header(); ++ ksft_set_plan(6); ++ ++ ksft_print_msg("Testing posix timers. False negative may happen on CPU execution \n"); ++ ksft_print_msg("based timers if other threads run on the CPU...\n"); + + if (check_itimer(ITIMER_VIRTUAL) < 0) + return ksft_exit_fail(); +@@ -294,5 +286,5 @@ int main(int argc, char **argv) + if (check_timer_distribution() < 0) + return ksft_exit_fail(); + +- return ksft_exit_pass(); ++ ksft_finished(); + }